| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | """Helpers for introspecting and wrapping annotations.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ast | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  | import builtins | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | import enum | 
					
						
							|  |  |  | import functools | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  | import keyword | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | import sys | 
					
						
							|  |  |  | import types | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-11 16:44:51 -07:00
										 |  |  | __all__ = [ | 
					
						
							|  |  |  |     "Format", | 
					
						
							|  |  |  |     "ForwardRef", | 
					
						
							|  |  |  |     "call_annotate_function", | 
					
						
							|  |  |  |     "call_evaluate_function", | 
					
						
							|  |  |  |     "get_annotate_function", | 
					
						
							|  |  |  |     "get_annotations", | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  |     "annotations_to_string", | 
					
						
							|  |  |  |     "value_to_string", | 
					
						
							| 
									
										
										
										
											2024-08-11 16:44:51 -07:00
										 |  |  | ] | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Format(enum.IntEnum): | 
					
						
							|  |  |  |     VALUE = 1 | 
					
						
							| 
									
										
										
										
											2024-11-26 07:40:13 -08:00
										 |  |  |     VALUE_WITH_FAKE_GLOBALS = 2 | 
					
						
							|  |  |  |     FORWARDREF = 3 | 
					
						
							|  |  |  |     STRING = 4 | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _Union = None | 
					
						
							|  |  |  | _sentinel = object() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Slots shared by ForwardRef and _Stringifier. The __forward__ names must be | 
					
						
							|  |  |  | # preserved for compatibility with the old typing.ForwardRef class. The remaining | 
					
						
							|  |  |  | # names are private. | 
					
						
							|  |  |  | _SLOTS = ( | 
					
						
							|  |  |  |     "__forward_evaluated__", | 
					
						
							|  |  |  |     "__forward_value__", | 
					
						
							|  |  |  |     "__forward_is_argument__", | 
					
						
							|  |  |  |     "__forward_is_class__", | 
					
						
							|  |  |  |     "__forward_module__", | 
					
						
							|  |  |  |     "__weakref__", | 
					
						
							|  |  |  |     "__arg__", | 
					
						
							|  |  |  |     "__ast_node__", | 
					
						
							|  |  |  |     "__code__", | 
					
						
							|  |  |  |     "__globals__", | 
					
						
							|  |  |  |     "__owner__", | 
					
						
							|  |  |  |     "__cell__", | 
					
						
							| 
									
										
										
										
											2024-10-23 16:27:55 -07:00
										 |  |  |     "__stringifier_dict__", | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ForwardRef: | 
					
						
							| 
									
										
										
										
											2024-09-23 12:06:19 -07:00
										 |  |  |     """Wrapper that holds a forward reference.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Constructor arguments: | 
					
						
							|  |  |  |     * arg: a string representing the code to be evaluated. | 
					
						
							|  |  |  |     * module: the module where the forward reference was created. | 
					
						
							|  |  |  |       Must be a string, not a module object. | 
					
						
							|  |  |  |     * owner: The owning object (module, class, or function). | 
					
						
							|  |  |  |     * is_argument: Does nothing, retained for compatibility. | 
					
						
							|  |  |  |     * is_class: True if the forward reference was created in class scope. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     __slots__ = _SLOTS | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__( | 
					
						
							|  |  |  |         self, | 
					
						
							|  |  |  |         arg, | 
					
						
							|  |  |  |         *, | 
					
						
							|  |  |  |         module=None, | 
					
						
							|  |  |  |         owner=None, | 
					
						
							|  |  |  |         is_argument=True, | 
					
						
							|  |  |  |         is_class=False, | 
					
						
							|  |  |  |     ): | 
					
						
							|  |  |  |         if not isinstance(arg, str): | 
					
						
							|  |  |  |             raise TypeError(f"Forward reference must be a string -- got {arg!r}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.__arg__ = arg | 
					
						
							|  |  |  |         self.__forward_evaluated__ = False | 
					
						
							|  |  |  |         self.__forward_value__ = None | 
					
						
							|  |  |  |         self.__forward_is_argument__ = is_argument | 
					
						
							|  |  |  |         self.__forward_is_class__ = is_class | 
					
						
							|  |  |  |         self.__forward_module__ = module | 
					
						
							|  |  |  |         self.__code__ = None | 
					
						
							|  |  |  |         self.__ast_node__ = None | 
					
						
							| 
									
										
										
										
											2024-09-23 12:06:19 -07:00
										 |  |  |         self.__globals__ = None | 
					
						
							|  |  |  |         self.__cell__ = None | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         self.__owner__ = owner | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init_subclass__(cls, /, *args, **kwds): | 
					
						
							|  |  |  |         raise TypeError("Cannot subclass ForwardRef") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None): | 
					
						
							|  |  |  |         """Evaluate the forward reference and return the value.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-11 16:42:57 -07:00
										 |  |  |         If the forward reference cannot be evaluated, raise an exception. | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         if self.__forward_evaluated__: | 
					
						
							|  |  |  |             return self.__forward_value__ | 
					
						
							|  |  |  |         if self.__cell__ is not None: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 value = self.__cell__.cell_contents | 
					
						
							|  |  |  |             except ValueError: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.__forward_evaluated__ = True | 
					
						
							|  |  |  |                 self.__forward_value__ = value | 
					
						
							|  |  |  |                 return value | 
					
						
							|  |  |  |         if owner is None: | 
					
						
							|  |  |  |             owner = self.__owner__ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-11 16:42:57 -07:00
										 |  |  |         if globals is None and self.__forward_module__ is not None: | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             globals = getattr( | 
					
						
							| 
									
										
										
										
											2024-08-11 16:42:57 -07:00
										 |  |  |                 sys.modules.get(self.__forward_module__, None), "__dict__", None | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             ) | 
					
						
							|  |  |  |         if globals is None: | 
					
						
							|  |  |  |             globals = self.__globals__ | 
					
						
							|  |  |  |         if globals is None: | 
					
						
							|  |  |  |             if isinstance(owner, type): | 
					
						
							|  |  |  |                 module_name = getattr(owner, "__module__", None) | 
					
						
							|  |  |  |                 if module_name: | 
					
						
							|  |  |  |                     module = sys.modules.get(module_name, None) | 
					
						
							|  |  |  |                     if module: | 
					
						
							|  |  |  |                         globals = getattr(module, "__dict__", None) | 
					
						
							|  |  |  |             elif isinstance(owner, types.ModuleType): | 
					
						
							|  |  |  |                 globals = getattr(owner, "__dict__", None) | 
					
						
							|  |  |  |             elif callable(owner): | 
					
						
							|  |  |  |                 globals = getattr(owner, "__globals__", None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-23 12:06:19 -07:00
										 |  |  |         # If we pass None to eval() below, the globals of this module are used. | 
					
						
							|  |  |  |         if globals is None: | 
					
						
							|  |  |  |             globals = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         if locals is None: | 
					
						
							|  |  |  |             locals = {} | 
					
						
							| 
									
										
										
										
											2024-08-11 16:42:57 -07:00
										 |  |  |             if isinstance(owner, type): | 
					
						
							|  |  |  |                 locals.update(vars(owner)) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-11 16:42:57 -07:00
										 |  |  |         if type_params is None and owner is not None: | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             # "Inject" type parameters into the local namespace | 
					
						
							|  |  |  |             # (unless they are shadowed by assignments *in* the local namespace), | 
					
						
							|  |  |  |             # as a way of emulating annotation scopes when calling `eval()` | 
					
						
							| 
									
										
										
										
											2024-08-11 16:42:57 -07:00
										 |  |  |             type_params = getattr(owner, "__type_params__", None) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # type parameters require some special handling, | 
					
						
							|  |  |  |         # as they exist in their own scope | 
					
						
							|  |  |  |         # but `eval()` does not have a dedicated parameter for that scope. | 
					
						
							|  |  |  |         # For classes, names in type parameter scopes should override | 
					
						
							|  |  |  |         # names in the global scope (which here are called `localns`!), | 
					
						
							|  |  |  |         # but should in turn be overridden by names in the class scope | 
					
						
							|  |  |  |         # (which here are called `globalns`!) | 
					
						
							|  |  |  |         if type_params is not None: | 
					
						
							| 
									
										
										
										
											2024-09-23 12:06:19 -07:00
										 |  |  |             globals = dict(globals) | 
					
						
							|  |  |  |             locals = dict(locals) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             for param in type_params: | 
					
						
							|  |  |  |                 param_name = param.__name__ | 
					
						
							|  |  |  |                 if not self.__forward_is_class__ or param_name not in globals: | 
					
						
							|  |  |  |                     globals[param_name] = param | 
					
						
							|  |  |  |                     locals.pop(param_name, None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |         arg = self.__forward_arg__ | 
					
						
							|  |  |  |         if arg.isidentifier() and not keyword.iskeyword(arg): | 
					
						
							|  |  |  |             if arg in locals: | 
					
						
							|  |  |  |                 value = locals[arg] | 
					
						
							|  |  |  |             elif arg in globals: | 
					
						
							|  |  |  |                 value = globals[arg] | 
					
						
							|  |  |  |             elif hasattr(builtins, arg): | 
					
						
							|  |  |  |                 return getattr(builtins, arg) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise NameError(arg) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             code = self.__forward_code__ | 
					
						
							|  |  |  |             value = eval(code, globals=globals, locals=locals) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         self.__forward_evaluated__ = True | 
					
						
							|  |  |  |         self.__forward_value__ = value | 
					
						
							|  |  |  |         return value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _evaluate(self, globalns, localns, type_params=_sentinel, *, recursive_guard): | 
					
						
							|  |  |  |         import typing | 
					
						
							|  |  |  |         import warnings | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if type_params is _sentinel: | 
					
						
							|  |  |  |             typing._deprecation_warning_for_no_type_params_passed( | 
					
						
							|  |  |  |                 "typing.ForwardRef._evaluate" | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             type_params = () | 
					
						
							|  |  |  |         warnings._deprecated( | 
					
						
							|  |  |  |             "ForwardRef._evaluate", | 
					
						
							|  |  |  |             "{name} is a private API and is retained for compatibility, but will be removed" | 
					
						
							|  |  |  |             " in Python 3.16. Use ForwardRef.evaluate() or typing.evaluate_forward_ref() instead.", | 
					
						
							|  |  |  |             remove=(3, 16), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         return typing.evaluate_forward_ref( | 
					
						
							|  |  |  |             self, | 
					
						
							|  |  |  |             globals=globalns, | 
					
						
							|  |  |  |             locals=localns, | 
					
						
							|  |  |  |             type_params=type_params, | 
					
						
							|  |  |  |             _recursive_guard=recursive_guard, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def __forward_arg__(self): | 
					
						
							|  |  |  |         if self.__arg__ is not None: | 
					
						
							|  |  |  |             return self.__arg__ | 
					
						
							|  |  |  |         if self.__ast_node__ is not None: | 
					
						
							|  |  |  |             self.__arg__ = ast.unparse(self.__ast_node__) | 
					
						
							|  |  |  |             return self.__arg__ | 
					
						
							|  |  |  |         raise AssertionError( | 
					
						
							|  |  |  |             "Attempted to access '__forward_arg__' on an uninitialized ForwardRef" | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def __forward_code__(self): | 
					
						
							|  |  |  |         if self.__code__ is not None: | 
					
						
							|  |  |  |             return self.__code__ | 
					
						
							|  |  |  |         arg = self.__forward_arg__ | 
					
						
							|  |  |  |         # If we do `def f(*args: *Ts)`, then we'll have `arg = '*Ts'`. | 
					
						
							|  |  |  |         # Unfortunately, this isn't a valid expression on its own, so we | 
					
						
							|  |  |  |         # do the unpacking manually. | 
					
						
							|  |  |  |         if arg.startswith("*"): | 
					
						
							|  |  |  |             arg_to_compile = f"({arg},)[0]"  # E.g. (*Ts,)[0] or (*tuple[int, int],)[0] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             arg_to_compile = arg | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.__code__ = compile(arg_to_compile, "<string>", "eval") | 
					
						
							|  |  |  |         except SyntaxError: | 
					
						
							|  |  |  |             raise SyntaxError(f"Forward reference must be an expression -- got {arg!r}") | 
					
						
							|  |  |  |         return self.__code__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __eq__(self, other): | 
					
						
							|  |  |  |         if not isinstance(other, ForwardRef): | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         if self.__forward_evaluated__ and other.__forward_evaluated__: | 
					
						
							|  |  |  |             return ( | 
					
						
							|  |  |  |                 self.__forward_arg__ == other.__forward_arg__ | 
					
						
							|  |  |  |                 and self.__forward_value__ == other.__forward_value__ | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         return ( | 
					
						
							|  |  |  |             self.__forward_arg__ == other.__forward_arg__ | 
					
						
							|  |  |  |             and self.__forward_module__ == other.__forward_module__ | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __hash__(self): | 
					
						
							|  |  |  |         return hash((self.__forward_arg__, self.__forward_module__)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __or__(self, other): | 
					
						
							|  |  |  |         global _Union | 
					
						
							|  |  |  |         if _Union is None: | 
					
						
							|  |  |  |             from typing import Union as _Union | 
					
						
							|  |  |  |         return _Union[self, other] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __ror__(self, other): | 
					
						
							|  |  |  |         global _Union | 
					
						
							|  |  |  |         if _Union is None: | 
					
						
							|  |  |  |             from typing import Union as _Union | 
					
						
							|  |  |  |         return _Union[other, self] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         if self.__forward_module__ is None: | 
					
						
							|  |  |  |             module_repr = "" | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             module_repr = f", module={self.__forward_module__!r}" | 
					
						
							|  |  |  |         return f"ForwardRef({self.__forward_arg__!r}{module_repr})" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _Stringifier: | 
					
						
							|  |  |  |     # Must match the slots on ForwardRef, so we can turn an instance of one into an | 
					
						
							|  |  |  |     # instance of the other in place. | 
					
						
							|  |  |  |     __slots__ = _SLOTS | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-23 16:27:55 -07:00
										 |  |  |     def __init__( | 
					
						
							|  |  |  |         self, | 
					
						
							|  |  |  |         node, | 
					
						
							|  |  |  |         globals=None, | 
					
						
							|  |  |  |         owner=None, | 
					
						
							|  |  |  |         is_class=False, | 
					
						
							|  |  |  |         cell=None, | 
					
						
							|  |  |  |         *, | 
					
						
							|  |  |  |         stringifier_dict, | 
					
						
							|  |  |  |     ): | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |         # Either an AST node or a simple str (for the common case where a ForwardRef | 
					
						
							|  |  |  |         # represent a single name). | 
					
						
							|  |  |  |         assert isinstance(node, (ast.AST, str)) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         self.__arg__ = None | 
					
						
							|  |  |  |         self.__forward_evaluated__ = False | 
					
						
							|  |  |  |         self.__forward_value__ = None | 
					
						
							|  |  |  |         self.__forward_is_argument__ = False | 
					
						
							|  |  |  |         self.__forward_is_class__ = is_class | 
					
						
							|  |  |  |         self.__forward_module__ = None | 
					
						
							|  |  |  |         self.__code__ = None | 
					
						
							|  |  |  |         self.__ast_node__ = node | 
					
						
							|  |  |  |         self.__globals__ = globals | 
					
						
							|  |  |  |         self.__cell__ = cell | 
					
						
							|  |  |  |         self.__owner__ = owner | 
					
						
							| 
									
										
										
										
											2024-10-23 16:27:55 -07:00
										 |  |  |         self.__stringifier_dict__ = stringifier_dict | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |     def __convert_to_ast(self, other): | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         if isinstance(other, _Stringifier): | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |             if isinstance(other.__ast_node__, str): | 
					
						
							|  |  |  |                 return ast.Name(id=other.__ast_node__) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             return other.__ast_node__ | 
					
						
							|  |  |  |         elif isinstance(other, slice): | 
					
						
							|  |  |  |             return ast.Slice( | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  |                 lower=( | 
					
						
							|  |  |  |                     self.__convert_to_ast(other.start) | 
					
						
							|  |  |  |                     if other.start is not None | 
					
						
							|  |  |  |                     else None | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 upper=( | 
					
						
							|  |  |  |                     self.__convert_to_ast(other.stop) | 
					
						
							|  |  |  |                     if other.stop is not None | 
					
						
							|  |  |  |                     else None | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 step=( | 
					
						
							|  |  |  |                     self.__convert_to_ast(other.step) | 
					
						
							|  |  |  |                     if other.step is not None | 
					
						
							|  |  |  |                     else None | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             ) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return ast.Constant(value=other) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |     def __get_ast(self): | 
					
						
							|  |  |  |         node = self.__ast_node__ | 
					
						
							|  |  |  |         if isinstance(node, str): | 
					
						
							|  |  |  |             return ast.Name(id=node) | 
					
						
							|  |  |  |         return node | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |     def __make_new(self, node): | 
					
						
							| 
									
										
										
										
											2024-10-23 16:27:55 -07:00
										 |  |  |         stringifier = _Stringifier( | 
					
						
							|  |  |  |             node, | 
					
						
							|  |  |  |             self.__globals__, | 
					
						
							|  |  |  |             self.__owner__, | 
					
						
							|  |  |  |             self.__forward_is_class__, | 
					
						
							|  |  |  |             stringifier_dict=self.__stringifier_dict__, | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-10-23 16:27:55 -07:00
										 |  |  |         self.__stringifier_dict__.stringifiers.append(stringifier) | 
					
						
							|  |  |  |         return stringifier | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Must implement this since we set __eq__. We hash by identity so that | 
					
						
							|  |  |  |     # stringifiers in dict keys are kept separate. | 
					
						
							|  |  |  |     def __hash__(self): | 
					
						
							|  |  |  |         return id(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __getitem__(self, other): | 
					
						
							|  |  |  |         # Special case, to avoid stringifying references to class-scoped variables | 
					
						
							|  |  |  |         # as '__classdict__["x"]'. | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |         if self.__ast_node__ == "__classdict__": | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             raise KeyError | 
					
						
							|  |  |  |         if isinstance(other, tuple): | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |             elts = [self.__convert_to_ast(elt) for elt in other] | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             other = ast.Tuple(elts) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |             other = self.__convert_to_ast(other) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         assert isinstance(other, ast.AST), repr(other) | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |         return self.__make_new(ast.Subscript(self.__get_ast(), other)) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __getattr__(self, attr): | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |         return self.__make_new(ast.Attribute(self.__get_ast(), attr)) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __call__(self, *args, **kwargs): | 
					
						
							|  |  |  |         return self.__make_new( | 
					
						
							|  |  |  |             ast.Call( | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |                 self.__get_ast(), | 
					
						
							|  |  |  |                 [self.__convert_to_ast(arg) for arg in args], | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |                 [ | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |                     ast.keyword(key, self.__convert_to_ast(value)) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |                     for key, value in kwargs.items() | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __iter__(self): | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |         yield self.__make_new(ast.Starred(self.__get_ast())) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |         if isinstance(self.__ast_node__, str): | 
					
						
							|  |  |  |             return self.__ast_node__ | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         return ast.unparse(self.__ast_node__) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __format__(self, format_spec): | 
					
						
							|  |  |  |         raise TypeError("Cannot stringify annotation containing string formatting") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _make_binop(op: ast.AST): | 
					
						
							|  |  |  |         def binop(self, other): | 
					
						
							|  |  |  |             return self.__make_new( | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |                 ast.BinOp(self.__get_ast(), op, self.__convert_to_ast(other)) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return binop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __add__ = _make_binop(ast.Add()) | 
					
						
							|  |  |  |     __sub__ = _make_binop(ast.Sub()) | 
					
						
							|  |  |  |     __mul__ = _make_binop(ast.Mult()) | 
					
						
							|  |  |  |     __matmul__ = _make_binop(ast.MatMult()) | 
					
						
							|  |  |  |     __truediv__ = _make_binop(ast.Div()) | 
					
						
							|  |  |  |     __mod__ = _make_binop(ast.Mod()) | 
					
						
							|  |  |  |     __lshift__ = _make_binop(ast.LShift()) | 
					
						
							|  |  |  |     __rshift__ = _make_binop(ast.RShift()) | 
					
						
							|  |  |  |     __or__ = _make_binop(ast.BitOr()) | 
					
						
							|  |  |  |     __xor__ = _make_binop(ast.BitXor()) | 
					
						
							|  |  |  |     __and__ = _make_binop(ast.BitAnd()) | 
					
						
							|  |  |  |     __floordiv__ = _make_binop(ast.FloorDiv()) | 
					
						
							|  |  |  |     __pow__ = _make_binop(ast.Pow()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     del _make_binop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _make_rbinop(op: ast.AST): | 
					
						
							|  |  |  |         def rbinop(self, other): | 
					
						
							|  |  |  |             return self.__make_new( | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |                 ast.BinOp(self.__convert_to_ast(other), op, self.__get_ast()) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return rbinop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __radd__ = _make_rbinop(ast.Add()) | 
					
						
							|  |  |  |     __rsub__ = _make_rbinop(ast.Sub()) | 
					
						
							|  |  |  |     __rmul__ = _make_rbinop(ast.Mult()) | 
					
						
							|  |  |  |     __rmatmul__ = _make_rbinop(ast.MatMult()) | 
					
						
							|  |  |  |     __rtruediv__ = _make_rbinop(ast.Div()) | 
					
						
							|  |  |  |     __rmod__ = _make_rbinop(ast.Mod()) | 
					
						
							|  |  |  |     __rlshift__ = _make_rbinop(ast.LShift()) | 
					
						
							|  |  |  |     __rrshift__ = _make_rbinop(ast.RShift()) | 
					
						
							|  |  |  |     __ror__ = _make_rbinop(ast.BitOr()) | 
					
						
							|  |  |  |     __rxor__ = _make_rbinop(ast.BitXor()) | 
					
						
							|  |  |  |     __rand__ = _make_rbinop(ast.BitAnd()) | 
					
						
							|  |  |  |     __rfloordiv__ = _make_rbinop(ast.FloorDiv()) | 
					
						
							|  |  |  |     __rpow__ = _make_rbinop(ast.Pow()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     del _make_rbinop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _make_compare(op): | 
					
						
							|  |  |  |         def compare(self, other): | 
					
						
							|  |  |  |             return self.__make_new( | 
					
						
							|  |  |  |                 ast.Compare( | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |                     left=self.__get_ast(), | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |                     ops=[op], | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |                     comparators=[self.__convert_to_ast(other)], | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |                 ) | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return compare | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __lt__ = _make_compare(ast.Lt()) | 
					
						
							|  |  |  |     __le__ = _make_compare(ast.LtE()) | 
					
						
							|  |  |  |     __eq__ = _make_compare(ast.Eq()) | 
					
						
							|  |  |  |     __ne__ = _make_compare(ast.NotEq()) | 
					
						
							|  |  |  |     __gt__ = _make_compare(ast.Gt()) | 
					
						
							|  |  |  |     __ge__ = _make_compare(ast.GtE()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     del _make_compare | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _make_unary_op(op): | 
					
						
							|  |  |  |         def unary_op(self): | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |             return self.__make_new(ast.UnaryOp(op, self.__get_ast())) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return unary_op | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __invert__ = _make_unary_op(ast.Invert()) | 
					
						
							|  |  |  |     __pos__ = _make_unary_op(ast.UAdd()) | 
					
						
							|  |  |  |     __neg__ = _make_unary_op(ast.USub()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     del _make_unary_op | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _StringifierDict(dict): | 
					
						
							|  |  |  |     def __init__(self, namespace, globals=None, owner=None, is_class=False): | 
					
						
							|  |  |  |         super().__init__(namespace) | 
					
						
							|  |  |  |         self.namespace = namespace | 
					
						
							|  |  |  |         self.globals = globals | 
					
						
							|  |  |  |         self.owner = owner | 
					
						
							|  |  |  |         self.is_class = is_class | 
					
						
							|  |  |  |         self.stringifiers = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __missing__(self, key): | 
					
						
							|  |  |  |         fwdref = _Stringifier( | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |             key, | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |             globals=self.globals, | 
					
						
							|  |  |  |             owner=self.owner, | 
					
						
							|  |  |  |             is_class=self.is_class, | 
					
						
							| 
									
										
										
										
											2024-10-23 16:27:55 -07:00
										 |  |  |             stringifier_dict=self, | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         ) | 
					
						
							|  |  |  |         self.stringifiers.append(fwdref) | 
					
						
							|  |  |  |         return fwdref | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-27 10:24:10 -07:00
										 |  |  | def call_evaluate_function(evaluate, format, *, owner=None): | 
					
						
							|  |  |  |     """Call an evaluate function. Evaluate functions are normally generated for
 | 
					
						
							|  |  |  |     the value of type aliases and the bounds, constraints, and defaults of | 
					
						
							|  |  |  |     type parameter objects. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     return call_annotate_function(evaluate, format, owner=owner, _is_evaluate=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-11 16:44:51 -07:00
										 |  |  | def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |     """Call an __annotate__ function. __annotate__ functions are normally
 | 
					
						
							|  |  |  |     generated by the compiler to defer the evaluation of annotations. They | 
					
						
							|  |  |  |     can be called with any of the format arguments in the Format enum, but | 
					
						
							|  |  |  |     compiler-generated __annotate__ functions only support the VALUE format. | 
					
						
							|  |  |  |     This function provides additional functionality to call __annotate__ | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  |     functions with the FORWARDREF and STRING formats. | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     *annotate* must be an __annotate__ function, which takes a single argument | 
					
						
							|  |  |  |     and returns a dict of annotations. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *format* must be a member of the Format enum or one of the corresponding | 
					
						
							|  |  |  |     integer values. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *owner* can be the object that owns the annotations (i.e., the module, | 
					
						
							|  |  |  |     class, or function that the __annotate__ function derives from). With the | 
					
						
							|  |  |  |     FORWARDREF format, it is used to provide better evaluation capabilities | 
					
						
							|  |  |  |     on the generated ForwardRef objects. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-11-26 07:40:13 -08:00
										 |  |  |     if format == Format.VALUE_WITH_FAKE_GLOBALS: | 
					
						
							|  |  |  |         raise ValueError("The VALUE_WITH_FAKE_GLOBALS format is for internal use only") | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |     try: | 
					
						
							|  |  |  |         return annotate(format) | 
					
						
							|  |  |  |     except NotImplementedError: | 
					
						
							|  |  |  |         pass | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  |     if format == Format.STRING: | 
					
						
							|  |  |  |         # STRING is implemented by calling the annotate function in a special | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         # environment where every name lookup results in an instance of _Stringifier. | 
					
						
							|  |  |  |         # _Stringifier supports every dunder operation and returns a new _Stringifier. | 
					
						
							|  |  |  |         # At the end, we get a dictionary that mostly contains _Stringifier objects (or | 
					
						
							|  |  |  |         # possibly constants if the annotate function uses them directly). We then | 
					
						
							|  |  |  |         # convert each of those into a string to get an approximation of the | 
					
						
							|  |  |  |         # original source. | 
					
						
							|  |  |  |         globals = _StringifierDict({}) | 
					
						
							|  |  |  |         if annotate.__closure__: | 
					
						
							|  |  |  |             freevars = annotate.__code__.co_freevars | 
					
						
							|  |  |  |             new_closure = [] | 
					
						
							|  |  |  |             for i, cell in enumerate(annotate.__closure__): | 
					
						
							|  |  |  |                 if i < len(freevars): | 
					
						
							|  |  |  |                     name = freevars[i] | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     name = "__cell__" | 
					
						
							| 
									
										
										
										
											2024-10-23 16:27:55 -07:00
										 |  |  |                 fwdref = _Stringifier(name, stringifier_dict=globals) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |                 new_closure.append(types.CellType(fwdref)) | 
					
						
							|  |  |  |             closure = tuple(new_closure) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             closure = None | 
					
						
							| 
									
										
										
										
											2024-08-11 16:44:51 -07:00
										 |  |  |         func = types.FunctionType( | 
					
						
							|  |  |  |             annotate.__code__, | 
					
						
							|  |  |  |             globals, | 
					
						
							|  |  |  |             closure=closure, | 
					
						
							|  |  |  |             argdefs=annotate.__defaults__, | 
					
						
							|  |  |  |             kwdefaults=annotate.__kwdefaults__, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-11-26 07:40:13 -08:00
										 |  |  |         annos = func(Format.VALUE_WITH_FAKE_GLOBALS) | 
					
						
							| 
									
										
										
										
											2024-07-27 10:24:10 -07:00
										 |  |  |         if _is_evaluate: | 
					
						
							|  |  |  |             return annos if isinstance(annos, str) else repr(annos) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         return { | 
					
						
							|  |  |  |             key: val if isinstance(val, str) else repr(val) | 
					
						
							|  |  |  |             for key, val in annos.items() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     elif format == Format.FORWARDREF: | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  |         # FORWARDREF is implemented similarly to STRING, but there are two changes, | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         # at the beginning and the end of the process. | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  |         # First, while STRING uses an empty dictionary as the namespace, so that all | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         # name lookups result in _Stringifier objects, FORWARDREF uses the globals | 
					
						
							|  |  |  |         # and builtins, so that defined names map to their real values. | 
					
						
							|  |  |  |         # Second, instead of returning strings, we want to return either real values | 
					
						
							|  |  |  |         # or ForwardRef objects. To do this, we keep track of all _Stringifier objects | 
					
						
							|  |  |  |         # created while the annotation is being evaluated, and at the end we convert | 
					
						
							|  |  |  |         # them all to ForwardRef objects by assigning to __class__. To make this | 
					
						
							|  |  |  |         # technique work, we have to ensure that the _Stringifier and ForwardRef | 
					
						
							|  |  |  |         # classes share the same attributes. | 
					
						
							|  |  |  |         # We use this technique because while the annotations are being evaluated, | 
					
						
							|  |  |  |         # we want to support all operations that the language allows, including even | 
					
						
							|  |  |  |         # __getattr__ and __eq__, and return new _Stringifier objects so we can accurately | 
					
						
							|  |  |  |         # reconstruct the source. But in the dictionary that we eventually return, we | 
					
						
							|  |  |  |         # want to return objects with more user-friendly behavior, such as an __eq__ | 
					
						
							|  |  |  |         # that returns a bool and an defined set of attributes. | 
					
						
							|  |  |  |         namespace = {**annotate.__builtins__, **annotate.__globals__} | 
					
						
							|  |  |  |         is_class = isinstance(owner, type) | 
					
						
							|  |  |  |         globals = _StringifierDict(namespace, annotate.__globals__, owner, is_class) | 
					
						
							|  |  |  |         if annotate.__closure__: | 
					
						
							|  |  |  |             freevars = annotate.__code__.co_freevars | 
					
						
							|  |  |  |             new_closure = [] | 
					
						
							|  |  |  |             for i, cell in enumerate(annotate.__closure__): | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     cell.cell_contents | 
					
						
							|  |  |  |                 except ValueError: | 
					
						
							|  |  |  |                     if i < len(freevars): | 
					
						
							|  |  |  |                         name = freevars[i] | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         name = "__cell__" | 
					
						
							|  |  |  |                     fwdref = _Stringifier( | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |                         name, | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |                         cell=cell, | 
					
						
							|  |  |  |                         owner=owner, | 
					
						
							|  |  |  |                         globals=annotate.__globals__, | 
					
						
							|  |  |  |                         is_class=is_class, | 
					
						
							| 
									
										
										
										
											2024-10-23 16:27:55 -07:00
										 |  |  |                         stringifier_dict=globals, | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |                     ) | 
					
						
							|  |  |  |                     globals.stringifiers.append(fwdref) | 
					
						
							|  |  |  |                     new_closure.append(types.CellType(fwdref)) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     new_closure.append(cell) | 
					
						
							|  |  |  |             closure = tuple(new_closure) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             closure = None | 
					
						
							| 
									
										
										
										
											2024-08-11 16:44:51 -07:00
										 |  |  |         func = types.FunctionType( | 
					
						
							|  |  |  |             annotate.__code__, | 
					
						
							|  |  |  |             globals, | 
					
						
							|  |  |  |             closure=closure, | 
					
						
							|  |  |  |             argdefs=annotate.__defaults__, | 
					
						
							|  |  |  |             kwdefaults=annotate.__kwdefaults__, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-11-26 07:40:13 -08:00
										 |  |  |         result = func(Format.VALUE_WITH_FAKE_GLOBALS) | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         for obj in globals.stringifiers: | 
					
						
							|  |  |  |             obj.__class__ = ForwardRef | 
					
						
							| 
									
										
										
										
											2024-10-23 16:27:55 -07:00
										 |  |  |             obj.__stringifier_dict__ = None  # not needed for ForwardRef | 
					
						
							| 
									
										
										
										
											2024-09-25 14:14:03 -07:00
										 |  |  |             if isinstance(obj.__ast_node__, str): | 
					
						
							|  |  |  |                 obj.__arg__ = obj.__ast_node__ | 
					
						
							|  |  |  |                 obj.__ast_node__ = None | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  |         return result | 
					
						
							|  |  |  |     elif format == Format.VALUE: | 
					
						
							|  |  |  |         # Should be impossible because __annotate__ functions must not raise | 
					
						
							|  |  |  |         # NotImplementedError for this format. | 
					
						
							|  |  |  |         raise RuntimeError("annotate function does not support VALUE format") | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         raise ValueError(f"Invalid format: {format!r}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-27 09:36:06 -07:00
										 |  |  | # We use the descriptors from builtins.type instead of accessing | 
					
						
							|  |  |  | # .__annotations__ and .__annotate__ directly on class objects, because | 
					
						
							|  |  |  | # otherwise we could get wrong results in some cases involving metaclasses. | 
					
						
							|  |  |  | # See PEP 749. | 
					
						
							|  |  |  | _BASE_GET_ANNOTATE = type.__dict__["__annotate__"].__get__ | 
					
						
							|  |  |  | _BASE_GET_ANNOTATIONS = type.__dict__["__annotations__"].__get__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_annotate_function(obj): | 
					
						
							|  |  |  |     """Get the __annotate__ function for an object.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     obj may be a function, class, or module, or a user-defined type with | 
					
						
							|  |  |  |     an `__annotate__` attribute. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns the __annotate__ function or None. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if isinstance(obj, type): | 
					
						
							| 
									
										
										
										
											2024-09-18 08:39:22 -07:00
										 |  |  |         try: | 
					
						
							|  |  |  |             return _BASE_GET_ANNOTATE(obj) | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							|  |  |  |             # AttributeError is raised for static types. | 
					
						
							|  |  |  |             return None | 
					
						
							| 
									
										
										
										
											2024-07-27 09:36:06 -07:00
										 |  |  |     return getattr(obj, "__annotate__", None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | def get_annotations( | 
					
						
							|  |  |  |     obj, *, globals=None, locals=None, eval_str=False, format=Format.VALUE | 
					
						
							|  |  |  | ): | 
					
						
							|  |  |  |     """Compute the annotations dict for an object.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     obj may be a callable, class, or module. | 
					
						
							|  |  |  |     Passing in an object of any other type raises TypeError. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns a dict.  get_annotations() returns a new dict every time | 
					
						
							|  |  |  |     it's called; calling it twice on the same object will return two | 
					
						
							|  |  |  |     different but equivalent dicts. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This function handles several details for you: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       * If eval_str is true, values of type str will | 
					
						
							|  |  |  |         be un-stringized using eval().  This is intended | 
					
						
							|  |  |  |         for use with stringized annotations | 
					
						
							|  |  |  |         ("from __future__ import annotations"). | 
					
						
							|  |  |  |       * If obj doesn't have an annotations dict, returns an | 
					
						
							|  |  |  |         empty dict.  (Functions and methods always have an | 
					
						
							|  |  |  |         annotations dict; classes, modules, and other types of | 
					
						
							|  |  |  |         callables may not.) | 
					
						
							|  |  |  |       * Ignores inherited annotations on classes.  If a class | 
					
						
							|  |  |  |         doesn't have its own annotations dict, returns an empty dict. | 
					
						
							|  |  |  |       * All accesses to object members and dict values are done | 
					
						
							|  |  |  |         using getattr() and dict.get() for safety. | 
					
						
							|  |  |  |       * Always, always, always returns a freshly-created dict. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     eval_str controls whether or not values of type str are replaced | 
					
						
							|  |  |  |     with the result of calling eval() on those values: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       * If eval_str is true, eval() is called on values of type str. | 
					
						
							|  |  |  |       * If eval_str is false (the default), values of type str are unchanged. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     globals and locals are passed in to eval(); see the documentation | 
					
						
							|  |  |  |     for eval() for more information.  If either globals or locals is | 
					
						
							|  |  |  |     None, this function may replace that value with a context-specific | 
					
						
							|  |  |  |     default, contingent on type(obj): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       * If obj is a module, globals defaults to obj.__dict__. | 
					
						
							|  |  |  |       * If obj is a class, globals defaults to | 
					
						
							|  |  |  |         sys.modules[obj.__module__].__dict__ and locals | 
					
						
							|  |  |  |         defaults to the obj class namespace. | 
					
						
							|  |  |  |       * If obj is a callable, globals defaults to obj.__globals__, | 
					
						
							|  |  |  |         although if obj is a wrapped function (using | 
					
						
							|  |  |  |         functools.update_wrapper()) it is first unwrapped. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if eval_str and format != Format.VALUE: | 
					
						
							|  |  |  |         raise ValueError("eval_str=True is only supported with format=Format.VALUE") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-25 15:32:45 -07:00
										 |  |  |     match format: | 
					
						
							|  |  |  |         case Format.VALUE: | 
					
						
							|  |  |  |             # For VALUE, we only look at __annotations__ | 
					
						
							|  |  |  |             ann = _get_dunder_annotations(obj) | 
					
						
							|  |  |  |         case Format.FORWARDREF: | 
					
						
							|  |  |  |             # For FORWARDREF, we use __annotations__ if it exists | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2024-09-30 15:25:56 -07:00
										 |  |  |                 return dict(_get_dunder_annotations(obj)) | 
					
						
							| 
									
										
										
										
											2024-09-25 15:32:45 -07:00
										 |  |  |             except NameError: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # But if __annotations__ threw a NameError, we try calling __annotate__ | 
					
						
							|  |  |  |             ann = _get_and_call_annotate(obj, format) | 
					
						
							|  |  |  |             if ann is not None: | 
					
						
							|  |  |  |                 return ann | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # If that didn't work either, we have a very weird object: evaluating | 
					
						
							|  |  |  |             # __annotations__ threw NameError and there is no __annotate__. In that case, | 
					
						
							|  |  |  |             # we fall back to trying __annotations__ again. | 
					
						
							|  |  |  |             return dict(_get_dunder_annotations(obj)) | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  |         case Format.STRING: | 
					
						
							|  |  |  |             # For STRING, we try to call __annotate__ | 
					
						
							| 
									
										
										
										
											2024-09-25 15:32:45 -07:00
										 |  |  |             ann = _get_and_call_annotate(obj, format) | 
					
						
							|  |  |  |             if ann is not None: | 
					
						
							|  |  |  |                 return ann | 
					
						
							|  |  |  |             # But if we didn't get it, we use __annotations__ instead. | 
					
						
							|  |  |  |             ann = _get_dunder_annotations(obj) | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  |             return annotations_to_string(ann) | 
					
						
							| 
									
										
										
										
											2024-11-26 07:40:13 -08:00
										 |  |  |         case Format.VALUE_WITH_FAKE_GLOBALS: | 
					
						
							|  |  |  |             raise ValueError("The VALUE_WITH_FAKE_GLOBALS format is for internal use only") | 
					
						
							| 
									
										
										
										
											2024-09-25 15:32:45 -07:00
										 |  |  |         case _: | 
					
						
							|  |  |  |             raise ValueError(f"Unsupported format {format!r}") | 
					
						
							| 
									
										
										
										
											2024-07-23 14:16:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if not ann: | 
					
						
							|  |  |  |         return {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not eval_str: | 
					
						
							|  |  |  |         return dict(ann) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if isinstance(obj, type): | 
					
						
							|  |  |  |         # class | 
					
						
							|  |  |  |         obj_globals = None | 
					
						
							|  |  |  |         module_name = getattr(obj, "__module__", None) | 
					
						
							|  |  |  |         if module_name: | 
					
						
							|  |  |  |             module = sys.modules.get(module_name, None) | 
					
						
							|  |  |  |             if module: | 
					
						
							|  |  |  |                 obj_globals = getattr(module, "__dict__", None) | 
					
						
							|  |  |  |         obj_locals = dict(vars(obj)) | 
					
						
							|  |  |  |         unwrap = obj | 
					
						
							|  |  |  |     elif isinstance(obj, types.ModuleType): | 
					
						
							|  |  |  |         # module | 
					
						
							|  |  |  |         obj_globals = getattr(obj, "__dict__") | 
					
						
							|  |  |  |         obj_locals = None | 
					
						
							|  |  |  |         unwrap = None | 
					
						
							|  |  |  |     elif callable(obj): | 
					
						
							|  |  |  |         # this includes types.Function, types.BuiltinFunctionType, | 
					
						
							|  |  |  |         # types.BuiltinMethodType, functools.partial, functools.singledispatch, | 
					
						
							|  |  |  |         # "class funclike" from Lib/test/test_inspect... on and on it goes. | 
					
						
							|  |  |  |         obj_globals = getattr(obj, "__globals__", None) | 
					
						
							|  |  |  |         obj_locals = None | 
					
						
							|  |  |  |         unwrap = obj | 
					
						
							|  |  |  |     elif ann is not None: | 
					
						
							|  |  |  |         obj_globals = obj_locals = unwrap = None | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         raise TypeError(f"{obj!r} is not a module, class, or callable.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if unwrap is not None: | 
					
						
							|  |  |  |         while True: | 
					
						
							|  |  |  |             if hasattr(unwrap, "__wrapped__"): | 
					
						
							|  |  |  |                 unwrap = unwrap.__wrapped__ | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             if isinstance(unwrap, functools.partial): | 
					
						
							|  |  |  |                 unwrap = unwrap.func | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  |         if hasattr(unwrap, "__globals__"): | 
					
						
							|  |  |  |             obj_globals = unwrap.__globals__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if globals is None: | 
					
						
							|  |  |  |         globals = obj_globals | 
					
						
							|  |  |  |     if locals is None: | 
					
						
							|  |  |  |         locals = obj_locals | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # "Inject" type parameters into the local namespace | 
					
						
							|  |  |  |     # (unless they are shadowed by assignments *in* the local namespace), | 
					
						
							|  |  |  |     # as a way of emulating annotation scopes when calling `eval()` | 
					
						
							|  |  |  |     if type_params := getattr(obj, "__type_params__", ()): | 
					
						
							|  |  |  |         if locals is None: | 
					
						
							|  |  |  |             locals = {} | 
					
						
							|  |  |  |         locals = {param.__name__: param for param in type_params} | locals | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return_value = { | 
					
						
							|  |  |  |         key: value if not isinstance(value, str) else eval(value, globals, locals) | 
					
						
							|  |  |  |         for key, value in ann.items() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return return_value | 
					
						
							| 
									
										
										
										
											2024-09-25 15:32:45 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  | def value_to_string(value): | 
					
						
							|  |  |  |     """Convert a Python value to a format suitable for use with the STRING format.
 | 
					
						
							| 
									
										
										
										
											2024-09-25 17:01:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  |     This is inteded as a helper for tools that support the STRING format but do | 
					
						
							| 
									
										
										
										
											2024-09-25 17:01:09 -07:00
										 |  |  |     not have access to the code that originally produced the annotations. It uses | 
					
						
							|  |  |  |     repr() for most objects. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if isinstance(value, type): | 
					
						
							|  |  |  |         if value.__module__ == "builtins": | 
					
						
							|  |  |  |             return value.__qualname__ | 
					
						
							|  |  |  |         return f"{value.__module__}.{value.__qualname__}" | 
					
						
							|  |  |  |     if value is ...: | 
					
						
							|  |  |  |         return "..." | 
					
						
							|  |  |  |     if isinstance(value, (types.FunctionType, types.BuiltinFunctionType)): | 
					
						
							|  |  |  |         return value.__name__ | 
					
						
							|  |  |  |     return repr(value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  | def annotations_to_string(annotations): | 
					
						
							|  |  |  |     """Convert an annotation dict containing values to approximately the STRING format.""" | 
					
						
							| 
									
										
										
										
											2024-09-25 17:01:09 -07:00
										 |  |  |     return { | 
					
						
							| 
									
										
										
										
											2024-09-26 13:49:48 -07:00
										 |  |  |         n: t if isinstance(t, str) else value_to_string(t) | 
					
						
							| 
									
										
										
										
											2024-09-25 17:01:09 -07:00
										 |  |  |         for n, t in annotations.items() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-25 15:32:45 -07:00
										 |  |  | def _get_and_call_annotate(obj, format): | 
					
						
							|  |  |  |     annotate = get_annotate_function(obj) | 
					
						
							|  |  |  |     if annotate is not None: | 
					
						
							|  |  |  |         ann = call_annotate_function(annotate, format, owner=obj) | 
					
						
							|  |  |  |         if not isinstance(ann, dict): | 
					
						
							|  |  |  |             raise ValueError(f"{obj!r}.__annotate__ returned a non-dict") | 
					
						
							|  |  |  |         return dict(ann) | 
					
						
							|  |  |  |     return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _get_dunder_annotations(obj): | 
					
						
							|  |  |  |     if isinstance(obj, type): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             ann = _BASE_GET_ANNOTATIONS(obj) | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							|  |  |  |             # For static types, the descriptor raises AttributeError. | 
					
						
							|  |  |  |             return {} | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         ann = getattr(obj, "__annotations__", None) | 
					
						
							|  |  |  |         if ann is None: | 
					
						
							|  |  |  |             return {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not isinstance(ann, dict): | 
					
						
							|  |  |  |         raise ValueError(f"{obj!r}.__annotations__ is neither a dict nor None") | 
					
						
							|  |  |  |     return dict(ann) |