|
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