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

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

161 statements  

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 

22from astroid.util import safe_infer 

23 

24 

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

26 proxy = raw_building.build_class(cls_name, 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]: 

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 metaclass = inferred.metaclass(context=context) 

56 if metaclass: 

57 yield metaclass 

58 continue 

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

60 elif isinstance( 

61 inferred, 

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

63 ): 

64 yield _function_type(inferred, builtins) 

65 elif isinstance(inferred, scoped_nodes.Module): 

66 yield _build_proxy_class("module", builtins) 

67 elif isinstance(inferred, nodes.Unknown): 

68 raise InferenceError 

69 elif isinstance(inferred, (nodes.TypeVar, nodes.TypeVarTuple, nodes.ParamSpec)): 

70 # PEP 695 generic type parameters have no concrete type at 

71 # static-analysis time, so the type cannot be determined. 

72 yield util.Uninferable 

73 elif isinstance(inferred, util.UninferableBase): 

74 yield inferred 

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

76 yield inferred._proxied 

77 else: # pragma: no cover 

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

79 

80 

81def object_type( 

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

83) -> InferenceResult | None: 

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

85 

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

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

88 in the inference. 

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

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

91 """ 

92 

93 try: 

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

95 except InferenceError: 

96 return util.Uninferable 

97 if len(types) != 1: 

98 return util.Uninferable 

99 return next(iter(types)) 

100 

101 

102def _object_type_is_subclass( 

103 obj_type: InferenceResult | None, 

104 class_or_seq: list[InferenceResult], 

105 context: InferenceContext | None = None, 

106) -> util.UninferableBase | bool: 

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

108 obj_type, nodes.ClassDef 

109 ): 

110 return util.Uninferable 

111 

112 # Instances are not types 

113 class_seq = [ 

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

115 for item in class_or_seq 

116 ] 

117 # strict compatibility with issubclass 

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

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

120 obj_mro = obj_type.mro() 

121 for klass in class_seq: 

122 if isinstance(klass, util.UninferableBase): 

123 raise AstroidTypeError( 

124 "arg 2 must be a type or tuple of types, not <class 'astroid.util.UninferableBase'>" 

125 ) 

126 

127 for obj_subclass in obj_mro: 

128 if obj_subclass == klass: 

129 return True 

130 return False 

131 

132 

133def object_isinstance( 

134 node: InferenceResult, 

135 class_or_seq: list[InferenceResult], 

136 context: InferenceContext | None = None, 

137) -> util.UninferableBase | bool: 

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

139 

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

141 """ 

142 obj_type = object_type(node, context) 

143 if isinstance(obj_type, util.UninferableBase): 

144 return util.Uninferable 

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

146 

147 

148def object_issubclass( 

149 node: nodes.NodeNG, 

150 class_or_seq: list[InferenceResult], 

151 context: InferenceContext | None = None, 

152) -> util.UninferableBase | bool: 

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

154 

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

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

157 or its type's mro doesn't work 

158 """ 

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

160 raise TypeError(f"{node} needs to be a ClassDef node, not {type(node)!r}") 

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

162 

163 

164def class_or_tuple_to_container( 

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

166) -> list[InferenceResult]: 

167 # Move inferences results into container 

168 # to simplify later logic 

169 # raises InferenceError if any of the inferences fall through 

170 try: 

171 node_infer = next(node.infer(context=context)) 

172 except StopIteration as e: # pragma: no cover 

173 raise InferenceError(node=node, context=context) from e 

174 # arg2 MUST be a type or a TUPLE of types 

175 # for isinstance 

176 if isinstance(node_infer, nodes.Tuple): 

177 try: 

178 class_container = [ 

179 next(node.infer(context=context)) for node in node_infer.elts 

180 ] 

181 except StopIteration as e: # pragma: no cover 

182 raise InferenceError(node=node, context=context) from e 

183 else: 

184 class_container = [node_infer] 

185 return class_container 

186 

187 

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

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

190 if not isinstance(klass, scoped_nodes.ClassDef): 

191 # Not a class, so it has no bases at all. This happens for the type of a 

192 # class whose metaclass is not a class either, as in 

193 # ``class C(metaclass=sum)``, whose type is the ``sum`` function. 

194 return False 

195 try: 

196 return klass._all_bases_known 

197 except AttributeError: 

198 pass 

199 for base in klass.bases: 

200 result = safe_infer(base, context=context) 

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

202 if ( 

203 not isinstance(result, scoped_nodes.ClassDef) 

204 or result is klass 

205 or not has_known_bases(result, context=context) 

206 ): 

207 klass._all_bases_known = False 

208 return False 

209 klass._all_bases_known = True 

210 return True 

211 

212 

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

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

215 raise _NonDeducibleTypeHierarchy 

216 

217 try: 

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

219 except MroError as e: 

220 # The MRO is invalid. 

221 raise _NonDeducibleTypeHierarchy from e 

222 

223 

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

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

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

227 

228 

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

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

231 return _type_check(type1, type2) 

232 

233 

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

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

236 

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

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

239 for instance when multiplying or subscripting a list. 

240 """ 

