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
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
9import warnings
10from collections.abc import Generator
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
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)
39def _build_proxy_class(cls_name: str, builtins: nodes.Module) -> nodes.ClassDef:
40 proxy = raw_building.build_class(cls_name, builtins)
41 return proxy
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)
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()
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")
91def object_type(
92 node: InferenceResult, context: InferenceContext | None = None
93) -> InferenceResult | None:
94 """Obtain the type of the given node.
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 """
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))
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
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")
134 for obj_subclass in obj_type.mro():
135 if obj_subclass == klass:
136 return True
137 return False
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.
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)
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.
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)
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
191def _type_check(type1, type2) -> bool:
192 if not all(map(has_known_bases, (type1, type2))):
193 raise _NonDeducibleTypeHierarchy
195 try:
196 return type1 in type2.mro()[:-1]
197 except MroError as e:
198 # The MRO is invalid.
199 raise _NonDeducibleTypeHierarchy from e
202def is_subtype(type1, type2) -> bool:
203 """Check if *type1* is a subtype of *type2*."""
204 return _type_check(type1=type2, type2=type1)
207def is_supertype(type1, type2) -> bool:
208 """Check if *type2* is a supertype of *type1*."""
209 return _type_check(type1, type2)
212def class_instance_as_index(node: bases.Instance) -> nodes.Const | None:
213 """Get the value as an index for the given instance.
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
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
235def object_len(node, context: InferenceContext | None = None):
236 """Infer length of given node object.
238 :param Union[nodes.ClassDef, nodes.Instance] node:
239 :param node: Node to infer length of
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
251 inferred_node = real_safe_infer(node, context=context)
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)
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)
281 node_type = object_type(inferred_node, context=context)
282 if not node_type:
283 raise InferenceError(node=node)
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
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 )
314def _higher_function_scope(node: nodes.NodeNG) -> nodes.FunctionDef | None:
315 """Search for the first function which encloses the given
316 scope.
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.
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