Skip to content

Commit 1857c0e

Browse files
committed
Optimise import time for annotationlib
1 parent 06822bf commit 1857c0e

File tree

2 files changed

+213
-202
lines changed

2 files changed

+213
-202
lines changed

Lib/annotationlib.py renamed to Lib/annotationlib/__init__.py

Lines changed: 10 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
"""Helpers for introspecting and wrapping annotations."""
22

3-
import ast
43
import builtins
54
import enum
6-
import functools
75
import keyword
86
import sys
97
import types
@@ -50,6 +48,14 @@ class Format(enum.IntEnum):
5048
)
5149

5250

51+
def _Stringifier(*args, **kwds):
52+
# This function replaces itself with the real class when first called,
53+
# to delay the import of ``ast`` for performance.
54+
global _Stringifier
55+
from annotationlib._stringifier import Stringifier as _Stringifier
56+
return _Stringifier(*args, **kwds)
57+
58+
5359
class ForwardRef:
5460
"""Wrapper that holds a forward reference.
5561
@@ -205,6 +211,7 @@ def __forward_arg__(self):
205211
if self.__arg__ is not None:
206212
return self.__arg__
207213
if self.__ast_node__ is not None:
214+
import ast # deferred import for performance
208215
self.__arg__ = ast.unparse(self.__ast_node__)
209216
return self.__arg__
210217
raise AssertionError(
@@ -265,206 +272,6 @@ def __repr__(self):
265272
return f"ForwardRef({self.__forward_arg__!r}{module_repr})"
266273

267274

268-
class _Stringifier:
269-
# Must match the slots on ForwardRef, so we can turn an instance of one into an
270-
# instance of the other in place.
271-
__slots__ = _SLOTS
272-
273-
def __init__(
274-
self,
275-
node,
276-
globals=None,
277-
owner=None,
278-
is_class=False,
279-
cell=None,
280-
*,
281-
stringifier_dict,
282-
):
283-
# Either an AST node or a simple str (for the common case where a ForwardRef
284-
# represent a single name).
285-
assert isinstance(node, (ast.AST, str))
286-
self.__arg__ = None
287-
self.__forward_evaluated__ = False
288-
self.__forward_value__ = None
289-
self.__forward_is_argument__ = False
290-
self.__forward_is_class__ = is_class
291-
self.__forward_module__ = None
292-
self.__code__ = None
293-
self.__ast_node__ = node
294-
self.__globals__ = globals
295-
self.__cell__ = cell
296-
self.__owner__ = owner
297-
self.__stringifier_dict__ = stringifier_dict
298-
299-
def __convert_to_ast(self, other):
300-
if isinstance(other, _Stringifier):
301-
if isinstance(other.__ast_node__, str):
302-
return ast.Name(id=other.__ast_node__)
303-
return other.__ast_node__
304-
elif isinstance(other, slice):
305-
return ast.Slice(
306-
lower=(
307-
self.__convert_to_ast(other.start)
308-
if other.start is not None
309-
else None
310-
),
311-
upper=(
312-
self.__convert_to_ast(other.stop)
313-
if other.stop is not None
314-
else None
315-
),
316-
step=(
317-
self.__convert_to_ast(other.step)
318-
if other.step is not None
319-
else None
320-
),
321-
)
322-
else:
323-
return ast.Constant(value=other)
324-
325-
def __get_ast(self):
326-
node = self.__ast_node__
327-
if isinstance(node, str):
328-
return ast.Name(id=node)
329-
return node
330-
331-
def __make_new(self, node):
332-
stringifier = _Stringifier(
333-
node,
334-
self.__globals__,
335-
self.__owner__,
336-
self.__forward_is_class__,
337-
stringifier_dict=self.__stringifier_dict__,
338-
)
339-
self.__stringifier_dict__.stringifiers.append(stringifier)
340-
return stringifier
341-
342-
# Must implement this since we set __eq__. We hash by identity so that
343-
# stringifiers in dict keys are kept separate.
344-
def __hash__(self):
345-
return id(self)
346-
347-
def __getitem__(self, other):
348-
# Special case, to avoid stringifying references to class-scoped variables
349-
# as '__classdict__["x"]'.
350-
if self.__ast_node__ == "__classdict__":
351-
raise KeyError
352-
if isinstance(other, tuple):
353-
elts = [self.__convert_to_ast(elt) for elt in other]
354-
other = ast.Tuple(elts)
355-
else:
356-
other = self.__convert_to_ast(other)
357-
assert isinstance(other, ast.AST), repr(other)
358-
return self.__make_new(ast.Subscript(self.__get_ast(), other))
359-
360-
def __getattr__(self, attr):
361-
return self.__make_new(ast.Attribute(self.__get_ast(), attr))
362-
363-
def __call__(self, *args, **kwargs):
364-
return self.__make_new(
365-
ast.Call(
366-
self.__get_ast(),
367-
[self.__convert_to_ast(arg) for arg in args],
368-
[
369-
ast.keyword(key, self.__convert_to_ast(value))
370-
for key, value in kwargs.items()
371-
],
372-
)
373-
)
374-
375-
def __iter__(self):
376-
yield self.__make_new(ast.Starred(self.__get_ast()))
377-
378-
def __repr__(self):
379-
if isinstance(self.__ast_node__, str):
380-
return self.__ast_node__
381-
return ast.unparse(self.__ast_node__)
382-
383-
def __format__(self, format_spec):
384-
raise TypeError("Cannot stringify annotation containing string formatting")
385-
386-
def _make_binop(op: ast.AST):
387-
def binop(self, other):
388-
return self.__make_new(
389-
ast.BinOp(self.__get_ast(), op, self.__convert_to_ast(other))
390-
)
391-
392-
return binop
393-
394-
__add__ = _make_binop(ast.Add())
395-
__sub__ = _make_binop(ast.Sub())
396-
__mul__ = _make_binop(ast.Mult())
397-
__matmul__ = _make_binop(ast.MatMult())
398-
__truediv__ = _make_binop(ast.Div())
399-
__mod__ = _make_binop(ast.Mod())
400-
__lshift__ = _make_binop(ast.LShift())
401-
__rshift__ = _make_binop(ast.RShift())
402-
__or__ = _make_binop(ast.BitOr())
403-
__xor__ = _make_binop(ast.BitXor())
404-
__and__ = _make_binop(ast.BitAnd())
405-
__floordiv__ = _make_binop(ast.FloorDiv())
406-
__pow__ = _make_binop(ast.Pow())
407-
408-
del _make_binop
409-
410-
def _make_rbinop(op: ast.AST):
411-
def rbinop(self, other):
412-
return self.__make_new(
413-
ast.BinOp(self.__convert_to_ast(other), op, self.__get_ast())
414-
)
415-
416-
return rbinop
417-
418-
__radd__ = _make_rbinop(ast.Add())
419-
__rsub__ = _make_rbinop(ast.Sub())
420-
__rmul__ = _make_rbinop(ast.Mult())
421-
__rmatmul__ = _make_rbinop(ast.MatMult())
422-
__rtruediv__ = _make_rbinop(ast.Div())
423-
__rmod__ = _make_rbinop(ast.Mod())
424-
__rlshift__ = _make_rbinop(ast.LShift())
425-
__rrshift__ = _make_rbinop(ast.RShift())
426-
__ror__ = _make_rbinop(ast.BitOr())
427-
__rxor__ = _make_rbinop(ast.BitXor())
428-
__rand__ = _make_rbinop(ast.BitAnd())
429-
__rfloordiv__ = _make_rbinop(ast.FloorDiv())
430-
__rpow__ = _make_rbinop(ast.Pow())
431-
432-
del _make_rbinop
433-
434-
def _make_compare(op):
435-
def compare(self, other):
436-
return self.__make_new(
437-
ast.Compare(
438-
left=self.__get_ast(),
439-
ops=[op],
440-
comparators=[self.__convert_to_ast(other)],
441-
)
442-
)
443-
444-
return compare
445-
446-
__lt__ = _make_compare(ast.Lt())
447-
__le__ = _make_compare(ast.LtE())
448-
__eq__ = _make_compare(ast.Eq())
449-
__ne__ = _make_compare(ast.NotEq())
450-
__gt__ = _make_compare(ast.Gt())
451-
__ge__ = _make_compare(ast.GtE())
452-
453-
del _make_compare
454-
455-
def _make_unary_op(op):
456-
def unary_op(self):
457-
return self.__make_new(ast.UnaryOp(op, self.__get_ast()))
458-
459-
return unary_op
460-
461-
__invert__ = _make_unary_op(ast.Invert())
462-
__pos__ = _make_unary_op(ast.UAdd())
463-
__neg__ = _make_unary_op(ast.USub())
464-
465-
del _make_unary_op
466-
467-
468275
class _StringifierDict(dict):
469276
def __init__(self, namespace, globals=None, owner=None, is_class=False):
470277
super().__init__(namespace)
@@ -768,6 +575,7 @@ def get_annotations(
768575
raise TypeError(f"{obj!r} is not a module, class, or callable.")
769576

770577
if unwrap is not None:
578+
import functools
771579
while True:
772580
if hasattr(unwrap, "__wrapped__"):
773581
unwrap = unwrap.__wrapped__

0 commit comments

Comments
 (0)