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