Skip to content
This repository was archived by the owner on Feb 23, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1e45eb4
chore: remove support for Python 3.7 and 3.8
vchudnov-g Dec 4, 2025
e3b50d9
chore: format
vchudnov-g Dec 4, 2025
4e73563
fix `nox -s prerelease_deps` by adding dependencies to noxfile
vchudnov-g Dec 5, 2025
735b849
chore: Use async mock in async test
vchudnov-g Dec 5, 2025
6d057a4
chore: Simplify code intended for Python 3.9+
vchudnov-g Dec 5, 2025
717c652
chore: remove unused import
vchudnov-g Dec 5, 2025
eb5ebef
chore:remove unused import; cosmetic
vchudnov-g Dec 5, 2025
807a2fa
exclude coverage calculations on some exceptional lines
vchudnov-g Dec 5, 2025
03d47bd
fix coverage: runder under proto 4.x
vchudnov-g Dec 5, 2025
9012e92
test(proto-4.25.8): Fix to run on supported Python version (3.11)
vchudnov-g Dec 5, 2025
5ef9f1f
Have nox run the legacy protobuf test
vchudnov-g Dec 5, 2025
38cdc12
fix coverage file names
vchudnov-g Dec 5, 2025
be692f4
don't worry about covering unit test
vchudnov-g Dec 5, 2025
397d7e1
Cover legacy protobuf tests
vchudnov-g Dec 5, 2025
32132a1
fix session skip in test
vchudnov-g Dec 5, 2025
6e20230
chore: require protobuf ≥ 4.25.8
vchudnov-g Dec 5, 2025
9697839
feat: warn users of upcoming minimum protobuf 5.0.0 requirement
vchudnov-g Dec 5, 2025
657dcf5
chore: Pin depencies to lower bounds in constraints for lowest Python…
vchudnov-g Dec 8, 2025
26b8162
chore: bumpgoogleapis-common-protos to vbersion supporting protobuf 4.x
vchudnov-g Dec 8, 2025
b49e1a8
chjore: Update minimum version of google-auth to 2.35.0
vchudnov-g Dec 8, 2025
eb08a55
chore: require requests ≥ 2.20.0
vchudnov-g Dec 8, 2025
e6d3c4c
chore: try removing NO COVER pragmas
vchudnov-g Dec 8, 2025
c57a4cf
restore one NO COVER pragma
vchudnov-g Dec 8, 2025
b5f1126
clean up removed pragmas
vchudnov-g Dec 8, 2025
7294f00
reuse nox `unit` session as per review comment
vchudnov-g Feb 3, 2026
a0a278a
chore: fix details lost during rebse to updated `main`
vchudnov-g Feb 4, 2026
0f0afeb
chore: update version and README
vchudnov-g Feb 4, 2026
4339dd3
Fix more merge issues
vchudnov-g Feb 4, 2026
6d4c2eb
Tweaks for running nox; formatting
vchudnov-g Feb 4, 2026
1ec9ad2
Code review: pass major proto version
vchudnov-g Feb 5, 2026
ff8b6b5
Increase a timeout to avoid flaky failures
vchudnov-g Feb 5, 2026
697680c
Tweak noxfile comments
vchudnov-g Feb 6, 2026
681ec47
Add async constraints for testing
vchudnov-g Feb 6, 2026
c47dc40
Lower standard required auth version
vchudnov-g Feb 9, 2026
60e9c90
Remove unncessary `pragma: NO COVER`s
vchudnov-g Feb 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .github/workflows/unittest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,10 @@ jobs:
nox -s prerelease_deps
unit:
name: unit${{ matrix.option }}-${{ matrix.python }}
# TODO(https://github.com/googleapis/gapic-generator-python/issues/2303): use `ubuntu-latest` once this bug is fixed.
# Use ubuntu-22.04 until Python 3.7 is removed from the test matrix
# https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
strategy:
matrix:
python:
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "3.11"
Expand Down
40 changes: 0 additions & 40 deletions .kokoro/samples/python3.7/common.cfg

This file was deleted.

6 changes: 0 additions & 6 deletions .kokoro/samples/python3.7/continuous.cfg

This file was deleted.

11 changes: 0 additions & 11 deletions .kokoro/samples/python3.7/periodic-head.cfg

This file was deleted.

6 changes: 0 additions & 6 deletions .kokoro/samples/python3.7/periodic.cfg

This file was deleted.

6 changes: 0 additions & 6 deletions .kokoro/samples/python3.7/presubmit.cfg

This file was deleted.

40 changes: 0 additions & 40 deletions .kokoro/samples/python3.8/common.cfg

This file was deleted.

6 changes: 0 additions & 6 deletions .kokoro/samples/python3.8/continuous.cfg

This file was deleted.

11 changes: 0 additions & 11 deletions .kokoro/samples/python3.8/periodic-head.cfg

This file was deleted.

6 changes: 0 additions & 6 deletions .kokoro/samples/python3.8/periodic.cfg

This file was deleted.

6 changes: 0 additions & 6 deletions .kokoro/samples/python3.8/presubmit.cfg

This file was deleted.

