Skip to content

Commit eca55a7

Browse files
Merge branch 'main' into renovate/all
2 parents dad7300 + c97b3a0 commit eca55a7

22 files changed

+1007
-59
lines changed

.github/.OwlBot.lock.yaml

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

.github/.OwlBot.yaml

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

.github/release-please.yml

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

.github/release-trigger.yml

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

.librarian/config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
global_files_allowlist:
2+
# Allow the container to read and write the root `CHANGELOG.md`
3+
# file during the `release` step to update the latest client library
4+
# versions which are hardcoded in the file.
5+
- path: "CHANGELOG.md"
6+
permissions: "read-write"

.librarian/state.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator:latest
2+
libraries:
3+
- id: google-api-core
4+
version: 2.28.1
5+
apis: []
6+
source_roots:
7+
- .
8+
preserve_regex: []
9+
remove_regex: []
10+
tag_format: v{version}

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,27 @@
44

55
[1]: https://pypi.org/project/google-api-core/#history
66

7+
## [2.28.1](https://github.com/googleapis/python-api-core/compare/v2.28.0...v2.28.1) (2025-10-28)
8+
9+
10+
### Bug Fixes
11+
12+
* Remove dependency on packaging and pkg_resources ([#852](https://github.com/googleapis/python-api-core/issues/852)) ([ca59a86](https://github.com/googleapis/python-api-core/commit/ca59a863b08a79c2bf0607f9085de1417422820b))
13+
14+
## [2.28.0](https://github.com/googleapis/python-api-core/compare/v2.27.0...v2.28.0) (2025-10-24)
15+
16+
17+
### Features
18+
19+
* Provide and use Python version support check ([#832](https://github.com/googleapis/python-api-core/issues/832)) ([d36e896](https://github.com/googleapis/python-api-core/commit/d36e896f98a2371c4d58ce1a7a3bc1a77a081836))
20+
21+
## [2.27.0](https://github.com/googleapis/python-api-core/compare/v2.26.0...v2.27.0) (2025-10-22)
22+
23+
24+
### Features
25+
26+
* Support for async bidi streaming apis ([#836](https://github.com/googleapis/python-api-core/issues/836)) ([9530548](https://github.com/googleapis/python-api-core/commit/95305480d234b6dd0903960db020e55125a997e0))
27+
728
## [2.26.0](https://github.com/googleapis/python-api-core/compare/v2.25.2...v2.26.0) (2025-10-08)
829

930

google/api_core/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@
1717
This package contains common code and utilities used by Google client libraries.
1818
"""
1919

20+
from google.api_core import _python_package_support
21+
from google.api_core import _python_version_support
2022
from google.api_core import version as api_core_version
2123

2224
__version__ = api_core_version.__version__
25+
26+
# NOTE: Until dependent artifacts require this version of
27+
# google.api_core, the functionality below must be made available
28+
# manually in those artifacts.
29+
30+
# expose dependency checks for external callers
31+
check_python_version = _python_version_support.check_python_version
32+
check_dependency_versions = _python_package_support.check_dependency_versions
33+
warn_deprecation_for_versions_less_than = (
34+
_python_package_support.warn_deprecation_for_versions_less_than
35+
)
36+
DependencyConstraint = _python_package_support.DependencyConstraint
37+
38+
# perform version checks against api_core, and emit warnings if needed
39+
check_python_version(package="google.api_core")
40+
check_dependency_versions("google.api_core")
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Code to check versions of dependencies used by Google Cloud Client Libraries."""
16+
17+
import warnings
18+
import sys
19+
from typing import Optional, Tuple
20+
21+
from collections import namedtuple
22+
23+
from ._python_version_support import (
24+
_flatten_message,
25+
_get_distribution_and_import_packages,
26+
)
27+
28+
if sys.version_info >= (3, 8):
29+
from importlib import metadata
30+
else:
31+
# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
32+
# this code path once we drop support for Python 3.7
33+
import importlib_metadata as metadata
34+
35+
ParsedVersion = Tuple[int, ...]
36+
37+
# Here we list all the packages for which we want to issue warnings
38+
# about deprecated and unsupported versions.
39+
DependencyConstraint = namedtuple(
40+
"DependencyConstraint",
41+
["package_name", "minimum_fully_supported_version", "recommended_version"],
42+
)
43+
_PACKAGE_DEPENDENCY_WARNINGS = [
44+
DependencyConstraint(
45+
"google.protobuf",
46+
minimum_fully_supported_version="4.25.8",
47+
recommended_version="6.x",
48+
)
49+
]
50+
51+
52+
DependencyVersion = namedtuple("DependencyVersion", ["version", "version_string"])
53+
# Version string we provide in a DependencyVersion when we can't determine the version of a
54+
# package.
55+
UNKNOWN_VERSION_STRING = "--"
56+
57+
58+
def parse_version_to_tuple(version_string: str) -> ParsedVersion:
59+
"""Safely converts a semantic version string to a comparable tuple of integers.
60+
61+
Example: "4.25.8" -> (4, 25, 8)
62+
Ignores non-numeric parts and handles common version formats.
63+
64+
Args:
65+
version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
66+
67+
Returns:
68+
Tuple of integers for the parsed version string.
69+
"""
70+
parts = []
71+
for part in version_string.split("."):
72+
try:
73+
parts.append(int(part))
74+
except ValueError:
75+
# If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
76+
# This is a simplification compared to 'packaging.parse_version', but sufficient
77+
# for comparing strictly numeric semantic versions.
78+
break
79+
return tuple(parts)
80+
81+
82+
def get_dependency_version(
83+
dependency_name: str,
84+
) -> DependencyVersion:
85+
"""Get the parsed version of an installed package dependency.
86+
87+
This function checks for an installed package and returns its version
88+
as a comparable tuple of integers object for safe comparison. It handles
89+
both modern (Python 3.8+) and legacy (Python 3.7) environments.
90+
91+
Args:
92+
dependency_name: The distribution name of the package (e.g., 'requests').
93+
94+
Returns:
95+
A DependencyVersion namedtuple with `version` (a tuple of integers) and
96+
`version_string` attributes, or `DependencyVersion(None,
97+
UNKNOWN_VERSION_STRING)` if the package is not found or
98+
another error occurs during version discovery.
99+
100+
"""
101+
try:
102+
version_string: str = metadata.version(dependency_name)
103+
parsed_version = parse_version_to_tuple(version_string)
104+
return DependencyVersion(parsed_version, version_string)
105+
except Exception:
106+
# Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
107+
# or errors during parse_version_to_tuple
108+
return DependencyVersion(None, UNKNOWN_VERSION_STRING)
109+
110+
111+
def warn_deprecation_for_versions_less_than(
112+
consumer_import_package: str,
113+
dependency_import_package: str,
114+
minimum_fully_supported_version: str,
115+
recommended_version: Optional[str] = None,
116+
message_template: Optional[str] = None,
117+
):
118+
"""Issue any needed deprecation warnings for `dependency_import_package`.
119+
120+
If `dependency_import_package` is installed at a version less than
121+
`minimum_fully_supported_version`, this issues a warning using either a
122+
default `message_template` or one provided by the user. The
123+
default `message_template` informs the user that they will not receive
124+
future updates for `consumer_import_package` if
125+
`dependency_import_package` is somehow pinned to a version lower
126+
than `minimum_fully_supported_version`.
127+
128+
Args:
129+
consumer_import_package: The import name of the package that
130+
needs `dependency_import_package`.
131+
dependency_import_package: The import name of the dependency to check.
132+
minimum_fully_supported_version: The dependency_import_package version number
133+
below which a deprecation warning will be logged.
134+
recommended_version: If provided, the recommended next version, which
135+
could be higher than `minimum_fully_supported_version`.
136+
message_template: A custom default message template to replace
137+
the default. This `message_template` is treated as an
138+
f-string, where the following variables are defined:
139+
`dependency_import_package`, `consumer_import_package` and
140+
`dependency_distribution_package` and
141+
`consumer_distribution_package` and `dependency_package`,
142+
`consumer_package` , which contain the import packages, the
143+
distribution packages, and pretty string with both the
144+
distribution and import packages for the dependency and the
145+
consumer, respectively; and `minimum_fully_supported_version`,
146+
`version_used`, and `version_used_string`, which refer to supported
147+
and currently-used versions of the dependency.
148+
149+
"""
150+
if (
151+
not consumer_import_package
152+
or not dependency_import_package
153+
or not minimum_fully_supported_version
154+
): # pragma: NO COVER
155+
return
156+
157+
dependency_version = get_dependency_version(dependency_import_package)
158+
if not dependency_version.version:
159+
return
160+
161+
if dependency_version.version < parse_version_to_tuple(
162+
minimum_fully_supported_version
163+
):
164+
(
165+
dependency_package,
166+
dependency_distribution_package,
167+
) = _get_distribution_and_import_packages(dependency_import_package)
168+
(
169+
consumer_package,
170+
consumer_distribution_package,
171+
) = _get_distribution_and_import_packages(consumer_import_package)
172+
173+
recommendation = (
174+
" (we recommend {recommended_version})" if recommended_version else ""
175+
)
176+
message_template = message_template or _flatten_message(
177+
"""
178+
DEPRECATION: Package {consumer_package} depends on
179+
{dependency_package}, currently installed at version
180+
{version_used_string}. Future updates to
181+
{consumer_package} will require {dependency_package} at
182+
version {minimum_fully_supported_version} or
183+
higher{recommendation}. Please ensure that either (a) your
184+
Python environment doesn't pin the version of
185+
{dependency_package}, so that updates to
186+
{consumer_package} can require the higher version, or (b)
187+
you manually update your Python environment to use at
188+
least version {minimum_fully_supported_version} of
189+
{dependency_package}.
190+
"""
191+
)
192+
warnings.warn(
193+
message_template.format(
194+
consumer_import_package=consumer_import_package,
195+
dependency_import_package=dependency_import_package,
196+
consumer_distribution_package=consumer_distribution_package,
197+
dependency_distribution_package=dependency_distribution_package,
198+
dependency_package=dependency_package,
199+
consumer_package=consumer_package,
200+
minimum_fully_supported_version=minimum_fully_supported_version,
201+
recommendation=recommendation,
202+
version_used=dependency_version.version,
203+
version_used_string=dependency_version.version_string,
204+
),
205+
FutureWarning,
206+
)
207+
208+
209+
def check_dependency_versions(
210+
consumer_import_package: str, *package_dependency_warnings: DependencyConstraint
211+
):
212+
"""Bundle checks for all package dependencies.
213+
214+
This function can be called by all consumers of google.api_core,
215+
to emit needed deprecation warnings for any of their
216+
dependencies. The dependencies to check can be passed as arguments, or if
217+
none are provided, it will default to the list in
218+
`_PACKAGE_DEPENDENCY_WARNINGS`.
219+
220+
Args:
221+
consumer_import_package: The distribution name of the calling package, whose
222+
dependencies we're checking.
223+
*package_dependency_warnings: A variable number of DependencyConstraint
224+
objects, each specifying a dependency to check.
225+
"""
226+
if not package_dependency_warnings:
227+
package_dependency_warnings = tuple(_PACKAGE_DEPENDENCY_WARNINGS)
228+
for package_info in package_dependency_warnings:
229+
warn_deprecation_for_versions_less_than(
230+
consumer_import_package,
231+
package_info.package_name,
232+
package_info.minimum_fully_supported_version,
233+
recommended_version=package_info.recommended_version,
234+
)

0 commit comments

Comments
 (0)