Skip to content

feat: lazy imports google auth#17679

Open
hebaalazzeh wants to merge 24 commits into
mainfrom
feature/lazy-imports-google-auth
Open

feat: lazy imports google auth#17679
hebaalazzeh wants to merge 24 commits into
mainfrom
feature/lazy-imports-google-auth

Conversation

@hebaalazzeh

@hebaalazzeh hebaalazzeh commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Description

This PR initiates the rollout of our PEP 0810 lazy-loading architecture to google-auth, starting with the transport module.

By applying Python 3.15's native explicit lazy imports directly inside our transport wrappers (requests.py, urllib3.py, and grpc.py), we defer the eager parsing and compilation of the heavy third-party networking libraries (requests, urllib3, and grpc). This acts as the first step in addressing the high initialization latency and peak memory footprints we're seeing on Serverless cold-starts.

Older Python runtimes (3.14 and below) safely ignore the __lazy_modules__ set and fall back to standard eager execution, meaning this introduces zero backwards compatibility risk.

Related Documents

Note: This PR was kept intentionally small for review speed. We will be executing an iterative rollout, extending this pattern to other heavy modules like oauth2 and compute_engine in fast follow-up PRs once this structural pattern is approved.

@hebaalazzeh hebaalazzeh marked this pull request as ready for review July 9, 2026 05:09
@hebaalazzeh hebaalazzeh requested review from a team as code owners July 9, 2026 05:09

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a __lazy_modules__ set in packages/google-auth/google/auth/transport/__init__.py to support lazy loading of transport modules. The review feedback correctly identifies an incorrect module name (_aiohttp_requests instead of aiohttp_requests) that would prevent lazy importing in Python 3.15+.

Comment thread packages/google-auth/google/auth/transport/__init__.py Outdated

@parthea parthea left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM but holding off on formal approval until we have tests. We should assert that on Python 3.15+ the modules are indeed absent from sys.modules until accessed, and that on pre-3.15 environments they fallback cleanly to standard eager imports

import sys
import pytest

# List of modules we expect to be lazy
LAZY_MODULES = [
    "google.auth.transport.requests",
    "google.auth.transport.urllib3",
    "google.auth.transport.grpc",
]

def clean_sys_modules():
    """Helper to ensure we start with a clean slate for import testing."""
    for mod in LAZY_MODULES:
        sys.modules.pop(mod, None)

@pytest.mark.skipif(sys.version_info < (3, 15), reason="PEP 810 requires Python 3.15+")
def test_lazy_imports_on_python_315():
    clean_sys_modules()
    
    # 1. Import the transport package
    import google.auth.transport
    
    # 2. Assert that none of the lazy modules have been eagerly loaded into sys.modules
    for mod in LAZY_MODULES:
        assert mod not in sys.modules
        
    # 3. Access an attribute to trigger reification
    from google.auth.transport import requests
    _ = requests.__name__  # Trigger first-use reification
    
    # 4. Assert that the module has now been reified and loaded
    assert "google.auth.transport.requests" in sys.modules


@pytest.mark.skipif(sys.version_info >= (3, 15), reason="Testing fallback behavior on < 3.15")
def test_fallback_eager_imports_pre_315():
    clean_sys_modules()
    
    # On older Python, __lazy_modules__ is safely ignored, meaning they should eager-load
    import google.auth.transport
    
    for mod in LAZY_MODULES:
        assert mod in sys.modules

@parthea

parthea commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

I see the tests are failing. We also need to add from google.auth.transport import requests to the test file

@parthea

parthea commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Disregard my last comment.

Under PEP 810, __lazy_modules__ is a module-local opt-in . It only intercepts import statements written inside the module where it is defined

We should only add __lazy_modules__ to files where we also have the same import statements

From https://docs.python.org/3.15/reference/simple_stmts.html#lazy-imports

Any regular (non-lazy) import statement at module scope whose target appears in lazy_modules is treated as a lazy import, exactly as if the lazy keyword had been used.

Comment thread packages/google-auth/google/auth/transport/__init__.py Outdated
@hebaalazzeh hebaalazzeh requested a review from a team as a code owner July 10, 2026 01:33
@hebaalazzeh hebaalazzeh force-pushed the feature/lazy-imports-google-auth branch from 6e6fe6e to eef11dc Compare July 10, 2026 06:43
@hebaalazzeh

Copy link
Copy Markdown
Contributor Author

addressed all comments

@parthea parthea assigned parthea and unassigned hebaalazzeh Jul 13, 2026
@parthea

