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

1# SPDX-License-Identifier: MIT 

2 

3 

4import inspect 

5import platform 

6import sys 

7import threading 

8import types 

9import warnings 

10 

11from collections.abc import Mapping, Sequence # noqa 

12 

13 

14PYPY = platform.python_implementation() == "PyPy" 

15PY310 = sys.version_info[:2] >= (3, 10) 

16PY_3_12_PLUS = sys.version_info[:2] >= (3, 12) 

17 

18 

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 ) 

27 

28 

29class _AnnotationExtractor: 

30 """ 

31 Extract type annotations from a callable, returning None whenever there 

32 is none. 

33 """ 

34 

35 __slots__ = ["sig"] 

36 

37 def __init__(self, callable): 

38 try: 

39 self.sig = inspect.signature(callable) 

40 except (ValueError, TypeError): # inspect failed 

41 self.sig = None 

42 

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 

49 

50 params = list(self.sig.parameters.values()) 

51 if params and params[0].annotation is not inspect.Parameter.empty: 

52 return params[0].annotation 

53 

54 return None 

55 

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 

65 

66 return None 

67 

68 

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: 

76 

77 def set_closure_cell(cell, value): 

78 cell.__setstate__((value,)) 

79 

80 return set_closure_cell 

81 

82 # Otherwise gotta do it the hard way. 

83 

84 # Create a function that will set its first cellvar to `value`. 

85 def set_first_cellvar_to(value): 

86 x = value 

87 return 

88 

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 

94 

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 

101 

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): 

105 

106 def set_closure_cell(cell, value): 

107 cell.cell_contents = value 

108 

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) 

131 

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) 

141 

142 # Make sure it works on this interpreter: 

143 def make_func_with_cell(): 

144 x = None 

145 

146 def func(): 

147 return x # pragma: no cover 

148 

149 return func 

150 

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 

155 

156 except Exception: 

157 return just_warn 

158 else: 

159 return set_closure_cell 

160 

161 

162set_closure_cell = make_set_closure_cell() 

163 

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()