Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
13fff54
feat: provide Python run-time version support
vchudnov-g Jul 29, 2025
486519c
feat: apply Python version suport warnings to api_core
vchudnov-g Jul 29, 2025
1036d6b
feat: add deprecation check for the protobuf package
vchudnov-g Jul 30, 2025
36e1d09
format files
vchudnov-g Jul 30, 2025
3a9a59c
fix lint warning
vchudnov-g Jul 30, 2025
ce632c1
add docstring to `warn_deprecation_for_versions_less_than`
vchudnov-g Jul 30, 2025
70b67d5
Add/fix docstrings
vchudnov-g Jul 30, 2025
d0f130c
fix typo
vchudnov-g Jul 31, 2025
f66de11
add test for _python_package_support.py
vchudnov-g Jul 31, 2025
9f9c000
add constants for various buffer periods
vchudnov-g Jul 31, 2025
35c32d5
Update warning code to only require import package names
vchudnov-g Jul 31, 2025
1f8fafa
Fix messaegs and test
vchudnov-g Jul 31, 2025
529d9da
Add TODO: provide the functionality in previous versions of api_core
vchudnov-g Jul 31, 2025
9ebe274
Fix mypy failures
vchudnov-g Aug 12, 2025
0e27513
Try to remove a round-off error causing a test mock failure
vchudnov-g Aug 12, 2025
5833182
Remove more potential test failures/warnings
vchudnov-g Aug 12, 2025
86da524
Format
vchudnov-g Aug 12, 2025
177e3f1
Try making the specified_timeout a float
vchudnov-g Aug 12, 2025
801776e
fix: tweak message parameter names
vchudnov-g Sep 3, 2025
9ffd73d
fix: add PYTHON_VERSION_STATUS_UNSPECIFIED enum value
vchudnov-g Sep 3, 2025
b6c7702
docs: tweak TODOs
vchudnov-g Sep 3, 2025
050be33
fix test to match code changes
vchudnov-g Sep 3, 2025
8735472
fix: restore asyncio designator for async tests
vchudnov-g Sep 4, 2025
7229c28
Remove some workarounds trying to fix presubmit errors (since fixed)
vchudnov-g Sep 5, 2025
93a8e5a
Additional test tweaks to prevent non-significant failures
vchudnov-g Sep 5, 2025
9b1fe75
chore: fix test that was waiting before initiating operation
vchudnov-g Sep 5, 2025
6415e99
fix: skip coverage checks for code specific to Pyton 3.7
vchudnov-g Sep 5, 2025
fcdfe40
chore: try to address coverage failures
vchudnov-g Sep 8, 2025
417064d
chore: try to address more coverage failures
vchudnov-g Sep 8, 2025
fda4b75
chore: fix comments and exported function
vchudnov-g Sep 9, 2025
9c95bee
fix lint
vchudnov-g Sep 9, 2025
99c4fd2
chore: fix unit test
vchudnov-g Sep 9, 2025
86b5710
chore: lint
vchudnov-g Sep 9, 2025
3a437b7
Use warnings module instead of logging module
vchudnov-g Sep 10, 2025
cbcc5fa
fix: print the current package versions correctly
vchudnov-g Sep 15, 2025
1b2372f
wip: before gemini doc fixes
vchudnov-g Sep 15, 2025
c3107db
fix: return tuple of version tuple and version string, fix tests
vchudnov-g Sep 16, 2025
f031bf1
lint
vchudnov-g Sep 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions google/api_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,22 @@
This package contains common code and utilities used by Google client libraries.
"""

from google.api_core import _python_package_support
from google.api_core import _python_version_support
from google.api_core import version as api_core_version

__version__ = api_core_version.__version__

# NOTE: Until dependent artifacts require this version of
# google.api_core, the functionality below must be made available
# manually in those artifacts.

check_python_version = _python_version_support.check_python_version
check_python_version(package="google.api_core")

check_dependency_versions = _python_package_support.check_dependency_versions
check_dependency_versions("google.api_core")

warn_deprecation_for_versions_less_than = (
_python_package_support.warn_deprecation_for_versions_less_than
)
166 changes: 166 additions & 0 deletions google/api_core/_python_package_support.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Code to check versions of dependencies used by Google Cloud Client Libraries."""

