Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/helpers.py: 56%

158 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:53 +0000

1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html 

2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE 

3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt 

4 

5"""Various helper utilities.""" 

6 

7from __future__ import annotations 

8 

9from collections.abc import Generator 

10 

11from astroid import bases, manager, nodes, objects, raw_building, util 

12from astroid.context import CallContext, InferenceContext 

13from astroid.exceptions import ( 

14 AstroidTypeError, 

15 AttributeInferenceError, 

16 InferenceError, 

17 MroError, 

18 _NonDeducibleTypeHierarchy, 

19) 

20from astroid.nodes import scoped_nodes 

21from astroid.typing import InferenceResult 

22 

23 

24def _build_proxy_class(cls_name: str, builtins: nodes.Module) -> nodes.ClassDef: 

25 proxy = raw_building.build_class(cls_name) 

26 proxy.parent = builtins 

27 return proxy 

28 

29 

30def _function_type( 

31 function: nodes.Lambda | nodes.FunctionDef | bases.UnboundMethod, 

32 builtins: nodes.Module, 

33) -> nodes.ClassDef: 

34 if isinstance(function, (scoped_nodes.Lambda, scoped_nodes.FunctionDef)): 

35 if function.root().name == "builtins": 

36 cls_name = "builtin_function_or_method" 

37 else: 

38 cls_name = "function" 

39 elif isinstance(function, bases.BoundMethod): 

40 cls_name = "method" 

41 else: 

42 cls_name = "function" 

43 return _build_proxy_class(cls_name, builtins) 

44 

45 

46def _object_type( 

47 node: InferenceResult, context: InferenceContext | None = None 

48) -> Generator[InferenceResult | None, None, None]: 

49 astroid_manager = manager.AstroidManager() 

50 builtins = astroid_manager.builtins_module 

51 context = context or InferenceContext() 

52 

53 for inferred in node.infer(context=context): 

54 if isinstance(inferred, scoped_nodes.ClassDef): 

55 if inferred.newstyle: 

56 metaclass = inferred.metaclass(context=context) 

57 if metaclass: 

58 yield metaclass 

59 continue 

60 yield builtins.getattr("type")[0] 

61 elif isinstance( 

62 inferred, 

63 (scoped_nodes.Lambda, bases.UnboundMethod, scoped_nodes.FunctionDef), 

64 ): 

65 yield _function_type(inferred, builtins) 

66 elif isinstance(inferred, scoped_nodes.Module): 

67 yield _build_proxy_class("module", builtins) 

68 elif isinstance(inferred, nodes.Unknown): 

69 raise InferenceError 

70 elif isinstance(inferred, util.UninferableBase): 

71 yield inferred 

72 elif isinstance(inferred, (bases.Proxy, nodes.Slice, objects.Super)): 

73 yield inferred._proxied 

74 else: # pragma: no cover 

75 raise AssertionError(f"We don't handle {type(inferred)} currently") 

76 

77 

78def object_type( 

79 node: InferenceResult, context: InferenceContext | None = None 

80) -> InferenceResult | None: 

81 """Obtain the type of the given node. 

82 

83 This is used to implement the ``type`` builtin, which means that it's 

84 used for inferring type calls, as well as used in a couple of other places 

85 in the inference. 

86 The node will be inferred first, so this function can support all 

87 sorts of objects, as long as they support inference. 

88 """ 

89 

90 try: 

91 types = set(_object_type(node, context)) 

92 except InferenceError: 

93 return util.Uninferable 

94 if len(types) > 1 or not types: 

95 return util.Uninferable 

96 return list(types)[0] 

97 

98 

99def _object_type_is_subclass( 

100 obj_type: InferenceResult | None, 

101 class_or_seq: list[InferenceResult], 

102 context: InferenceContext | None = None, 

103) -> util.UninferableBase | bool: 

104 if isinstance(obj_type, util.UninferableBase) or not isinstance( 

105 obj_type, nodes.ClassDef 

106 ): 

107 return util.Uninferable 

108 

109 # Instances are not types 

