Skip to content

Commit f119dc2

Browse files
authored
refactor(pubsub): remove EOL Python 3.7/3.8/3.9 false positives and compatibility checks (#17485)
Since `google-cloud-pubsub` requires Python >= 3.10, this PR cleans up obsolete compatibility checks, warnings, and pytest skip conditions targeting Python 3.7, 3.8, and 3.9. Why this is needed: * Simplifies the codebase by removing dead compatibility blocks and checks that are always true under Python >= 3.10. * Cleans up EOL warnings and deprecated import workarounds that are no longer relevant to current runtimes. * Cleans up unit test decorators to run all OpenTelemetry tests unconditionally. * Supports clean up efforts to eliminate false positives as found by the version_scanner. 🦕
1 parent 0d2d40d commit f119dc2

15 files changed

Lines changed: 69 additions & 169 deletions

File tree

packages/google-cloud-pubsub/CONTRIBUTING.rst

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ In order to add a feature:
2222
documentation.
2323

2424
- The feature must work fully on the following CPython versions:
25-
3.9, 3.10, 3.11, 3.12, 3.13 and 3.14 on both UNIX and Windows.
25+
3.10, 3.11, 3.12, 3.13 and 3.14 on both UNIX and Windows.
2626

2727
- The feature must not add unnecessary dependencies (where
2828
"unnecessary" is of course subjective, but new dependencies should
@@ -221,14 +221,12 @@ Supported Python Versions
221221

222222
We support:
223223

224-
- `Python 3.9`_
225224
- `Python 3.10`_
226225
- `Python 3.11`_
227226
- `Python 3.12`_
228227
- `Python 3.13`_
229228
- `Python 3.14`_
230229

231-
.. _Python 3.9: https://docs.python.org/3.9/
232230
.. _Python 3.10: https://docs.python.org/3.10/
233231
.. _Python 3.11: https://docs.python.org/3.11/
234232
.. _Python 3.12: https://docs.python.org/3.12/
@@ -241,18 +239,6 @@ Supported versions can be found in our ``noxfile.py`` `config`_.
241239
.. _config: https://github.com/googleapis/python-pubsub/blob/main/noxfile.py
242240

243241

244-
We also explicitly decided to support Python 3 beginning with version 3.7.
245-
Reasons for this include:
246-
247-
- Encouraging use of newest versions of Python 3
248-
- Taking the lead of `prominent`_ open-source `projects`_
249-
- `Unicode literal support`_ which allows for a cleaner codebase that
250-
works in both Python 2 and Python 3
251-
252-
.. _prominent: https://docs.djangoproject.com/en/1.9/faq/install/#what-python-version-can-i-use-with-django
253-
.. _projects: http://flask.pocoo.org/docs/0.10/python3/
254-
.. _Unicode literal support: https://www.python.org/dev/peps/pep-0414/
255-
256242
**********
257243
Versioning
258244
**********

packages/google-cloud-pubsub/README.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,6 @@ Python >= 3.10, including 3.14
6767
.. _active: https://devguide.python.org/devcycle/#in-development-main-branch
6868
.. _maintenance: https://devguide.python.org/devcycle/#maintenance-branches
6969

70-
Unsupported Python Versions
71-
^^^^^^^^^^^^^^^^^^^^^^^^^^^
72-
Python <= 3.9
73-
7470

7571
If you are using an `end-of-life`_
7672
version of Python, we recommend that you update as soon as possible to an actively supported version.

packages/google-cloud-pubsub/google/cloud/pubsub_v1/publisher/client.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import copy
1818
import logging
1919
import os
20-
import sys
2120
import threading
2221
import time
2322
import typing
@@ -165,18 +164,6 @@ def __init__(
165164
self._open_telemetry_enabled = (
166165
self.publisher_options.enable_open_telemetry_tracing
167166
)
168-
# OpenTelemetry features used by the library are not supported in Python versions <= 3.7.
169-
# Refer https://github.com/open-telemetry/opentelemetry-python/issues/3993#issuecomment-2211976389
170-
if (
171-
self.publisher_options.enable_open_telemetry_tracing
172-
and sys.version_info.major == 3
173-
and sys.version_info.minor < 8
174-
):
175-
warnings.warn(
176-
message="Open Telemetry for Python version 3.7 or lower is not supported. Disabling Open Telemetry tracing.",
177-
category=RuntimeWarning,
178-
)
179-
self._open_telemetry_enabled = False
180167

181168
@classmethod
182169
def from_service_account_file( # type: ignore[override]

packages/google-cloud-pubsub/google/cloud/pubsub_v1/subscriber/_protocol/leaser.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,7 @@
2727
)
2828
from google.cloud.pubsub_v1.subscriber._protocol.dispatcher import _MAX_BATCH_LATENCY
2929

30-
try:
31-
from collections.abc import KeysView
32-
33-
KeysView[None] # KeysView is only subscriptable in Python 3.9+
34-
except TypeError:
35-
# Deprecated since Python 3.9, thus only use as a fallback in older Python versions
36-
from typing import KeysView
30+
from collections.abc import KeysView
3731

3832
from google.cloud.pubsub_v1.subscriber._protocol import requests
3933

packages/google-cloud-pubsub/google/cloud/pubsub_v1/subscriber/client.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from __future__ import absolute_import
1616

1717
import os
18-
import sys
1918
import typing
2019
import warnings
2120
from typing import Any, Callable, Optional, Sequence, Union, cast
@@ -102,18 +101,6 @@ def __init__(
102101
self._open_telemetry_enabled = (
103102
self.subscriber_options.enable_open_telemetry_tracing
104103
)
105-
# OpenTelemetry features used by the library are not supported in Python versions <= 3.7.
106-
# Refer https://github.com/open-telemetry/opentelemetry-python/issues/3993#issuecomment-2211976389
107-
if (
108-
self.subscriber_options.enable_open_telemetry_tracing
109-
and sys.version_info.major == 3
110-
and sys.version_info.minor < 8
111-
):
112-
warnings.warn(
113-
message="Open Telemetry for Python version 3.7 or lower is not supported. Disabling Open Telemetry tracing.",
114-
category=RuntimeWarning,
115-
)
116-
self._open_telemetry_enabled = False
117104

118105
@property
119106
def open_telemetry_enabled(self) -> bool:

packages/google-cloud-pubsub/google/cloud/pubsub_v1/subscriber/scheduler.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,6 @@ def shutdown(
153153
"""
154154
dropped_messages = []
155155

156-
# Drop all pending item from the executor. Without this, the executor will also
157-
# try to process any pending work items before termination, which is undesirable.
158-
#
159-
# TODO: Replace the logic below by passing `cancel_futures=True` to shutdown()
160-
# once we only need to support Python 3.9+.
161156
try:
162157
while True:
163158
work_item = self._executor._work_queue.get(block=False)

packages/google-cloud-pubsub/pytest.ini

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ filterwarnings =
1616
# Remove once the minimum supported version of googleapis-common-protos is 1.62.0
1717
ignore:.*pkg_resources.declare_namespace:DeprecationWarning
1818
ignore:.*pkg_resources is deprecated as an API:DeprecationWarning
19-
# Remove once https://github.com/googleapis/gapic-generator-python/issues/2303 is fixed
20-
ignore:The python-bigquery library will stop supporting Python 3.7:PendingDeprecationWarning
2119
# Remove once we move off credential files https://github.com/googleapis/google-auth-library-python/pull/1812
2220
# Note that these are used in tests only
2321
ignore:Your config file at [/home/kbuilder/.docker/config.json] contains these credential helper entries:DeprecationWarning

packages/google-cloud-pubsub/tests/system.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,11 +447,19 @@ def test_subscriber_not_leaking_open_sockets(
447447
subscriber = pubsub_v1.SubscriberClient(transport="grpc")
448448
subscriber_2 = pubsub_v1.SubscriberClient(transport="grpc")
449449

450+
# Construct a secondary publisher client to clean up the topic,
451+
# so we can safely close the main publisher client inside the test.
452+
if "Rest" in type(publisher._transport).__name__:
453+
publisher_2 = pubsub_v1.PublisherClient(transport="rest")
454+
else:
455+
publisher_2 = pubsub_v1.PublisherClient(transport="grpc")
456+
450457
cleanup.append(
451458
(subscriber_2.delete_subscription, (), {"subscription": subscription_path})
452459
)
453460
cleanup.append((subscriber_2.close, (), {}))
454-
cleanup.append((publisher.delete_topic, (), {"topic": topic_path}))
461+
cleanup.append((publisher_2.delete_topic, (), {"topic": topic_path}))
462+
cleanup.append((publisher_2._transport.close, (), {}))
455463

456464
# Create topic before starting to track connection count (any sockets opened
457465
# by the publisher client are not counted by this test).
@@ -477,7 +485,16 @@ def test_subscriber_not_leaking_open_sockets(
477485
response = subscriber.pull(subscription=subscription_path, max_messages=3)
478486
assert len(response.received_messages) == 3
479487

480-
conn_count_end = len(current_process.net_connections())
488+
# Close the publisher client's transport used in the test to ensure all its socket connections
489+
# (including any opened asynchronously on the background publisher threads) are closed.
490+
publisher._transport.close()
491+
492+
# Wait a bit for the asynchronous channel teardown to complete and the socket to be closed.
493+
for _ in range(30):
494+
conn_count_end = len(current_process.net_connections())
495+
if conn_count_end <= conn_count_start:
496+
break
497+
time.sleep(0.1)
481498

482499
# To avoid flakiness, use <= in the assertion, since on rare occasions additional
483500
# sockets are closed, causing the == assertion to fail.

packages/google-cloud-pubsub/tests/unit/pubsub_v1/conftest.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,27 @@ def set_trace_provider():
3939

4040
@pytest.fixture(scope="function")
4141
def span_exporter():
42+
"""Provides an InMemorySpanExporter for testing OpenTelemetry traces.
43+
44+
Registers a SimpleSpanProcessor with the global TracerProvider at start,
45+
and removes it during teardown to prevent trace/span processor accumulation
46+
and test pollution across tests.
47+
"""
4248
exporter = InMemorySpanExporter()
4349
processor = SimpleSpanProcessor(exporter)
4450
provider = trace.get_tracer_provider()
4551
provider.add_span_processor(processor)
46-
yield exporter
52+
try:
53+
yield exporter
54+
finally:
55+
if hasattr(provider, "_active_span_processor") and hasattr(
56+
provider._active_span_processor, "_span_processors"
57+
):
58+
processors = provider._active_span_processor._span_processors
59+
if isinstance(processors, tuple):
60+
provider._active_span_processor._span_processors = tuple(
61+
p for p in processors if p is not processor
62+
)
4763

4864

4965
@pytest.fixture()

packages/google-cloud-pubsub/tests/unit/pubsub_v1/publisher/batch/test_thread.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
# limitations under the License.
1414

1515
import datetime
16-
import sys
1716
import threading
1817
import time
1918
from unittest import mock
@@ -723,9 +722,6 @@ def test_batch_done_callback_called_on_publish_response_invalid():
723722

724723

725724
# Refer https://opentelemetry.io/docs/languages/python/#version-support
726-
@pytest.mark.skipif(
727-
sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher"
728-
)
729725
def test_open_telemetry_commit_publish_rpc_span_none(span_exporter):
730726
"""
731727
Test scenario where OpenTelemetry is enabled, publish RPC
@@ -771,9 +767,6 @@ def test_open_telemetry_commit_publish_rpc_span_none(span_exporter):
771767

772768

773769
# Refer https://opentelemetry.io/docs/languages/python/#version-support
774-
@pytest.mark.skipif(
775-
sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher"
776-
)
777770
def test_open_telemetry_commit_publish_rpc_exception(span_exporter):
778771
TOPIC = "projects/projectID/topics/topicID"
779772
batch = create_batch(topic=TOPIC, enable_open_telemetry=True)
@@ -819,9 +812,6 @@ def test_open_telemetry_commit_publish_rpc_exception(span_exporter):
819812

820813

821814
# Refer https://opentelemetry.io/docs/languages/python/#version-support
822-
@pytest.mark.skipif(
823-
sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher"
824-
)
825815
def test_opentelemetry_commit_sampling(span_exporter):
826816
TOPIC = "projects/projectID/topics/topic"
827817
batch = create_batch(
@@ -886,9 +876,6 @@ def test_opentelemetry_commit_sampling(span_exporter):
886876
assert span.events[1].name == "publish end"
887877

888878

889-
@pytest.mark.skipif(
890-
sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher"
891-
)
892879
def test_opentelemetry_commit(span_exporter):
893880
TOPIC = "projects/projectID/topics/topic"
894881
batch = create_batch(

0 commit comments

Comments
 (0)