|
1 | 1 | """Helpers for introspecting and wrapping annotations."""
|
2 | 2 |
|
3 |
| -import ast |
4 | 3 | import builtins
|
5 | 4 | import enum
|
6 |
| -import functools |
7 | 5 | import keyword
|
8 | 6 | import sys
|
9 | 7 | import types
|
@@ -50,6 +48,14 @@ class Format(enum.IntEnum):
|
50 | 48 | )
|
51 | 49 |
|
52 | 50 |
|
| 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 | + |
53 | 59 | class ForwardRef:
|
54 | 60 | """Wrapper that holds a forward reference.
|
55 | 61 |
|
@@ -205,6 +211,7 @@ def __forward_arg__(self):
|
205 | 211 | if self.__arg__ is not None:
|
206 | 212 | return self.__arg__
|
207 | 213 | if self.__ast_node__ is not None:
|
| 214 | + import ast # deferred import for performance |
208 | 215 | self.__arg__ = ast.unparse(self.__ast_node__)
|
209 | 216 | return self.__arg__
|
210 | 217 | raise AssertionError(
|
@@ -265,206 +272,6 @@ def __repr__(self):
|
265 | 272 | return f"ForwardRef({self.__forward_arg__!r}{module_repr})"
|
266 | 273 |
|
267 | 274 |
|
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 |
| - |
468 | 275 | class _StringifierDict(dict):
|
469 | 276 | def __init__(self, namespace, globals=None, owner=None, is_class=False):
|
470 | 277 | super().__init__(namespace)
|
@@ -768,6 +575,7 @@ def get_annotations(
|
768 | 575 | raise TypeError(f"{obj!r} is not a module, class, or callable.")
|
769 | 576 |
|
770 | 577 | if unwrap is not None:
|
| 578 | + import functools |
771 | 579 | while True:
|
772 | 580 | if hasattr(unwrap, "__wrapped__"):
|
773 | 581 | unwrap = unwrap.__wrapped__
|
|
0 commit comments