3 changes: 1 addition & 2 deletions .kokoro/test-samples-impl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ export PYTHONUNBUFFERED=1
env | grep KOKORO

# Install nox
# `virtualenv==20.26.6` is added for Python 3.7 compatibility
python3.9 -m pip install --upgrade --quiet nox virtualenv==20.26.6
python3.9 -m pip install --upgrade --quiet nox

# Use secrets acessor service account to get secrets
if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then
Expand Down
6 changes: 1 addition & 5 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ In order to add a feature:
documentation.

- The feature must work fully on the following CPython versions:
3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14 on both UNIX and Windows.
3.9, 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
Expand Down Expand Up @@ -197,17 +197,13 @@ Supported Python Versions

We support:

- `Python 3.7`_
- `Python 3.8`_
- `Python 3.9`_
- `Python 3.10`_
- `Python 3.11`_
- `Python 3.12`_
- `Python 3.13`_
- `Python 3.14`_

.. _Python 3.7: https://docs.python.org/3.7/
.. _Python 3.8: https://docs.python.org/3.8/
.. _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/
Expand Down
7 changes: 5 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@ common helpers used by all Google API clients. For more information, see the

Supported Python Versions
-------------------------
Python >= 3.7
Python >= 3.9


Unsupported Python Versions
---------------------------

Python == 2.7, Python == 3.5, Python == 3.6.
Python == 2.7, Python == 3.5, Python == 3.6, Python == 3.7, Python == 3.8.

The last version of this library compatible with Python 2.7 and 3.5 is
`google-api-core==1.31.1`.

The last version of this library compatible with Python 3.6 is
`google-api-core==2.8.2`.

The last version of this library compatible with Python 3.7 and 3.8 is
`google-api-core==2.29.0`.
11 changes: 2 additions & 9 deletions google/api_core/_python_package_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"""Code to check versions of dependencies used by Google Cloud Client Libraries."""

import warnings
import sys
from typing import Optional, Tuple

from collections import namedtuple
Expand All @@ -25,12 +24,7 @@
_get_distribution_and_import_packages,
)

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
from importlib import metadata

ParsedVersion = Tuple[int, ...]

