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 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
208def _type_check(type1, type2) -> bool:
209 if not all(map(has_known_bases, (type1, type2))):
210 raise _NonDeducibleTypeHierarchy
212 try:
213 return type1 in type2.mro()[:-1]
214 except MroError as e:
215 # The MRO is invalid.
216 raise _NonDeducibleTypeHierarchy from e
219def is_subtype(type1, type2) -> bool:
220 """Check if *type1* is a subtype of *type2*."""
221 return _type_check(type1=type2, type2=type1)
224def is_supertype(type1, type2) -> bool:
225 """Check if *type2* is a supertype of *type1*."""
226 return _type_check(type1, type2)
229def class_instance_as_index(node: bases.Instance) -> nodes.Const | None:
230 """Get the value as an index for the given instance.
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
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
252def object_len(node, context: InferenceContext | None = None):
253 """Infer length of given node object.
255 :param Union[nodes.ClassDef, nodes.Instance] node:
256 :param node: Node to infer length of
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
268 inferred_node = safe_infer(node, context=context)
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)
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)
296 node_type = object_type(inferred_node, context=context)
297 if not node_type:
298 raise InferenceError(node=node)
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
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 )
329def _higher_function_scope(node: nodes.NodeNG) -> nodes.FunctionDef | None:
330 """Search for the first function which encloses the given
331 scope.
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.
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