diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 4d1c0f2c724b86e..9e891f113840bef 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -9373,6 +9373,10 @@ def test_all(self): self.assertIn('SupportsComplex', a) def test_all_exported_names(self): + # ensure all dynamically created objects are actualised + for name in typing.__all__: + getattr(typing, name) + actual_all = set(typing.__all__) computed_all = { k for k, v in vars(typing).items() diff --git a/Lib/typing.py b/Lib/typing.py index 183d5b29a233622..639be75747dae0d 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -23,10 +23,8 @@ from collections import defaultdict import collections.abc import copyreg -import contextlib import functools import operator -import re as stdlib_re # Avoid confusion with the typing.re namespace on <=3.11 import sys import types from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType, GenericAlias @@ -2580,8 +2578,6 @@ class Other(Leaf): # Error reported by type checker KeysView = _alias(collections.abc.KeysView, 1) ItemsView = _alias(collections.abc.ItemsView, 2) ValuesView = _alias(collections.abc.ValuesView, 1) -ContextManager = _alias(contextlib.AbstractContextManager, 1, name='ContextManager') -AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, 1, name='AsyncContextManager') Dict = _alias(dict, 2, inst=False, name='Dict') DefaultDict = _alias(collections.defaultdict, 2, name='DefaultDict') OrderedDict = _alias(collections.OrderedDict, 2) @@ -3238,10 +3234,6 @@ def __enter__(self) -> 'TextIO': pass -Pattern = _alias(stdlib_re.Pattern, 1) -Match = _alias(stdlib_re.Match, 1) - - def reveal_type[T](obj: T, /) -> T: """Reveal the inferred type of a variable. @@ -3426,3 +3418,21 @@ def get_protocol_members(tp: type, /) -> frozenset[str]: if not is_protocol(tp): raise TypeError(f'{tp!r} is not a Protocol') return frozenset(tp.__protocol_attrs__) + + +def __getattr__(attr): + """Improve the import time of the typing module. + + Soft-deprecated objects which are costly to create + are only created on-demand here. + """ + if attr in {"Pattern", "Match"}: + import re + obj = _alias(getattr(re, attr), 1) + elif attr in {"ContextManager", "AsyncContextManager"}: + import contextlib + obj = _alias(getattr(contextlib, f"Abstract{attr}"), 1, name=attr) + else: + raise AttributeError(f"module {__name__!r} has no attribute {attr!r}") + globals()[attr] = obj + return obj diff --git a/Misc/NEWS.d/next/Library/2023-09-21-19-42-22.gh-issue-109653.bL3iLH.rst b/Misc/NEWS.d/next/Library/2023-09-21-19-42-22.gh-issue-109653.bL3iLH.rst new file mode 100644 index 000000000000000..9f794bb58ba63b1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-21-19-42-22.gh-issue-109653.bL3iLH.rst @@ -0,0 +1,2 @@ +Reduce the import time of :mod:`typing` by around a third. +Patch by Alex Waygood.