110 class_seq = [ 

111 item if not isinstance(item, bases.Instance) else util.Uninferable 

112 for item in class_or_seq 

113 ] 

114 # strict compatibility with issubclass 

115 # issubclass(type, (object, 1)) evaluates to true 

116 # issubclass(object, (1, type)) raises TypeError 

117 for klass in class_seq: 

118 if isinstance(klass, util.UninferableBase): 

119 raise AstroidTypeError("arg 2 must be a type or tuple of types") 

120 

121 for obj_subclass in obj_type.mro(): 

122 if obj_subclass == klass: 

123 return True 

124 return False 

125 

126 

127def object_isinstance( 

128 node: InferenceResult, 

129 class_or_seq: list[InferenceResult], 

130 context: InferenceContext | None = None, 

131) -> util.UninferableBase | bool: 

132 """Check if a node 'isinstance' any node in class_or_seq. 

133 

134 :raises AstroidTypeError: if the given ``classes_or_seq`` are not types 

135 """ 

136 obj_type = object_type(node, context) 

137 if isinstance(obj_type, util.UninferableBase): 

138 return util.Uninferable 

139 return _object_type_is_subclass(obj_type, class_or_seq, context=context) 

140 

141 

142def object_issubclass( 

143 node: nodes.NodeNG, 

144 class_or_seq: list[InferenceResult], 

145 context: InferenceContext | None = None, 

146) -> util.UninferableBase | bool: 

147 """Check if a type is a subclass of any node in class_or_seq. 

148 

149 :raises AstroidTypeError: if the given ``classes_or_seq`` are not types 

150 :raises AstroidError: if the type of the given node cannot be inferred 

151 or its type's mro doesn't work 

152 """ 

153 if not isinstance(node, nodes.ClassDef): 

154 raise TypeError(f"{node} needs to be a ClassDef node") 

155 return _object_type_is_subclass(node, class_or_seq, context=context) 

156 

157 

158def safe_infer( 

159 node: nodes.NodeNG | bases.Proxy | util.UninferableBase, 

160 context: InferenceContext | None = None, 

161) -> InferenceResult | None: 

162 """Return the inferred value for the given node. 

163 

164 Return None if inference failed or if there is some ambiguity (more than 

165 one node has been inferred). 

166 """ 

167 if isinstance(node, util.UninferableBase): 

168 return node 

169 try: 

170 inferit = node.infer(context=context) 

171 value = next(inferit) 

172 except (InferenceError, StopIteration): 

173 return None 

174 try: 

175 next(inferit) 

176 return None # None if there is ambiguity on the inferred node 

177 except InferenceError: 

178 return None # there is some kind of ambiguity 

179 except StopIteration: 

180 return value 

181 

182 

183def has_known_bases(klass, context: InferenceContext | None = None) -> bool: 

184 """Return whether all base classes of a class could be inferred.""" 

185 try: 

186 return klass._all_bases_known 

187 except AttributeError: 

188 pass 

189 for base in klass.bases: 

190 result = safe_infer(base, context=context) 

191 # TODO: check for A->B->A->B pattern in class structure too? 

192 if ( 

193 not isinstance(result, scoped_nodes.ClassDef) 

194 or result is klass 

195 or not has_known_bases(result, context=context) 

196 ): 

197 klass._all_bases_known = False 

198 return False 

199 klass._all_bases_known = True 

200 return True 

201 

202 

203def _type_check(type1, type2) -> bool: 

204 if not all(map(has_known_bases, (type1, type2))): 

205 raise _NonDeducibleTypeHierarchy 

206 

207 if not all([type1.newstyle, type2.newstyle]): 

208 return False 

209 try: 

210 return type1 in type2.mro()[:-1] 

211 except MroError as e: 

212 # The MRO is invalid. 

213 raise _NonDeducibleTypeHierarchy from e 

214 

215 

216def is_subtype(type1, type2) -> bool: 

217 """Check if *type1* is a subtype of *type2*.""" 

218 return _type_check(type1=type2, type2=type1) 

219 

220 

221def is_supertype(type1, type2) -> bool: 

222 """Check if *type2* is a supertype of *type1*.""" 

