Skip to content

Commit 62a6472

Browse files
rustyconoverclaude
andcommitted
vgi.auth: import the HTTP and JWT auth helpers on first use
vgi/__init__ imports vgi.auth, which imported vgi_rpc.http to re-export its auth helpers. That module brings in the HTTP server and client stack (falcon, httpx2, cryptography, joserfc): about 180 ms of every 'import vgi', and so of every subprocess worker's start, which never uses it. The helpers now resolve on first access through a module __getattr__. 'from vgi.auth import bearer_authenticate' works as before, and raises ImportError when the extra is missing, as it did. Type checkers see them through TYPE_CHECKING imports. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 7c0634b commit 62a6472

1 file changed

Lines changed: 59 additions & 21 deletions

File tree

‎vgi/auth.py‎

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818

1919
from __future__ import annotations
2020

21-
import contextlib
21+
import importlib
2222
from collections.abc import Callable
23+
from typing import TYPE_CHECKING, Any
2324

2425
from vgi_rpc.rpc import AuthContext, CallContext
2526

@@ -43,9 +44,28 @@
4344
"TokenResolver",
4445
]
4546

46-
# HTTP auth helpers — available when vgi[http] is installed.
47-
with contextlib.suppress(ImportError):
48-
from vgi_rpc.http import ( # noqa: F401
47+
# HTTP auth helpers (``vgi[http]``) and JWT auth (``vgi[oauth]``), resolved on
48+
# first access rather than at import. ``vgi/__init__`` imports this module, and
49+
# ``vgi_rpc.http`` brings in the HTTP server and client stack (falcon, httpx2,
50+
# cryptography, joserfc): importing it here put about 180 ms on every
51+
# ``import vgi``, and so on the start of every subprocess worker, which never
52+
# uses it. ``from vgi.auth import bearer_authenticate`` works as before; when
53+
# the extra is missing it raises ImportError, as it did.
54+
_LAZY_EXPORTS = {
55+
"AuthUnavailableError": "vgi_rpc.http",
56+
"OAuthResourceMetadata": "vgi_rpc.http",
57+
"bearer_authenticate": "vgi_rpc.http",
58+
"bearer_authenticate_static": "vgi_rpc.http",
59+
"chain_authenticate": "vgi_rpc.http",
60+
"parse_client_id": "vgi_rpc.http",
61+
"parse_client_secret": "vgi_rpc.http",
62+
"parse_device_code_client_id": "vgi_rpc.http",
63+
"parse_device_code_client_secret": "vgi_rpc.http",
64+
"jwt_authenticate": "vgi_rpc.http._oauth_jwt",
65+
}
66+
67+
if TYPE_CHECKING:
68+
from vgi_rpc.http import (
4969
AuthUnavailableError,
5070
OAuthResourceMetadata,
5171
bearer_authenticate,
@@ -56,21 +76,39 @@
5676
parse_device_code_client_id,
5777
parse_device_code_client_secret,
5878
)
79+
from vgi_rpc.http._oauth_jwt import jwt_authenticate
80+
81+
82+
def __getattr__(name: str) -> Any:
83+
"""Import an HTTP or JWT auth helper on first access (PEP 562).
84+
85+
Args:
86+
name: The attribute being looked up.
87+
88+
Returns:
89+
The helper, which is then cached in this module's namespace.
90+
91+
Raises:
92+
AttributeError: If ``name`` is not a helper this module re-exports, or
93+
its extra is not installed.
94+
"""
95+
module_name = _LAZY_EXPORTS.get(name)
96+
if module_name is None:
97+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
98+
try:
99+
value = getattr(importlib.import_module(module_name), name)
100+
except ImportError as exc:
101+
raise AttributeError(
102+
f"module {__name__!r} has no attribute {name!r}: it needs {module_name}, which failed to import ({exc})"
103+
) from exc
104+
globals()[name] = value
105+
return value
106+
107+
108+
def __dir__() -> list[str]:
109+
"""List this module's names, the lazily imported helpers included.
59110
60-
__all__ += [
61-
"AuthUnavailableError",
62-
"OAuthResourceMetadata",
63-
"bearer_authenticate",
64-
"bearer_authenticate_static",
65-
"chain_authenticate",
66-
"parse_client_id",
67-
"parse_client_secret",
68-
"parse_device_code_client_id",
69-
"parse_device_code_client_secret",
70-
]
71-
72-
# JWT auth — available when vgi[oauth] is installed (requires authlib).
73-
with contextlib.suppress(ImportError):
74-
from vgi_rpc.http._oauth_jwt import jwt_authenticate # noqa: F401
75-
76-
__all__ += ["jwt_authenticate"]
111+
Returns:
112+
The module's attribute names.
113+
"""
114+
return sorted({*globals(), *_LAZY_EXPORTS})

0 commit comments

Comments
 (0)