Skip to content

Conversation

@daniel-sanche
Copy link
Contributor

feat: update image to us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209

…prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209
@daniel-sanche daniel-sanche requested review from a team as code owners December 16, 2025 20:04
@product-auto-label product-auto-label bot added the size: xl Pull request size is extra large. label Dec 16, 2025
@product-auto-label product-auto-label bot added the api: spanner Issues related to the googleapis/python-spanner API. label Dec 16, 2025
@gemini-code-assist
Copy link

Summary of Changes

Hello @daniel-sanche, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces Python 3.14 support, integrates OpenTelemetry as a core dependency, and updates the librarian generator image. A significant portion of the changes involves refactoring and enhancing the mTLS client certificate handling logic across Spanner client libraries, along with corresponding test updates. Additionally, several outdated keyword fixup scripts and testing constraints have been removed to streamline the codebase.

Highlights

  • Python 3.14 Support: The project's testing configuration (noxfile.py) and package metadata (setup.py) have been updated to officially include and support Python 3.14, ensuring compatibility and future-proofing.
  • OpenTelemetry Core Dependency: OpenTelemetry-related dependencies, previously optional under a 'tracing' extra, have been promoted to core dependencies in setup.py, making tracing capabilities more readily available.
  • Librarian Image Update: The python-librarian-generator image used by the project has been updated to a new SHA in librarian/state.yaml, reflecting a new version of the generator.
  • Refined mTLS Client Certificate Handling: The logic for determining when to use mTLS client certificates has been centralized and improved across various client files. A new static method _use_client_cert_effective now intelligently checks for google-auth's automatic mTLS enablement and falls back to environment variables if needed.
  • Removal of Keyword Fixup Scripts: Three Python scripts (fixup_spanner_admin_database_v1_keywords.py, fixup_spanner_admin_instance_v1_keywords.py, fixup_spanner_v1_keywords.py) have been removed, indicating that their functionality is no longer required, likely due to a one-time migration being complete.
  • Updated Testing Constraints: The grpc-google-iam-v1 dependency has been removed from several testing/constraints-*.txt files, streamlining the testing environment.
  • Enhanced mTLS Test Coverage: Unit tests related to mTLS client certificate handling have been significantly expanded and updated to cover the new _use_client_cert_effective logic and ensure robust behavior across different google-auth versions and environment variable configurations.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request appears to be an automated update to add support for Python 3.14 and refresh dependencies. The changes include updating Python versions in noxfile.py, moving OpenTelemetry dependencies to core, and refactoring mTLS client certificate handling.

My main concerns are:

  1. The OpenTelemetry dependencies have been moved from an optional tracing extra to core dependencies. This significantly increases the dependency footprint for all users and should be reverted if unintentional.
  2. There is a large block of duplicated code for version checking in spanner_admin_database_v1/__init__.py and spanner_admin_instance_v1/__init__.py which also uses overly broad exception handling. This should be refactored for better maintainability.
  3. A -s flag was added to pytest calls, which could make CI logs overly verbose.

I've added specific comments with suggestions for these points.

