Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/astroid/objects.py: 36%
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"""
6Inference objects are a way to represent composite AST nodes,
7which are used only as inference results, so they can't be found in the
8original AST tree. For instance, inferring the following frozenset use,
9leads to an inferred FrozenSet:
11 Call(func=Name('frozenset'), args=Tuple(...))
12"""
14from __future__ import annotations
16import sys
17from collections.abc import Generator, Iterator
18from functools import cached_property
19from typing import Any, Literal, NoReturn
21from astroid import bases, util
22from astroid.context import InferenceContext
23from astroid.exceptions import (
24 AttributeInferenceError,
25 InferenceError,
26 MroError,
27 SuperError,
28)
29from astroid.interpreter import objectmodel
30from astroid.manager import AstroidManager
31from astroid.nodes import node_classes, scoped_nodes
32from astroid.typing import InferenceResult, SuccessfulInferenceResult
34if sys.version_info >= (3, 11):
35 from typing import Self
36else:
37 from typing_extensions import Self
40class FrozenSet(node_classes.BaseContainer):
41 """Class representing a FrozenSet composite node."""
43 def pytype(self) -> Literal["builtins.frozenset"]:
44 return "builtins.frozenset"
46 def _infer(self, context: InferenceContext | None = None, **kwargs: Any):
47 yield self
49 @cached_property
50 def _proxied(self): # pylint: disable=method-hidden
51 ast_builtins = AstroidManager().builtins_module
52 return ast_builtins.getattr("frozenset")[0]
55class Super(node_classes.NodeNG):
56 """Proxy class over a super call.
58 This class offers almost the same behaviour as Python's super,
59 which is MRO lookups for retrieving attributes from the parents.
61 The *mro_pointer* is the place in the MRO from where we should
62 start looking, not counting it. *mro_type* is the object which
63 provides the MRO, it can be both a type or an instance.
64 *self_class* is the class where the super call is, while
65 *scope* is the function where the super call is.
66 """
68 special_attributes = objectmodel.SuperModel()
70 def __init__(
71 self,
72 mro_pointer: SuccessfulInferenceResult,
73 mro_type: SuccessfulInferenceResult,
74 self_class: scoped_nodes.ClassDef,
75 scope: scoped_nodes.FunctionDef,
76 call: node_classes.Call,
77 ) -> None:
78 self.type = mro_type
79 self.mro_pointer = mro_pointer
80 self._class_based = False
81 self._self_class = self_class
82 self._scope = scope
83 super().__init__(
84 parent=scope,
85 lineno=scope.lineno,
86 col_offset=scope.col_offset,
87 end_lineno=scope.end_lineno,
88 end_col_offset=scope.end_col_offset,
89 )
91 def _infer(self, context: InferenceContext | None = None, **kwargs: Any):
92 yield self
94 def super_mro(self):
95 """Get the MRO which will be used to lookup attributes in this super."""
96 if not isinstance(self.mro_pointer, scoped_nodes.ClassDef):
97 raise SuperError(
98 "The first argument to super must be a subtype of "
99 "type, not {mro_pointer}.",
100 super_=self,
101 )
103 if isinstance(self.type, scoped_nodes.ClassDef):
104 # `super(type, type)`, most likely in a class method.
105 self._class_based = True
106 mro_type = self.type
107 else:
108 mro_type = getattr(self.type, "_proxied", None)
109 if not isinstance(mro_type, (bases.Instance, scoped_nodes.ClassDef)):
110 raise SuperError(
111 "The second argument to super must be an "
112 "instance or subtype of type, not {type}.",
113 super_=self,
114 )
116 mro = mro_type.mro()
117 if self.mro_pointer not in mro:
118 raise SuperError(
119 "The second argument to super must be an "
120 "instance or subtype of type, not {type}.",
121 super_=self,
122 )
124 index = mro.index(self.mro_pointer)
125 return mro[index + 1 :]
127 @cached_property
128 def _proxied(self):
129 ast_builtins = AstroidManager().builtins_module
130 return ast_builtins.getattr("super")[0]
132 def pytype(self) -> Literal["builtins.super"]:
133 return "builtins.super"
135 def display_type(self) -> str:
136 return "Super of"
138 @property
139 def name(self):
140 """Get the name of the MRO pointer."""
141 return self.mro_pointer.name
143 def qname(self) -> Literal["super"]:
144 return "super"
146 def igetattr( # noqa: C901
147 self, name: str, context: InferenceContext | None = None
148 ) -> Iterator[InferenceResult]:
149 """Retrieve the inferred values of the given attribute name."""
150 # '__class__' is a special attribute that should be taken directly
151 # from the special attributes dict
152 if name == "__class__":
153 yield self.special_attributes.lookup(name)
154 return
156 try:
157 mro = self.super_mro()
158 # Don't let invalid MROs or invalid super calls
159 # leak out as is from this function.
160 except SuperError as exc:
161 raise AttributeInferenceError(
162 (
163 "Lookup for {name} on {target!r} because super call {super!r} "
164 "is invalid."
165 ),
166 target=self,
167 attribute=name,
168 context=context,
169 super_=exc.super_,
170 ) from exc
171 except MroError as exc:
172 raise AttributeInferenceError(
173 (
174 "Lookup for {name} on {target!r} failed because {cls!r} has an "
175 "invalid MRO."
176 ),
177 target=self,
178 attribute=name,
179 context=context,
180 mros=exc.mros,
181 cls=exc.cls,
182 ) from exc
183 found = False
184 for cls in mro:
185 if name not in cls.locals:
186 continue
188 found = True
189 for inferred in bases._infer_stmts([cls[name]], context, frame=self):
190 if not isinstance(inferred, scoped_nodes.FunctionDef):
191 yield inferred
192 continue
194 # We can obtain different descriptors from a super depending
195 # on what we are accessing and where the super call is.
196 if inferred.type == "classmethod":
197 # Pass original caller for classmethod too
198 yield bases.BoundMethod(inferred, cls, original_caller=self.type)
199 elif self._scope.type == "classmethod" and inferred.type == "method":
200 yield inferred
201 elif self._class_based or inferred.type == "staticmethod":
202 yield inferred
203 elif isinstance(inferred, Property):
204 function = inferred.function
205 try:
206 yield from function.infer_call_result(
207 caller=self, context=context
208 )
209 except InferenceError:
210 yield util.Uninferable
211 elif bases._is_property(inferred):
212 # TODO: support other descriptors as well.
213 try:
214 yield from inferred.infer_call_result(self, context)
215 except InferenceError:
216 yield util.Uninferable
217 else:
218 # Pass original caller (self.type) so infer_call_result can
219 # correctly resolve Self return types to the actual caller type
220 yield bases.BoundMethod(inferred, cls, original_caller=self.type)
222 # Only if we haven't found any explicit overwrites for the
223 # attribute we look it up in the special attributes
224 if not found and name in self.special_attributes:
225 special_attr = self.special_attributes.lookup(name)
226 if not isinstance(special_attr, node_classes.Unknown):
227 yield special_attr
228 return
230 if not found:
231 raise AttributeInferenceError(target=self, attribute=name, context=context)
233 def getattr(self, name, context: InferenceContext | None = None):
234 return list(self.igetattr(name, context=context))
237class ExceptionInstance(bases.Instance):
238 """Class for instances of exceptions.
240 It has special treatment for some of the exceptions's attributes,
241 which are transformed at runtime into certain concrete objects, such as
242 the case of .args.
243 """
245 @cached_property
246 def special_attributes(self):
247 qname = self.qname()
248 instance = objectmodel.BUILTIN_EXCEPTIONS.get(
249 qname, objectmodel.ExceptionInstanceModel
250 )
251 return instance()(self)
254class DictInstance(bases.Instance):
255 """Special kind of instances for dictionaries.
257 This instance knows the underlying object model of the dictionaries, which means
258 that methods such as .values or .items can be properly inferred.
259 """
261 special_attributes = objectmodel.DictModel()
264# Custom objects tailored for dictionaries, which are used to
265# disambiguate between the types of Python 2 dict's method returns
266# and Python 3 (where they return set like objects).
267class DictItems(bases.Proxy):
268 __str__ = node_classes.NodeNG.__str__
269 __repr__ = node_classes.NodeNG.__repr__
272class DictKeys(bases.Proxy):
273 __str__ = node_classes.NodeNG.__str__
274 __repr__ = node_classes.NodeNG.__repr__
277class DictValues(bases.Proxy):
278 __str__ = node_classes.NodeNG.__str__
279 __repr__ = node_classes.NodeNG.__repr__
282class PartialFunction(scoped_nodes.FunctionDef):
283 """A class representing partial function obtained via functools.partial."""
285 def __init__(
286 self,
287 call=None,
288 name=None,
289 lineno=None,
290 col_offset=None,
291 parent=None,
292 *,
293 end_lineno=None,
294 end_col_offset=None,
295 filled_args=None,
296 filled_keywords=None,
297 ):
298 super().__init__(
299 name,
300 lineno=lineno,
301 col_offset=col_offset,
302 end_col_offset=end_col_offset,
303 end_lineno=end_lineno,
304 parent=parent,
305 )
306 if call is None:
307 self.filled_args = list(filled_args or [])
308 self.filled_keywords = dict(filled_keywords or {})
309 else:
310 self.filled_args = call.positional_arguments[1:]
311 self.filled_keywords = call.keyword_arguments
313 wrapped_function = call.positional_arguments[0]
314 inferred_wrapped_function = next(wrapped_function.infer())
315 if isinstance(inferred_wrapped_function, PartialFunction):
316 self.filled_args = (
317 inferred_wrapped_function.filled_args + self.filled_args
318 )
319 self.filled_keywords = {
320 **inferred_wrapped_function.filled_keywords,
321 **self.filled_keywords,
322 }
324 self.filled_positionals = len(self.filled_args)
326 def infer_call_result(
327 self,
328 caller: SuccessfulInferenceResult | None,
329 context: InferenceContext | None = None,
330 ) -> Iterator[InferenceResult]:
331 if context:
332 assert (
333 context.callcontext
334 ), "CallContext should be set before inferring call result"
335 current_passed_keywords = {
336 keyword for (keyword, _) in context.callcontext.keywords
337 }
338 for keyword, value in self.filled_keywords.items():
339 if keyword not in current_passed_keywords:
340 context.callcontext.keywords.append((keyword, value))
342 call_context_args = context.callcontext.args or []
343 context.callcontext.args = self.filled_args + call_context_args
345 return super().infer_call_result(caller=caller, context=context)
347 def qname(self) -> str:
348 return self.__class__.__name__
351# TODO: Hack to solve the circular import problem between node_classes and objects
352# This is not needed in 2.0, which has a cleaner design overall
353node_classes.Dict.__bases__ = (node_classes.NodeNG, DictInstance)
356class Property(scoped_nodes.FunctionDef):
357 """Class representing a Python property."""
359 def __init__(self, function, name=None, lineno=None, col_offset=None, parent=None):
360 self.function = function
361 super().__init__(
362 name,
363 lineno=lineno,
364 col_offset=col_offset,
365 parent=parent,
366 end_col_offset=function.end_col_offset,
367 end_lineno=function.end_lineno,
368 )
370 special_attributes = objectmodel.PropertyModel()
371 type = "property"
373 def pytype(self) -> Literal["builtins.property"]:
374 return "builtins.property"
376 def infer_call_result(
377 self,
378 caller: SuccessfulInferenceResult | None,
379 context: InferenceContext | None = None,
380 ) -> NoReturn:
381 raise InferenceError("Properties are not callable")
383 def _infer(
384 self, context: InferenceContext | None = None, **kwargs: Any
385 ) -> Generator[Self]:
386 yield self