Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/attr/_compat.py: 64%
69 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
1# SPDX-License-Identifier: MIT
4import inspect
5import platform
6import sys
7import threading
8import types
9import warnings
11from collections.abc import Mapping, Sequence # noqa
12from typing import _GenericAlias
15PYPY = platform.python_implementation() == "PyPy"
16PY_3_9_PLUS = sys.version_info[:2] >= (3, 9)
17PY310 = sys.version_info[:2] >= (3, 10)
18PY_3_12_PLUS = sys.version_info[:2] >= (3, 12)
21def just_warn(*args, **kw):
22 warnings.warn(
23 "Running interpreter doesn't sufficiently support code object "
24 "introspection. Some features like bare super() or accessing "
25 "__class__ will not work with slotted classes.",
26 RuntimeWarning,
27 stacklevel=2,
28 )
31class _AnnotationExtractor:
32 """
33 Extract type annotations from a callable, returning None whenever there
34 is none.
35 """
37 __slots__ = ["sig"]
39 def __init__(self, callable):
40 try:
41 self.sig = inspect.signature(callable)
42 except (ValueError, TypeError): # inspect failed
43 self.sig = None
45 def get_first_param_type(self):
46 """
47 Return the type annotation of the first argument if it's not empty.
48 """
49 if not self.sig:
50 return None
52 params = list(self.sig.parameters.values())
53 if params and params[0].annotation is not inspect.Parameter.empty:
54 return params[0].annotation
56 return None
58 def get_return_type(self):
59 """
60 Return the return type if it's not empty.
61 """
62 if (
63 self.sig
64 and self.sig.return_annotation is not inspect.Signature.empty
65 ):
66 return self.sig.return_annotation
68 return None
71def make_set_closure_cell():
72 """Return a function of two arguments (cell, value) which sets
73 the value stored in the closure cell `cell` to `value`.
74 """
75 # pypy makes this easy. (It also supports the logic below, but
76 # why not do the easy/fast thing?)
77 if PYPY:
79 def set_closure_cell(cell, value):
80 cell.__setstate__((value,))
82 return set_closure_cell
84 # Otherwise gotta do it the hard way.
86 try:
87 if sys.version_info >= (3, 8):
89 def set_closure_cell(cell, value):
90 cell.cell_contents = value
92 else:
93 # Create a function that will set its first cellvar to `value`.
94 def set_first_cellvar_to(value):
95 x = value
96 return
98 # This function will be eliminated as dead code, but
99 # not before its reference to `x` forces `x` to be
100 # represented as a closure cell rather than a local.
101 def force_x_to_be_a_cell(): # pragma: no cover
102 return x
104 # Extract the code object and make sure our assumptions about
105 # the closure behavior are correct.
106 co = set_first_cellvar_to.__code__
107 if co.co_cellvars != ("x",) or co.co_freevars != ():
108 raise AssertionError # pragma: no cover
110 # Convert this code object to a code object that sets the
111 # function's first _freevar_ (not cellvar) to the argument.
112 args = [co.co_argcount]
113 args.append(co.co_kwonlyargcount)
114 args.extend(
115 [
116 co.co_nlocals,
117 co.co_stacksize,
118 co.co_flags,
119 co.co_code,
120 co.co_consts,
121 co.co_names,
122 co.co_varnames,
123 co.co_filename,
124 co.co_name,
125 co.co_firstlineno,
126 co.co_lnotab,
127 # These two arguments are reversed:
128 co.co_cellvars,
129 co.co_freevars,
130 ]
131 )
132 set_first_freevar_code = types.CodeType(*args)
134 def set_closure_cell(cell, value):
135 # Create a function using the set_first_freevar_code,
136 # whose first closure cell is `cell`. Calling it will
137 # change the value of that cell.
138 setter = types.FunctionType(
139 set_first_freevar_code, {}, "setter", (), (cell,)
140 )
141 # And call it to set the cell.
142 setter(value)
144 # Make sure it works on this interpreter:
145 def make_func_with_cell():
146 x = None
148 def func():
149 return x # pragma: no cover
151 return func
153 cell = make_func_with_cell().__closure__[0]
154 set_closure_cell(cell, 100)
155 if cell.cell_contents != 100:
156 raise AssertionError # pragma: no cover
158 except Exception:
159 return just_warn
160 else:
161 return set_closure_cell
164set_closure_cell = make_set_closure_cell()
166# Thread-local global to track attrs instances which are already being repr'd.
167# This is needed because there is no other (thread-safe) way to pass info
168# about the instances that are already being repr'd through the call stack
169# in order to ensure we don't perform infinite recursion.
170#
171# For instance, if an instance contains a dict which contains that instance,
172# we need to know that we're already repr'ing the outside instance from within
173# the dict's repr() call.
174#
175# This lives here rather than in _make.py so that the functions in _make.py
176# don't have a direct reference to the thread-local in their globals dict.
177# If they have such a reference, it breaks cloudpickle.
178repr_context = threading.local()
181def get_generic_base(cl):
182 """If this is a generic class (A[str]), return the generic base for it."""
183 if cl.__class__ is _GenericAlias:
184 return cl.__origin__
185 return None