Skip to content

Commit cddabc0

Browse files
authored
feat(spanner): drop python runtime 3.9 (#17070)
## Description This Pull Request drops support for Python 3.9 in the `google-cloud-spanner` package. This is part of our ongoing effort to modernize the Cloud SDK libraries and remove support for End-of-Life (EOL) Python versions. ## Changes - Removed Python 3.9 from testing configurations and constraints files. - Resolved dependency conflicts arising from dropping support. - Cleaned up obsolete code related to Python 3.9 support.
1 parent 0d39c95 commit cddabc0

13 files changed

Lines changed: 66 additions & 284 deletions

File tree

packages/google-cloud-spanner/README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ Supported Python Versions
6262
Our client libraries are compatible with all current `active`_ and `maintenance`_ versions of
6363
Python.
6464

65-
Python >= 3.9, including 3.14
65+
Python >= 3.10, including 3.14
6666

6767
.. _active: https://devguide.python.org/devcycle/#in-development-main-branch
6868
.. _maintenance: https://devguide.python.org/devcycle/#maintenance-branches
6969

7070
Unsupported Python Versions
7171
^^^^^^^^^^^^^^^^^^^^^^^^^^^
72-
Python <= 3.8
72+
Python <= 3.9
7373

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

packages/google-cloud-spanner/docs/README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ Supported Python Versions
6262
Our client libraries are compatible with all current `active`_ and `maintenance`_ versions of
6363
Python.
6464

65-
Python >= 3.9, including 3.14
65+
Python >= 3.10, including 3.14
6666

6767
.. _active: https://devguide.python.org/devcycle/#in-development-main-branch
6868
.. _maintenance: https://devguide.python.org/devcycle/#maintenance-branches
6969

7070
Unsupported Python Versions
7171
^^^^^^^^^^^^^^^^^^^^^^^^^^^
72-
Python <= 3.8
72+
Python <= 3.9
7373

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

packages/google-cloud-spanner/google/cloud/spanner_admin_database_v1/__init__.py

Lines changed: 10 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,6 @@
2121

2222
__version__ = package_version.__version__
2323

24-
if sys.version_info >= (3, 8): # pragma: NO COVER
25-
from importlib import metadata
26-
else: # pragma: NO COVER
27-
# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
28-
# this code path once we drop support for Python 3.7
29-
import importlib_metadata as metadata
30-
3124

3225
from .services.database_admin import DatabaseAdminAsyncClient, DatabaseAdminClient
3326
from .types.backup import (
@@ -106,92 +99,18 @@
10699
api_core.check_python_version("google.cloud.spanner_admin_database_v1") # type: ignore
107100
api_core.check_dependency_versions("google.cloud.spanner_admin_database_v1") # type: ignore
108101
else: # pragma: NO COVER
109-
# An older version of api_core is installed which does not define the
110-
# functions above. We do equivalent checks manually.
111-
try:
112-
import sys
113-
import warnings
114-
115-
_py_version_str = sys.version.split()[0]
116-
_package_label = "google.cloud.spanner_admin_database_v1"
117-
if sys.version_info < (3, 9):
118-
warnings.warn(
119-
"You are using a non-supported Python version "
120-
+ f"({_py_version_str}). Google will not post any further "
121-
+ f"updates to {_package_label} supporting this Python version. "
122-
+ "Please upgrade to the latest Python version, or at "
123-
+ f"least to Python 3.9, and then update {_package_label}.",
124-
FutureWarning,
125-
)
126-
if sys.version_info[:2] == (3, 9):
127-
warnings.warn(
128-
f"You are using a Python version ({_py_version_str}) "
129-
+ f"which Google will stop supporting in {_package_label} in "
130-
+ "January 2026. Please "
131-
+ "upgrade to the latest Python version, or at "
132-
+ "least to Python 3.10, before then, and "
133-
+ f"then update {_package_label}.",
134-
FutureWarning,
135-
)
136-
137-
def parse_version_to_tuple(version_string: str):
138-
"""Safely converts a semantic version string to a comparable tuple of integers.
139-
Example: "4.25.8" -> (4, 25, 8)
140-
Ignores non-numeric parts and handles common version formats.
141-
Args:
142-
version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
143-
Returns:
144-
Tuple of integers for the parsed version string.
145-
"""
146-
parts = []
147-
for part in version_string.split("."):
148-
try:
149-
parts.append(int(part))
150-
except ValueError:
151-
# If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
152-
# This is a simplification compared to 'packaging.parse_version', but sufficient
153-
# for comparing strictly numeric semantic versions.
154-
break
155-
return tuple(parts)
156-
157-
def _get_version(dependency_name):
158-
try:
159-
version_string: str = metadata.version(dependency_name)
160-
parsed_version = parse_version_to_tuple(version_string)
161-
return (parsed_version, version_string)
162-
except Exception:
163-
# Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
164-
# or errors during parse_version_to_tuple
165-
return (None, "--")
102+
import warnings
166103

167-
_dependency_package = "google.protobuf"
168-
_next_supported_version = "4.25.8"
169-
_next_supported_version_tuple = (4, 25, 8)
170-
_recommendation = " (we recommend 6.x)"
171-
(_version_used, _version_used_string) = _get_version(_dependency_package)
172-
if _version_used and _version_used < _next_supported_version_tuple:
173-
warnings.warn(
174-
f"Package {_package_label} depends on "
175-
+ f"{_dependency_package}, currently installed at version "
176-
+ f"{_version_used_string}. Future updates to "
177-
+ f"{_package_label} will require {_dependency_package} at "
178-
+ f"version {_next_supported_version} or higher{_recommendation}."
179-
+ " Please ensure "
180-
+ "that either (a) your Python environment doesn't pin the "
181-
+ f"version of {_dependency_package}, so that updates to "
182-
+ f"{_package_label} can require the higher version, or "
183-
+ "(b) you manually update your Python environment to use at "
184-
+ f"least version {_next_supported_version} of "
185-
+ f"{_dependency_package}.",
186-
FutureWarning,
187-
)
188-
except Exception:
104+
_py_version_str = sys.version.split()[0]
105+
# version-scanner: ignore-next-line
106+
if sys.version_info < (3, 10):
189107
warnings.warn(
190-
"Could not determine the version of Python "
191-
+ "currently being used. To continue receiving "
192-
+ "updates for {_package_label}, ensure you are "
193-
+ "using a supported version of Python; see "
194-
+ "https://devguide.python.org/versions/"
108+
"You are using a non-supported Python version "
109+
+ f"({_py_version_str}). Google will not post any further "
110+
+ "updates to google.cloud.spanner_admin_database_v1 supporting this Python version. "
111+
+ "Please upgrade to the latest Python version, or at "
112+
+ "least to Python 3.10, and then update google.cloud.spanner_admin_database_v1.",
113+
FutureWarning,
195114
)
196115

197116
__all__ = (

packages/google-cloud-spanner/google/cloud/spanner_admin_instance_v1/__init__.py

Lines changed: 10 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,6 @@
2121

2222
__version__ = package_version.__version__
2323

24-
if sys.version_info >= (3, 8): # pragma: NO COVER
25-
from importlib import metadata
26-
else: # pragma: NO COVER
27-
# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
28-
# this code path once we drop support for Python 3.7
29-
import importlib_metadata as metadata
30-
3124

3225
from .services.instance_admin import InstanceAdminAsyncClient, InstanceAdminClient
3326
from .types.common import FulfillmentPeriod, OperationProgress, ReplicaSelection
@@ -78,92 +71,18 @@
7871
api_core.check_python_version("google.cloud.spanner_admin_instance_v1") # type: ignore
7972
api_core.check_dependency_versions("google.cloud.spanner_admin_instance_v1") # type: ignore
8073
else: # pragma: NO COVER
81-
# An older version of api_core is installed which does not define the
82-
# functions above. We do equivalent checks manually.
83-
try:
84-
import sys
85-
import warnings
86-
87-
_py_version_str = sys.version.split()[0]
88-
_package_label = "google.cloud.spanner_admin_instance_v1"
89-
if sys.version_info < (3, 9):
90-
warnings.warn(
91-
"You are using a non-supported Python version "
92-
+ f"({_py_version_str}). Google will not post any further "
93-
+ f"updates to {_package_label} supporting this Python version. "
94-
+ "Please upgrade to the latest Python version, or at "
95-
+ f"least to Python 3.9, and then update {_package_label}.",
96-
FutureWarning,
97-
)
98-
if sys.version_info[:2] == (3, 9):
99-
warnings.warn(
100-
f"You are using a Python version ({_py_version_str}) "
101-
+ f"which Google will stop supporting in {_package_label} in "
102-
+ "January 2026. Please "
103-
+ "upgrade to the latest Python version, or at "
104-
+ "least to Python 3.10, before then, and "
105-
+ f"then update {_package_label}.",
106-
FutureWarning,
107-
)
108-
109-
def parse_version_to_tuple(version_string: str):
110-
"""Safely converts a semantic version string to a comparable tuple of integers.
111-
Example: "4.25.8" -> (4, 25, 8)
112-
Ignores non-numeric parts and handles common version formats.
113-
Args:
114-
version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
115-
Returns:
116-
Tuple of integers for the parsed version string.
117-
"""
118-
parts = []
119-
for part in version_string.split("."):
120-
try:
121-
parts.append(int(part))
122-
except ValueError:
123-
# If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
124-
# This is a simplification compared to 'packaging.parse_version', but sufficient
125-
# for comparing strictly numeric semantic versions.
126-
break
127-
return tuple(parts)
128-
129-
def _get_version(dependency_name):
130-
try:
131-
version_string: str = metadata.version(dependency_name)
132-
parsed_version = parse_version_to_tuple(version_string)
133-
return (parsed_version, version_string)
134-
except Exception:
135-
# Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
136-
# or errors during parse_version_to_tuple
137-
return (None, "--")
74+
import warnings
13875

139-
_dependency_package = "google.protobuf"
140-
_next_supported_version = "4.25.8"
141-
_next_supported_version_tuple = (4, 25, 8)
142-
_recommendation = " (we recommend 6.x)"
143-
(_version_used, _version_used_string) = _get_version(_dependency_package)
144-
if _version_used and _version_used < _next_supported_version_tuple:
145-
warnings.warn(
146-
f"Package {_package_label} depends on "
147-
+ f"{_dependency_package}, currently installed at version "
148-
+ f"{_version_used_string}. Future updates to "
149-
+ f"{_package_label} will require {_dependency_package} at "
150-
+ f"version {_next_supported_version} or higher{_recommendation}."
151-
+ " Please ensure "
152-
+ "that either (a) your Python environment doesn't pin the "
153-
+ f"version of {_dependency_package}, so that updates to "
154-
+ f"{_package_label} can require the higher version, or "
155-
+ "(b) you manually update your Python environment to use at "
156-
+ f"least version {_next_supported_version} of "
157-
+ f"{_dependency_package}.",
158-
FutureWarning,
159-
)
160-
except Exception:
76+
_py_version_str = sys.version.split()[0]
77+
# version-scanner: ignore-next-line
78+
if sys.version_info < (3, 10):
16179
warnings.warn(
162-
"Could not determine the version of Python "
163-
+ "currently being used. To continue receiving "
164-
+ "updates for {_package_label}, ensure you are "
165-
+ "using a supported version of Python; see "
166-
+ "https://devguide.python.org/versions/"
80+
"You are using a non-supported Python version "
81+
+ f"({_py_version_str}). Google will not post any further "
82+
+ "updates to google.cloud.spanner_admin_instance_v1 supporting this Python version. "
83+
+ "Please upgrade to the latest Python version, or at "
84+
+ "least to Python 3.10, and then update google.cloud.spanner_admin_instance_v1.",
85+
FutureWarning,
16786
)
16887

16988
__all__ = (

packages/google-cloud-spanner/google/cloud/spanner_v1/_opentelemetry_tracing.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@
1919
from datetime import datetime
2020

2121
from opentelemetry import trace
22-
from opentelemetry.semconv.attributes.otel_attributes import (
23-
OTEL_SCOPE_NAME,
24-
OTEL_SCOPE_VERSION,
25-
)
22+
2623
from opentelemetry.trace.status import Status, StatusCode
2724

2825
from google.cloud.spanner_v1._helpers import (
@@ -99,9 +96,9 @@ def trace_call(
9996
"db.url": SpannerClient.DEFAULT_ENDPOINT,
10097
"db.instance": db_name,
10198
"net.host.name": SpannerClient.DEFAULT_ENDPOINT,
102-
OTEL_SCOPE_NAME: TRACER_NAME,
99+
"otel.scope.name": TRACER_NAME,
103100
"cloud.region": cloud_region,
104-
OTEL_SCOPE_VERSION: TRACER_VERSION,
101+
"otel.scope.version": TRACER_VERSION,
105102
# Standard GCP attributes for OTel, attributes are used for internal purpose and are subjected to change
106103
"gcp.client.service": "spanner",
107104
"gcp.client.version": TRACER_VERSION,

packages/google-cloud-spanner/noxfile.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.12"]
3232

3333
ALL_PYTHON: List[str] = [
34-
"3.9",
3534
"3.10",
3635
"3.11",
3736
"3.12",

packages/google-cloud-spanner/setup.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@
3939
release_status = "Development Status :: 5 - Production/Stable"
4040

4141
dependencies = [
42-
"google-api-core[grpc] >= 1.34.0, <3.0.0,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*",
43-
"google-cloud-core >= 1.4.4, < 3.0.0",
42+
"google-api-core[grpc] >= 2.19.0, <3.0.0",
43+
"google-cloud-core >= 2.0.0, < 3.0.0",
4444
"grpc-google-iam-v1 >= 0.12.4, <1.0.0",
45-
"proto-plus >= 1.22.0, <2.0.0",
45+
"proto-plus >= 1.22.3, <2.0.0",
4646
"sqlparse >= 0.4.4",
47-
"proto-plus >= 1.22.2, <2.0.0; python_version>='3.11'",
4847
"protobuf >= 4.25.8, < 8.0.0",
4948
"grpc-interceptor >= 0.15.4",
5049
# Make OpenTelemetry a core dependency
@@ -86,7 +85,6 @@
8685
"License :: OSI Approved :: Apache Software License",
8786
"Programming Language :: Python",
8887
"Programming Language :: Python :: 3",
89-
"Programming Language :: Python :: 3.9",
9088
"Programming Language :: Python :: 3.10",
9189
"Programming Language :: Python :: 3.11",
9290
"Programming Language :: Python :: 3.12",
@@ -97,7 +95,7 @@
9795
],
9896
platforms="Posix; MacOS X; Windows",
9997
packages=packages,
100-
python_requires=">=3.9",
98+
python_requires=">=3.10",
10199
install_requires=dependencies,
102100
extras_require=extras,
103101
include_package_data=True,
Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
# -*- coding: utf-8 -*-
2-
# This constraints file is required for unit tests.
3-
# List all library dependencies and extras in this file.
4-
google-api-core
5-
google-auth
6-
grpcio
7-
proto-plus
8-
protobuf
9-
# cryptography is a direct dependency of google-auth
10-
cryptography
1+
# This constraints file is used to check that lower bounds
2+
# are correct in setup.py
3+
# List all library dependencies and extras in this file,
4+
# pinning their versions to their lower bounds.
5+
# For example, if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0",
6+
# then this file should have google-cloud-foo==1.14.0
7+
google-api-core==2.19.0
8+
google-cloud-core==2.0.0
9+
grpc-google-iam-v1==0.12.4
10+
proto-plus==1.22.3
11+
sqlparse==0.4.4
12+
protobuf==4.25.8
13+
grpc-interceptor==0.15.4
14+
opentelemetry-api==1.22.0
15+
opentelemetry-sdk==1.22.0
16+
opentelemetry-semantic-conventions==0.43b0
17+
opentelemetry-resourcedetector-gcp==1.8.0a0
18+
google-cloud-monitoring==2.16.0
19+
mmh3==4.1.0
20+
libcst==0.2.5

packages/google-cloud-spanner/testing/constraints-3.9.txt

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)