| | |
| |
|
| |
|
| | import inspect |
| | import platform |
| | import sys |
| | import threading |
| | import types |
| | import warnings |
| |
|
| | from collections.abc import Mapping, Sequence |
| | from typing import _GenericAlias |
| |
|
| |
|
| | PYPY = platform.python_implementation() == "PyPy" |
| | PY_3_9_PLUS = sys.version_info[:2] >= (3, 9) |
| | PY310 = sys.version_info[:2] >= (3, 10) |
| | PY_3_12_PLUS = sys.version_info[:2] >= (3, 12) |
| |
|
| |
|
| | def just_warn(*args, **kw): |
| | warnings.warn( |
| | "Running interpreter doesn't sufficiently support code object " |
| | "introspection. Some features like bare super() or accessing " |
| | "__class__ will not work with slotted classes.", |
| | RuntimeWarning, |
| | stacklevel=2, |
| | ) |
| |
|
| |
|
| | class _AnnotationExtractor: |
| | """ |
| | Extract type annotations from a callable, returning None whenever there |
| | is none. |
| | """ |
| |
|
| | __slots__ = ["sig"] |
| |
|
| | def __init__(self, callable): |
| | try: |
| | self.sig = inspect.signature(callable) |
| | except (ValueError, TypeError): |
| | self.sig = None |
| |
|
| | def get_first_param_type(self): |
| | """ |
| | Return the type annotation of the first argument if it's not empty. |
| | """ |
| | if not self.sig: |
| | return None |
| |
|
| | params = list(self.sig.parameters.values()) |
| | if params and params[0].annotation is not inspect.Parameter.empty: |
| | return params[0].annotation |
| |
|
| | return None |
| |
|
| | def get_return_type(self): |
| | """ |
| | Return the return type if it's not empty. |
| | """ |
| | if ( |
| | self.sig |
| | and self.sig.return_annotation is not inspect.Signature.empty |
| | ): |
| | return self.sig.return_annotation |
| |
|
| | return None |
| |
|
| |
|
| | def make_set_closure_cell(): |
| | """Return a function of two arguments (cell, value) which sets |
| | the value stored in the closure cell `cell` to `value`. |
| | """ |
| | |
| | |
| | if PYPY: |
| |
|
| | def set_closure_cell(cell, value): |
| | cell.__setstate__((value,)) |
| |
|
| | return set_closure_cell |
| |
|
| | |
| |
|
| | try: |
| | if sys.version_info >= (3, 8): |
| |
|
| | def set_closure_cell(cell, value): |
| | cell.cell_contents = value |
| |
|
| | else: |
| | |
| | def set_first_cellvar_to(value): |
| | x = value |
| | return |
| |
|
| | |
| | |
| | |
| | def force_x_to_be_a_cell(): |
| | return x |
| |
|
| | |
| | |
| | co = set_first_cellvar_to.__code__ |
| | if co.co_cellvars != ("x",) or co.co_freevars != (): |
| | raise AssertionError |
| |
|
| | |
| | |
| | args = [co.co_argcount] |
| | args.append(co.co_kwonlyargcount) |
| | args.extend( |
| | [ |
| | co.co_nlocals, |
| | co.co_stacksize, |
| | co.co_flags, |
| | co.co_code, |
| | co.co_consts, |
| | co.co_names, |
| | co.co_varnames, |
| | co.co_filename, |
| | co.co_name, |
| | co.co_firstlineno, |
| | co.co_lnotab, |
| | |
| | co.co_cellvars, |
| | co.co_freevars, |
| | ] |
| | ) |
| | set_first_freevar_code = types.CodeType(*args) |
| |
|
| | def set_closure_cell(cell, value): |
| | |
| | |
| | |
| | setter = types.FunctionType( |
| | set_first_freevar_code, {}, "setter", (), (cell,) |
| | ) |
| | |
| | setter(value) |
| |
|
| | |
| | def make_func_with_cell(): |
| | x = None |
| |
|
| | def func(): |
| | return x |
| |
|
| | return func |
| |
|
| | cell = make_func_with_cell().__closure__[0] |
| | set_closure_cell(cell, 100) |
| | if cell.cell_contents != 100: |
| | raise AssertionError |
| |
|
| | except Exception: |
| | return just_warn |
| | else: |
| | return set_closure_cell |
| |
|
| |
|
| | set_closure_cell = make_set_closure_cell() |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | repr_context = threading.local() |
| |
|
| |
|
| | def get_generic_base(cl): |
| | """If this is a generic class (A[str]), return the generic base for it.""" |
| | if cl.__class__ is _GenericAlias: |
| | return cl.__origin__ |
| | return None |
| |
|