Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/helpers.py: 87%
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)
41 proxy.parent = builtins
42 return proxy
45def _function_type(
46 function: nodes.Lambda | nodes.FunctionDef | bases.UnboundMethod,
47 builtins: nodes.Module,
48) -> nodes.ClassDef:
49 if isinstance(function, (scoped_nodes.Lambda, scoped_nodes.FunctionDef)):
50 if function.root().name == "builtins":
51 cls_name = "builtin_function_or_method"
52 else:
53 cls_name = "function"
54 elif isinstance(function, bases.BoundMethod):
55 cls_name = "method"
56 else:
57 cls_name = "function"
58 return _build_proxy_class(cls_name, builtins)
61def _object_type(
62 node: InferenceResult, context: InferenceContext | None = None
63) -> Generator[InferenceResult | None, None, None]:
64 astroid_manager = manager.AstroidManager()
65 builtins = astroid_manager.builtins_module
66 context = context or InferenceContext()
68 for inferred in node.infer(context=context):
69 if isinstance(inferred, scoped_nodes.ClassDef):
70 if inferred.newstyle:
71 metaclass = inferred.metaclass(context=context)
72 if metaclass:
73 yield metaclass
74 continue
75 yield builtins.getattr("type")[0]
76 elif isinstance(
77 inferred,
78 (scoped_nodes.Lambda, bases.UnboundMethod, scoped_nodes.FunctionDef),
79 ):
80 yield _function_type(inferred, builtins)
81 elif isinstance(inferred, scoped_nodes.Module):
82 yield _build_proxy_class("module", builtins)
83 elif isinstance(inferred, nodes.Unknown):
84 raise InferenceError
85 elif isinstance(inferred, util.UninferableBase):
86 yield inferred
87 elif isinstance(inferred, (bases.Proxy, nodes.Slice, objects.Super)):
88 yield inferred._proxied
89 else: # pragma: no cover
90 raise AssertionError(f"We don't handle {type(inferred)} currently")
93def object_type(
94 node: InferenceResult, context: InferenceContext | None = None
95) -> InferenceResult | None:
96 """Obtain the type of the given node.
98 This is used to implement the ``type`` builtin, which means that it's
99 used for inferring type calls, as well as used in a couple of other places
100 in the inference.
101 The node will be inferred first, so this function can support all
102 sorts of objects, as long as they support inference.
103 """
105 try:
106 types = set(_object_type(node, context))
107 except InferenceError:
108 return util.Uninferable
109 if len(types) > 1 or not types:
110 return util.Uninferable
111 return next(iter(types))
114def _object_type_is_subclass(
115 obj_type: InferenceResult | None,
116 class_or_seq: list[InferenceResult],
117 context: InferenceContext | None = None,
118) -> util.UninferableBase | bool:
119 if isinstance(obj_type, util.UninferableBase) or not isinstance(
120 obj_type, nodes.ClassDef
121 ):
122 return util.Uninferable
124 # Instances are not types
125 class_seq = [
126 item if not isinstance(item, bases.Instance) else util.Uninferable
127 for item in class_or_seq
128 ]
129 # strict compatibility with issubclass
130 # issubclass(type, (object, 1)) evaluates to true
131 # issubclass(object, (1, type)) raises TypeError
132 for klass in class_seq:
133 if isinstance(klass, util.UninferableBase):
134 raise AstroidTypeError("arg 2 must be a type or tuple of types")
136 for obj_subclass in obj_type.mro():
137 if obj_subclass == klass:
138 return True
139 return False
142def object_isinstance(
143 node: InferenceResult,
144 class_or_seq: list[InferenceResult],
145 context: InferenceContext | None = None,
146) -> util.UninferableBase | bool:
147 """Check if a node 'isinstance' any node in class_or_seq.
149 :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
150 """
151 obj_type = object_type(node, context)
152 if isinstance(obj_type, util.UninferableBase):
153 return util.Uninferable
154 return _object_type_is_subclass(obj_type, class_or_seq, context=context)
157def object_issubclass(
158 node: nodes.NodeNG,
159 class_or_seq: list[InferenceResult],
160 context: InferenceContext | None = None,
161) -> util.UninferableBase | bool:
162 """Check if a type is a subclass of any node in class_or_seq.
164 :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
165 :raises AstroidError: if the type of the given node cannot be inferred
166 or its type's mro doesn't work
167 """
168 if not isinstance(node, nodes.ClassDef):
169 raise TypeError(f"{node} needs to be a ClassDef node")
170 return _object_type_is_subclass(node, class_or_seq, context=context)
173def has_known_bases(klass, context: InferenceContext | None = None) -> bool:
174 """Return whether all base classes of a class could be inferred."""
175 try:
176 return klass._all_bases_known
177 except AttributeError:
178 pass
179 for base in klass.bases:
180 result = real_safe_infer(base, context=context)
181 # TODO: check for A->B->A->B pattern in class structure too?
182 if (
183 not isinstance(result, scoped_nodes.ClassDef)
184 or result is klass
185 or not has_known_bases(result, context=context)
186 ):
187 klass._all_bases_known = False
188 return False
189 klass._all_bases_known = True
190 return True
193def _type_check(type1, type2) -> bool:
194 if not all(map(has_known_bases, (type1, type2))):
195 raise _NonDeducibleTypeHierarchy
197 if not all([type1.newstyle, type2.newstyle]):
198 return False
199 try:
200 return type1 in type2.mro()[:-1]
201 except MroError as e:
202 # The MRO is invalid.
203 raise _NonDeducibleTypeHierarchy from e
206def is_subtype(type1, type2) -> bool:
207 """Check if *type1* is a subtype of *type2*."""
208 return _type_check(type1=type2, type2=type1)
211def is_supertype(type1, type2) -> bool:
212 """Check if *type2* is a supertype of *type1*."""
213 return _type_check(type1, type2)
216def class_instance_as_index(node: bases.Instance) -> nodes.Const | None:
217 """Get the value as an index for the given instance.
219 If an instance provides an __index__ method, then it can
220 be used in some scenarios where an integer is expected,
221 for instance when multiplying or subscripting a list.
222 """
223 context = InferenceContext()
224 try:
225 for inferred in node.igetattr("__index__", context=context):
226 if not isinstance(inferred, bases.BoundMethod):
227 continue
229 context.boundnode = node
230 context.callcontext = CallContext(args=[], callee=inferred)
231 for result in inferred.infer_call_result(node, context=context):
232 if isinstance(result, nodes.Const) and isinstance(result.value, int):
233 return result
234 except InferenceError:
235 pass
236 return None
239def object_len(node, context: InferenceContext | None = None):
240 """Infer length of given node object.
242 :param Union[nodes.ClassDef, nodes.Instance] node:
243 :param node: Node to infer length of
245 :raises AstroidTypeError: If an invalid node is returned
246 from __len__ method or no __len__ method exists
247 :raises InferenceError: If the given node cannot be inferred
248 or if multiple nodes are inferred or if the code executed in python
249 would result in a infinite recursive check for length
250 :rtype int: Integer length of node
251 """
252 # pylint: disable=import-outside-toplevel; circular import
253 from astroid.objects import FrozenSet
255 inferred_node = real_safe_infer(node, context=context)
257 # prevent self referential length calls from causing a recursion error
258 # see https://github.com/pylint-dev/astroid/issues/777
259 node_frame = node.frame()
260 if (
261 isinstance(node_frame, scoped_nodes.FunctionDef)
262 and node_frame.name == "__len__"
263 and isinstance(inferred_node, bases.Proxy)
264 and inferred_node._proxied == node_frame.parent
265 ):
266 message = (
267 "Self referential __len__ function will "
268 "cause a RecursionError on line {} of {}".format(
269 node.lineno, node.root().file
270 )
271 )
272 raise InferenceError(message)
274 if inferred_node is None or isinstance(inferred_node, util.UninferableBase):
275 raise InferenceError(node=node)
276 if isinstance(inferred_node, nodes.Const) and isinstance(
277 inferred_node.value, (bytes, str)
278 ):
279 return len(inferred_node.value)
280 if isinstance(inferred_node, (nodes.List, nodes.Set, nodes.Tuple, FrozenSet)):
281 return len(inferred_node.elts)
282 if isinstance(inferred_node, nodes.Dict):
283 return len(inferred_node.items)
285 node_type = object_type(inferred_node, context=context)
286 if not node_type:
287 raise InferenceError(node=node)
289 try:
290 len_call = next(node_type.igetattr("__len__", context=context))
291 except StopIteration as e:
292 raise AstroidTypeError(str(e)) from e
293 except AttributeInferenceError as e:
294 raise AstroidTypeError(
295 f"object of type '{node_type.pytype()}' has no len()"
296 ) from e
298 inferred = len_call.infer_call_result(node, context)
299 if isinstance(inferred, util.UninferableBase):
300 raise InferenceError(node=node, context=context)
301 result_of_len = next(inferred, None)
302 if (
303 isinstance(result_of_len, nodes.Const)
304 and result_of_len.pytype() == "builtins.int"
305 ):
306 return result_of_len.value
307 if (
308 result_of_len is None
309 or isinstance(result_of_len, bases.Instance)
310 and result_of_len.is_subtype_of("builtins.int")
311 ):
312 # Fake a result as we don't know the arguments of the instance call.
313 return 0
314 raise AstroidTypeError(
315 f"'{result_of_len}' object cannot be interpreted as an integer"
316 )
319def _higher_function_scope(node: nodes.NodeNG) -> nodes.FunctionDef | None:
320 """Search for the first function which encloses the given
321 scope.
323 This can be used for looking up in that function's
324 scope, in case looking up in a lower scope for a particular
325 name fails.
327 :param node: A scope node.
328 :returns:
329 ``None``, if no parent function scope was found,
330 otherwise an instance of :class:`astroid.nodes.scoped_nodes.Function`,
331 which encloses the given node.
332 """
333 current = node
334 while current.parent and not isinstance(current.parent, nodes.FunctionDef):
335 current = current.parent
336 if current and current.parent:
337 return current.parent
338 return None