Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2408166

Browse filesBrowse files
authored
feat(spanner): drop Python 3.7-3.9 support and regenerate (#17169)
Updates post processing to account for dropping support for Python 3.7, 3.8, 3.9 and the impacts that has on using 3.10 for lower bounds testing. ### Changes * updates the lower bound versions for several libraries to avoid conflicts and install issues in both `setup.py` and `constraints-3.10.txt` * updates post-processing scripts to ensure the above updates persist
1 parent 819ce1b commit 2408166
Copy full SHA for 2408166

33 files changed

+392-237Lines changed: 392 additions & 237 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎.librarian/generator-input/client-post-processing/spanner-integration.yaml‎

Copy file name to clipboardExpand all lines: .librarian/generator-input/client-post-processing/spanner-integration.yaml
+68-10Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,17 +122,22 @@ replacements:
122122
\g<3>\g<4>
123123
count: 1
124124
- paths: [packages/google-cloud-spanner/setup.py]
125-
before: '(?s)dependencies = \[.*?\]\nextras = \{\}'
125+
before: '(?s)dependencies = \[.*?\]\nextras = \{\s*\}'
126126
after: |
127127
dependencies = [
128-
"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.*",
129-
"google-cloud-core >= 1.4.4, < 3.0.0",
128+
"google-api-core[grpc] >= 2.17.1, <3.0.0",
129+
# Exclude incompatible versions of `google-auth`
130+
# See https://github.com/googleapis/google-cloud-python/issues/12364
131+
"google-auth >= 2.14.1, <3.0.0,!=2.24.0,!=2.25.0",
132+
"google-cloud-core >= 2.0.0, < 3.0.0",
133+
"grpcio >= 1.49.1, < 2.0.0",
134+
"grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'",
130135
"grpc-google-iam-v1 >= 0.12.4, <1.0.0",
131-
"proto-plus >= 1.22.0, <2.0.0",
132-
"sqlparse >= 0.4.4",
133-
"proto-plus >= 1.22.2, <2.0.0; python_version>='3.11'",
136+
"proto-plus >= 1.22.3, <2.0.0",
137+
"proto-plus >= 1.25.0, <2.0.0; python_version >= '3.13'",
134138
"protobuf >= 4.25.8, < 8.0.0",
135139
"grpc-interceptor >= 0.15.4",
140+
"sqlparse >= 0.4.4",
136141
# Make OpenTelemetry a core dependency
137142
"opentelemetry-api >= 1.22.0",
138143
"opentelemetry-sdk >= 1.22.0",
@@ -601,6 +606,40 @@ replacements:
601606
602607
Next Steps
603608
count: 1
609+
610+
- paths: [
611+
packages/google-cloud-spanner/README.rst
612+
]
613+
before: |
614+
Supported Python Versions
615+
\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^
616+
Our client libraries are compatible with all current `active`_ and `maintenance`_ versions of
617+
Python\.
618+
619+
Python >= 3\.9, including 3\.14
620+
621+
\.\. _active: https://devguide\.python\.org/devcycle/#in-development-main-branch
622+
\.\. _maintenance: https://devguide\.python\.org/devcycle/#maintenance-branches
623+
624+
Unsupported Python Versions
625+
\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^
626+
Python <= 3\.8
627+
after: |
628+
Supported Python Versions
629+
^^^^^^^^^^^^^^^^^^^^^^^^^
630+
Our client libraries are compatible with all current `active`_ and `maintenance`_ versions of
631+
Python.
632+
633+
Python >= 3.10, including 3.14
634+
635+
.. _active: https://devguide.python.org/devcycle/#in-development-main-branch
636+
.. _maintenance: https://devguide.python.org/devcycle/#maintenance-branches
637+
638+
Unsupported Python Versions
639+
^^^^^^^^^^^^^^^^^^^^^^^^^^^
640+
Python <= 3.9
641+
count: 1
642+
604643
# Note: noxfile.py is heavily customized so we clobber the whole file.
605644
- paths: [
606645
packages/google-cloud-spanner/noxfile.py
@@ -627,7 +666,6 @@ replacements:
627666
SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.12"]
628667
629668
ALL_PYTHON: List[str] = [
630-
"3.9",
631669
"3.10",
632670
"3.11",
633671
"3.12",
@@ -667,7 +705,6 @@ replacements:
667705
CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()
668706
669707
nox.options.sessions = [
670-
"unit-3.9",
671708
"unit-3.10",
672709
"unit-3.11",
673710
"unit-3.12",
@@ -816,8 +853,6 @@ replacements:
816853
def unit(session, protobuf_implementation):
817854
# Install all test dependencies, then install this package in-place.
818855
819-
if session.python in ("3.7",):
820-
session.skip("Python 3.7 is no longer supported")
821856
if protobuf_implementation == "cpp" and session.python in (
822857
"3.11",
823858
"3.12",
@@ -1379,4 +1414,27 @@ replacements:
13791414
)
13801415
def core_deps_from_source(session, protobuf_implementation):
13811416
"""Run all tests with core dependencies installed from source,
1417+
count: 1
1418+
- paths: [packages/google-cloud-spanner/testing/constraints-3.10.txt]
1419+
before: '(?s)protobuf==4.25.8\n(?!google-cloud-core)'
1420+
after: |
1421+
protobuf==4.25.8
1422+
google-cloud-core==2.0.0
1423+
grpc-google-iam-v1==0.12.4
1424+
sqlparse==0.4.4
1425+
grpc-interceptor==0.15.4
1426+
opentelemetry-api==1.22.0
1427+
opentelemetry-sdk==1.22.0
1428+
opentelemetry-semantic-conventions==0.43b0
1429+
opentelemetry-resourcedetector-gcp==1.8.0a0
1430+
google-cloud-monitoring==2.16.0
1431+
mmh3==4.1.0
1432+
libcst==0.2.5
1433+
googleapis-common-protos==1.60.0
1434+
count: 1
1435+
- paths: [packages/google-cloud-spanner/testing/constraints-3.10.txt]
1436+
before: 'grpcio==1\.44\.0\n(?!grpcio-status)'
1437+
after: |
1438+
grpcio==1.49.1
1439+
grpcio-status==1.49.1
13821440
count: 1
Collapse file

‎librarian.yaml‎

Copy file name to clipboardExpand all lines: librarian.yaml
-1Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1906,7 +1906,6 @@ libraries:
19061906
- docs/spanner_v1/table.rst
19071907
- docs/spanner_v1/transaction.rst
19081908
- tests/unit/gapic/conftest.py
1909-
skip_generate: true
19101909
python:
19111910
library_type: GAPIC_COMBO
19121911
opt_args_by_api:
Collapse file

‎packages/google-cloud-spanner/.devcontainer/devcontainer.json‎

Copy file name to clipboardExpand all lines: packages/google-cloud-spanner/.devcontainer/devcontainer.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"name": "Python 3",
55
"build": {
66
// Sets the run context to one level up instead of the .devcontainer folder.
7-
"args": { "VARIANT": "3.8" },
7+
"args": { "VARIANT": "3.10" },
88
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
99
"dockerfile": "Dockerfile"
1010
},
Collapse file

‎packages/google-cloud-spanner/.devcontainer/requirements.txt‎

Copy file name to clipboardExpand all lines: packages/google-cloud-spanner/.devcontainer/requirements.txt
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# This file is autogenerated by pip-compile with Python 3.8
2+
# This file is autogenerated by pip-compile with Python 3.8 # version-scanner: ignore
33
# by the following command:
44
#
55
# pip-compile --generate-hashes requirements.in
Collapse file

‎packages/google-cloud-spanner/docs/conf.py‎

Copy file name to clipboardExpand all lines: packages/google-cloud-spanner/docs/conf.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
# Copyright 2025 Google LLC
2+
# Copyright 2026 Google LLC
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -83,7 +83,7 @@
8383

8484
# General information about the project.
8585
project = "google-cloud-spanner"
86-
copyright = "2025, Google, LLC"
86+
copyright = "2026, Google, LLC"
8787
author = "Google APIs"
8888

8989
# The version info for the project you're documenting, acts as replacement for
Collapse file

‎packages/google-cloud-spanner/google/cloud/aio/_cross_sync/cross_sync.py‎

Copy file name to clipboardExpand all lines: packages/google-cloud-spanner/google/cloud/aio/_cross_sync/cross_sync.py
+1-2Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ async def async_func(self, arg: int) -> int:
4242
import concurrent.futures
4343
import inspect
4444
import queue
45-
import sys
4645
import threading
4746
import time
4847
import typing
@@ -269,7 +268,7 @@ def create_task(
269268
sync_executor: ThreadPoolExecutor to use for sync operations. Ignored in async version
270269
"""
271270
task: CrossSync.Task[T] = asyncio.create_task(fn(*fn_args, **fn_kwargs))
272-
if task_name and sys.version_info >= (3, 8):
271+
if task_name:
273272
task.set_name(task_name)
274273
return task
275274

Collapse file

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

Copy file name to clipboardExpand all lines: packages/google-cloud-spanner/google/cloud/spanner_admin_database_v1/__init__.py
+75-11Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
# Copyright 2025 Google LLC
2+
# Copyright 2026 Google LLC
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121

2222
__version__ = package_version.__version__
2323

24+
from importlib import metadata
2425

2526
from .services.database_admin import DatabaseAdminAsyncClient, DatabaseAdminClient
2627
from .types.backup import (
@@ -99,18 +100,81 @@
99100
api_core.check_python_version("google.cloud.spanner_admin_database_v1") # type: ignore
100101
api_core.check_dependency_versions("google.cloud.spanner_admin_database_v1") # type: ignore
101102
else: # pragma: NO COVER
102-
import warnings
103+
# An older version of api_core is installed which does not define the
104+
# functions above. We do equivalent checks manually.
105+
try:
106+
import warnings
103107

104-
_py_version_str = sys.version.split()[0]
105-
# version-scanner: ignore-next-line
106-
if sys.version_info < (3, 10):
108+
_py_version_str = sys.version.split()[0]
109+
_package_label = "google.cloud.spanner_admin_database_v1"
110+
if sys.version_info < (3, 10):
111+
warnings.warn(
112+
"You are using a non-supported Python version "
113+
+ f"({_py_version_str}). Google will not post any further "
114+
+ f"updates to {_package_label} supporting this Python version. "
115+
+ "Please upgrade to the latest Python version, or at "
116+
+ f"least to Python 3.10, and then update {_package_label}.",
117+
FutureWarning,
118+
)
119+
120+
def parse_version_to_tuple(version_string: str):
121+
"""Safely converts a semantic version string to a comparable tuple of integers.
122+
Example: "4.25.8" -> (4, 25, 8)
123+
Ignores non-numeric parts and handles common version formats.
124+
Args:
125+
version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
126+
Returns:
127+
Tuple of integers for the parsed version string.
128+
"""
129+
parts = []
130+
for part in version_string.split("."):
131+
try:
132+
parts.append(int(part))
133+
except ValueError:
134+
# If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
135+
# This is a simplification compared to 'packaging.parse_version', but sufficient
136+
# for comparing strictly numeric semantic versions.
137+
break
138+
return tuple(parts)
139+
140+
def _get_version(dependency_name):
141+
try:
142+
version_string: str = metadata.version(dependency_name)
143+
parsed_version = parse_version_to_tuple(version_string)
144+
return (parsed_version, version_string)
145+
except Exception:
146+
# Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
147+
# or errors during parse_version_to_tuple
148+
return (None, "--")
149+
150+
_dependency_package = "google.protobuf"
151+
_next_supported_version = "4.25.8"
152+
_next_supported_version_tuple = (4, 25, 8)
153+
_recommendation = " (we recommend 6.x)"
154+
(_version_used, _version_used_string) = _get_version(_dependency_package)
155+
if _version_used and _version_used < _next_supported_version_tuple:
156+
warnings.warn(
157+
f"Package {_package_label} depends on "
158+
+ f"{_dependency_package}, currently installed at version "
159+
+ f"{_version_used_string}. Future updates to "
160+
+ f"{_package_label} will require {_dependency_package} at "
161+
+ f"version {_next_supported_version} or higher{_recommendation}."
162+
+ " Please ensure "
163+
+ "that either (a) your Python environment doesn't pin the "
164+
+ f"version of {_dependency_package}, so that updates to "
165+
+ f"{_package_label} can require the higher version, or "
166+
+ "(b) you manually update your Python environment to use at "
167+
+ f"least version {_next_supported_version} of "
168+
+ f"{_dependency_package}.",
169+
FutureWarning,
170+
)
171+
except Exception:
107172
warnings.warn(
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,
173+
"Could not determine the version of Python "
174+
+ "currently being used. To continue receiving "
175+
+ "updates for {_package_label}, ensure you are "
176+
+ "using a supported version of Python; see "
177+
+ "https://devguide.python.org/versions/"
114178
)
115179

116180
__all__ = (
Collapse file

‎packages/google-cloud-spanner/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py‎

Copy file name to clipboardExpand all lines: packages/google-cloud-spanner/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py
+13-13Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
# Copyright 2025 Google LLC
2+
# Copyright 2026 Google LLC
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -518,11 +518,11 @@ async def sample_create_database():
518518
)
519519
520520
# Make the request
521-
operation = client.create_database(request=request)
521+
operation = await client.create_database(request=request)
522522
523523
print("Waiting for operation to complete...")
524524
525-
response = (await operation).result()
525+
response = await operation.result()
526526
527527
# Handle the response
528528
print(response)
@@ -814,11 +814,11 @@ async def sample_update_database():
814814
)
815815
816816
# Make the request
817-
operation = client.update_database(request=request)
817+
operation = await client.update_database(request=request)
818818
819819
print("Waiting for operation to complete...")
820820
821-
response = (await operation).result()
821+
response = await operation.result()
822822
823823
# Handle the response
824824
print(response)
@@ -964,11 +964,11 @@ async def sample_update_database_ddl():
964964
)
965965
966966
# Make the request
967-
operation = client.update_database_ddl(request=request)
967+
operation = await client.update_database_ddl(request=request)
968968
969969
print("Waiting for operation to complete...")
970970
971-
response = (await operation).result()
971+
response = await operation.result()
972972
973973
# Handle the response
974974
print(response)
@@ -1778,11 +1778,11 @@ async def sample_create_backup():
17781778
)
17791779
17801780
# Make the request
1781-
operation = client.create_backup(request=request)
1781+
operation = await client.create_backup(request=request)
17821782
17831783
print("Waiting for operation to complete...")
17841784
1785-
response = (await operation).result()
1785+
response = await operation.result()
17861786
17871787
# Handle the response
17881788
print(response)
@@ -1944,11 +1944,11 @@ async def sample_copy_backup():
19441944
)
19451945
19461946
# Make the request
1947-
operation = client.copy_backup(request=request)
1947+
operation = await client.copy_backup(request=request)
19481948
19491949
print("Waiting for operation to complete...")
19501950
1951-
response = (await operation).result()
1951+
response = await operation.result()
19521952
19531953
# Handle the response
19541954
print(response)
@@ -2602,11 +2602,11 @@ async def sample_restore_database():
26022602
)
26032603
26042604
# Make the request
2605-
operation = client.restore_database(request=request)
2605+
operation = await client.restore_database(request=request)
26062606
26072607
print("Waiting for operation to complete...")
26082608
2609-
response = (await operation).result()
2609+
response = await operation.result()
26102610
26112611
# Handle the response
26122612
print(response)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.