Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 305be5f

Browse filesBrowse files
gh-118761: Lazily import annotationlib in typing (#132060)
annotationlib is used quite a few times in typing.py, but I think the usages are just rare enough that this makes sense. The import would get triggered by: - Using get_type_hints(), evaluate_forward_ref(), and similar introspection functions - Using a string annotation anywhere that goes through _type_convert (e.g., "Final['x']" will trigger an annotationlib import in order to access the ForwardRef class). - Creating a TypedDict or NamedTuple (unless it's empty or PEP 563 is on). Lots of programs will want to use typing without any of these, so the tradeoff seems worth it.
1 parent 04bc681 commit 305be5f
Copy full SHA for 305be5f

File tree

1 file changed

+48
-31
lines changed
Filter options

1 file changed

+48
-31
lines changed

‎Lib/typing.py

Copy file name to clipboardExpand all lines: Lib/typing.py
+48-31Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
"""
2020

2121
from abc import abstractmethod, ABCMeta
22-
import annotationlib
23-
from annotationlib import ForwardRef
2422
import collections
2523
from collections import defaultdict
2624
import collections.abc
@@ -163,6 +161,15 @@
163161
'Unpack',
164162
]
165163

164+
class _LazyAnnotationLib:
165+
def __getattr__(self, attr):
166+
global _lazy_annotationlib
167+
import annotationlib
168+
_lazy_annotationlib = annotationlib
169+
return getattr(annotationlib, attr)
170+
171+
_lazy_annotationlib = _LazyAnnotationLib()
172+
166173

167174
def _type_convert(arg, module=None, *, allow_special_forms=False):
168175
"""For converting None to type(None), and strings to ForwardRef."""
@@ -246,7 +253,7 @@ def _type_repr(obj):
246253
if isinstance(obj, tuple):
247254
# Special case for `repr` of types with `ParamSpec`:
248255
return '[' + ', '.join(_type_repr(t) for t in obj) + ']'
249-
return annotationlib.value_to_string(obj)
256+
return _lazy_annotationlib.value_to_string(obj)
250257

251258

252259
def _collect_type_parameters(args, *, enforce_default_ordering: bool = True):
@@ -423,7 +430,7 @@ def __repr__(self):
423430

424431

425432
def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=frozenset(),
426-
format=annotationlib.Format.VALUE, owner=None):
433+
format=None, owner=None):
427434
"""Evaluate all forward references in the given type t.
428435
429436
For use of globalns and localns see the docstring for get_type_hints().
@@ -433,7 +440,7 @@ def _eval_type(t, globalns, localns, type_params=_sentinel, *, recursive_guard=f
433440
if type_params is _sentinel:
434441
_deprecation_warning_for_no_type_params_passed("typing._eval_type")
435442
type_params = ()
436-
if isinstance(t, ForwardRef):
443+
if isinstance(t, _lazy_annotationlib.ForwardRef):
437444
# If the forward_ref has __forward_module__ set, evaluate() infers the globals
438445
# from the module, and it will probably pick better than the globals we have here.
439446
if t.__forward_module__ is not None:
@@ -930,7 +937,7 @@ def run(arg: Child | Unrelated):
930937

931938

932939
def _make_forward_ref(code, **kwargs):
933-
forward_ref = ForwardRef(code, **kwargs)
940+
forward_ref = _lazy_annotationlib.ForwardRef(code, **kwargs)
934941
# For compatibility, eagerly compile the forwardref's code.
935942
forward_ref.__forward_code__
936943
return forward_ref
@@ -943,7 +950,7 @@ def evaluate_forward_ref(
943950
globals=None,
944951
locals=None,
945952
type_params=None,
946-
format=annotationlib.Format.VALUE,
953+
format=None,
947954
_recursive_guard=frozenset(),
948955
):
949956
"""Evaluate a forward reference as a type hint.
@@ -965,10 +972,11 @@ def evaluate_forward_ref(
965972
evaluating the forward reference. This parameter should be provided (though
966973
it may be an empty tuple) if *owner* is not given and the forward reference
967974
does not already have an owner set. *format* specifies the format of the
968-
annotation and is a member of the annotationlib.Format enum.
975+
annotation and is a member of the annotationlib.Format enum, defaulting to
976+
VALUE.
969977
970978
"""
971-
if format == annotationlib.Format.STRING:
979+
if format == _lazy_annotationlib.Format.STRING:
972980
return forward_ref.__forward_arg__
973981
if forward_ref.__forward_arg__ in _recursive_guard:
974982
return forward_ref
@@ -977,7 +985,7 @@ def evaluate_forward_ref(
977985
value = forward_ref.evaluate(globals=globals, locals=locals,
978986
type_params=type_params, owner=owner)
979987
except NameError:
980-
if format == annotationlib.Format.FORWARDREF:
988+
if format == _lazy_annotationlib.Format.FORWARDREF:
981989
return forward_ref
982990
else:
983991
raise
@@ -2257,7 +2265,7 @@ def greet(name: str) -> None:
22572265

22582266

22592267
def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
2260-
*, format=annotationlib.Format.VALUE):
2268+
*, format=None):
22612269
"""Return type hints for an object.
22622270
22632271
This is often the same as obj.__annotations__, but it handles
@@ -2290,12 +2298,15 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
22902298
"""
22912299
if getattr(obj, '__no_type_check__', None):
22922300
return {}
2301+
Format = _lazy_annotationlib.Format
2302+
if format is None:
2303+
format = Format.VALUE
22932304
# Classes require a special treatment.
22942305
if isinstance(obj, type):
22952306
hints = {}
22962307
for base in reversed(obj.__mro__):
2297-
ann = annotationlib.get_annotations(base, format=format)
2298-
if format == annotationlib.Format.STRING:
2308+
ann = _lazy_annotationlib.get_annotations(base, format=format)
2309+
if format == Format.STRING:
22992310
hints.update(ann)
23002311
continue
23012312
if globalns is None:
@@ -2319,12 +2330,12 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
23192330
value = _eval_type(value, base_globals, base_locals, base.__type_params__,
23202331
format=format, owner=obj)
23212332
hints[name] = value
2322-
if include_extras or format == annotationlib.Format.STRING:
2333+
if include_extras or format == Format.STRING:
23232334
return hints
23242335
else:
23252336
return {k: _strip_annotations(t) for k, t in hints.items()}
23262337

2327-
hints = annotationlib.get_annotations(obj, format=format)
2338+
hints = _lazy_annotationlib.get_annotations(obj, format=format)
23282339
if (
23292340
not hints
23302341
and not isinstance(obj, types.ModuleType)
@@ -2333,7 +2344,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
23332344
and not hasattr(obj, '__annotate__')
23342345
):
23352346
raise TypeError(f"{obj!r} is not a module, class, or callable.")
2336-
if format == annotationlib.Format.STRING:
2347+
if format == Format.STRING:
23372348
return hints
23382349

23392350
if globalns is None:
@@ -2850,10 +2861,10 @@ def _make_eager_annotate(types):
28502861
for key, val in types.items()}
28512862
def annotate(format):
28522863
match format:
2853-
case annotationlib.Format.VALUE | annotationlib.Format.FORWARDREF:
2864+
case _lazy_annotationlib.Format.VALUE | _lazy_annotationlib.Format.FORWARDREF:
28542865
return checked_types
2855-
case annotationlib.Format.STRING:
2856-
return annotationlib.annotations_to_string(types)
2866+
case _lazy_annotationlib.Format.STRING:
2867+
return _lazy_annotationlib.annotations_to_string(types)
28572868
case _:
28582869
raise NotImplementedError(format)
28592870
return annotate
@@ -2884,16 +2895,18 @@ def __new__(cls, typename, bases, ns):
28842895
annotate = _make_eager_annotate(types)
28852896
elif "__annotate__" in ns:
28862897
original_annotate = ns["__annotate__"]
2887-
types = annotationlib.call_annotate_function(original_annotate, annotationlib.Format.FORWARDREF)
2898+
types = _lazy_annotationlib.call_annotate_function(
2899+
original_annotate, _lazy_annotationlib.Format.FORWARDREF)
28882900
field_names = list(types)
28892901

28902902
# For backward compatibility, type-check all the types at creation time
28912903
for typ in types.values():
28922904
_type_check(typ, "field annotation must be a type")
28932905

28942906
def annotate(format):
2895-
annos = annotationlib.call_annotate_function(original_annotate, format)
2896-
if format != annotationlib.Format.STRING:
2907+
annos = _lazy_annotationlib.call_annotate_function(
2908+
original_annotate, format)
2909+
if format != _lazy_annotationlib.Format.STRING:
28972910
return {key: _type_check(val, f"field {key} annotation must be a type")
28982911
for key, val in annos.items()}
28992912
return annos
@@ -3069,8 +3082,8 @@ def __new__(cls, name, bases, ns, total=True):
30693082
own_annotations = ns["__annotations__"]
30703083
elif "__annotate__" in ns:
30713084
own_annotate = ns["__annotate__"]
3072-
own_annotations = annotationlib.call_annotate_function(
3073-
own_annotate, annotationlib.Format.FORWARDREF, owner=tp_dict
3085+
own_annotations = _lazy_annotationlib.call_annotate_function(
3086+
own_annotate, _lazy_annotationlib.Format.FORWARDREF, owner=tp_dict
30743087
)
30753088
else:
30763089
own_annotate = None
@@ -3137,18 +3150,20 @@ def __annotate__(format):
31373150
base_annotate = base.__annotate__
31383151
if base_annotate is None:
31393152
continue
3140-
base_annos = annotationlib.call_annotate_function(base.__annotate__, format, owner=base)
3153+
base_annos = _lazy_annotationlib.call_annotate_function(
3154+
base.__annotate__, format, owner=base)
31413155
annos.update(base_annos)
31423156
if own_annotate is not None:
3143-
own = annotationlib.call_annotate_function(own_annotate, format, owner=tp_dict)
3144-
if format != annotationlib.Format.STRING:
3157+
own = _lazy_annotationlib.call_annotate_function(
3158+
own_annotate, format, owner=tp_dict)
3159+
if format != _lazy_annotationlib.Format.STRING:
31453160
own = {
31463161
n: _type_check(tp, msg, module=tp_dict.__module__)
31473162
for n, tp in own.items()
31483163
}
3149-
elif format == annotationlib.Format.STRING:
3150-
own = annotationlib.annotations_to_string(own_annotations)
3151-
elif format in (annotationlib.Format.FORWARDREF, annotationlib.Format.VALUE):
3164+
elif format == _lazy_annotationlib.Format.STRING:
3165+
own = _lazy_annotationlib.annotations_to_string(own_annotations)
3166+
elif format in (_lazy_annotationlib.Format.FORWARDREF, _lazy_annotationlib.Format.VALUE):
31523167
own = own_checked_annotations
31533168
else:
31543169
raise NotImplementedError(format)
@@ -3732,7 +3747,9 @@ def __getattr__(attr):
37323747
Soft-deprecated objects which are costly to create
37333748
are only created on-demand here.
37343749
"""
3735-
if attr in {"Pattern", "Match"}:
3750+
if attr == "ForwardRef":
3751+
obj = _lazy_annotationlib.ForwardRef
3752+
elif attr in {"Pattern", "Match"}:
37363753
import re
37373754
obj = _alias(getattr(re, attr), 1)
37383755
elif attr in {"ContextManager", "AsyncContextManager"}:

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.