From 5c07e1c4f52bc77a1b16fa3b7b3c5269c242f6f4 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 5 Jan 2026 23:50:18 +0200 Subject: [PATCH 1/4] feat: drop `cachetools` dependency in favor of simple local implementation (#1590) This PR drops the dependency on `cachetools`, which was only used for its LRU cache class, in favor of a simple local implementation. This should have a small but positive effect on many downstream users given how many times this library is downloaded per day. --------- Co-authored-by: Chalmer Lowe --- google/auth/_cache.py | 64 +++++++++++++++++++++++++++++ google/auth/jwt.py | 7 ++-- noxfile.py | 1 - setup.py | 1 - testing/constraints-3.7.txt | 1 - tests/test__cache.py | 82 +++++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 google/auth/_cache.py create mode 100644 tests/test__cache.py diff --git a/google/auth/_cache.py b/google/auth/_cache.py new file mode 100644 index 000000000..0a4a2af46 --- /dev/null +++ b/google/auth/_cache.py @@ -0,0 +1,64 @@ +# 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 +# +# https://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. + +from collections import OrderedDict + + +class LRUCache(dict): + def __init__(self, maxsize): + super().__init__() + self._order = OrderedDict() + self.maxsize = maxsize + + def clear(self): + super().clear() + self._order.clear() + + def get(self, key, default=None): + try: + value = super().__getitem__(key) + self._update(key) + return value + except KeyError: + return default + + def __getitem__(self, key): + value = super().__getitem__(key) + self._update(key) + return value + + def __setitem__(self, key, value): + maxsize = self.maxsize + if maxsize <= 0: + return + if key not in self: + while len(self) >= maxsize: + self.popitem() + super().__setitem__(key, value) + self._update(key) + + def __delitem__(self, key): + super().__delitem__(key) + del self._order[key] + + def popitem(self): + """Remove and return the least recently used key-value pair.""" + key, _ = self._order.popitem(last=False) + return key, super().pop(key) + + def _update(self, key): + try: + self._order.move_to_end(key) + except KeyError: + self._order[key] = None diff --git a/google/auth/jwt.py b/google/auth/jwt.py index 3b28bb518..b6fe60736 100644 --- a/google/auth/jwt.py +++ b/google/auth/jwt.py @@ -50,8 +50,7 @@ import json import urllib -import cachetools - +from google.auth import _cache from google.auth import _helpers from google.auth import _service_account_info from google.auth import crypt @@ -630,7 +629,7 @@ def __init__( token_lifetime (int): The amount of time in seconds for which the token is valid. Defaults to 1 hour. max_cache_size (int): The maximum number of JWT tokens to keep in - cache. Tokens are cached using :class:`cachetools.LRUCache`. + cache. Tokens are cached using :class:`google.auth._cache.LRUCache`. quota_project_id (Optional[str]): The project ID used for quota and billing. @@ -646,7 +645,7 @@ def __init__( additional_claims = {} self._additional_claims = additional_claims - self._cache = cachetools.LRUCache(maxsize=max_cache_size) + self._cache = _cache.LRUCache(maxsize=max_cache_size) @classmethod def _from_signer_and_info(cls, signer, info, **kwargs): diff --git a/noxfile.py b/noxfile.py index b2f9ae4ff..8a97f74cd 100644 --- a/noxfile.py +++ b/noxfile.py @@ -97,7 +97,6 @@ def mypy(session): session.install("-e", ".") session.install( "mypy", - "types-cachetools", "types-certifi", "types-freezegun", "types-pyOpenSSL", diff --git a/setup.py b/setup.py index 33694506b..3db2d8cf9 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,6 @@ DEPENDENCIES = ( - "cachetools>=2.0.0,<7.0", "pyasn1-modules>=0.2.1", # rsa==4.5 is the last version to support 2.7 # https://github.com/sybrenstuvel/python-rsa/issues/152#issuecomment-643470233 diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt index f3fd641a9..52ad3af91 100644 --- a/testing/constraints-3.7.txt +++ b/testing/constraints-3.7.txt @@ -5,7 +5,6 @@ # # e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev", # Then this file should have foo==1.14.0 -cachetools==2.0.0 pyasn1-modules==0.2.1 setuptools==40.3.0 rsa==3.1.4 diff --git a/tests/test__cache.py b/tests/test__cache.py new file mode 100644 index 000000000..be9f703fb --- /dev/null +++ b/tests/test__cache.py @@ -0,0 +1,82 @@ +from google.auth._cache import LRUCache + + +def test_lru_cache(): + """Test the LRUCache for generally expected functionality and ordering.""" + lru_cache = LRUCache(2) + lru_cache["a"] = 1 + lru_cache["b"] = 2 + assert lru_cache["a"] == 1 + lru_cache["c"] = 3 + assert "b" not in lru_cache + assert lru_cache["a"] == 1 + assert lru_cache["c"] == 3 + lru_cache["d"] = 4 + assert "a" not in lru_cache + assert lru_cache["c"] == 3 + assert lru_cache["d"] == 4 + + +def test_zero_size_lru_cache(): + """Confirm the LRUCache handles zero-size correctly.""" + lru_cache = LRUCache(0) + lru_cache["a"] = 1 + assert "a" not in lru_cache + + +def test_lru_cache_get_updates_lru(): + """Confirm the LRUCache handles get calls correctly.""" + lru_cache = LRUCache(2) + lru_cache["a"] = 1 + lru_cache["b"] = 2 + + # Access "a" via get(), making it MRU. + assert lru_cache.get("a") == 1 + + # Add "c", which should evict "b" (LRU), not "a". + lru_cache["c"] = 3 + + assert "a" in lru_cache + assert "b" not in lru_cache + assert "c" in lru_cache + + +def test_lru_cache_get_missing(): + """Confirm the LRUCache handles missing keys correctly.""" + lru_cache = LRUCache(2) + assert lru_cache.get("missing") is None + assert lru_cache.get("missing", "default") == "default" + + +def test_lru_cache_clear(): + """Confirm the LRUCache clears the cache properly.""" + lru_cache = LRUCache(2) + lru_cache["a"] = 1 + lru_cache["b"] = 2 + assert len(lru_cache) == 2 + + lru_cache.clear() + assert len(lru_cache) == 0 + assert "a" not in lru_cache + assert "b" not in lru_cache + # Ensure internal order is also cleared + assert len(lru_cache._order) == 0 + + +def test_lru_cache_delitem(): + """Confirm the LRUCache deletes individual items properly.""" + lru_cache = LRUCache(2) + lru_cache["a"] = 1 + lru_cache["b"] = 2 + + del lru_cache["a"] + assert "a" not in lru_cache + assert len(lru_cache) == 1 + # Ensure it's removed from internal order + assert "a" not in lru_cache._order + + # Test that we can continue using the cache + lru_cache["c"] = 3 + assert "c" in lru_cache + assert "b" in lru_cache + assert len(lru_cache) == 2 From 60dc20014a35ec4ba71e8065b9a33ecbdbeca97a Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 12:07:50 -0800 Subject: [PATCH 2/4] fix: Python 3.8 support (#1918) There were recently some type annotation changes that weren't compatible with python 3.8. This PR fixes those, and adds back 3.8 tests using github actions --------- Co-authored-by: Anthonios Partheniou --- .github/workflows/unittest.yml | 76 ++++++++++++++++++++++++++++++++++ google/auth/_default.py | 4 +- google/oauth2/id_token.py | 1 + noxfile.py | 15 ++++--- 4 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/unittest.yml diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml new file mode 100644 index 000000000..a5ddd035d --- /dev/null +++ b/.github/workflows/unittest.yml @@ -0,0 +1,76 @@ +# Copyright 2026 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 +# +# https://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. + +on: + pull_request: + branches: + - main + +permissions: + contents: read + +name: unittest +jobs: + unit: + runs-on: ubuntu-22.04 + strategy: + matrix: + python: ['3.8'] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - name: Install nox + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install nox + - name: Run unit tests + env: + COVERAGE_FILE: .coverage-${{ matrix.python }} + run: | + nox -s unit-${{ matrix.python }} + - name: Upload coverage results + uses: actions/upload-artifact@v4 + with: + name: coverage-artifact-${{ matrix.python }} + path: .coverage-${{ matrix.python }} + include-hidden-files: true + + cover: + runs-on: ubuntu-latest + needs: + - unit + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install coverage + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install coverage + - name: Download coverage results + uses: actions/download-artifact@v4 + with: + path: .coverage-results/ + - name: Report coverage results + run: | + find .coverage-results -type f -name '*.zip' -exec unzip {} \; + coverage combine .coverage-results/**/.coverage* + coverage report --show-missing --fail-under=99 diff --git a/google/auth/_default.py b/google/auth/_default.py index d854163c4..01bd02a21 100644 --- a/google/auth/_default.py +++ b/google/auth/_default.py @@ -16,13 +16,13 @@ Implements application default credentials and project ID detection. """ +from __future__ import annotations -from collections.abc import Sequence import io import json import logging import os -from typing import Optional, TYPE_CHECKING +from typing import Optional, Sequence, TYPE_CHECKING import warnings from google.auth import environment_vars diff --git a/google/oauth2/id_token.py b/google/oauth2/id_token.py index fe4bebd78..d21be1a06 100644 --- a/google/oauth2/id_token.py +++ b/google/oauth2/id_token.py @@ -54,6 +54,7 @@ http://openid.net/specs/openid-connect-core-1_0.html#IDToken .. _CacheControl: https://cachecontrol.readthedocs.io """ +from __future__ import annotations import http.client as http_client import json diff --git a/noxfile.py b/noxfile.py index 8a97f74cd..70173e55c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -32,9 +32,16 @@ ] DEFAULT_PYTHON_VERSION = "3.14" -# TODO(https://github.com/googleapis/google-auth-library-python/issues/1787): -# Remove or restore testing for Python 3.7/3.8 -UNIT_TEST_PYTHON_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] +UNIT_TEST_PYTHON_VERSIONS = [ + "3.7", + "3.8", + "3.9", + "3.10", + "3.11", + "3.12", + "3.13", + "3.14", +] # Error if a python version is missing nox.options.error_on_missing_interpreters = True @@ -44,8 +51,6 @@ "lint", "blacken", "mypy", - # TODO(https://github.com/googleapis/google-auth-library-python/issues/1787): - # Remove or restore testing for Python 3.7/3.8 "unit-3.9", "unit-3.10", "unit-3.11", From 25f4ca658c1880c7a27ed817ded4c17d84c26854 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 13:03:15 -0800 Subject: [PATCH 3/4] chore: remove Python 3.7 support (#1919) Removing support for EOL Python 3.7. A warning was present in the README that support would be removed after January 2024 Also added a warning that Python 3.8 and 3.9 are also EOL, and will be removed in future updates --------- Co-authored-by: Victor Chudnovsky --- CONTRIBUTING.rst | 2 +- README.rst | 19 ++++++---- google/auth/__init__.py | 18 ++++++---- google/auth/_helpers.py | 3 +- google/auth/pluggable.py | 10 ------ google/oauth2/__init__.py | 18 ++++++---- setup.py | 6 +--- tests/test_pluggable.py | 24 ------------- tests/test_version_warnings.py | 63 ++++++++++++++++++++++++++++++++++ 9 files changed, 101 insertions(+), 62 deletions(-) create mode 100644 tests/test_version_warnings.py diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 3975a3d2a..a186da7d4 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -19,7 +19,7 @@ A few notes on making changes to ``google-auth-library-python``. using ``nox -s docs``. - The change must work fully on the following CPython versions: - 3.7, 3.8, 3.9, 3.10, 3.11, 3.12 and 3.13 across macOS, Linux, and Windows. + 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14 across macOS, Linux, and Windows. - The codebase *must* have 100% test statement coverage after each commit. You can test coverage via ``nox -e cover``. diff --git a/README.rst b/README.rst index e058f2471..45da8044d 100644 --- a/README.rst +++ b/README.rst @@ -35,15 +35,16 @@ Note that the extras pyopenssl and enterprise_cert should not be used together b Supported Python Versions ^^^^^^^^^^^^^^^^^^^^^^^^^ -Python >= 3.7 +Python >= 3.8 **NOTE**: -Python 3.7 was marked as `unsupported`_ by the python community in June 2023. -We recommend that all developers upgrade to Python 3.8 and newer as soon as -they can. Support for Python 3.7 will be removed from this library after -January 1 2024. Previous releases that support Python 3.7 will continue to be available -for download, but releases after January 1 2024 will only target Python 3.8 and -newer. +Python 3.8 and Python 3.9 were marked as `unsupported`_ by the python community in +October 2024 and October 2025, respectively. +We recommend that all developers upgrade to Python 3.10 and newer as soon as +they can. Support for end-of-life Python runtimes will be removed from this +library in future updates. +Previous releases that support end-of-life Python versions will continue to be available +for download, but future releases will only target supported versions. .. _unsupported: https://devguide.python.org/versions/#unsupported-versions @@ -58,6 +59,10 @@ Unsupported Python Versions - Python 3.6: The last version of this library with support for Python 3.6 was `google.auth == 2.22.0`. +- Python 3.7: The last version of this library with support for Python 3.7 + was `google.auth == 2.45.0`. + + Documentation ------------- diff --git a/google/auth/__init__.py b/google/auth/__init__.py index 765bbd705..2217558a4 100644 --- a/google/auth/__init__.py +++ b/google/auth/__init__.py @@ -41,13 +41,17 @@ class Python37DeprecationWarning(DeprecationWarning): # pragma: NO COVER pass -# Checks if the current runtime is Python 3.7. -if sys.version_info.major == 3 and sys.version_info.minor == 7: # pragma: NO COVER - message = ( - "After January 1, 2024, new releases of this library will drop support " - "for Python 3.7." - ) - warnings.warn(message, Python37DeprecationWarning) +# Raise warnings for deprecated versions +eol_message = """ + You are using a Python version {} past its end of life. Google will update + google-auth with critical bug fixes on a best-effort basis, but not + with any other fixes or features. Please upgrade your Python version, + and then update google-auth. + """ +if sys.version_info.major == 3 and sys.version_info.minor == 8: # pragma: NO COVER + warnings.warn(eol_message.format("3.8"), FutureWarning) +elif sys.version_info.major == 3 and sys.version_info.minor == 9: # pragma: NO COVER + warnings.warn(eol_message.format("3.9"), FutureWarning) # Set default logging handler to avoid "No handler found" warnings. logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/google/auth/_helpers.py b/google/auth/_helpers.py index 4a69d4c61..e9033ca8c 100644 --- a/google/auth/_helpers.py +++ b/google/auth/_helpers.py @@ -334,7 +334,8 @@ def is_python_3(): Returns: bool: True if the Python interpreter is Python 3 and False otherwise. """ - return sys.version_info > (3, 0) + + return sys.version_info > (3, 0) # pragma: NO COVER def _hash_sensitive_info(data: Union[dict, list]) -> Union[dict, list, str]: diff --git a/google/auth/pluggable.py b/google/auth/pluggable.py index 87d03e737..b7d832da9 100644 --- a/google/auth/pluggable.py +++ b/google/auth/pluggable.py @@ -201,11 +201,6 @@ def retrieve_subject_token(self, request): else: return subject_token - if not _helpers.is_python_3(): - raise exceptions.RefreshError( - "Pluggable auth is only supported for python 3.7+" - ) - # Inject env vars. env = os.environ.copy() self._inject_env_variables(env) @@ -263,11 +258,6 @@ def revoke(self, request): ) self._validate_running_mode() - if not _helpers.is_python_3(): - raise exceptions.RefreshError( - "Pluggable auth is only supported for python 3.7+" - ) - # Inject variables env = os.environ.copy() self._inject_env_variables(env) diff --git a/google/oauth2/__init__.py b/google/oauth2/__init__.py index accae9657..18b8ada67 100644 --- a/google/oauth2/__init__.py +++ b/google/oauth2/__init__.py @@ -27,10 +27,14 @@ class Python37DeprecationWarning(DeprecationWarning): # pragma: NO COVER pass -# Checks if the current runtime is Python 3.7. -if sys.version_info.major == 3 and sys.version_info.minor == 7: # pragma: NO COVER - message = ( - "After January 1, 2024, new releases of this library will drop support " - "for Python 3.7." - ) - warnings.warn(message, Python37DeprecationWarning) +# Raise warnings for deprecated versions +eol_message = """ + You are using a Python version {} past its end of life. Google will update + google-auth with critical bug fixes on a best-effort basis, but not + with any other fixes or features. Please upgrade your Python version, + and then update google-auth. + """ +if sys.version_info.major == 3 and sys.version_info.minor == 8: # pragma: NO COVER + warnings.warn(eol_message.format("3.8"), FutureWarning) +elif sys.version_info.major == 3 and sys.version_info.minor == 9: # pragma: NO COVER + warnings.warn(eol_message.format("3.9"), FutureWarning) diff --git a/setup.py b/setup.py index 3db2d8cf9..74036339a 100644 --- a/setup.py +++ b/setup.py @@ -26,11 +26,8 @@ "rsa>=3.1.4,<5", ) -# TODO(https://github.com/googleapis/google-auth-library-python/issues/1737): Unit test fails with -# `No module named 'cryptography.hazmat.backends.openssl.x509' for Python 3.7``. cryptography_base_require = [ "cryptography >= 38.0.3", - "cryptography < 39.0.0; python_version < '3.8'", ] requests_extra_require = ["requests >= 2.20.0, < 3.0.0"] @@ -116,12 +113,11 @@ package_data={"google.auth": ["py.typed"], "google.oauth2": ["py.typed"]}, install_requires=DEPENDENCIES, extras_require=extras, - python_requires=">=3.7", + python_requires=">=3.8", license="Apache 2.0", keywords="google auth oauth client", classifiers=[ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", diff --git a/tests/test_pluggable.py b/tests/test_pluggable.py index ba1b780de..b2764361b 100644 --- a/tests/test_pluggable.py +++ b/tests/test_pluggable.py @@ -1230,16 +1230,6 @@ def test_revoke_successfully(self): ) _ = credentials.revoke(None) - @mock.patch.dict(os.environ, {"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES": "1"}) - def test_retrieve_subject_token_python_2(self): - with mock.patch("sys.version_info", (2, 7)): - credentials = self.make_pluggable(credential_source=self.CREDENTIAL_SOURCE) - - with pytest.raises(exceptions.RefreshError) as excinfo: - _ = credentials.retrieve_subject_token(None) - - assert excinfo.match(r"Pluggable auth is only supported for python 3.7+") - @mock.patch.dict(os.environ, {"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES": "1"}) def test_retrieve_subject_token_with_quoted_command(self): command_with_spaces = '"/path/with spaces/to/executable" "arg with spaces"' @@ -1269,17 +1259,3 @@ def test_retrieve_subject_token_with_quoted_command(self): stderr=subprocess.STDOUT, env=mock.ANY, ) - - @mock.patch.dict(os.environ, {"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES": "1"}) - def test_revoke_subject_token_python_2(self): - with mock.patch("sys.version_info", (2, 7)): - credentials = self.make_pluggable( - audience=WORKFORCE_AUDIENCE, - credential_source=self.CREDENTIAL_SOURCE, - interactive=True, - ) - - with pytest.raises(exceptions.RefreshError) as excinfo: - _ = credentials.revoke(None) - - assert excinfo.match(r"Pluggable auth is only supported for python 3.7+") diff --git a/tests/test_version_warnings.py b/tests/test_version_warnings.py new file mode 100644 index 000000000..d975221c4 --- /dev/null +++ b/tests/test_version_warnings.py @@ -0,0 +1,63 @@ +# Copyright 2026 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. + +import importlib +import sys +from unittest import mock +import warnings + +import pytest + +import google.auth +import google.oauth2 + + +@pytest.mark.parametrize("module", [google.auth, google.oauth2]) +@pytest.mark.parametrize( + "version, expected_warning", + [ + ((3, 8), True), + ((3, 9), True), + ((3, 10), False), + ((3, 13), False), + ], +) +def test_python_version_warnings(module, version, expected_warning): + # Mock sys.version_info + # We use a MagicMock that has major and minor attributes + mock_version = mock.Mock() + mock_version.major = version[0] + mock_version.minor = version[1] + + with mock.patch.object(sys, "version_info", mock_version): + with warnings.catch_warnings(record=True) as caught_warnings: + warnings.simplefilter("always") + importlib.reload(module) + + future_warnings = [ + w + for w in caught_warnings + if issubclass(w.category, FutureWarning) + and "past its end of life" in str(w.message) + ] + + if expected_warning: + assert ( + len(future_warnings) > 0 + ), f"Expected FutureWarning for Python {version} in {module.__name__}" + assert str(version[1]) in str(future_warnings[0].message) + else: + assert ( + len(future_warnings) == 0 + ), f"Did not expect FutureWarning for Python {version} in {module.__name__}" From f32301a03372eb72a8b5542a4cc223e01ee1e33b Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 6 Jan 2026 13:31:56 -0800 Subject: [PATCH 4/4] chore: librarian release pull request: 20260106T130342Z (#1922) PR created by the Librarian CLI to initialize a release. Merging this PR will auto trigger a release. Librarian Version: v1.0.1 Language Image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209
google-auth: 2.47.0 ## [2.47.0](https://github.com/googleapis/google-auth-library-python/compare/v2.46.0...v2.47.0) (2026-01-06) ### Features * drop `cachetools` dependency in favor of simple local implementation (#1590) ([5c07e1c4](https://github.com/googleapis/google-auth-library-python/commit/5c07e1c4)) ### Bug Fixes * Python 3.8 support (#1918) ([60dc2001](https://github.com/googleapis/google-auth-library-python/commit/60dc2001))
--- .librarian/state.yaml | 2 +- CHANGELOG.md | 12 ++++++++++++ google/auth/version.py | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.librarian/state.yaml b/.librarian/state.yaml index cbc6f9303..199662649 100644 --- a/.librarian/state.yaml +++ b/.librarian/state.yaml @@ -1,7 +1,7 @@ image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209 libraries: - id: google-auth - version: 2.46.0 + version: 2.47.0 last_generated_commit: 102d9f92ac6ed649a61efd9b208e4d1de278e9bb apis: [] source_roots: diff --git a/CHANGELOG.md b/CHANGELOG.md index bcf04f5dd..b5663e510 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ [1]: https://pypi.org/project/google-auth/#history +## [2.47.0](https://github.com/googleapis/google-auth-library-python/compare/v2.46.0...v2.47.0) (2026-01-06) + + +### Features + +* drop `cachetools` dependency in favor of simple local implementation (#1590) ([5c07e1c4f52bc77a1b16fa3b7b3c5269c242f6f4](https://github.com/googleapis/google-auth-library-python/commit/5c07e1c4f52bc77a1b16fa3b7b3c5269c242f6f4)) + + +### Bug Fixes + +* Python 3.8 support (#1918) ([60dc20014a35ec4ba71e8065b9a33ecbdbeca97a](https://github.com/googleapis/google-auth-library-python/commit/60dc20014a35ec4ba71e8065b9a33ecbdbeca97a)) + ## [2.46.0](https://github.com/googleapis/google-auth-library-python/compare/v2.45.0...v2.46.0) (2026-01-05) diff --git a/google/auth/version.py b/google/auth/version.py index 9447bded1..03b96ced7 100644 --- a/google/auth/version.py +++ b/google/auth/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2.46.0" +__version__ = "2.47.0"