Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-google-api-core for
openSUSE:Factory checked in at 2026-01-19 18:38:49
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-google-api-core (Old)
and /work/SRC/openSUSE:Factory/.python-google-api-core.new.1928 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-google-api-core"
Mon Jan 19 18:38:49 2026 rev:45 rq:1328093 version:2.29.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-google-api-core/python-google-api-core.changes
2025-10-29 21:07:01.843569265 +0100
+++
/work/SRC/openSUSE:Factory/.python-google-api-core.new.1928/python-google-api-core.changes
2026-01-19 18:42:45.941628758 +0100
@@ -1,0 +2,23 @@
+Mon Jan 19 13:44:31 UTC 2026 - Dirk Müller <[email protected]>
+
+- fix tests
+
+-------------------------------------------------------------------
+Mon Jan 12 10:41:08 UTC 2026 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Update to 2.29.0
+ * Auto enable mTLS when supported certificates are detected (#869)
+ * make parse_version_to_tuple public (#864)
+ * flaky tests due to imprecision in floating point calculation and
+ performance test setup (#865)
+ * remove call to importlib.metadata.packages_distributions() for py38/py39
(#859)
+ * Log version check errors (#858)
+ * closes tailing streams in bidi classes. (#851)
+
+-------------------------------------------------------------------
+Mon Nov 3 11:17:52 UTC 2025 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Update to 2.28.1
+ * Remove dependency on packaging and pkg_resources (#852)
+
+-------------------------------------------------------------------
Old:
----
google_api_core-2.28.0.tar.gz
New:
----
google_api_core-2.29.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-google-api-core.spec ++++++
--- /var/tmp/diff_new_pack.AGKHyL/_old 2026-01-19 18:42:46.505652095 +0100
+++ /var/tmp/diff_new_pack.AGKHyL/_new 2026-01-19 18:42:46.509652261 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-google-api-core
#
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -26,7 +26,7 @@
%endif
%{?sle15_python_module_pythons}
Name: python-google-api-core
-Version: 2.28.0
+Version: 2.29.0
Release: 0
Summary: Google API client core library
License: Apache-2.0
@@ -76,7 +76,8 @@
%check
%if %{with test}
-%pytest
+# test_get_dependency_version: requires mocker which is unpackaged
+%pytest -k "not test_get_dependency_version"
%endif
%if !%{with test}
++++++ google_api_core-2.28.0.tar.gz -> google_api_core-2.29.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google_api_core-2.28.0/PKG-INFO
new/google_api_core-2.29.0/PKG-INFO
--- old/google_api_core-2.28.0/PKG-INFO 2025-10-27 23:38:26.463393000 +0100
+++ new/google_api_core-2.29.0/PKG-INFO 2026-01-08 23:09:05.462095700 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: google-api-core
-Version: 2.28.0
+Version: 2.29.0
Summary: Google API client core library
Author-email: Google LLC <[email protected]>
License: Apache 2.0
@@ -30,6 +30,7 @@
Requires-Dist: proto-plus<2.0.0,>=1.25.0; python_version >= "3.13"
Requires-Dist: google-auth<3.0.0,>=2.14.1
Requires-Dist: requests<3.0.0,>=2.18.0
+Requires-Dist: importlib_metadata>=1.4; python_version < "3.8"
Provides-Extra: async-rest
Requires-Dist: google-auth[aiohttp]<3.0.0,>=2.35.0; extra == "async-rest"
Provides-Extra: grpc
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google_api_core-2.28.0/google/api_core/__init__.py
new/google_api_core-2.29.0/google/api_core/__init__.py
--- old/google_api_core-2.28.0/google/api_core/__init__.py 2025-10-27
23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/__init__.py 2026-01-08
23:08:42.000000000 +0100
@@ -30,6 +30,7 @@
# expose dependency checks for external callers
check_python_version = _python_version_support.check_python_version
check_dependency_versions = _python_package_support.check_dependency_versions
+parse_version_to_tuple = _python_package_support.parse_version_to_tuple
warn_deprecation_for_versions_less_than = (
_python_package_support.warn_deprecation_for_versions_less_than
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/google/api_core/_python_package_support.py
new/google_api_core-2.29.0/google/api_core/_python_package_support.py
--- old/google_api_core-2.28.0/google/api_core/_python_package_support.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/_python_package_support.py
2026-01-08 23:08:42.000000000 +0100
@@ -16,7 +16,7 @@
import warnings
import sys
-from typing import Optional
+from typing import Optional, Tuple
from collections import namedtuple
@@ -25,7 +25,14 @@
_get_distribution_and_import_packages,
)
-from packaging.version import parse as parse_version
+if sys.version_info >= (3, 8):
+ from importlib import metadata
+else:
+ # TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
+ # this code path once we drop support for Python 3.7
+ import importlib_metadata as metadata
+
+ParsedVersion = Tuple[int, ...]
# Here we list all the packages for which we want to issue warnings
# about deprecated and unsupported versions.
@@ -48,42 +55,56 @@
UNKNOWN_VERSION_STRING = "--"
+def parse_version_to_tuple(version_string: str) -> ParsedVersion:
+ """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_dependency_version(
dependency_name: str,
) -> DependencyVersion:
"""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
+ as a comparable tuple of integers 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 DependencyVersion namedtuple with `version` and
+ A DependencyVersion namedtuple with `version` (a tuple of integers)
and
`version_string` attributes, or `DependencyVersion(None,
UNKNOWN_VERSION_STRING)` 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 DependencyVersion(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 DependencyVersion(parse_version(version_string),
version_string)
-
+ version_string: str = metadata.version(dependency_name)
+ parsed_version = parse_version_to_tuple(version_string)
+ return DependencyVersion(parsed_version, version_string)
except Exception:
+ # Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
+ # or errors during parse_version_to_tuple
return DependencyVersion(None, UNKNOWN_VERSION_STRING)
@@ -132,10 +153,14 @@
or not minimum_fully_supported_version
): # pragma: NO COVER
return
+
dependency_version = get_dependency_version(dependency_import_package)
if not dependency_version.version:
return
- if dependency_version.version <
parse_version(minimum_fully_supported_version):
+
+ if dependency_version.version < parse_version_to_tuple(
+ minimum_fully_supported_version
+ ):
(
dependency_package,
dependency_distribution_package,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/google/api_core/_python_version_support.py
new/google_api_core-2.29.0/google/api_core/_python_version_support.py
--- old/google_api_core-2.28.0/google/api_core/_python_version_support.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/_python_version_support.py
2026-01-08 23:08:42.000000000 +0100
@@ -16,12 +16,16 @@
import datetime
import enum
+import logging
import warnings
import sys
import textwrap
from typing import Any, List, NamedTuple, Optional, Dict, Tuple
+_LOGGER = logging.getLogger(__name__)
+
+
class PythonVersionStatus(enum.Enum):
"""Support status of a Python version in this client library artifact
release.
@@ -147,9 +151,11 @@
return " ".join(textwrap.dedent(text).strip().split())
-# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove once
we
-# no longer support Python 3.7
-if sys.version_info < (3, 8):
+# TODO(https://github.com/googleapis/python-api-core/issues/835):
+# Remove once we no longer support Python 3.9.
+# `importlib.metadata.packages_distributions()` is only supported in Python
3.10 and newer
+#
https://docs.python.org/3/library/importlib.metadata.html#importlib.metadata.packages_distributions
+if sys.version_info < (3, 10):
def _get_pypi_package_name(module_name): # pragma: NO COVER
"""Determine the PyPI package name for a given module name."""
@@ -168,11 +174,14 @@
if module_name in module_to_distributions: # pragma: NO COVER
# The value is a list of distribution names, take the first one
return module_to_distributions[module_name][0]
- else:
- return None # Module not found in the mapping
- except Exception as e:
- print(f"An error occurred: {e}")
- return None
+ except Exception as e: # pragma: NO COVER
+ _LOGGER.info(
+ "An error occurred while determining PyPI package name for %s:
%s",
+ module_name,
+ e,
+ )
+
+ return None
def _get_distribution_and_import_packages(import_package: str) -> Tuple[str,
Any]:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google_api_core-2.28.0/google/api_core/bidi.py
new/google_api_core-2.29.0/google/api_core/bidi.py
--- old/google_api_core-2.28.0/google/api_core/bidi.py 2025-10-27
23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/bidi.py 2026-01-08
23:08:42.000000000 +0100
@@ -281,11 +281,11 @@
def close(self):
"""Closes the stream."""
- if self.call is None:
- return
+ if self.call is not None:
+ self.call.cancel()
+ # Put None in request queue to signal termination.
self._request_queue.put(None)
- self.call.cancel()
self._request_generator = None
self._initial_request = None
self._callbacks = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google_api_core-2.28.0/google/api_core/bidi_async.py
new/google_api_core-2.29.0/google/api_core/bidi_async.py
--- old/google_api_core-2.28.0/google/api_core/bidi_async.py 2025-10-27
23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/bidi_async.py 2026-01-08
23:08:42.000000000 +0100
@@ -197,11 +197,11 @@
async def close(self) -> None:
"""Closes the stream."""
- if self.call is None:
- return
+ if self.call is not None:
+ self.call.cancel()
+ # Put None in request queue to signal termination.
await self._request_queue.put(None)
- self.call.cancel()
self._request_generator = None
self._initial_request = None
self._callbacks = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/google/api_core/grpc_helpers_async.py
new/google_api_core-2.29.0/google/api_core/grpc_helpers_async.py
--- old/google_api_core-2.28.0/google/api_core/grpc_helpers_async.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/grpc_helpers_async.py
2026-01-08 23:08:42.000000000 +0100
@@ -220,7 +220,7 @@
default_host=None,
compression=None,
attempt_direct_path: Optional[bool] = False,
- **kwargs
+ **kwargs,
):
"""Create an AsyncIO secure channel with credentials.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google_api_core-2.28.0/google/api_core/operation.py
new/google_api_core-2.29.0/google/api_core/operation.py
--- old/google_api_core-2.28.0/google/api_core/operation.py 2025-10-27
23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/operation.py 2026-01-08
23:08:42.000000000 +0100
@@ -78,7 +78,7 @@
result_type,
metadata_type=None,
polling=polling.DEFAULT_POLLING,
- **kwargs
+ **kwargs,
):
super(Operation, self).__init__(polling=polling, **kwargs)
self._operation = operation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/google/api_core/operations_v1/abstract_operations_base_client.py
new/google_api_core-2.29.0/google/api_core/operations_v1/abstract_operations_base_client.py
---
old/google_api_core-2.28.0/google/api_core/operations_v1/abstract_operations_base_client.py
2025-10-27 23:37:20.000000000 +0100
+++
new/google_api_core-2.29.0/google/api_core/operations_v1/abstract_operations_base_client.py
2026-01-08 23:08:42.000000000 +0100
@@ -300,16 +300,22 @@
client_options = client_options_lib.ClientOptions()
# Create SSL credentials for mutual TLS if needed.
- use_client_cert = os.getenv(
- "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
- ).lower()
- if use_client_cert not in ("true", "false"):
- raise ValueError(
- "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must
be either `true` or `false`"
- )
+ if hasattr(mtls, "should_use_client_cert"):
+ use_client_cert = mtls.should_use_client_cert()
+ else:
+ # if unsupported, fallback to reading from env var
+ use_client_cert_str = os.getenv(
+ "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
+ ).lower()
+ if use_client_cert_str not in ("true", "false"):
+ raise ValueError(
+ "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE`
must be"
+ " either `true` or `false`"
+ )
+ use_client_cert = use_client_cert_str == "true"
client_cert_source_func = None
is_mtls = False
- if use_client_cert == "true":
+ if use_client_cert:
if client_options.client_cert_source:
is_mtls = True
client_cert_source_func = client_options.client_cert_source
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/google/api_core/operations_v1/pagers.py
new/google_api_core-2.29.0/google/api_core/operations_v1/pagers.py
--- old/google_api_core-2.28.0/google/api_core/operations_v1/pagers.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/operations_v1/pagers.py
2026-01-08 23:08:42.000000000 +0100
@@ -48,7 +48,7 @@
request: operations_pb2.ListOperationsRequest,
response: operations_pb2.ListOperationsResponse,
*,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, str]] = (),
):
super().__init__(
method=method, request=request, response=response,
metadata=metadata
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/google/api_core/operations_v1/pagers_async.py
new/google_api_core-2.29.0/google/api_core/operations_v1/pagers_async.py
--- old/google_api_core-2.28.0/google/api_core/operations_v1/pagers_async.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/operations_v1/pagers_async.py
2026-01-08 23:08:42.000000000 +0100
@@ -48,7 +48,7 @@
request: operations_pb2.ListOperationsRequest,
response: operations_pb2.ListOperationsResponse,
*,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, str]] = (),
):
super().__init__(
method=method, request=request, response=response,
metadata=metadata
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/google/api_core/operations_v1/pagers_base.py
new/google_api_core-2.29.0/google/api_core/operations_v1/pagers_base.py
--- old/google_api_core-2.28.0/google/api_core/operations_v1/pagers_base.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/operations_v1/pagers_base.py
2026-01-08 23:08:42.000000000 +0100
@@ -47,7 +47,7 @@
request: operations_pb2.ListOperationsRequest,
response: operations_pb2.ListOperationsResponse,
*,
- metadata: Sequence[Tuple[str, str]] = ()
+ metadata: Sequence[Tuple[str, str]] = (),
):
"""Instantiate the pager.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/google/api_core/operations_v1/transports/base.py
new/google_api_core-2.29.0/google/api_core/operations_v1/transports/base.py
--- old/google_api_core-2.28.0/google/api_core/operations_v1/transports/base.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/operations_v1/transports/base.py
2026-01-08 23:08:42.000000000 +0100
@@ -119,8 +119,6 @@
host += ":443" # pragma: NO COVER
self._host = host
- scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES}
-
# Save the scopes.
self._scopes = scopes
@@ -133,12 +131,17 @@
if credentials_file is not None:
credentials, _ = google.auth.load_credentials_from_file(
- credentials_file, **scopes_kwargs,
quota_project_id=quota_project_id
+ credentials_file,
+ scopes=scopes,
+ quota_project_id=quota_project_id,
+ default_scopes=self.AUTH_SCOPES,
)
elif credentials is None:
credentials, _ = google.auth.default(
- **scopes_kwargs, quota_project_id=quota_project_id
+ scopes=scopes,
+ quota_project_id=quota_project_id,
+ default_scopes=self.AUTH_SCOPES,
)
# If the credentials are service account credentials, then always try
to use self signed JWT.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google_api_core-2.28.0/google/api_core/version.py
new/google_api_core-2.29.0/google/api_core/version.py
--- old/google_api_core-2.28.0/google/api_core/version.py 2025-10-27
23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/google/api_core/version.py 2026-01-08
23:08:42.000000000 +0100
@@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-__version__ = "2.28.0"
+__version__ = "2.29.0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/google_api_core.egg-info/PKG-INFO
new/google_api_core-2.29.0/google_api_core.egg-info/PKG-INFO
--- old/google_api_core-2.28.0/google_api_core.egg-info/PKG-INFO
2025-10-27 23:38:26.000000000 +0100
+++ new/google_api_core-2.29.0/google_api_core.egg-info/PKG-INFO
2026-01-08 23:09:05.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: google-api-core
-Version: 2.28.0
+Version: 2.29.0
Summary: Google API client core library
Author-email: Google LLC <[email protected]>
License: Apache 2.0
@@ -30,6 +30,7 @@
Requires-Dist: proto-plus<2.0.0,>=1.25.0; python_version >= "3.13"
Requires-Dist: google-auth<3.0.0,>=2.14.1
Requires-Dist: requests<3.0.0,>=2.18.0
+Requires-Dist: importlib_metadata>=1.4; python_version < "3.8"
Provides-Extra: async-rest
Requires-Dist: google-auth[aiohttp]<3.0.0,>=2.35.0; extra == "async-rest"
Provides-Extra: grpc
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/google_api_core.egg-info/requires.txt
new/google_api_core-2.29.0/google_api_core.egg-info/requires.txt
--- old/google_api_core-2.28.0/google_api_core.egg-info/requires.txt
2025-10-27 23:38:26.000000000 +0100
+++ new/google_api_core-2.29.0/google_api_core.egg-info/requires.txt
2026-01-08 23:09:05.000000000 +0100
@@ -4,6 +4,9 @@
google-auth<3.0.0,>=2.14.1
requests<3.0.0,>=2.18.0
+[:python_version < "3.8"]
+importlib_metadata>=1.4
+
[:python_version >= "3.13"]
proto-plus<2.0.0,>=1.25.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google_api_core-2.28.0/pyproject.toml
new/google_api_core-2.29.0/pyproject.toml
--- old/google_api_core-2.28.0/pyproject.toml 2025-10-27 23:37:20.000000000
+0100
+++ new/google_api_core-2.29.0/pyproject.toml 2026-01-08 23:08:42.000000000
+0100
@@ -51,6 +51,9 @@
"proto-plus >= 1.25.0, < 2.0.0; python_version >= '3.13'",
"google-auth >= 2.14.1, < 3.0.0",
"requests >= 2.18.0, < 3.0.0",
+ # TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
+ # `importlib_metadata` once we drop support for Python 3.7
+ "importlib_metadata>=1.4; python_version<'3.8'",
]
dynamic = ["version"]
@@ -80,7 +83,7 @@
include = ["google*"]
[tool.mypy]
-python_version = "3.7"
+python_version = "3.14"
namespace_packages = true
ignore_missing_imports = true
@@ -88,6 +91,8 @@
filterwarnings = [
# treat all warnings as errors
"error",
+ # Prevent Python version warnings from interfering with tests
+ "ignore:.* Python version .*:FutureWarning",
# Remove once https://github.com/pytest-dev/pytest-cov/issues/621 is fixed
"ignore:.*The --rsyncdir command line argument and rsyncdirs config variable
are deprecated:DeprecationWarning",
# Remove once https://github.com/protocolbuffers/protobuf/issues/12186 is
fixed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google_api_core-2.28.0/setup.cfg
new/google_api_core-2.29.0/setup.cfg
--- old/google_api_core-2.28.0/setup.cfg 2025-10-27 23:38:26.463393000
+0100
+++ new/google_api_core-2.29.0/setup.cfg 2026-01-08 23:09:05.463430000
+0100
@@ -1,12 +1,3 @@
-[pytype]
-python_version = 3.7
-inputs =
- google/
-exclude =
- tests/
-output = .pytype/
-disable = pyi-error
-
[egg_info]
tag_build =
tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/asyncio/gapic/test_method_async.py
new/google_api_core-2.29.0/tests/asyncio/gapic/test_method_async.py
--- old/google_api_core-2.28.0/tests/asyncio/gapic/test_method_async.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/asyncio/gapic/test_method_async.py
2026-01-08 23:08:42.000000000 +0100
@@ -260,7 +260,7 @@
actual_timeout = method.call_args[1]["timeout"]
metadata = method.call_args[1]["metadata"]
assert metadata == mock.ANY
- assert actual_timeout == pytest.approx(22, abs=0.01)
+ assert actual_timeout == pytest.approx(22, abs=0.05)
@pytest.mark.asyncio
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/asyncio/test_bidi_async.py
new/google_api_core-2.29.0/tests/asyncio/test_bidi_async.py
--- old/google_api_core-2.28.0/tests/asyncio/test_bidi_async.py 2025-10-27
23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/asyncio/test_bidi_async.py 2026-01-08
23:08:42.000000000 +0100
@@ -256,6 +256,21 @@
assert not bidi_rpc._callbacks
@pytest.mark.asyncio
+ async def test_close_with_no_rpc(self):
+ bidi_rpc = bidi_async.AsyncBidiRpc(None)
+
+ await bidi_rpc.close()
+
+ assert bidi_rpc.call is None
+ assert bidi_rpc.is_active is False
+ # ensure the request queue was signaled to stop.
+ assert bidi_rpc.pending_requests == 1
+ assert await bidi_rpc._request_queue.get() is None
+ # ensure request and callbacks are cleaned up
+ assert bidi_rpc._initial_request is None
+ assert not bidi_rpc._callbacks
+
+ @pytest.mark.asyncio
async def test_close_no_rpc(self):
bidi_rpc = bidi_async.AsyncBidiRpc(None)
await bidi_rpc.close()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/asyncio/test_grpc_helpers_async.py
new/google_api_core-2.29.0/tests/asyncio/test_grpc_helpers_async.py
--- old/google_api_core-2.28.0/tests/asyncio/test_grpc_helpers_async.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/asyncio/test_grpc_helpers_async.py
2026-01-08 23:08:42.000000000 +0100
@@ -17,6 +17,7 @@
from unittest.mock import AsyncMock # pragma: NO COVER # noqa: F401
except ImportError: # pragma: NO COVER
import mock # type: ignore
+from ..helpers import warn_deprecated_credentials_file
import pytest # noqa: I202
try:
@@ -522,11 +523,12 @@
target = "example:443"
with pytest.raises(exceptions.DuplicateCredentialArgs) as excinfo:
- grpc_helpers_async.create_channel(
- target,
- credentials_file="credentials.json",
- credentials=mock.sentinel.credentials,
- )
+ with warn_deprecated_credentials_file():
+ grpc_helpers_async.create_channel(
+ target,
+ credentials_file="credentials.json",
+ credentials=mock.sentinel.credentials,
+ )
assert "mutually exclusive" in str(excinfo.value)
@@ -641,9 +643,10 @@
credentials_file = "/path/to/credentials/file.json"
composite_creds = composite_creds_call.return_value
- channel = grpc_helpers_async.create_channel(
- target, credentials_file=credentials_file
- )
+ with warn_deprecated_credentials_file():
+ channel = grpc_helpers_async.create_channel(
+ target, credentials_file=credentials_file
+ )
google.auth.load_credentials_from_file.assert_called_once_with(
credentials_file, scopes=None, default_scopes=None
@@ -670,9 +673,10 @@
credentials_file = "/path/to/credentials/file.json"
composite_creds = composite_creds_call.return_value
- channel = grpc_helpers_async.create_channel(
- target, credentials_file=credentials_file, scopes=scopes
- )
+ with warn_deprecated_credentials_file():
+ channel = grpc_helpers_async.create_channel(
+ target, credentials_file=credentials_file, scopes=scopes
+ )
google.auth.load_credentials_from_file.assert_called_once_with(
credentials_file, scopes=scopes, default_scopes=None
@@ -699,9 +703,10 @@
credentials_file = "/path/to/credentials/file.json"
composite_creds = composite_creds_call.return_value
- channel = grpc_helpers_async.create_channel(
- target, credentials_file=credentials_file,
default_scopes=default_scopes
- )
+ with warn_deprecated_credentials_file():
+ channel = grpc_helpers_async.create_channel(
+ target, credentials_file=credentials_file,
default_scopes=default_scopes
+ )
google.auth.load_credentials_from_file.assert_called_once_with(
credentials_file, scopes=None, default_scopes=default_scopes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/asyncio/test_rest_streaming_async.py
new/google_api_core-2.29.0/tests/asyncio/test_rest_streaming_async.py
--- old/google_api_core-2.28.0/tests/asyncio/test_rest_streaming_async.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/asyncio/test_rest_streaming_async.py
2026-01-08 23:08:42.000000000 +0100
@@ -292,7 +292,6 @@
@pytest.mark.asyncio
@pytest.mark.parametrize("response_type", [EchoResponse,
httpbody_pb2.HttpBody])
async def test_next_not_array(response_type):
-
data = '{"hello": 0}'
with mock.patch.object(
ResponseMock, "content", return_value=mock_async_gen(data)
@@ -352,7 +351,6 @@
@pytest.mark.asyncio
@pytest.mark.parametrize("response_type", [EchoResponse,
httpbody_pb2.HttpBody])
async def test_next_html(response_type):
-
data = "<!DOCTYPE html><html></html>"
with mock.patch.object(
ResponseMock, "content", return_value=mock_async_gen(data)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google_api_core-2.28.0/tests/helpers.py
new/google_api_core-2.29.0/tests/helpers.py
--- old/google_api_core-2.28.0/tests/helpers.py 2025-10-27 23:37:20.000000000
+0100
+++ new/google_api_core-2.29.0/tests/helpers.py 2026-01-08 23:08:42.000000000
+0100
@@ -14,7 +14,9 @@
"""Helpers for tests"""
+import functools
import logging
+import pytest # noqa: I202
from typing import List
import proto
@@ -69,3 +71,12 @@
logging.info(f"Sending JSON stream: {json_responses}")
ret_val = "[{}]".format(",".join(json_responses))
return bytes(ret_val, "utf-8")
+
+
+warn_deprecated_credentials_file = functools.partial(
+ # This is used to test that the auth credentials file deprecation
+ # warning is emitted as expected.
+ pytest.warns,
+ DeprecationWarning,
+ match="argument is deprecated because of a potential security risk",
+)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/unit/gapic/test_method.py
new/google_api_core-2.29.0/tests/unit/gapic/test_method.py
--- old/google_api_core-2.28.0/tests/unit/gapic/test_method.py 2025-10-27
23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/unit/gapic/test_method.py 2026-01-08
23:08:42.000000000 +0100
@@ -192,6 +192,7 @@
)
[email protected](reason="Known flaky due to floating point comparison. #866")
def test_wrap_method_with_overriding_timeout_as_a_number():
method = mock.Mock(spec=["__call__"], return_value=42)
default_retry = retry.Retry()
@@ -200,6 +201,8 @@
method, default_retry, default_timeout
)
+ # Using "result = wrapped_method(timeout=22)" fails since wrapped_method
+ # does floating point calculations that results in 21.987.. instead of 22
result = wrapped_method(timeout=22)
assert result == 42
@@ -210,6 +213,24 @@
assert actual_timeout == pytest.approx(22, abs=0.01)
+def test_wrap_method_with_overriding_constant_timeout():
+ method = mock.Mock(spec=["__call__"], return_value=42)
+ default_retry = retry.Retry()
+ default_timeout = timeout.ConstantTimeout(60)
+ wrapped_method = google.api_core.gapic_v1.method.wrap_method(
+ method, default_retry, default_timeout
+ )
+
+ result = wrapped_method(timeout=timeout.ConstantTimeout(22))
+
+ assert result == 42
+
+ actual_timeout = method.call_args[1]["timeout"]
+ metadata = method.call_args[1]["metadata"]
+ assert metadata == mock.ANY
+ assert actual_timeout == 22
+
+
def test_wrap_method_with_call():
method = mock.Mock()
mock_call = mock.Mock()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/unit/gapic/test_routing_header.py
new/google_api_core-2.29.0/tests/unit/gapic/test_routing_header.py
--- old/google_api_core-2.28.0/tests/unit/gapic/test_routing_header.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/unit/gapic/test_routing_header.py
2026-01-08 23:08:42.000000000 +0100
@@ -90,8 +90,8 @@
def test__urlencode_param_caching_performance():
import time
- key = "key" * 100
- value = "value" * 100
+ key = "key" * 10000
+ value = "value" * 10000
# time with empty cache
start_time = time.perf_counter()
routing_header._urlencode_param(key, value)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/unit/operations_v1/test_operations_rest_client.py
new/google_api_core-2.29.0/tests/unit/operations_v1/test_operations_rest_client.py
---
old/google_api_core-2.28.0/tests/unit/operations_v1/test_operations_rest_client.py
2025-10-27 23:37:20.000000000 +0100
+++
new/google_api_core-2.29.0/tests/unit/operations_v1/test_operations_rest_client.py
2026-01-08 23:08:42.000000000 +0100
@@ -23,6 +23,7 @@
import pytest
from typing import Any, List
+from ...helpers import warn_deprecated_credentials_file
try:
import grpc # noqa: F401
@@ -34,6 +35,7 @@
from google.api_core import client_options
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
+from google.api_core import parse_version_to_tuple
from google.api_core.operations_v1 import AbstractOperationsClient
import google.auth
@@ -41,6 +43,7 @@
from google.api_core.operations_v1 import pagers_async
from google.api_core.operations_v1 import transports
from google.auth import credentials as ga_credentials
+from google.auth import __version__ as auth_version
from google.auth.exceptions import MutualTLSChannelError
from google.longrunning import operations_pb2
from google.oauth2 import service_account
@@ -225,7 +228,6 @@
PYPARAM_CLIENT,
)
def test_operations_client_from_service_account_file(client_class):
-
if "async" in str(client_class):
# TODO(): Add support for service account creds to async REST
transport.
with pytest.raises(NotImplementedError):
@@ -345,12 +347,30 @@
with pytest.raises(MutualTLSChannelError):
client = client_class()
- # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value.
+ # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value
with mock.patch.dict(
os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
):
- with pytest.raises(ValueError):
- client = client_class()
+ # Test behavior for google.auth versions < 2.43.0.
+ # These versions do not have the updated mtls.should_use_client_cert
logic.
+ # Verify that a ValueError is raised when
GOOGLE_API_USE_CLIENT_CERTIFICATE
+ # is set to an unsupported value, as expected in these older versions.
+ if parse_version_to_tuple(auth_version) < (2, 43, 0):
+ with pytest.raises(ValueError):
+ client = client_class()
+ # Test behavior for google.auth versions >= 2.43.0.
+ # In these versions, if GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an
+ # unsupported value (e.g., not 'true' or 'false'), the expected
behavior
+ # of the internal google.auth.mtls.should_use_client_cert() function
+ # is to return False. Expect should_use_client_cert to return False, so
+ # client creation should proceed without requiring a client
certificate.
+ else:
+ with mock.patch.object(transport_class, "__init__") as patched:
+ patched.return_value = None
+ client = client_class(
+ credentials=ga_credentials.AnonymousCredentials(),
+ transport=transport_name,
+ )
# Check the case quota_project_id is provided
options = client_options.ClientOptions(quota_project_id="octopus")
@@ -369,7 +389,8 @@
)
# Check the case credentials_file is provided
- options = client_options.ClientOptions(credentials_file="credentials.json")
+ with warn_deprecated_credentials_file():
+ options =
client_options.ClientOptions(credentials_file="credentials.json")
with mock.patch.object(transport_class, "__init__") as patched:
patched.return_value = None
client = client_class(client_options=options, transport=transport_name)
@@ -539,11 +560,13 @@
client_class, transport_class, transport_name
):
# Check the case credentials file is provided.
- options = client_options.ClientOptions(credentials_file="credentials.json")
+ with warn_deprecated_credentials_file():
+ options =
client_options.ClientOptions(credentials_file="credentials.json")
if "async" in str(client_class):
# TODO(): Add support for credentials file to async REST transport.
with pytest.raises(core_exceptions.AsyncRestUnsupportedParameterError):
- client_class(client_options=options, transport=transport_name)
+ with warn_deprecated_credentials_file():
+ client_class(client_options=options, transport=transport_name)
else:
with mock.patch.object(transport_class, "__init__") as patched:
patched.return_value = None
@@ -570,10 +593,18 @@
return_value=(mock.sentinel.credentials, mock.sentinel.project),
)
def test_list_operations_rest(google_auth_default, credentials_file):
- sync_transport = transports.rest.OperationsRestTransport(
- credentials_file=credentials_file,
- http_options=HTTP_OPTIONS,
- )
+ if credentials_file:
+ with warn_deprecated_credentials_file():
+ sync_transport = transports.rest.OperationsRestTransport(
+ credentials_file=credentials_file,
+ http_options=HTTP_OPTIONS,
+ )
+ else:
+ # no warning expected
+ sync_transport = transports.rest.OperationsRestTransport(
+ credentials_file=credentials_file,
+ http_options=HTTP_OPTIONS,
+ )
client = AbstractOperationsClient(transport=sync_transport)
@@ -1076,7 +1107,6 @@
PYPARAM_CLIENT_TRANSPORT_CREDENTIALS,
)
def test_credentials_transport_error(client_class, transport_class,
credentials):
-
# It is an error to provide credentials and a transport instance.
transport = transport_class(credentials=credentials)
with pytest.raises(ValueError):
@@ -1130,10 +1160,11 @@
def test_operations_base_transport_error():
# Passing both a credentials object and credentials_file should raise an
error
with pytest.raises(core_exceptions.DuplicateCredentialArgs):
- transports.OperationsTransport(
- credentials=ga_credentials.AnonymousCredentials(),
- credentials_file="credentials.json",
- )
+ with warn_deprecated_credentials_file():
+ transports.OperationsTransport(
+ credentials=ga_credentials.AnonymousCredentials(),
+ credentials_file="credentials.json",
+ )
def test_operations_base_transport():
@@ -1171,10 +1202,11 @@
) as Transport:
Transport.return_value = None
load_creds.return_value = (ga_credentials.AnonymousCredentials(), None)
- transports.OperationsTransport(
- credentials_file="credentials.json",
- quota_project_id="octopus",
- )
+ with warn_deprecated_credentials_file():
+ transports.OperationsTransport(
+ credentials_file="credentials.json",
+ quota_project_id="octopus",
+ )
load_creds.assert_called_once_with(
"credentials.json",
scopes=None,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google_api_core-2.28.0/tests/unit/test_bidi.py
new/google_api_core-2.29.0/tests/unit/test_bidi.py
--- old/google_api_core-2.28.0/tests/unit/test_bidi.py 2025-10-27
23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/unit/test_bidi.py 2026-01-08
23:08:42.000000000 +0100
@@ -301,10 +301,19 @@
assert bidi_rpc._initial_request is None
assert not bidi_rpc._callbacks
- def test_close_no_rpc(self):
+ def test_close_with_no_rpc(self):
bidi_rpc = bidi.BidiRpc(None)
bidi_rpc.close()
+ assert bidi_rpc.call is None
+ assert bidi_rpc.is_active is False
+ # ensure the request queue was signaled to stop.
+ assert bidi_rpc.pending_requests == 1
+ assert bidi_rpc._request_queue.get() is None
+ # ensure request and callbacks are cleaned up
+ assert bidi_rpc._initial_request is None
+ assert not bidi_rpc._callbacks
+
def test_send(self):
rpc, call = make_rpc()
bidi_rpc = bidi.BidiRpc(rpc)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/unit/test_client_logging.py
new/google_api_core-2.29.0/tests/unit/test_client_logging.py
--- old/google_api_core-2.28.0/tests/unit/test_client_logging.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/unit/test_client_logging.py
2026-01-08 23:08:42.000000000 +0100
@@ -88,7 +88,6 @@
def test_initialize_logging():
-
with mock.patch("os.getenv", return_value="foogle.bar"):
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME",
"foogle"):
initialize_logging()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/unit/test_client_options.py
new/google_api_core-2.29.0/tests/unit/test_client_options.py
--- old/google_api_core-2.28.0/tests/unit/test_client_options.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/unit/test_client_options.py
2026-01-08 23:08:42.000000000 +0100
@@ -14,6 +14,7 @@
from re import match
import pytest
+from ..helpers import warn_deprecated_credentials_file
from google.api_core import client_options
@@ -27,19 +28,19 @@
def test_constructor():
-
- options = client_options.ClientOptions(
- api_endpoint="foo.googleapis.com",
- client_cert_source=get_client_cert,
- quota_project_id="quote-proj",
- credentials_file="path/to/credentials.json",
- scopes=[
- "https://www.googleapis.com/auth/cloud-platform",
- "https://www.googleapis.com/auth/cloud-platform.read-only",
- ],
- api_audience="foo2.googleapis.com",
- universe_domain="googleapis.com",
- )
+ with warn_deprecated_credentials_file():
+ options = client_options.ClientOptions(
+ api_endpoint="foo.googleapis.com",
+ client_cert_source=get_client_cert,
+ quota_project_id="quote-proj",
+ credentials_file="path/to/credentials.json",
+ scopes=[
+ "https://www.googleapis.com/auth/cloud-platform",
+ "https://www.googleapis.com/auth/cloud-platform.read-only",
+ ],
+ api_audience="foo2.googleapis.com",
+ universe_domain="googleapis.com",
+ )
assert options.api_endpoint == "foo.googleapis.com"
assert options.client_cert_source() == (b"cert", b"key")
@@ -54,7 +55,6 @@
def test_constructor_with_encrypted_cert_source():
-
options = client_options.ClientOptions(
api_endpoint="foo.googleapis.com",
client_encrypted_cert_source=get_client_encrypted_cert,
@@ -78,7 +78,6 @@
def test_constructor_with_api_key():
-
options = client_options.ClientOptions(
api_endpoint="foo.googleapis.com",
client_cert_source=get_client_cert,
@@ -102,10 +101,11 @@
def test_constructor_with_both_api_key_and_credentials_file():
with pytest.raises(ValueError):
- client_options.ClientOptions(
- api_key="api-key",
- credentials_file="path/to/credentials.json",
- )
+ with warn_deprecated_credentials_file():
+ client_options.ClientOptions(
+ api_key="api-key",
+ credentials_file="path/to/credentials.json",
+ )
def test_from_dict():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/unit/test_grpc_helpers.py
new/google_api_core-2.29.0/tests/unit/test_grpc_helpers.py
--- old/google_api_core-2.28.0/tests/unit/test_grpc_helpers.py 2025-10-27
23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/unit/test_grpc_helpers.py 2026-01-08
23:08:42.000000000 +0100
@@ -15,6 +15,7 @@
from unittest import mock
import pytest
+from ..helpers import warn_deprecated_credentials_file
try:
import grpc
@@ -581,11 +582,12 @@
target = "example.com:443"
with pytest.raises(exceptions.DuplicateCredentialArgs):
- grpc_helpers.create_channel(
- target,
- credentials_file="credentials.json",
- credentials=mock.sentinel.credentials,
- )
+ with warn_deprecated_credentials_file():
+ grpc_helpers.create_channel(
+ target,
+ credentials_file="credentials.json",
+ credentials=mock.sentinel.credentials,
+ )
@mock.patch("grpc.compute_engine_channel_credentials")
@@ -710,7 +712,8 @@
credentials_file = "/path/to/credentials/file.json"
composite_creds = composite_creds_call.return_value
- channel = grpc_helpers.create_channel(target,
credentials_file=credentials_file)
+ with warn_deprecated_credentials_file():
+ channel = grpc_helpers.create_channel(target,
credentials_file=credentials_file)
google.auth.load_credentials_from_file.assert_called_once_with(
credentials_file, scopes=None, default_scopes=None
@@ -742,9 +745,10 @@
credentials_file = "/path/to/credentials/file.json"
composite_creds = composite_creds_call.return_value
- channel = grpc_helpers.create_channel(
- target, credentials_file=credentials_file, scopes=scopes
- )
+ with warn_deprecated_credentials_file():
+ channel = grpc_helpers.create_channel(
+ target, credentials_file=credentials_file, scopes=scopes
+ )
google.auth.load_credentials_from_file.assert_called_once_with(
credentials_file, scopes=scopes, default_scopes=None
@@ -776,9 +780,10 @@
credentials_file = "/path/to/credentials/file.json"
composite_creds = composite_creds_call.return_value
- channel = grpc_helpers.create_channel(
- target, credentials_file=credentials_file,
default_scopes=default_scopes
- )
+ with warn_deprecated_credentials_file():
+ channel = grpc_helpers.create_channel(
+ target, credentials_file=credentials_file,
default_scopes=default_scopes
+ )
load_credentials_from_file.assert_called_once_with(
credentials_file, scopes=None, default_scopes=default_scopes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/unit/test_page_iterator.py
new/google_api_core-2.29.0/tests/unit/test_page_iterator.py
--- old/google_api_core-2.28.0/tests/unit/test_page_iterator.py 2025-10-27
23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/unit/test_page_iterator.py 2026-01-08
23:08:42.000000000 +0100
@@ -360,7 +360,6 @@
assert iterator._has_next_page()
def test__has_next_page_w_max_results_done(self):
-
iterator = page_iterator.HTTPIterator(
mock.sentinel.client,
mock.sentinel.api_request,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/unit/test_python_package_support.py
new/google_api_core-2.29.0/tests/unit/test_python_package_support.py
--- old/google_api_core-2.28.0/tests/unit/test_python_package_support.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/unit/test_python_package_support.py
2026-01-08 23:08:42.000000000 +0100
@@ -14,12 +14,12 @@
import sys
import warnings
-from unittest.mock import patch, MagicMock
+from unittest.mock import patch
import pytest
-from packaging.version import parse as parse_version
from google.api_core._python_package_support import (
+ parse_version_to_tuple,
get_dependency_version,
warn_deprecation_for_versions_less_than,
check_dependency_versions,
@@ -28,39 +28,28 @@
)
-# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
-# this mark once we drop support for Python 3.7
[email protected](sys.version_info < (3, 8), reason="requires python3.8 or
higher")
-@patch("importlib.metadata.version")
-def test_get_dependency_version_py38_plus(mock_version):
- """Test get_dependency_version on Python 3.8+."""
- mock_version.return_value = "1.2.3"
- expected = DependencyVersion(parse_version("1.2.3"), "1.2.3")
[email protected]("version_string_to_test", ["1.2.3", "1.2.3b1"])
+def test_get_dependency_version(mocker, version_string_to_test):
+ """Test get_dependency_version."""
+ if sys.version_info >= (3, 8):
+ mock_importlib = mocker.patch(
+ "importlib.metadata.version", return_value=version_string_to_test
+ )
+ else:
+ # TODO(https://github.com/googleapis/python-api-core/issues/835):
Remove
+ # `importlib_metadata` once we drop support for Python 3.7
+ mock_importlib = mocker.patch(
+ "importlib_metadata.version", return_value=version_string_to_test
+ )
+ expected = DependencyVersion(
+ parse_version_to_tuple(version_string_to_test), version_string_to_test
+ )
assert get_dependency_version("some-package") == expected
- mock_version.assert_called_once_with("some-package")
-
- # Test package not found
- mock_version.side_effect = ImportError
- assert get_dependency_version("not-a-package") == DependencyVersion(None,
"--")
-
-# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
-# this test function once we drop support for Python 3.7
[email protected](sys.version_info >= (3, 8), reason="requires python3.7")
-@patch("pkg_resources.get_distribution")
-def test_get_dependency_version_py37(mock_get_distribution):
- """Test get_dependency_version on Python 3.7."""
- mock_dist = MagicMock()
- mock_dist.version = "4.5.6"
- mock_get_distribution.return_value = mock_dist
- expected = DependencyVersion(parse_version("4.5.6"), "4.5.6")
- assert get_dependency_version("another-package") == expected
- mock_get_distribution.assert_called_once_with("another-package")
+ mock_importlib.assert_called_once_with("some-package")
# Test package not found
- mock_get_distribution.side_effect = (
- Exception # pkg_resources has its own exception types
- )
+ mock_importlib.side_effect = ImportError
assert get_dependency_version("not-a-package") == DependencyVersion(None,
"--")
@@ -74,7 +63,9 @@
("my-package (my.package)", "my-package"),
]
- mock_get_version.return_value = DependencyVersion(parse_version("1.0.0"),
"1.0.0")
+ mock_get_version.return_value = DependencyVersion(
+ parse_version_to_tuple("1.0.0"), "1.0.0"
+ )
with pytest.warns(FutureWarning) as record:
warn_deprecation_for_versions_less_than("my.package", "dep.package",
"2.0.0")
assert len(record) == 1
@@ -90,14 +81,14 @@
# Case 2: Installed version is equal to required, should not warn.
mock_get_packages.reset_mock()
mock_get_version.return_value = DependencyVersion(
- parse_version("2.0.0"), "2.0.0"
+ parse_version_to_tuple("2.0.0"), "2.0.0"
)
warn_deprecation_for_versions_less_than("my.package", "dep.package",
"2.0.0")
# Case 3: Installed version is greater than required, should not warn.
mock_get_packages.reset_mock()
mock_get_version.return_value = DependencyVersion(
- parse_version("3.0.0"), "3.0.0"
+ parse_version_to_tuple("3.0.0"), "3.0.0"
)
warn_deprecation_for_versions_less_than("my.package", "dep.package",
"2.0.0")
@@ -115,7 +106,9 @@
("dep-package (dep.package)", "dep-package"),
("my-package (my.package)", "my-package"),
]
- mock_get_version.return_value = DependencyVersion(parse_version("1.0.0"),
"1.0.0")
+ mock_get_version.return_value = DependencyVersion(
+ parse_version_to_tuple("1.0.0"), "1.0.0"
+ )
template = "Custom warning for {dependency_package} used by
{consumer_package}."
with pytest.warns(FutureWarning) as record:
warn_deprecation_for_versions_less_than(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google_api_core-2.28.0/tests/unit/test_python_version_support.py
new/google_api_core-2.29.0/tests/unit/test_python_version_support.py
--- old/google_api_core-2.28.0/tests/unit/test_python_version_support.py
2025-10-27 23:37:20.000000000 +0100
+++ new/google_api_core-2.29.0/tests/unit/test_python_version_support.py
2026-01-08 23:08:42.000000000 +0100
@@ -178,17 +178,20 @@
"google.api_core._python_version_support.PYTHON_VERSION_INFO",
{version_tuple: overridden_info},
):
- result_before_boundary = check_python_version(
- today=custom_gapic_end + datetime.timedelta(days=-1)
- )
+ with pytest.warns(FutureWarning, match="past its end of life"):
+ result_before_boundary = check_python_version(
+ today=custom_gapic_end + datetime.timedelta(days=-1)
+ )
assert result_before_boundary ==
PythonVersionStatus.PYTHON_VERSION_EOL
- result_at_boundary = check_python_version(today=custom_gapic_end)
+ with pytest.warns(FutureWarning, match="past its end of life"):
+ result_at_boundary =
check_python_version(today=custom_gapic_end)
assert result_at_boundary == PythonVersionStatus.PYTHON_VERSION_EOL
- result_after_boundary = check_python_version(
- today=custom_gapic_end + datetime.timedelta(days=1)
- )
+ with pytest.warns(FutureWarning, match="non-supported Python
version"):
+ result_after_boundary = check_python_version(
+ today=custom_gapic_end + datetime.timedelta(days=1)
+ )
assert (
result_after_boundary ==
PythonVersionStatus.PYTHON_VERSION_UNSUPPORTED
)
@@ -217,7 +220,8 @@
result_before_boundary ==
PythonVersionStatus.PYTHON_VERSION_SUPPORTED
)
- result_at_boundary = check_python_version(today=custom_gapic_dep)
+ with pytest.warns(FutureWarning, match="Google will stop
supporting"):
+ result_at_boundary =
check_python_version(today=custom_gapic_dep)
assert result_at_boundary ==
PythonVersionStatus.PYTHON_VERSION_DEPRECATED