223 return _type_check(type1, type2) 

224 

225 

226def class_instance_as_index(node: bases.Instance) -> nodes.Const | None: 

227 """Get the value as an index for the given instance. 

228 

229 If an instance provides an __index__ method, then it can 

230 be used in some scenarios where an integer is expected, 

231 for instance when multiplying or subscripting a list. 

232 """ 

233 context = InferenceContext() 

234 try: 

235 for inferred in node.igetattr("__index__", context=context): 

236 if not isinstance(inferred, bases.BoundMethod): 

237 continue 

238 

239 context.boundnode = node 

240 context.callcontext = CallContext(args=[], callee=inferred) 

241 for result in inferred.infer_call_result(node, context=context): 

242 if isinstance(result, nodes.Const) and isinstance(result.value, int): 

243 return result 

244 except InferenceError: 

245 pass 

246 return None 

247 

248 

249def object_len(node, context: InferenceContext | None = None): 

250 """Infer length of given node object. 

251 

252 :param Union[nodes.ClassDef, nodes.Instance] node: 

253 :param node: Node to infer length of 

254 

255 :raises AstroidTypeError: If an invalid node is returned 

256 from __len__ method or no __len__ method exists 

257 :raises InferenceError: If the given node cannot be inferred 

258 or if multiple nodes are inferred or if the code executed in python 

259 would result in a infinite recursive check for length 

260 :rtype int: Integer length of node 

261 """ 

262 # pylint: disable=import-outside-toplevel; circular import 

263 from astroid.objects import FrozenSet 

264 

265 inferred_node = safe_infer(node, context=context) 

266 

267 # prevent self referential length calls from causing a recursion error 

268 # see https://github.com/pylint-dev/astroid/issues/777 

269 node_frame = node.frame(future=True) 

270 if ( 

271 isinstance(node_frame, scoped_nodes.FunctionDef) 

272 and node_frame.name == "__len__" 

273 and isinstance(inferred_node, bases.Proxy) 

274 and inferred_node._proxied == node_frame.parent 

275 ): 

276 message = ( 

277 "Self referential __len__ function will " 

278 "cause a RecursionError on line {} of {}".format( 

279 node.lineno, node.root().file 

280 ) 

281 ) 

282 raise InferenceError(message) 

283 

284 if inferred_node is None or isinstance(inferred_node, util.UninferableBase): 

285 raise InferenceError(node=node) 

286 if isinstance(inferred_node, nodes.Const) and isinstance( 

287 inferred_node.value, (bytes, str) 

288 ): 

289 return len(inferred_node.value) 

290 if isinstance(inferred_node, (nodes.List, nodes.Set, nodes.Tuple, FrozenSet)): 

291 return len(inferred_node.elts) 

292 if isinstance(inferred_node, nodes.Dict): 

293 return len(inferred_node.items) 

294 

295 node_type = object_type(inferred_node, context=context) 

296 if not node_type: 

297 raise InferenceError(node=node) 

298 

299 try: 

300 len_call = next(node_type.igetattr("__len__", context=context)) 

301 except StopIteration as e: 

302 raise AstroidTypeError(str(e)) from e 

303 except AttributeInferenceError as e: 

304 raise AstroidTypeError( 

305 f"object of type '{node_type.pytype()}' has no len()" 

306 ) from e 

307 

308 inferred = len_call.infer_call_result(node, context) 

309 if isinstance(inferred, util.UninferableBase): 

310 raise InferenceError(node=node, context=context) 

311 result_of_len = next(inferred, None) 

312 if ( 

313 isinstance(result_of_len, nodes.Const) 

314 and result_of_len.pytype() == "builtins.int" 

315 ): 

316 return result_of_len.value 

317 if ( 

318 result_of_len is None 

319 or isinstance(result_of_len, bases.Instance) 

320 and result_of_len.is_subtype_of("builtins.int") 

321 ): 

322 # Fake a result as we don't know the arguments of the instance call. 

323 return 0 

324 raise AstroidTypeError( 

325 f"'{result_of_len}' object cannot be interpreted as an integer" 

326 )