parthea commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces lazy import support for Python 3.15+ by defining __lazy_modules__ in several transport modules (grpc.py, requests.py, and urllib3.py) and adding corresponding integration tests. However, the feedback highlights that the lazy loading mechanism is currently defeated in grpc.py due to inheriting from grpc.AuthMetadataPlugin at the module level, and in urllib3.py due to a module-level version check on urllib3.__version__.

Comment thread packages/google-auth/google/auth/transport/grpc.py
Comment thread packages/google-auth/google/auth/transport/urllib3.py

@parthea parthea left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please can you address the feedback from GCA?

@parthea parthea assigned hebaalazzeh and unassigned parthea Jul 13, 2026
@hebaalazzeh

Copy link
Copy Markdown
Contributor Author

\gemini

@parthea

parthea commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces lazy loading support for heavy transport modules (grpc, requests, and urllib3) to optimize import times, particularly targeting Python 3.15+ (PEP 810), with fallback behavior for older versions. Key changes include defining __lazy_modules__, decoupling classes from eager parent imports, and adding a test suite to verify the lazy loading behavior. Feedback focuses on optimizing a performance bottleneck in urllib3.py's dynamic attribute resolution, addressing a bypassed ImportError check in requests.py due to deferred imports, and adding explanatory comments in grpc.py to prevent accidental regressions.

Comment thread packages/google-auth/google/auth/transport/urllib3.py
Comment thread packages/google-auth/google/auth/transport/requests.py
Comment thread packages/google-auth/google/auth/transport/grpc.py
@hebaalazzeh

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for PEP 0810 (Explicit Lazy Imports) in Python 3.15+ across the grpc, requests, and urllib3 transports. This is achieved by defining lazy_modules, removing eager base class inheritance, and lazily resolving attributes (such as RequestMethods in AuthorizedHttp). A test suite is also added to verify this behavior. The reviewer suggests improving the getattr implementation in AuthorizedHttp by dynamically checking for attributes on the underlying RequestMethods class rather than hardcoding specific method names, and lazily importing version to avoid eagerly loading the packaging module.

Comment thread packages/google-auth/google/auth/transport/urllib3.py
Comment on lines +218 to 219
class AuthorizedHttp(object):
"""A urllib3 HTTP class with credentials.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Changing AuthorizedHttp from subclassing urllib3.request.RequestMethods (or urllib3._request_methods.RequestMethods) to object is a breaking change across all Python versions (3.9–3.15+). Any downstream code that checks isinstance(authed_http, urllib3.request.RequestMethods) (or urllib3.PoolManager) will now return False.

Comment on lines +304 to +320
def __getattr__(self, name):
if name in ("request", "request_encode_url", "request_encode_body"):
import types

if AuthorizedHttp._request_methods_class is None:
if version.parse(urllib3.__version__) >= version.parse("2.0.0"):
AuthorizedHttp._request_methods_class = (
urllib3._request_methods.RequestMethods
)
else:
AuthorizedHttp._request_methods_class = (
urllib3.request.RequestMethods
)

method = getattr(AuthorizedHttp._request_methods_class, name)
bound_method = types.MethodType(method, self)
setattr(self, name, bound_method)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If we revert class AuthorizedHttp(object): back to inheriting from RequestMethods to avoid the breaking isinstance() change, this __getattr__ method and the _request_methods_class lookup won't be needed and should be removed to keep the implementation simple and safe.

Comment on lines +45 to +47
# Inheriting from object instead of grpc.AuthMetadataPlugin is intentional.
# This prevents eagerly loading grpc at module import time, preserving lazy loading support.
class AuthMetadataPlugin(object):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Similar to AuthorizedHttp in urllib3.py, changing AuthMetadataPlugin from subclassing grpc.AuthMetadataPlugin to object introduces a breaking change across all Python versions for any downstream code checking isinstance(plugin, grpc.AuthMetadataPlugin).

Comment on lines +53 to +54
__lazy_modules__: Set[str] = {"requests", "requests.adapters", "requests.exceptions"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should this be declared before the import statements for it to actually work?

:class:`requests.exceptions.Timeout`.
"""

def __init__(self, timeout, timeout_error_type=requests.exceptions.Timeout):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Default parameter expressions in function signatures (requests.exceptions.Timeout) are evaluated when the function is defined at module import time. This immediately reifies requests.exceptions (and requests) when requests.py is imported.

@@ -0,0 +1,77 @@
# Copyright 2026 Google LLC

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should remove test_lazy_imports.py entirely rather than maintaining subprocess-based tests:

  • Testing Python's Internal implementation: __lazy_modules__ is a standard declarative hint for PEP 0810. We don't need to maintain custom subprocess unit tests to verify Python 3.15's built-in module reification behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants