diff --git a/packages/google-api-core/CONTRIBUTING.rst b/packages/google-api-core/CONTRIBUTING.rst index bb3f5f55b166..d6250b839447 100644 --- a/packages/google-api-core/CONTRIBUTING.rst +++ b/packages/google-api-core/CONTRIBUTING.rst @@ -21,7 +21,7 @@ In order to add a feature: documentation. - The feature must work fully on the following CPython versions: - 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14 on both UNIX and Windows. + 3.10, 3.11, 3.12, 3.13 and 3.14 on both UNIX and Windows. - The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should @@ -171,11 +171,11 @@ configure them just like the System Tests. # Run all tests in a folder $ cd samples/snippets - $ nox -s py-3.8 + $ nox -s py-3.10 # Run a single sample test $ cd samples/snippets - $ nox -s py-3.8 -- -k + $ nox -s py-3.10 -- -k ******************************************** Note About ``README`` as it pertains to PyPI @@ -197,14 +197,12 @@ Supported Python Versions We support: -- `Python 3.9`_ - `Python 3.10`_ - `Python 3.11`_ - `Python 3.12`_ - `Python 3.13`_ - `Python 3.14`_ -.. _Python 3.9: https://docs.python.org/3.9/ .. _Python 3.10: https://docs.python.org/3.10/ .. _Python 3.11: https://docs.python.org/3.11/ .. _Python 3.12: https://docs.python.org/3.12/ diff --git a/packages/google-api-core/README.rst b/packages/google-api-core/README.rst index 680556768fee..f5346b279e79 100644 --- a/packages/google-api-core/README.rst +++ b/packages/google-api-core/README.rst @@ -16,13 +16,12 @@ common helpers used by all Google API clients. For more information, see the Supported Python Versions ------------------------- -Python >= 3.9 +Python >= 3.10 Unsupported Python Versions --------------------------- - -Python == 2.7, Python == 3.5, Python == 3.6, Python == 3.7, Python == 3.8. +Python <= 3.9 The last version of this library compatible with Python 2.7 and 3.5 is `google-api-core==1.31.1`. @@ -32,3 +31,6 @@ The last version of this library compatible with Python 3.6 is The last version of this library compatible with Python 3.7 and 3.8 is `google-api-core==2.29.0`. + +The last version of this library compatible with Python 3.9 is +`google-api-core==2.30.3`. diff --git a/packages/google-api-core/google/api_core/_python_version_support.py b/packages/google-api-core/google/api_core/_python_version_support.py index 66782bfc9ea8..1430fe222641 100644 --- a/packages/google-api-core/google/api_core/_python_version_support.py +++ b/packages/google-api-core/google/api_core/_python_version_support.py @@ -17,6 +17,7 @@ import datetime import enum import functools +from importlib import metadata import logging import warnings import sys @@ -71,13 +72,6 @@ class VersionInfo(NamedTuple): PYTHON_VERSIONS: List[VersionInfo] = [ # Refer to https://devguide.python.org/versions/ and the PEPs linked therefrom. - VersionInfo( - version="3.9", - python_beta=datetime.date(2020, 5, 18), - python_start=datetime.date(2020, 10, 5), - python_eol=datetime.date(2025, 10, 5), - gapic_end=datetime.date(2025, 10, 5) + datetime.timedelta(days=90), - ), VersionInfo( version="3.10", python_beta=datetime.date(2021, 5, 3), @@ -140,38 +134,26 @@ def _flatten_message(text: str) -> str: 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.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.""" - return None +@functools.cache +def _cached_packages_distributions(): + return metadata.packages_distributions() -else: - from importlib import metadata - @functools.cache - def _cached_packages_distributions(): - return metadata.packages_distributions() +def _get_pypi_package_name(module_name): + """Determine the PyPI package name for a given module name.""" + try: + module_to_distributions = _cached_packages_distributions() - def _get_pypi_package_name(module_name): - """Determine the PyPI package name for a given module name.""" - try: - module_to_distributions = _cached_packages_distributions() - - if module_name in module_to_distributions: # pragma: NO COVER - return module_to_distributions[module_name][0] - except Exception as e: # pragma: NO COVER - _LOGGER.info( - "An error occurred while determining PyPI package name for %s: %s", - module_name, - e, - ) + if module_name in module_to_distributions: # pragma: NO COVER + return module_to_distributions[module_name][0] + 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 + return None def _get_distribution_and_import_packages(import_package: str) -> Tuple[str, Any]: diff --git a/packages/google-api-core/google/api_core/client_info.py b/packages/google-api-core/google/api_core/client_info.py index f0678d24ba1e..024da1e8eb2e 100644 --- a/packages/google-api-core/google/api_core/client_info.py +++ b/packages/google-api-core/google/api_core/client_info.py @@ -45,7 +45,7 @@ class ClientInfo(object): Args: python_version (str): The Python interpreter version, for example, - ``'3.9.6'``. + ``'3.10.0'``. grpc_version (Optional[str]): The gRPC library version. api_core_version (str): The google-api-core library version. gapic_version (Optional[str]): The version of gapic-generated client diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_info.py b/packages/google-api-core/google/api_core/gapic_v1/client_info.py index 4b3b56493f8a..2cf07840b39b 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_info.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_info.py @@ -33,7 +33,7 @@ class ClientInfo(client_info.ClientInfo): Args: python_version (str): The Python interpreter version, for example, - ``'3.9.6'``. + ``'3.10.0'``. grpc_version (Optional[str]): The gRPC library version. api_core_version (str): The google-api-core library version. gapic_version (Optional[str]): The version of gapic-generated client diff --git a/packages/google-api-core/google/api_core/grpc_helpers_async.py b/packages/google-api-core/google/api_core/grpc_helpers_async.py index 9e1ad1105d45..b615d8a13252 100644 --- a/packages/google-api-core/google/api_core/grpc_helpers_async.py +++ b/packages/google-api-core/google/api_core/grpc_helpers_async.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""AsyncIO helpers for :mod:`grpc` supporting 3.7+. +"""AsyncIO helpers for :mod:`grpc` supporting 3.10+. Please combine more detailed docstring in grpc_helpers.py to use following functions. This module is implementing the same surface with AsyncIO semantics. diff --git a/packages/google-api-core/noxfile.py b/packages/google-api-core/noxfile.py index 3ae96e416736..27b9b9125a0c 100644 --- a/packages/google-api-core/noxfile.py +++ b/packages/google-api-core/noxfile.py @@ -14,7 +14,7 @@ # Helpful notes for local usage: # unset PYENV_VERSION -# pyenv local 3.14.1 3.13.10 3.12.11 3.11.4 3.10.12 3.9.17 +# pyenv local 3.14.1 3.13.10 3.12.11 3.11.4 3.10.12 # PIP_INDEX_URL=https://pypi.org/simple nox from __future__ import absolute_import @@ -34,8 +34,8 @@ # Black and flake8 clash on the syntax for ignoring flake8's F401 in this file. BLACK_EXCLUDES = ["--exclude", "^/google/api_core/operations_v1/__init__.py"] -ALL_PYTHON = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] -SUPPORTED_PYTHON_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] +ALL_PYTHON = ["3.10", "3.11", "3.12", "3.13", "3.14"] +SUPPORTED_PYTHON_VERSIONS = ["3.10", "3.11", "3.12", "3.13", "3.14"] DEFAULT_PYTHON_VERSION = "3.14" CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() @@ -303,7 +303,7 @@ def default( ( True, False, - ["3.9", "3.10", "3.11"], + ["3.10", "3.11"], 4, ), # Run proto4 tests with grpcio/grpcio-gcp installed ], @@ -325,13 +325,13 @@ def unit( session.log(f"Skipping session for Python {session.python}") session.skip() - # TODO: consider converting the following into a `match` statement once - # we drop Python 3.9 support. - if legacy_proto: - if legacy_proto == 4: + match legacy_proto: + case 4: # Pin protobuf to a 4.x version to ensure coverage for the legacy code path. session.install("protobuf>=4.25.8,<5.0.0") - else: + case None | False: + pass + case _: assert False, f"Unknown legacy_proto: {legacy_proto}" default( diff --git a/packages/google-api-core/pyproject.toml b/packages/google-api-core/pyproject.toml index c81667215d45..08b09d1d97b3 100644 --- a/packages/google-api-core/pyproject.toml +++ b/packages/google-api-core/pyproject.toml @@ -20,7 +20,7 @@ build-backend = "setuptools.build_meta" name = "google-api-core" authors = [{ name = "Google LLC", email = "googleapis-packages@google.com" }] license = { text = "Apache 2.0" } -requires-python = ">=3.9" +requires-python = ">=3.10" readme = "README.rst" description = "Google API client core library" classifiers = [ @@ -34,7 +34,6 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -48,8 +47,8 @@ dependencies = [ "protobuf >= 4.25.8, < 8.0.0", "proto-plus >= 1.22.3, < 2.0.0", "proto-plus >= 1.25.0, < 2.0.0; python_version >= '3.13'", - "google-auth >= 2.14.1, < 3.0.0", - "requests >= 2.20.0, < 3.0.0", + "google-auth >= 2.35.0, < 3.0.0", + "requests >= 2.33.0", ] dynamic = ["version"] @@ -59,9 +58,9 @@ Documentation = "https://googleapis.dev/python/google-api-core/latest/" Repository = "https://github.com/googleapis/google-cloud-python" [project.optional-dependencies] -async_rest = ["google-auth[aiohttp] >= 2.35.0, < 3.0.0"] +async_rest = ["google-auth[aiohttp] >= 2.35.0, < 3.0.0", "aiohttp >= 3.9.0"] grpc = [ - "grpcio >= 1.33.2, < 2.0.0", + "grpcio >= 1.80.0", "grpcio >= 1.49.1, < 2.0.0; python_version >= '3.11'", "grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'", "grpcio-status >= 1.33.2, < 2.0.0", diff --git a/packages/google-api-core/testing/constraints-3.10.txt b/packages/google-api-core/testing/constraints-3.10.txt index e69de29bb2d1..97712805206a 100644 --- a/packages/google-api-core/testing/constraints-3.10.txt +++ b/packages/google-api-core/testing/constraints-3.10.txt @@ -0,0 +1,14 @@ +# This constraints file is used to check that lower bounds +# are correct in pyproject.toml +# List *all* library dependencies and extras in this file. +# Pin the version to the lower bound. +# +# e.g., if pyproject.toml has "foo >= 1.14.0, < 2.0.0dev", +# Then this file should have foo==1.14.0 +googleapis-common-protos==1.63.2 +protobuf==4.25.8 +google-auth==2.35.0 +requests==2.33.0 +grpcio==1.80.0 +grpcio-status==1.33.2 +proto-plus==1.22.3 diff --git a/packages/google-api-core/testing/constraints-3.9.txt b/packages/google-api-core/testing/constraints-async-rest-3.10.txt similarity index 51% rename from packages/google-api-core/testing/constraints-3.9.txt rename to packages/google-api-core/testing/constraints-async-rest-3.10.txt index 98614e25b016..7afba4e21ede 100644 --- a/packages/google-api-core/testing/constraints-3.9.txt +++ b/packages/google-api-core/testing/constraints-async-rest-3.10.txt @@ -7,8 +7,13 @@ # Then this file should have foo==1.14.0 googleapis-common-protos==1.63.2 protobuf==4.25.8 -google-auth==2.14.1 -requests==2.20.0 -grpcio==1.33.2 +google-auth==2.35.0 +requests==2.33.0 +grpcio==1.80.0 grpcio-status==1.33.2 proto-plus==1.22.3 +# Note: We pin aiohttp to 3.9.0 to avoid deprecation warnings about '@coroutine' +# decorators which were deprecated since Python 3.8. When older versions of aiohttp +# load, because they used @coroutine deep inside their internals, the warnings +# bubble up and cause our nox session to fail. +aiohttp==3.9.0 diff --git a/packages/google-api-core/testing/constraints-async-rest-3.9.txt b/packages/google-api-core/testing/constraints-async-rest-3.9.txt deleted file mode 100644 index b2aa135cd04a..000000000000 --- a/packages/google-api-core/testing/constraints-async-rest-3.9.txt +++ /dev/null @@ -1,20 +0,0 @@ -# This constraints file is used to check that lower bounds -# are correct in pyproject.toml -# List *all* library dependencies and extras in this file. -# Pin the version to the lower bound. -# -# e.g., if pyproject.toml has "foo >= 1.14.0, < 2.0.0dev", -# Then this file should have foo==1.14.0 -googleapis-common-protos==1.63.2 -protobuf==4.25.8 -google-auth==2.35.0 -requests==2.20.0 -grpcio==1.33.2 -grpcio-status==1.33.2 -proto-plus==1.22.3 -# Some tests import aiohttp to test async rest -# from google-auth[aiohttp] -# google-auth[aiohttp] v2.39.0 is the first release that requires aiohttp 3.6.2 -# see - change: https://github.com/googleapis/google-auth-library-python/blame/9d5c0d81e8e69719a2b4cd034bf1ed5d128fdf0a/setup.py#L35 -# - release: https://github.com/googleapis/google-auth-library-python/commits/main/?after=9d5c0d81e8e69719a2b4cd034bf1ed5d128fdf0a+104 -aiohttp==3.6.2 diff --git a/packages/google-api-core/tests/asyncio/test_bidi_async.py b/packages/google-api-core/tests/asyncio/test_bidi_async.py index add685a96c2c..a39982bf92aa 100644 --- a/packages/google-api-core/tests/asyncio/test_bidi_async.py +++ b/packages/google-api-core/tests/asyncio/test_bidi_async.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys + import asyncio from unittest import mock @@ -33,15 +33,6 @@ from google.api_core import bidi_async from google.api_core import exceptions -# TODO: remove this when droppping support for "Python 3.10" and below. -if sys.version_info < (3, 10): # type: ignore[operator] - - def aiter(obj): - return obj.__aiter__() - - async def anext(obj): - return await obj.__anext__() - @pytest.mark.asyncio class Test_AsyncRequestQueueGenerator: @@ -185,10 +176,6 @@ def test_done_callbacks(self): callback.assert_called_once_with(mock.sentinel.future) @pytest.mark.asyncio - @pytest.mark.skipif( - sys.version_info < (3, 8), # type: ignore[operator] - reason="Versions of Python below 3.8 don't provide support for assert_awaited_once", - ) async def test_metadata(self): rpc, call = make_async_rpc() bidi_rpc = bidi_async.AsyncBidiRpc(rpc, metadata=mock.sentinel.A) diff --git a/packages/google-api-core/tests/unit/test_python_version_support.py b/packages/google-api-core/tests/unit/test_python_version_support.py index 76eb821e0205..07620934c93f 100644 --- a/packages/google-api-core/tests/unit/test_python_version_support.py +++ b/packages/google-api-core/tests/unit/test_python_version_support.py @@ -164,7 +164,7 @@ def test_all_tracked_versions_and_date_scenarios( def test_override_gapic_end_only(): """Test behavior when only gapic_end is manually overridden.""" - version_tuple = (3, 9) + version_tuple = (3, 10) original_info = PYTHON_VERSION_INFO[version_tuple] mock_py_version = VersionInfoMock(major=version_tuple[0], minor=version_tuple[1]) @@ -199,7 +199,7 @@ def test_override_gapic_end_only(): def test_override_gapic_deprecation_only(): """Test behavior when only gapic_deprecation is manually overridden.""" - version_tuple = (3, 9) + version_tuple = (3, 10) original_info = PYTHON_VERSION_INFO[version_tuple] mock_py_version = VersionInfoMock(major=version_tuple[0], minor=version_tuple[1])