import warnings
import sys
from typing import Optional, Tuple
from ._python_version_support import (
_flatten_message,
_get_distribution_and_import_packages,
)

from packaging.version import parse as parse_version, Version as PackagingVersion


def get_dependency_version(
dependency_name: str,
) -> Tuple[Optional[PackagingVersion], str]:
"""Get the parsed version of an installed package dependency.

This function checks for an installed package and returns its version
as a `packaging.version.Version` object for safe comparison. It handles
both modern (Python 3.8+) and legacy (Python 3.7) environments.

Args:
dependency_name: The distribution name of the package (e.g., 'requests').

Returns:
A tuple containing the `packaging.version.Version` object and the
version string, or `(None, '--')` if the package is not found or
another error occurs during version discovery.
"""
try:
if sys.version_info >= (3, 8):
from importlib import metadata

version_string = metadata.version(dependency_name)
return (parse_version(version_string), version_string)

# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
# this code path once we drop support for Python 3.7
else: # pragma: NO COVER
# Use pkg_resources, which is part of setuptools.
import pkg_resources

version_string = pkg_resources.get_distribution(dependency_name).version
return (parse_version(version_string), version_string)

except Exception:
return (None, "--")


def warn_deprecation_for_versions_less_than(
dependent_import_package: str,
dependency_import_package: str,
next_supported_version: str,
message_template: Optional[str] = None,
):
"""Issue any needed deprecation warnings for `dependency_import_package`.

If `dependency_import_package` is installed at a version less than
`next_supported_version`, this issues a warning using either a
default `message_template` or one provided by the user. The
default `message_template` informs the user that they will not receive
future updates for `dependent_import_package` if
`dependency_import_package` is somehow pinned to a version lower
than `next_supported_version`.

Args:
dependent_import_package: The import name of the package that
needs `dependency_import_package`.
dependency_import_package: The import name of the dependency to check.
next_supported_version: The dependency_import_package version number
below which a deprecation warning will be logged.
message_template: A custom default message template to replace
the default. This `message_template` is treated as an
f-string, where the following variables are defined:
`dependency_import_package`, `dependent_import_package` and
`dependency_distribution_package` and
`dependent_distribution_package` and `dependency_package`,
`dependent_package` , which contain the import packages, the
distribution packages, and pretty string with both the
distribution and import packages for the dependency and the
dependent, respectively; and `next_supported_version`,
`version_used`, and `version_used_string`, which refer to supported
and currently-used versions of the dependency.

"""
if (
not dependent_import_package
or not dependency_import_package
or not next_supported_version
): # pragma: NO COVER
return
(version_used, version_used_string) = get_dependency_version(
dependency_import_package
)
if not version_used:
return
if version_used < parse_version(next_supported_version):
(
dependency_package,
dependency_distribution_package,
) = _get_distribution_and_import_packages(dependency_import_package)
(
dependent_package,
dependent_distribution_package,
) = _get_distribution_and_import_packages(dependent_import_package)
message_template = message_template or _flatten_message(
"""
DEPRECATION: Package {dependent_package} depends on
{dependency_package}, currently installed at version
{version_used_string}. Future updates to
{dependent_package} will require {dependency_package} at
version {next_supported_version} or higher. Please ensure
that either (a) your Python environment doesn't pin the
version of {dependency_package}, so that updates to
{dependent_package} can require the higher version, or
(b) you manually update your Python environment to use at
least version {next_supported_version} of
{dependency_package}.
"""
)
warnings.warn(
message_template.format(
dependent_import_package=dependent_import_package,
dependency_import_package=dependency_import_package,
dependent_distribution_package=dependent_distribution_package,
dependency_distribution_package=dependency_distribution_package,
dependency_package=dependency_package,
dependent_package=dependent_package,
next_supported_version=next_supported_version,
version_used=version_used,
version_used_string=version_used_string,
),
FutureWarning,
)


def check_dependency_versions(dependent_import_package: str):
"""Bundle checks for all package dependencies.

This function can be called by all dependents of google.api_core,
to emit needed deprecation warnings for any of their
dependencies. The dependencies to check should be updated here.

Args:
dependent_import_package: The distribution name of the calling package, whose
dependencies we're checking.

"""
warn_deprecation_for_versions_less_than(
dependent_import_package, "google.protobuf", "4.25.8"
)
Loading