Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/wirerope/callable.py: 65%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

154 statements  

1from __future__ import absolute_import 

2 

3import types 

4import six 

5from ._util import cached_property 

6from ._compat import inspect 

7 

8__all__ = ('Callable', ) 

9 

10 

11_inspect_iscoroutinefunction = getattr( 

12 inspect, 'iscoroutinefunction', lambda f: False) 

13 

14 

15class _Reagent(object): 

16 pass 

17 

18 

19_reagent = _Reagent() 

20 

21 

22def _f(owner): 

23 return owner 

24 

25 

26def _name_binder(descriptor, obj, type): 

27 return type, None 

28 

29 

30def _type_binder(descriptor, obj, type): 

31 return type, type 

32 

33 

34def _obj_binder(descriptor, obj, type): 

35 return obj, obj 

36 

37 

38_descriptor_binder_cache = {} 

39 

40 

41class Descriptor(object): 

42 

43 def __init__(self, descriptor): 

44 self.descriptor = descriptor 

45 self.descriptor_class = type(descriptor) 

46 

47 def detect_function_attr_name(self): 

48 indicator = object() 

49 descriptor = self.descriptor_class(indicator) 

50 for name in dir(descriptor): 

51 try: 

52 attr = getattr(descriptor, name) 

53 except AttributeError: 

54 continue 

55 if attr is indicator: 

56 # detected! 

57 return name 

58 else: 

59 raise RuntimeError( 

60 "The given function doesn't hold the given function as an " 

61 "attribute. Is it a correct descriptor?") 

62 

63 def detect_property(self, obj, type_): 

64 d = self.descriptor_class(_f) 

65 method_or_value = d.__get__(obj, type_) 

66 return method_or_value is obj or method_or_value is type_ 

67 

68 def detect_binder(self, obj, type_): 

69 key = (self.descriptor_class, obj is not None) 

70 if key not in _descriptor_binder_cache: 

71 d = self.descriptor_class(_f) 

72 method = d.__get__(obj, type_) 

73 if isinstance(method, types.FunctionType): 

74 # not a boundmethod - probably staticmethod 

75 binder = _name_binder 

76 elif method is obj: 

77 binder = _obj_binder 

78 elif method is type_: 

79 binder = _type_binder 

80 elif callable(method): 

81 owner = method() 

82 if owner is type_: 

83 binder = _type_binder 

84 elif owner is obj: 

85 binder = _obj_binder 

86 else: 

87 binder = None 

88 elif method is d: 

89 # some non-method descriptor 

90 binder = _name_binder 

91 else: 

92 binder = None 

93 if binder is None: 

94 raise TypeError( 

95 "'descriptor_bind' fails to auto-detect binding rule " 

96 "of the given descriptor. Specify the rule by " 

97 "'wirerope.wire.descriptor_bind.register'.") 

98 _descriptor_binder_cache[key] = binder 

99 else: 

100 binder = _descriptor_binder_cache[key] 

101 return binder 

102 

103 

104class Callable(object): 

105 """A wrapper object including more information of callables.""" 

106 

107 def __init__(self, f): 

108 self.wrapped_object = f 

109 self.is_function_type = type(self) is types.FunctionType # noqa 

110 if self.is_descriptor: 

111 self.descriptor = Descriptor(f) 

112 f = getattr(f, self.descriptor.detect_function_attr_name()) 

113 else: 

114 self.descriptor = None 

115 self.wrapped_callable = f 

116 self.is_wrapped_coroutine = getattr(f, '_is_coroutine', None) 

117 self.is_coroutine = self.is_wrapped_coroutine or \ 

118 _inspect_iscoroutinefunction(f) 

119 

120 @cached_property 

121 def signature(self): 

122 return inspect.signature(self.wrapped_callable) 

123 

124 @cached_property 

125 def parameters(self): 

126 return list(self.signature.parameters.values()) 

127 

128 @property 

129 def first_parameter(self): 

130 return self.parameters[0] if self.parameters else None 

131 

132 @cached_property 

133 def is_boundmethod(self): 

134 if self.is_function_type or self.is_builtin_property: 

135 return False 

136 new_bound = self.wrapped_object.__get__(object()) 

137 try: 

138 if six.PY2: 

139 return new_bound is self.wrapped_object 

140 else: 

141 return type(new_bound) is type(self.wrapped_object) # noqa 

142 except Exception: 

143 return False 

144 

145 if six.PY2: 

146 @property 

147 def is_unboundmethod(self): 

148 return type(self.wrapped_object) is type(Callable.__init__) # noqa 

149 

150 @cached_property 

151 def is_descriptor(self): 

152 if self.is_boundmethod: 

153 return False 

154 is_descriptor = type(self.wrapped_object).__get__ \ 

155 is not types.FunctionType.__get__ # noqa 

156 if six.PY2: 

157 is_descriptor = is_descriptor and \ 

158 not (self.is_unboundmethod or self.is_boundmethod) 

159 return is_descriptor 

160 

161 @cached_property 

162 def is_builtin_property(self): 

163 return issubclass(type(self.wrapped_object), property) 

164 

165 @cached_property 

166 def is_property(self): 

167 return self.is_builtin_property or \ 

168 (self.is_descriptor and self.descriptor.detect_property( 

169 _reagent, _Reagent)) 

170 

171 if six.PY34: 

172 @cached_property 

173 def is_barefunction(self): 

174 cc = self.wrapped_callable 

175 method_name = cc.__qualname__.split('<locals>.')[-1] 

176 if method_name == cc.__name__: 

177 return True 

178 return False 

179 else: 

180 @cached_property 

181 def is_barefunction(self): 

182 # im_class does not exist at this point 

183 if self.is_descriptor: 

184 return False 

185 if six.PY2: 

186 if self.is_unboundmethod: 

187 return False 

188 return not (self.is_membermethod or self.is_classmethod) 

189 

190 @cached_property 

191 def is_member(self): 

192 """Test given argument is a method or not. 

193 

194 :rtype: bool 

195 

196 :note: The test is partially based on the first parameter name. 

197 The test result might be wrong. 

198 """ 

199 if six.PY34: 

200 if self.is_barefunction: 

201 return False 

202 if not self.is_descriptor: 

203 return True 

204 return self.first_parameter is not None \ 

205 and self.first_parameter.name == 'self' 

206 

207 @cached_property 

208 def is_membermethod(self): 

209 """Test given argument is a method or not. 

210 

211 :rtype: bool 

212 

213 :note: The test is partially based on the first parameter name. 

214 The test result might be wrong. 

215 """ 

216 if self.is_boundmethod: 

217 return False 

218 if self.is_property: 

219 return False 

220 return self.is_member 

221 

222 @cached_property 

223 def is_classmethod(self): 

224 """Test given argument is a classmethod or not. 

225 

226 :rtype: bool 

227 

228 :note: The test is partially based on the first parameter name. 

229 The test result might be wrong. 

230 """ 

231 if isinstance(self.wrapped_object, classmethod): 

232 return True 

233 if six.PY34: 

234 if self.is_barefunction: 

235 return False 

236 if not self.is_descriptor: 

237 return False 

238 

239 return self.first_parameter is not None \ 

240 and self.first_parameter.name == 'cls'