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

159 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 try: 

191 return klass._all_bases_known 

192 except AttributeError: 

193 pass 

194 for base in klass.bases: 

195 result = safe_infer(base, context=context) 

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

197 if ( 

198 not isinstance(result, scoped_nodes.ClassDef) 

199 or result is klass 

200 or not has_known_bases(result, context=context) 

201 ): 

202 klass._all_bases_known = False 

203 return False 

204 klass._all_bases_known = True 

205 return True 

206 

207 

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

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

210 raise _NonDeducibleTypeHierarchy 

211 

212 try: 

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

214 except MroError as e: 

215 # The MRO is invalid. 

216 raise _NonDeducibleTypeHierarchy from e 

217 

218 

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

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

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

222 

223 

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

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

226 return _type_check(type1, type2) 

227 

228 

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

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

231 

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

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

234 for instance when multiplying or subscripting a list. 

235 """ 

236 context = InferenceContext() 

237 try: 

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

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

240 continue 

241 

242 context.boundnode = node 

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

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

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

246 return result 

247 except InferenceError: 

248 pass 

249 return None 

250 

251 

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

253 """Infer length of given node object. 

254 

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

256 :param node: Node to infer length of 

257 

258 :raises AstroidTypeError: If an invalid node is returned 

259 from __len__ method or no __len__ method exists 

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

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

262 would result in a infinite recursive check for length 

263 :rtype int: Integer length of node 

264 """ 

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

266 from astroid.objects import FrozenSet 

267 

268 inferred_node = safe_infer(node, context=context) 

269 

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

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

272 node_frame = node.frame() 

273 if ( 

274 isinstance(node_frame, scoped_nodes.FunctionDef) 

275 and node_frame.name == "__len__" 

276 and isinstance(inferred_node, bases.Proxy) 

277 and inferred_node._proxied == node_frame.parent 

278 ): 

279 message = ( 

280 "Self referential __len__ function will " 

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

282 ) 

283 raise InferenceError(message) 

284 

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

286 raise InferenceError(node=node) 

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

288 inferred_node.value, (bytes, str) 

289 ): 

290 return len(inferred_node.value) 

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

292 return len(inferred_node.elts) 

293 if isinstance(inferred_node, nodes.Dict): 

294 return len(inferred_node.items) 

295 

296 node_type = object_type(inferred_node, context=context) 

297 if not node_type: 

298 raise InferenceError(node=node) 

299 

300 try: 

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

302 except StopIteration as e: 

303 raise AstroidTypeError(str(e)) from e 

304 except AttributeInferenceError as e: 

305 raise AstroidTypeError( 

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

307 ) from e 

308 

309 inferred = len_call.infer_call_result(node, context) 

310 if isinstance(inferred, util.UninferableBase): 

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

312 result_of_len = next(inferred, None) 

313 if ( 

314 isinstance(result_of_len, nodes.Const) 

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

316 ): 

317 return result_of_len.value 

318 if result_of_len is None or ( 

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

327 

328 

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

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

331 scope. 

332 

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

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

335 name fails. 

336 

337 :param node: A scope node. 

338 :returns: 

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

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

341 which encloses the given node. 

342 """ 

343 current = node 

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

345 current = current.parent 

346 if current and current.parent: 

347 return current.parent 

348 return None