session.run(
"py.test",
"--quiet",
"-s",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The -s flag (short for --capture=no) disables output capturing in pytest. While this can be useful for local debugging, it often makes CI logs excessively verbose. It's generally recommended to avoid this flag in CI configurations to keep logs concise and focused on failures. Please consider removing this flag.

Comment on lines +101 to +189
else: # pragma: NO COVER
# An older version of api_core is installed which does not define the
# functions above. We do equivalent checks manually.
try:
import warnings
import sys

_py_version_str = sys.version.split()[0]
_package_label = "google.cloud.spanner_admin_database_v1"
if sys.version_info < (3, 9):
warnings.warn(
"You are using a non-supported Python version "
+ f"({_py_version_str}). Google will not post any further "
+ f"updates to {_package_label} supporting this Python version. "
+ "Please upgrade to the latest Python version, or at "
+ f"least to Python 3.9, and then update {_package_label}.",
FutureWarning,
)
if sys.version_info[:2] == (3, 9):
warnings.warn(
f"You are using a Python version ({_py_version_str}) "
+ f"which Google will stop supporting in {_package_label} in "
+ "January 2026. Please "
+ "upgrade to the latest Python version, or at "
+ "least to Python 3.10, before then, and "
+ f"then update {_package_label}.",
FutureWarning,
)

def parse_version_to_tuple(version_string: str):
"""Safely converts a semantic version string to a comparable tuple of integers.
Example: "4.25.8" -> (4, 25, 8)
Ignores non-numeric parts and handles common version formats.
Args:
version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
Returns:
Tuple of integers for the parsed version string.
"""
parts = []
for part in version_string.split("."):
try:
parts.append(int(part))
except ValueError:
# If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
# This is a simplification compared to 'packaging.parse_version', but sufficient
# for comparing strictly numeric semantic versions.
break
return tuple(parts)

def _get_version(dependency_name):
try:
version_string: str = metadata.version(dependency_name)
parsed_version = parse_version_to_tuple(version_string)
return (parsed_version, version_string)
except Exception:
# Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
# or errors during parse_version_to_tuple
return (None, "--")

_dependency_package = "google.protobuf"
_next_supported_version = "4.25.8"
_next_supported_version_tuple = (4, 25, 8)
_recommendation = " (we recommend 6.x)"
(_version_used, _version_used_string) = _get_version(_dependency_package)
if _version_used and _version_used < _next_supported_version_tuple:
warnings.warn(
f"Package {_package_label} depends on "
+ f"{_dependency_package}, currently installed at version "
+ f"{_version_used_string}. Future updates to "
+ f"{_package_label} will require {_dependency_package} at "
+ f"version {_next_supported_version} or higher{_recommendation}."
+ " Please ensure "
+ "that either (a) your Python environment doesn't pin the "
+ f"version of {_dependency_package}, so that updates to "
+ f"{_package_label} can require the higher version, or "
+ "(b) you manually update your Python environment to use at "
+ f"least version {_next_supported_version} of "
+ f"{_dependency_package}.",
FutureWarning,
)
except Exception:
warnings.warn(
"Could not determine the version of Python "
+ "currently being used. To continue receiving "
+ "updates for {_package_label}, ensure you are "
+ "using a supported version of Python; see "
+ "https://devguide.python.org/versions/"
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This large block of version-checking code is duplicated in google/cloud/spanner_admin_instance_v1/__init__.py. To improve maintainability, this logic should be extracted into a shared utility module.

Additionally, the except Exception: clauses on lines 155 and 181 are too broad. They can hide unexpected errors. Please consider catching more specific exceptions. For example:

  • On line 155, you could catch (AttributeError, ValueError, ImportError).
  • On line 181, you could catch ImportError.

Comment on lines +81 to +169
else: # pragma: NO COVER
# An older version of api_core is installed which does not define the
# functions above. We do equivalent checks manually.
try:
import warnings
import sys

_py_version_str = sys.version.split()[0]
_package_label = "google.cloud.spanner_admin_instance_v1"
if sys.version_info < (3, 9):
warnings.warn(
"You are using a non-supported Python version "
+ f"({_py_version_str}). Google will not post any further "
+ f"updates to {_package_label} supporting this Python version. "
+ "Please upgrade to the latest Python version, or at "
+ f"least to Python 3.9, and then update {_package_label}.",
FutureWarning,
)
if sys.version_info[:2] == (3, 9):
warnings.warn(
f"You are using a Python version ({_py_version_str}) "
+ f"which Google will stop supporting in {_package_label} in "
+ "January 2026. Please "
+ "upgrade to the latest Python version, or at "
+ "least to Python 3.10, before then, and "
+ f"then update {_package_label}.",
FutureWarning,
)

def parse_version_to_tuple(version_string: str):
"""Safely converts a semantic version string to a comparable tuple of integers.
Example: "4.25.8" -> (4, 25, 8)
Ignores non-numeric parts and handles common version formats.
Args:
version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
Returns:
Tuple of integers for the parsed version string.
"""
parts = []
for part in version_string.split("."):
try:
parts.append(int(part))
except ValueError:
# If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
# This is a simplification compared to 'packaging.parse_version', but sufficient
# for comparing strictly numeric semantic versions.
break
return tuple(parts)

def _get_version(dependency_name):
try:
version_string: str = metadata.version(dependency_name)
parsed_version = parse_version_to_tuple(version_string)
return (parsed_version, version_string)
except Exception:
# Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
# or errors during parse_version_to_tuple
return (None, "--")

_dependency_package = "google.protobuf"
_next_supported_version = "4.25.8"
_next_supported_version_tuple = (4, 25, 8)
_recommendation = " (we recommend 6.x)"
(_version_used, _version_used_string) = _get_version(_dependency_package)
if _version_used and _version_used < _next_supported_version_tuple:
warnings.warn(
f"Package {_package_label} depends on "
+ f"{_dependency_package}, currently installed at version "
+ f"{_version_used_string}. Future updates to "
+ f"{_package_label} will require {_dependency_package} at "
+ f"version {_next_supported_version} or higher{_recommendation}."
+ " Please ensure "
+ "that either (a) your Python environment doesn't pin the "
+ f"version of {_dependency_package}, so that updates to "
+ f"{_package_label} can require the higher version, or "
+ "(b) you manually update your Python environment to use at "
+ f"least version {_next_supported_version} of "
+ f"{_dependency_package}.",
FutureWarning,
)
except Exception:
warnings.warn(
"Could not determine the version of Python "
+ "currently being used. To continue receiving "
+ "updates for {_package_label}, ensure you are "
+ "using a supported version of Python; see "
+ "https://devguide.python.org/versions/"
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This large block of version-checking code is duplicated from google/cloud/spanner_admin_database_v1/__init__.py. To improve maintainability, this logic should be extracted into a shared utility module.

Additionally, the except Exception: clauses are too broad and can hide unexpected errors. Please consider catching more specific exceptions.

@daniel-sanche daniel-sanche changed the title chore: librarian update image pull request: 20251216T120431Z feat(gapic): support mTLS certificates when available Dec 16, 2025
@daniel-sanche daniel-sanche merged commit df87c3e into main Dec 16, 2025
29 of 30 checks passed
@daniel-sanche daniel-sanche deleted the librarian-20251216T120431Z branch December 16, 2025 20:53
rahul2393 pushed a commit that referenced this pull request Dec 17, 2025
PR created by the Librarian CLI to initialize a release. Merging this PR
will auto trigger a release.

Librarian Version: v0.7.0
Language Image:
us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209
<details><summary>google-cloud-spanner: 3.61.0</summary>

##
[3.61.0](v3.60.0...v3.61.0)
(2025-12-16)

### Features

* support mTLS certificates when available (#1467)
([df87c3e](df87c3ed))

</details>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: spanner Issues related to the googleapis/python-spanner API. size: xl Pull request size is extra large.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants