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

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

151 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 

9import warnings 

10from collections.abc import Generator 

11 

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

13from astroid.context import CallContext, InferenceContext 

14from astroid.exceptions import ( 

15 AstroidTypeError, 

16 AttributeInferenceError, 

17 InferenceError, 

18 MroError, 

19 _NonDeducibleTypeHierarchy, 

20) 

21from astroid.nodes import scoped_nodes 

22from astroid.typing import InferenceResult 

23from astroid.util import safe_infer as real_safe_infer 

24 

25 

26def safe_infer( 

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

28 context: InferenceContext | None = None, 

29) -> InferenceResult | None: 

30 # When removing, also remove the real_safe_infer alias 

31 warnings.warn( 

32 "Import safe_infer from astroid.util; this shim in astroid.helpers will be removed.", 

33 DeprecationWarning, 

34 stacklevel=2, 

35 ) 

36 return real_safe_infer(node, context=context) 

37 

38 

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

40 proxy = raw_building.build_class(cls_name, builtins) 

41 return proxy 

42 

43 

44def _function_type( 

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

46 builtins: nodes.Module, 

47) -> nodes.ClassDef: 

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

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

50 cls_name = "builtin_function_or_method" 

51 else: 

52 cls_name = "function" 

53 elif isinstance(function, bases.BoundMethod): 

54 cls_name = "method" 

55 else: 

56 cls_name = "function" 

57 return _build_proxy_class(cls_name, builtins) 

58 

59 

60def _object_type( 

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

62) -> Generator[InferenceResult | None]: 

63 astroid_manager = manager.AstroidManager() 

64 builtins = astroid_manager.builtins_module 

65 context = context or InferenceContext() 

66 

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

68 if isinstance(inferred, scoped_nodes.ClassDef): 

69 metaclass = inferred.metaclass(context=context) 

70 if metaclass: 

71 yield metaclass 

72 continue 

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

74 elif isinstance( 

75 inferred, 

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

77 ): 

78 yield _function_type(inferred, builtins) 

79 elif isinstance(inferred, scoped_nodes.Module): 

80 yield _build_proxy_class("module", builtins) 

81 elif isinstance(inferred, nodes.Unknown): 

82 raise InferenceError 

83 elif isinstance(inferred, util.UninferableBase): 

84 yield inferred 

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

86 yield inferred._proxied 

87 else: # pragma: no cover 

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

89 

90 

91def object_type( 

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

93) -> InferenceResult | None: 

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

95 

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

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

98 in the inference. 

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

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

101 """ 

102 

103 try: 

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

105 except InferenceError: 

106 return util.Uninferable 

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

108 return util.Uninferable 

109 return next(iter(types)) 

110 

111 

112def _object_type_is_subclass( 

113 obj_type: InferenceResult | None, 

114 class_or_seq: list[InferenceResult], 

115 context: InferenceContext | None = None, 

116) -> util.UninferableBase | bool: 

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

118 obj_type, nodes.ClassDef 

119 ): 

120 return util.Uninferable 

121 

122 # Instances are not types 

123 class_seq = [ 

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

125 for item in class_or_seq 

126 ] 

127 # strict compatibility with issubclass 

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

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

130 for klass in class_seq: 

131 if isinstance(klass, util.UninferableBase): 

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

133 

134 for obj_subclass in obj_type.mro(): 

135 if obj_subclass == klass: 

136 return True 

137 return False 

138 

139 

140def object_isinstance( 

141 node: InferenceResult, 

142 class_or_seq: list[InferenceResult], 

143 context: InferenceContext | None = None, 

144) -> util.UninferableBase | bool: 

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

146 

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

148 """ 

149 obj_type = object_type(node, context) 

150 if isinstance(obj_type, util.UninferableBase): 

151 return util.Uninferable 

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

153 

154 

155def object_issubclass( 

156 node: nodes.NodeNG, 

157 class_or_seq: list[InferenceResult], 

158 context: InferenceContext | None = None, 

159) -> util.UninferableBase | bool: 

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

161 

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

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

164 or its type's mro doesn't work 

165 """ 

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

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

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

169 

170 

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

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

173 try: 

174 return klass._all_bases_known 

175 except AttributeError: 

176 pass 

177 for base in klass.bases: 

178 result = real_safe_infer(base, context=context) 

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

180 if ( 

181 not isinstance(result, scoped_nodes.ClassDef) 

182 or result is klass 

183 or not has_known_bases(result, context=context) 

184 ): 

185 klass._all_bases_known = False 

186 return False 

187 klass._all_bases_known = True 

188 return True 

189 

190 

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

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

193 raise _NonDeducibleTypeHierarchy 

194 

195 try: 

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

197 except MroError as e: 

198 # The MRO is invalid. 

199 raise _NonDeducibleTypeHierarchy from e 

200 

201 

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

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

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

205 

206 

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

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

209 return _type_check(type1, type2) 

210 

211 

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

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

214 

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

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

217 for instance when multiplying or subscripting a list. 

218 """ 

219 context = InferenceContext() 

220 try: 

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

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

223 continue 

224 

225 context.boundnode = node 

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

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

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

229 return result 

230 except InferenceError: 

231 pass 

232 return None 

233 

234 

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

236 """Infer length of given node object. 

237 

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

239 :param node: Node to infer length of 

240 

241 :raises AstroidTypeError: If an invalid node is returned 

242 from __len__ method or no __len__ method exists 

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

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

245 would result in a infinite recursive check for length 

246 :rtype int: Integer length of node 

247 """ 

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

249 from astroid.objects import FrozenSet 

250 

251 inferred_node = real_safe_infer(node, context=context) 

252 

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

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

255 node_frame = node.frame() 

256 if ( 

257 isinstance(node_frame, scoped_nodes.FunctionDef) 

258 and node_frame.name == "__len__" 

259 and isinstance(inferred_node, bases.Proxy) 

260 and inferred_node._proxied == node_frame.parent 

261 ): 

262 message = ( 

263 "Self referential __len__ function will " 

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

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

266 ) 

267 ) 

268 raise InferenceError(message) 

269 

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

271 raise InferenceError(node=node) 

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

273 inferred_node.value, (bytes, str) 

274 ): 

275 return len(inferred_node.value) 

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

277 return len(inferred_node.elts) 

278 if isinstance(inferred_node, nodes.Dict): 

279 return len(inferred_node.items) 

280 

281 node_type = object_type(inferred_node, context=context) 

282 if not node_type: 

283 raise InferenceError(node=node) 

284 

285 try: 

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

287 except StopIteration as e: 

288 raise AstroidTypeError(str(e)) from e 

289 except AttributeInferenceError as e: 

290 raise AstroidTypeError( 

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

292 ) from e 

293 

294 inferred = len_call.infer_call_result(node, context) 

295 if isinstance(inferred, util.UninferableBase): 

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

297 result_of_len = next(inferred, None) 

298 if ( 

299 isinstance(result_of_len, nodes.Const) 

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

301 ): 

302 return result_of_len.value 

303 if result_of_len is None or ( 

304 isinstance(result_of_len, bases.Instance) 

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

306 ): 

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

308 return 0 

309 raise AstroidTypeError( 

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

311 ) 

312 

313 

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

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

316 scope. 

317 

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

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

320 name fails. 

321 

322 :param node: A scope node. 

323 :returns: 

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

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

326 which encloses the given node. 

327 """ 

328 current = node 

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

330 current = current.parent 

331 if current and current.parent: 

332 return current.parent 

333 return None