1f3fbc7ca3
Three implementations of lazy module attributes had grown: the hand-rolled __getattr__/__dir__ in mcp/__init__.py, the submodule fallback factory used by the four package inits, and a one-name __getattr__ in mcp.server.elicitation. They shared two problems. An unconditional module-level __getattr__ is visible to type checkers, so pyright typed every misspelled `mcp.<name>` (and `mcp.client.<name>`, ...) as `object` instead of reporting it. And the submodule fallback ran a filesystem find_spec on every attribute miss and speculatively imported whatever matched, so a name sweep (cloudpickle's whichmodule, hasattr probes) could import real submodules as a side effect, while dir(mcp.client) no longer listed submodules it would happily resolve. mcp.shared._lazy.lazy_module_attrs now serves all of them: lazy exports (a `(module, attr)` pair or a zero-argument loader, resolved once and cached in the namespace) plus known submodules (an explicit set, or the package's real submodules listed once on first need). A miss is a plain AttributeError with no search and no import, and __dir__ reports the exports and submodules. Every caller binds the pair under `if not TYPE_CHECKING:`, so attribute typos are pyright errors again while the TYPE_CHECKING mirrors keep the real names typed. The mcp.server.auth package gets the same fallback so qualified annotations such as `mcp.server.auth.provider.TokenVerifier` resolve on demand. The elicitation gate's wire-schema type is resolved through the same mechanism from a cached accessor, so validating rendered schemas no longer re-executes the wire-package import on every call.
23 lines
617 B
Python
23 lines
617 B
Python
"""A package that installs the lazy-attribute helper, for tests/shared/test_lazy.py."""
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
from mcp.shared._lazy import lazy_module_attrs as _lazy_module_attrs
|
|
|
|
load_calls: int = 0
|
|
|
|
if TYPE_CHECKING:
|
|
# Type checkers see the lazy export as a plain module attribute.
|
|
ANSWER: int
|
|
|
|
|
|
def _load_answer() -> int:
|
|
"""A lazily-resolved re-export: a zero-argument loader that must run at most once."""
|
|
global load_calls
|
|
load_calls += 1
|
|
return 42
|
|
|
|
|
|
if not TYPE_CHECKING:
|
|
__getattr__, __dir__ = _lazy_module_attrs(__name__, globals(), exports={"ANSWER": _load_answer})
|