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

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 

12from typing import _GenericAlias 

13 

14 

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) 

19 

20 

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 ) 

29 

30 

31class _AnnotationExtractor: 

32 """ 

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

34 is none. 

35 """ 

36 

37 __slots__ = ["sig"] 

38 

39 def __init__(self, callable): 

40 try: 

41 self.sig = inspect.signature(callable) 

42 except (ValueError, TypeError): # inspect failed 

43 self.sig = None 

44 

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 

51 

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

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

54 return params[0].annotation 

55 

56 return None 

57 

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 

67 

68 return None 

69 

70 

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: 

78 

79 def set_closure_cell(cell, value): 

80 cell.__setstate__((value,)) 

81 

82 return set_closure_cell 

83 

84 # Otherwise gotta do it the hard way. 

85 

86 try: 

87 if sys.version_info >= (3, 8): 

88 

89 def set_closure_cell(cell, value): 

90 cell.cell_contents = value 

91 

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 

97 

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 

103 

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 

109 

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) 

133 

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) 

143 

144 # Make sure it works on this interpreter: 

145 def make_func_with_cell(): 

146 x = None 

147 

148 def func(): 

149 return x # pragma: no cover 

150 

151 return func 

152 

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 

157 

158 except Exception: 

159 return just_warn 

160 else: 

161 return set_closure_cell 

162 

163 

164set_closure_cell = make_set_closure_cell() 

165 

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

179 

180 

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