241 context = InferenceContext() 

242 try: 

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

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

245 continue 

246 

247 context.boundnode = node 

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

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

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

251 return result 

252 except InferenceError: 

253 pass 

254 return None 

255 

256 

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

258 """Infer length of given node object. 

259 

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

261 :param node: Node to infer length of 

262 

263 :raises AstroidTypeError: If an invalid node is returned 

264 from __len__ method or no __len__ method exists 

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

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

267 would result in a infinite recursive check for length 

268 :rtype int: Integer length of node 

269 """ 

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

271 from astroid.objects import FrozenSet 

272 

273 inferred_node = safe_infer(node, context=context) 

274 

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

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

277 node_frame = node.frame() 

278 if ( 

279 isinstance(node_frame, scoped_nodes.FunctionDef) 

280 and node_frame.name == "__len__" 

281 and isinstance(inferred_node, bases.Proxy) 

282 and inferred_node._proxied == node_frame.parent 

283 ): 

284 message = ( 

285 "Self referential __len__ function will " 

286 f"cause a RecursionError on line {node.lineno} of {node.root().file}" 

287 ) 

288 raise InferenceError(message) 

289 

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

291 raise InferenceError(node=node) 

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

293 inferred_node.value, (bytes, str) 

294 ): 

295 return len(inferred_node.value) 

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

297 return len(inferred_node.elts) 

298 if isinstance(inferred_node, nodes.Dict): 

299 return len(inferred_node.items) 

300 

301 node_type = object_type(inferred_node, context=context) 

302 if not node_type: 

303 raise InferenceError(node=node) 

304 

305 try: 

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

307 except StopIteration as e: 

308 raise AstroidTypeError(str(e)) from e 

309 except AttributeInferenceError as e: 

310 raise AstroidTypeError( 

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

312 ) from e 

313 

314 inferred = len_call.infer_call_result(node, context) 

315 if isinstance(inferred, util.UninferableBase): 

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

317 result_of_len = next(inferred, None) 

318 if ( 

319 isinstance(result_of_len, nodes.Const) 

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

321 ): 

322 return result_of_len.value 

323 if result_of_len is None or ( 

324 isinstance(result_of_len, bases.Instance) 

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

326 ): 

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

328 return 0 

329 raise AstroidTypeError( 

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

331 ) 

332 

333 

334def _higher_function_scope(node: nodes.NodeNG) -> nodes.FunctionDef | None: 

335 """Search for the first function which encloses the given 

336 scope. 

337 

338 This can be used for looking up in that function's 

339 scope, in case looking up in a lower scope for a particular 

340 name fails. 

341 

342 :param node: A scope node. 

343 :returns: 

344 ``None``, if no parent function scope was found, 

345 otherwise an instance of :class:`astroid.nodes.scoped_nodes.Function`, 

346 which encloses the given node. 

347 """ 

348 current = node 

349 while current.parent and not isinstance(current.parent, nodes.FunctionDef): 

350 current = current.parent 

351 if current and current.parent: 

352 return current.parent 

353 return None