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
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
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
5"""Various helper utilities."""
7from __future__ import annotations
9from collections.abc import Generator
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
25def _build_proxy_class(cls_name: str, builtins: nodes.Module) -> nodes.ClassDef:
26 proxy = raw_building.build_class(cls_name, builtins)
27 return proxy
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)
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()
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")
81def object_type(
82 node: InferenceResult, context: InferenceContext | None = None
83) -> InferenceResult | None:
84 """Obtain the type of the given node.
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 """
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))
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
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 )
127 for obj_subclass in obj_mro:
128 if obj_subclass == klass:
129 return True
130 return False
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.
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)
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.
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)
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
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
213def _type_check(type1, type2) -> bool:
214 if not all(map(has_known_bases, (type1, type2))):
215 raise _NonDeducibleTypeHierarchy
217 try:
218 return type1 in type2.mro()[:-1]
219 except MroError as e:
220 # The MRO is invalid.
221 raise _NonDeducibleTypeHierarchy from e
224def is_subtype(type1, type2) -> bool:
225 """Check if *type1* is a subtype of *type2*."""
226 return _type_check(type1=type2, type2=type1)
229def is_supertype(type1, type2) -> bool:
230 """Check if *type2* is a supertype of *type1*."""
231 return _type_check(type1, type2)
234def class_instance_as_index(node: bases.Instance) -> nodes.Const | None:
235 """Get the value as an index for the given instance.
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
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
257def object_len(node, context: InferenceContext | None = None):
258 """Infer length of given node object.
260 :param Union[nodes.ClassDef, nodes.Instance] node:
261 :param node: Node to infer length of
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
273 inferred_node = safe_infer(node, context=context)
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)
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)
301 node_type = object_type(inferred_node, context=context)
302 if not node_type:
303 raise InferenceError(node=node)
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
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 )
334def _higher_function_scope(node: nodes.NodeNG) -> nodes.FunctionDef | None:
335 """Search for the first function which encloses the given
336 scope.
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.
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