| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 24394d6 commit df87c3e
23 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -35,14 +35,15 @@ | |||
| 35 | 35 | DEFAULT_PYTHON_VERSION = "3.14" | |
| 36 | 36 | ||
| 37 | 37 | DEFAULT_MOCK_SERVER_TESTS_PYTHON_VERSION = "3.12" | |
| 38 | - SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.12"] | ||
| 38 | + SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.14"] | ||
| 39 | 39 | ||
| 40 | 40 | UNIT_TEST_PYTHON_VERSIONS: List[str] = [ | |
| 41 | 41 | "3.9", | |
| 42 | 42 | "3.10", | |
| 43 | 43 | "3.11", | |
| 44 | 44 | "3.12", | |
| 45 | 45 | "3.13", | |
| 46 | + "3.14", | ||
| 46 | 47 | ] | |
| 47 | 48 | UNIT_TEST_STANDARD_DEPENDENCIES = [ | |
| 48 | 49 | "mock", | |
@@ -81,6 +82,7 @@ | |||
| 81 | 82 | "unit-3.11", | |
| 82 | 83 | "unit-3.12", | |
| 83 | 84 | "unit-3.13", | |
| 85 | + "unit-3.14", | ||
| 84 | 86 | "system", | |
| 85 | 87 | "cover", | |
| 86 | 88 | "lint", | |
@@ -195,7 +197,12 @@ def install_unittest_dependencies(session, *constraints): | |||
| 195 | 197 | def unit(session, protobuf_implementation): | |
| 196 | 198 | # Install all test dependencies, then install this package in-place. | |
| 197 | 199 | ||
| 198 | - if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"): | ||
| 200 | + if protobuf_implementation == "cpp" and session.python in ( | ||
| 201 | + "3.11", | ||
| 202 | + "3.12", | ||
| 203 | + "3.13", | ||
| 204 | + "3.14", | ||
| 205 | + ): | ||
| 199 | 206 | session.skip("cpp implementation is not supported in python 3.11+") | |
| 200 | 207 | ||
| 201 | 208 | constraints_path = str( | |
@@ -213,6 +220,7 @@ def unit(session, protobuf_implementation): | |||
| 213 | 220 | session.run( | |
| 214 | 221 | "py.test", | |
| 215 | 222 | "--quiet", | |
| 223 | + "-s", | ||
| 216 | 224 | f"--junitxml=unit_{session.python}_sponge_log.xml", | |
| 217 | 225 | "--cov=google", | |
| 218 | 226 | "--cov=tests/unit", | |
@@ -326,7 +334,12 @@ def system(session, protobuf_implementation, database_dialect): | |||
| 326 | 334 | "Only run system tests on real Spanner with one protobuf implementation to speed up the build" | |
| 327 | 335 | ) | |
| 328 | 336 | ||
| 329 | - if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"): | ||
| 337 | + if protobuf_implementation == "cpp" and session.python in ( | ||
| 338 | + "3.11", | ||
| 339 | + "3.12", | ||
| 340 | + "3.13", | ||
| 341 | + "3.14", | ||
| 342 | + ): | ||
| 330 | 343 | session.skip("cpp implementation is not supported in python 3.11+") | |
| 331 | 344 | ||
| 332 | 345 | # Install pyopenssl for mTLS testing. | |
@@ -470,7 +483,7 @@ def docfx(session): | |||
| 470 | 483 | ) | |
| 471 | 484 | ||
| 472 | 485 | ||
| 473 | - @nox.session(python="3.13") | ||
| 486 | + @nox.session(python="3.14") | ||
| 474 | 487 | @nox.parametrize( | |
| 475 | 488 | "protobuf_implementation,database_dialect", | |
| 476 | 489 | [ | |
@@ -485,7 +498,12 @@ def docfx(session): | |||
| 485 | 498 | def prerelease_deps(session, protobuf_implementation, database_dialect): | |
| 486 | 499 | """Run all tests with prerelease versions of dependencies installed.""" | |
| 487 | 500 | ||
| 488 | - if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"): | ||
| 501 | + if protobuf_implementation == "cpp" and session.python in ( | ||
| 502 | + "3.11", | ||
| 503 | + "3.12", | ||
| 504 | + "3.13", | ||
| 505 | + "3.14", | ||
| 506 | + ): | ||
| 489 | 507 | session.skip("cpp implementation is not supported in python 3.11+") | |
| 490 | 508 | ||
| 491 | 509 | # Install all dependencies | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -44,18 +44,15 @@ | |||
| 44 | 44 | "proto-plus >= 1.22.2, <2.0.0; python_version>='3.11'", | |
| 45 | 45 | "protobuf>=3.20.2,<7.0.0,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", | |
| 46 | 46 | "grpc-interceptor >= 0.15.4", | |
| 47 | + # Make OpenTelemetry a core dependency | ||
| 48 | + "opentelemetry-api >= 1.22.0", | ||
| 49 | + "opentelemetry-sdk >= 1.22.0", | ||
| 50 | + "opentelemetry-semantic-conventions >= 0.43b0", | ||
| 51 | + "opentelemetry-resourcedetector-gcp >= 1.8.0a0", | ||
| 52 | + "google-cloud-monitoring >= 2.16.0", | ||
| 53 | + "mmh3 >= 4.1.0 ", | ||
| 47 | 54 | ] | |
| 48 | - extras = { | ||
| 49 | - "tracing": [ | ||
| 50 | - "opentelemetry-api >= 1.22.0", | ||
| 51 | - "opentelemetry-sdk >= 1.22.0", | ||
| 52 | - "opentelemetry-semantic-conventions >= 0.43b0", | ||
| 53 | - "opentelemetry-resourcedetector-gcp >= 1.8.0a0", | ||
| 54 | - "google-cloud-monitoring >= 2.16.0", | ||
| 55 | - "mmh3 >= 4.1.0 ", | ||
| 56 | - ], | ||
| 57 | - "libcst": "libcst >= 0.2.5", | ||
| 58 | - } | ||
| 55 | + extras = {"libcst": "libcst >= 0.2.5"} | ||
| 59 | 56 | ||
| 60 | 57 | url = "https://github.com/googleapis/python-spanner" | |
| 61 | 58 | ||
@@ -90,6 +87,7 @@ | |||
| 90 | 87 | "Programming Language :: Python :: 3.10", | |
| 91 | 88 | "Programming Language :: Python :: 3.11", | |
| 92 | 89 | "Programming Language :: Python :: 3.12", | |
| 90 | + "Programming Language :: Python :: 3.14", | ||
| 93 | 91 | "Operating System :: OS Independent", | |
| 94 | 92 | "Topic :: Internet", | |
| 95 | 93 | ], | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:8e2c32496077054105bd06c54a59d6a6694287bc053588e24debe6da6920ad91 | ||
| 1 | + image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209 | ||
| 2 | 2 | libraries: | |
| 3 | 3 | - id: google-cloud-spanner | |
| 4 | 4 | version: 3.60.0 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,8 +15,18 @@ | |||
| 15 | 15 | # | |
| 16 | 16 | from google.cloud.spanner_admin_database_v1 import gapic_version as package_version | |
| 17 | 17 | ||
| 18 | + import google.api_core as api_core | ||
| 19 | + import sys | ||
| 20 | + | ||
| 18 | 21 | __version__ = package_version.__version__ | |
| 19 | 22 | ||
| 23 | + if sys.version_info >= (3, 8): # pragma: NO COVER | ||
| 24 | + from importlib import metadata | ||
| 25 | + else: # pragma: NO COVER | ||
| 26 | + # TODO(https://github.com/googleapis/python-api-core/issues/835): Remove | ||
| 27 | + # this code path once we drop support for Python 3.7 | ||
| 28 | + import importlib_metadata as metadata | ||
| 29 | + | ||
| 20 | 30 | ||
| 21 | 31 | from .services.database_admin import DatabaseAdminClient | |
| 22 | 32 | from .services.database_admin import DatabaseAdminAsyncClient | |
@@ -83,6 +93,100 @@ | |||
| 83 | 93 | from .types.spanner_database_admin import UpdateDatabaseRequest | |
| 84 | 94 | from .types.spanner_database_admin import RestoreSourceType | |
| 85 | 95 | ||
| 96 | + if hasattr(api_core, "check_python_version") and hasattr( | ||
| 97 | + api_core, "check_dependency_versions" | ||
| 98 | + ): # pragma: NO COVER | ||
| 99 | + api_core.check_python_version("google.cloud.spanner_admin_database_v1") # type: ignore | ||
| 100 | + api_core.check_dependency_versions("google.cloud.spanner_admin_database_v1") # type: ignore | ||
| 101 | + else: # pragma: NO COVER | ||
| 102 | + # An older version of api_core is installed which does not define the | ||
| 103 | + # functions above. We do equivalent checks manually. | ||
| 104 | + try: | ||
| 105 | + import warnings | ||
| 106 | + import sys | ||
| 107 | + | ||
| 108 | + _py_version_str = sys.version.split()[0] | ||
| 109 | + _package_label = "google.cloud.spanner_admin_database_v1" | ||
| 110 | + if sys.version_info < (3, 9): | ||
| 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.9, and then update {_package_label}.", | ||
| 117 | + FutureWarning, | ||
| 118 | + ) | ||
| 119 | + if sys.version_info[:2] == (3, 9): | ||
| 120 | + warnings.warn( | ||
| 121 | + f"You are using a Python version ({_py_version_str}) " | ||
| 122 | + + f"which Google will stop supporting in {_package_label} in " | ||
| 123 | + + "January 2026. Please " | ||
| 124 | + + "upgrade to the latest Python version, or at " | ||
| 125 | + + "least to Python 3.10, before then, and " | ||
| 126 | + + f"then update {_package_label}.", | ||
| 127 | + FutureWarning, | ||
| 128 | + ) | ||
| 129 | + | ||
| 130 | + def parse_version_to_tuple(version_string: str): | ||
| 131 | + """Safely converts a semantic version string to a comparable tuple of integers. | ||
| 132 | + Example: "4.25.8" -> (4, 25, 8) | ||
| 133 | + Ignores non-numeric parts and handles common version formats. | ||
| 134 | + Args: | ||
| 135 | + version_string: Version string in the format "x.y.z" or "x.y.z<suffix>" | ||
| 136 | + Returns: | ||
| 137 | + Tuple of integers for the parsed version string. | ||
| 138 | + """ | ||
| 139 | + parts = [] | ||
| 140 | + for part in version_string.split("."): | ||
| 141 | + try: | ||
| 142 | + parts.append(int(part)) | ||
| 143 | + except ValueError: | ||
| 144 | + # If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here. | ||
| 145 | + # This is a simplification compared to 'packaging.parse_version', but sufficient | ||
| 146 | + # for comparing strictly numeric semantic versions. | ||
| 147 | + break | ||
| 148 | + return tuple(parts) | ||
| 149 | + | ||
| 150 | + def _get_version(dependency_name): | ||
| 151 | + try: | ||
| 152 | + version_string: str = metadata.version(dependency_name) | ||
| 153 | + parsed_version = parse_version_to_tuple(version_string) | ||
| 154 | + return (parsed_version, version_string) | ||
| 155 | + except Exception: | ||
| 156 | + # Catch exceptions from metadata.version() (e.g., PackageNotFoundError) | ||
| 157 | + # or errors during parse_version_to_tuple | ||
| 158 | + return (None, "--") | ||
| 159 | + | ||
| 160 | + _dependency_package = "google.protobuf" | ||
| 161 | + _next_supported_version = "4.25.8" | ||
| 162 | + _next_supported_version_tuple = (4, 25, 8) | ||
| 163 | + _recommendation = " (we recommend 6.x)" | ||
| 164 | + (_version_used, _version_used_string) = _get_version(_dependency_package) | ||
| 165 | + if _version_used and _version_used < _next_supported_version_tuple: | ||
| 166 | + warnings.warn( | ||
| 167 | + f"Package {_package_label} depends on " | ||
| 168 | + + f"{_dependency_package}, currently installed at version " | ||
| 169 | + + f"{_version_used_string}. Future updates to " | ||
| 170 | + + f"{_package_label} will require {_dependency_package} at " | ||
| 171 | + + f"version {_next_supported_version} or higher{_recommendation}." | ||
| 172 | + + " Please ensure " | ||
| 173 | + + "that either (a) your Python environment doesn't pin the " | ||
| 174 | + + f"version of {_dependency_package}, so that updates to " | ||
| 175 | + + f"{_package_label} can require the higher version, or " | ||
| 176 | + + "(b) you manually update your Python environment to use at " | ||
| 177 | + + f"least version {_next_supported_version} of " | ||
| 178 | + + f"{_dependency_package}.", | ||
| 179 | + FutureWarning, | ||
| 180 | + ) | ||
| 181 | + except Exception: | ||
| 182 | + warnings.warn( | ||
| 183 | + "Could not determine the version of Python " | ||
| 184 | + + "currently being used. To continue receiving " | ||
| 185 | + + "updates for {_package_label}, ensure you are " | ||
| 186 | + + "using a supported version of Python; see " | ||
| 187 | + + "https://devguide.python.org/versions/" | ||
| 188 | + ) | ||
| 189 | + | ||
| 86 | 190 | __all__ = ( | |
| 87 | 191 | "DatabaseAdminAsyncClient", | |
| 88 | 192 | "AddSplitPointsRequest", | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -172,6 +172,34 @@ def _get_default_mtls_endpoint(api_endpoint): | |||
| 172 | 172 | _DEFAULT_ENDPOINT_TEMPLATE = "spanner.{UNIVERSE_DOMAIN}" | |
| 173 | 173 | _DEFAULT_UNIVERSE = "googleapis.com" | |
| 174 | 174 | ||
| 175 | + @staticmethod | ||
| 176 | + def _use_client_cert_effective(): | ||
| 177 | + """Returns whether client certificate should be used for mTLS if the | ||
| 178 | + google-auth version supports should_use_client_cert automatic mTLS enablement. | ||
| 179 | + | ||
| 180 | + Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var. | ||
| 181 | + | ||
| 182 | + Returns: | ||
| 183 | + bool: whether client certificate should be used for mTLS | ||
| 184 | + Raises: | ||
| 185 | + ValueError: (If using a version of google-auth without should_use_client_cert and | ||
| 186 | + GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.) | ||
| 187 | + """ | ||
| 188 | + # check if google-auth version supports should_use_client_cert for automatic mTLS enablement | ||
| 189 | + if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER | ||
| 190 | + return mtls.should_use_client_cert() | ||
| 191 | + else: # pragma: NO COVER | ||
| 192 | + # if unsupported, fallback to reading from env var | ||
| 193 | + use_client_cert_str = os.getenv( | ||
| 194 | + "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" | ||
| 195 | + ).lower() | ||
| 196 | + if use_client_cert_str not in ("true", "false"): | ||
| 197 | + raise ValueError( | ||
| 198 | + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be" | ||
| 199 | + " either `true` or `false`" | ||
| 200 | + ) | ||
| 201 | + return use_client_cert_str == "true" | ||
| 202 | + | ||
| 175 | 203 | @classmethod | |
| 176 | 204 | def from_service_account_info(cls, info: dict, *args, **kwargs): | |
| 177 | 205 | """Creates an instance of this client using the provided credentials | |
@@ -518,20 +546,16 @@ def get_mtls_endpoint_and_cert_source( | |||
| 518 | 546 | ) | |
| 519 | 547 | if client_options is None: | |
| 520 | 548 | client_options = client_options_lib.ClientOptions() | |
| 521 | - use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") | ||
| 549 | + use_client_cert = DatabaseAdminClient._use_client_cert_effective() | ||
| 522 | 550 | use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") | |
| 523 | - if use_client_cert not in ("true", "false"): | ||
| 524 | - raise ValueError( | ||
| 525 | - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" | ||
| 526 | - ) | ||
| 527 | 551 | if use_mtls_endpoint not in ("auto", "never", "always"): | |
| 528 | 552 | raise MutualTLSChannelError( | |
| 529 | 553 | "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" | |
| 530 | 554 | ) | |
| 531 | 555 | ||
| 532 | 556 | # Figure out the client cert source to use. | |
| 533 | 557 | client_cert_source = None | |
| 534 | - if use_client_cert == "true": | ||
| 558 | + if use_client_cert: | ||
| 535 | 559 | if client_options.client_cert_source: | |
| 536 | 560 | client_cert_source = client_options.client_cert_source | |
| 537 | 561 | elif mtls.has_default_client_cert_source(): | |
@@ -563,20 +587,14 @@ def _read_environment_variables(): | |||
| 563 | 587 | google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT | |
| 564 | 588 | is not any of ["auto", "never", "always"]. | |
| 565 | 589 | """ | |
| 566 | - use_client_cert = os.getenv( | ||
| 567 | - "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" | ||
| 568 | - ).lower() | ||
| 590 | + use_client_cert = DatabaseAdminClient._use_client_cert_effective() | ||
| 569 | 591 | use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower() | |
| 570 | 592 | universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN") | |
| 571 | - if use_client_cert not in ("true", "false"): | ||
| 572 | - raise ValueError( | ||
| 573 | - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" | ||
| 574 | - ) | ||
| 575 | 593 | if use_mtls_endpoint not in ("auto", "never", "always"): | |
| 576 | 594 | raise MutualTLSChannelError( | |
| 577 | 595 | "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" | |
| 578 | 596 | ) | |
| 579 | - return use_client_cert == "true", use_mtls_endpoint, universe_domain_env | ||
| 597 | + return use_client_cert, use_mtls_endpoint, universe_domain_env | ||
| 580 | 598 | ||
| 581 | 599 | @staticmethod | |
| 582 | 600 | def _get_client_cert_source(provided_cert_source, use_cert_flag): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments