Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/helpers.py: 56%
158 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
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
24def _build_proxy_class(cls_name: str, builtins: nodes.Module) -> nodes.ClassDef:
25 proxy = raw_building.build_class(cls_name)
26 proxy.parent = 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, None, 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 if inferred.newstyle:
56 metaclass = inferred.metaclass(context=context)
57 if metaclass:
58 yield metaclass
59 continue
60 yield builtins.getattr("type")[0]
61 elif isinstance(
62 inferred,
63 (scoped_nodes.Lambda, bases.UnboundMethod, scoped_nodes.FunctionDef),
64 ):
65 yield _function_type(inferred, builtins)
66 elif isinstance(inferred, scoped_nodes.Module):
67 yield _build_proxy_class("module", builtins)
68 elif isinstance(inferred, nodes.Unknown):
69 raise InferenceError
70 elif isinstance(inferred, util.UninferableBase):
71 yield inferred
72 elif isinstance(inferred, (bases.Proxy, nodes.Slice, objects.Super)):
73 yield inferred._proxied
74 else: # pragma: no cover
75 raise AssertionError(f"We don't handle {type(inferred)} currently")
78def object_type(
79 node: InferenceResult, context: InferenceContext | None = None
80) -> InferenceResult | None:
81 """Obtain the type of the given node.
83 This is used to implement the ``type`` builtin, which means that it's
84 used for inferring type calls, as well as used in a couple of other places
85 in the inference.
86 The node will be inferred first, so this function can support all
87 sorts of objects, as long as they support inference.
88 """
90 try:
91 types = set(_object_type(node, context))
92 except InferenceError:
93 return util.Uninferable
94 if len(types) > 1 or not types:
95 return util.Uninferable
96 return list(types)[0]
99def _object_type_is_subclass(
100 obj_type: InferenceResult | None,
101 class_or_seq: list[InferenceResult],
102 context: InferenceContext | None = None,
103) -> util.UninferableBase | bool:
104 if isinstance(obj_type, util.UninferableBase) or not isinstance(
105 obj_type, nodes.ClassDef
106 ):
107 return util.Uninferable
109 # Instances are not types
110 class_seq = [
111 item if not isinstance(item, bases.Instance) else util.Uninferable
112 for item in class_or_seq
113 ]
114 # strict compatibility with issubclass
115 # issubclass(type, (object, 1)) evaluates to true
116 # issubclass(object, (1, type)) raises TypeError
117 for klass in class_seq:
118 if isinstance(klass, util.UninferableBase):
119 raise AstroidTypeError("arg 2 must be a type or tuple of types")
121 for obj_subclass in obj_type.mro():
122 if obj_subclass == klass:
123 return True
124 return False
127def object_isinstance(
128 node: InferenceResult,
129 class_or_seq: list[InferenceResult],
130 context: InferenceContext | None = None,
131) -> util.UninferableBase | bool:
132 """Check if a node 'isinstance' any node in class_or_seq.
134 :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
135 """
136 obj_type = object_type(node, context)
137 if isinstance(obj_type, util.UninferableBase):
138 return util.Uninferable
139 return _object_type_is_subclass(obj_type, class_or_seq, context=context)
142def object_issubclass(
143 node: nodes.NodeNG,
144 class_or_seq: list[InferenceResult],
145 context: InferenceContext | None = None,
146) -> util.UninferableBase | bool:
147 """Check if a type is a subclass of any node in class_or_seq.
149 :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
150 :raises AstroidError: if the type of the given node cannot be inferred
151 or its type's mro doesn't work
152 """
153 if not isinstance(node, nodes.ClassDef):
154 raise TypeError(f"{node} needs to be a ClassDef node")
155 return _object_type_is_subclass(node, class_or_seq, context=context)
158def safe_infer(
159 node: nodes.NodeNG | bases.Proxy | util.UninferableBase,
160 context: InferenceContext | None = None,
161) -> InferenceResult | None:
162 """Return the inferred value for the given node.
164 Return None if inference failed or if there is some ambiguity (more than
165 one node has been inferred).
166 """
167 if isinstance(node, util.UninferableBase):
168 return node
169 try:
170 inferit = node.infer(context=context)
171 value = next(inferit)
172 except (InferenceError, StopIteration):
173 return None
174 try:
175 next(inferit)
176 return None # None if there is ambiguity on the inferred node
177 except InferenceError:
178 return None # there is some kind of ambiguity
179 except StopIteration:
180 return value
183def has_known_bases(klass, context: InferenceContext | None = None) -> bool:
184 """Return whether all base classes of a class could be inferred."""
185 try:
186 return klass._all_bases_known
187 except AttributeError:
188 pass
189 for base in klass.bases:
190 result = safe_infer(base, context=context)
191 # TODO: check for A->B->A->B pattern in class structure too?
192 if (
193 not isinstance(result, scoped_nodes.ClassDef)
194 or result is klass
195 or not has_known_bases(result, context=context)
196 ):
197 klass._all_bases_known = False
198 return False
199 klass._all_bases_known = True
200 return True
203def _type_check(type1, type2) -> bool:
204 if not all(map(has_known_bases, (type1, type2))):
205 raise _NonDeducibleTypeHierarchy
207 if not all([type1.newstyle, type2.newstyle]):
208 return False
209 try:
210 return type1 in type2.mro()[:-1]
211 except MroError as e:
212 # The MRO is invalid.
213 raise _NonDeducibleTypeHierarchy from e
216def is_subtype(type1, type2) -> bool:
217 """Check if *type1* is a subtype of *type2*."""
218 return _type_check(type1=type2, type2=type1)
221def is_supertype(type1, type2) -> bool:
222 """Check if *type2* is a supertype of *type1*."""
223 return _type_check(type1, type2)
226def class_instance_as_index(node: bases.Instance) -> nodes.Const | None:
227 """Get the value as an index for the given instance.
229 If an instance provides an __index__ method, then it can
230 be used in some scenarios where an integer is expected,
231 for instance when multiplying or subscripting a list.
232 """
233 context = InferenceContext()
234 try:
235 for inferred in node.igetattr("__index__", context=context):
236 if not isinstance(inferred, bases.BoundMethod):
237 continue
239 context.boundnode = node
240 context.callcontext = CallContext(args=[], callee=inferred)
241 for result in inferred.infer_call_result(node, context=context):
242 if isinstance(result, nodes.Const) and isinstance(result.value, int):
243 return result
244 except InferenceError:
245 pass
246 return None
249def object_len(node, context: InferenceContext | None = None):
250 """Infer length of given node object.
252 :param Union[nodes.ClassDef, nodes.Instance] node:
253 :param node: Node to infer length of
255 :raises AstroidTypeError: If an invalid node is returned
256 from __len__ method or no __len__ method exists
257 :raises InferenceError: If the given node cannot be inferred
258 or if multiple nodes are inferred or if the code executed in python
259 would result in a infinite recursive check for length
260 :rtype int: Integer length of node
261 """
262 # pylint: disable=import-outside-toplevel; circular import
263 from astroid.objects import FrozenSet
265 inferred_node = safe_infer(node, context=context)
267 # prevent self referential length calls from causing a recursion error
268 # see https://github.com/pylint-dev/astroid/issues/777
269 node_frame = node.frame(future=True)
270 if (
271 isinstance(node_frame, scoped_nodes.FunctionDef)
272 and node_frame.name == "__len__"
273 and isinstance(inferred_node, bases.Proxy)
274 and inferred_node._proxied == node_frame.parent
275 ):
276 message = (
277 "Self referential __len__ function will "
278 "cause a RecursionError on line {} of {}".format(
279 node.lineno, node.root().file
280 )
281 )
282 raise InferenceError(message)
284 if inferred_node is None or isinstance(inferred_node, util.UninferableBase):
285 raise InferenceError(node=node)
286 if isinstance(inferred_node, nodes.Const) and isinstance(
287 inferred_node.value, (bytes, str)
288 ):
289 return len(inferred_node.value)
290 if isinstance(inferred_node, (nodes.List, nodes.Set, nodes.Tuple, FrozenSet)):
291 return len(inferred_node.elts)
292 if isinstance(inferred_node, nodes.Dict):
293 return len(inferred_node.items)
295 node_type = object_type(inferred_node, context=context)
296 if not node_type:
297 raise InferenceError(node=node)
299 try:
300 len_call = next(node_type.igetattr("__len__", context=context))
301 except StopIteration as e:
302 raise AstroidTypeError(str(e)) from e
303 except AttributeInferenceError as e:
304 raise AstroidTypeError(
305 f"object of type '{node_type.pytype()}' has no len()"
306 ) from e
308 inferred = len_call.infer_call_result(node, context)
309 if isinstance(inferred, util.UninferableBase):
310 raise InferenceError(node=node, context=context)
311 result_of_len = next(inferred, None)
312 if (
313 isinstance(result_of_len, nodes.Const)
314 and result_of_len.pytype() == "builtins.int"
315 ):
316 return result_of_len.value
317 if (
318 result_of_len is None
319 or 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 )