Expand Down Expand Up @@ -85,8 +79,7 @@ def get_dependency_version(
"""Get the parsed version of an installed package dependency.

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

Args:
dependency_name: The distribution name of the package (e.g., 'requests').
Expand Down
3 changes: 0 additions & 3 deletions google/api_core/gapic_v1/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@
class _MethodDefault(enum.Enum):
# Uses enum so that pytype/mypy knows that this is the only possible value.
# https://stackoverflow.com/a/60605919/101923
#
# Literal[_DEFAULT_VALUE] is an alternative, but only added in Python 3.8.
# https://docs.python.org/3/library/typing.html#typing.Literal
_DEFAULT_VALUE = object()


Expand Down
37 changes: 2 additions & 35 deletions google/api_core/grpc_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,7 @@

from google.api_core import exceptions, general_helpers

PROTOBUF_VERSION = google.protobuf.__version__

# The grpcio-gcp package only has support for protobuf < 4
if PROTOBUF_VERSION[0:2] == "3.": # pragma: NO COVER
try:
import grpc_gcp

warnings.warn(
"""Support for grpcio-gcp is deprecated. This feature will be
removed from `google-api-core` after January 1, 2024. If you need to
continue to use this feature, please pin to a specific version of
`google-api-core`.""",
DeprecationWarning,
)
HAS_GRPC_GCP = True
except ImportError:
HAS_GRPC_GCP = False
else:
HAS_GRPC_GCP = False
HAS_GRPC_GCP = False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HAS_GRPC_GCP does not appear to be used

Suggested change
HAS_GRPC_GCP = False

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created chaser PR #892



# The list of gRPC Callable interfaces that return iterators.
Expand Down Expand Up @@ -366,8 +348,7 @@ def create_channel(
result in `ValueError` as this combination is not yet supported.

kwargs: Additional key-word args passed to
:func:`grpc_gcp.secure_channel` or :func:`grpc.secure_channel`.
Note: `grpc_gcp` is only supported in environments with protobuf < 4.0.0.
:func:`grpc.secure_channel`.

Returns:
grpc.Channel: The created channel.
Expand All @@ -393,20 +374,6 @@ def create_channel(
default_host=default_host,
)

# Note that grpcio-gcp is deprecated
if HAS_GRPC_GCP: # pragma: NO COVER
if compression is not None and compression != grpc.Compression.NoCompression:
warnings.warn(
"The `compression` argument is ignored for grpc_gcp.secure_channel creation.",
DeprecationWarning,
)
if attempt_direct_path:
warnings.warn(
"""The `attempt_direct_path` argument is ignored for grpc_gcp.secure_channel creation.""",
DeprecationWarning,
)
return grpc_gcp.secure_channel(target, composite_credentials, **kwargs)

if attempt_direct_path:
target = _modify_target_for_direct_path(target)

Expand Down
2 changes: 1 addition & 1 deletion google/api_core/operations_v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from google.api_core.operations_v1.operations_rest_client_async import AsyncOperationsRestClient

__all__ += ["AsyncOperationsRestClient", "AsyncOperationsRestTransport"]
except ImportError:
except ImportError: # pragma: NO COVER
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mentioned removing the pragma mark in transports.init, but it seems like there are a few others we should handle the same way

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my other comment. I tried removing one and it didn't help, I don't think we should have a rule to remove them automatically; I'd rather be explicit. WDYT?

# This import requires the `async_rest` extra.
# Don't raise an exception if `AsyncOperationsRestTransport` cannot be imported
# as other transports are still available.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
)

HAS_ASYNC_REST_DEPENDENCIES = True
except ImportError as e:
except ImportError as e: # pragma: NO COVER
HAS_ASYNC_REST_DEPENDENCIES = False
ASYNC_REST_EXCEPTION = e

Expand All @@ -51,7 +51,7 @@ class AbstractOperationsBaseClientMeta(type):

_transport_registry = OrderedDict() # type: Dict[str, Type[OperationsTransport]]
_transport_registry["rest"] = OperationsRestTransport
if HAS_ASYNC_REST_DEPENDENCIES:
if HAS_ASYNC_REST_DEPENDENCIES: # pragma: NO COVER
_transport_registry["rest_asyncio"] = AsyncOperationsRestTransport

def get_transport_class(
Expand Down
2 changes: 1 addition & 1 deletion google/api_core/operations_v1/transports/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
_transport_registry["rest_asyncio"] = cast(
OperationsTransport, AsyncOperationsRestTransport
)
except ImportError:
except ImportError: # pragma: NO COVER
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should investigate why the # pragma: NO COVER is needed now but wasn't before. We may need to add constraints-async-rest-3.9.txt

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was necessary when I was working on this in December, before other work preempted continuing. I can try locally to remove the NO COVER and see what changes.

Are you referring just to this instance of the new NO COVER or to all of them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I tested removing this one pragma and coverage fails. We could add these exceptions to =.coveragerc= or =pyproject.toml=, but honestly, I do like being explicit about where this is being triggered, so I'm tempted to keep the pragma.

Thoughts? @parthea @daniel-sanche

(I'm keeping the pragma deletion for the moment, but if we decide we're OK with being explicit, I'll revert that change so the presubmits pass again.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think if we have to add a no cover mark, this would be the place to do it. But is there a way we can make sure it's covered? It sounds like we're never testing it without the extra? Is that what the constraints-async-rest-3.7.txt file was doing?

Copy link
Contributor

@daniel-sanche daniel-sanche Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked through the code a bit more, and it looks like the logic suggests this should raise an ImportError if google auth is installed without the aiohttp option. Which is what would have happened with auth<2.25, because it doesn't even have the AsyncAuthorizedSession class

But in 2.35, if aiohttp is missing, it suppresses the error at import time, and then raises it at init time. So it looks like these except clauses won't ever be evaluated with auth google-auth 2.35 installed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I think the right approach is to remove this except, and make it so that AsyncOperationsRestTransport is always exported, but will raise an exception on use if aiohttp isn't installed. That seems like the right behaviour

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the right approach is to remove this except,

I think we should discuss and possibly do this separately. I do agree that removing dead code is good, but I din't think it should be included in this PR:

  • this import/except pattern is used in multiple places [*]
  • this pattern makes it explicit locally (i.e. in the same file) whether something is installed, which affects other code in the file
  • async-rest is an optional dependency

Removing this pattern should be thought out well and implemented consistently, and should include guidelines for what to do in the future. We can file a separate issue to follow up.


[*] This pattern of catching ImportExceptions is used mostly for async REST, but also in a few other cases:

Copy link
Contributor

@daniel-sanche daniel-sanche Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this pattern makes it explicit locally (i.e. in the same file) whether something is installed, which affects other code in the file

I don't think this part is true. if we lock to google-auth 2.35, this except block will never be triggered, so it doesn't really tell us anything. Looking at the code, it's responding to the presence of AsyncAuthorizedSession, not the aiohttp dependency. So this except block is only relevant when google-auth < 2.35 is used, and will never be hit with the new pin

I'm fine with punting this change to a different release, but if that's the case, I think we should revert the google-auth version change as well. Otherwise we'll need to be adding in NO COVER marks over dead code paths, that can't actually be executed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The presubmits are passing with the code as is, so I think we should do this clean-up separately, both to avoid new problems and to have single-scope PRs.

My comment about the try/import making the code more obvious was referring to instances like this where we set flags that affect how the code is run later on. This makes it slightly less trivial than catching the import exception and re-issuing it. But granted, it's still simple and doesn't seem to happen often.

Filed #891 . Happy to work on that right after we submit this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what happened is that I must have opted for a uniform versions of gogle-auth, since since we required such disparate versions with or without the async-rest extra. I just reverted that so that we keep requiring the different versions, and tried removing the pragmas. Let's see whether the presubmits pass.

# This import requires the `async_rest` extra.
# Don't raise an exception if `AsyncOperationsRestTransport` cannot be imported
# as other transports are still available.
Expand Down
2 changes: 1 addition & 1 deletion google/api_core/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

__version__ = "2.29.0"
__version__ = "2.29.1"
Loading
Loading