Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py: 52%
1230 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"""
6This module contains the classes for "scoped" node, i.e. which are opening a
7new local scope in the language definition : Module, ClassDef, FunctionDef (and
8Lambda, GeneratorExp, DictComp and SetComp to some extent).
9"""
11from __future__ import annotations
13import io
14import itertools
15import os
16import warnings
17from collections.abc import Generator, Iterable, Iterator, Sequence
18from functools import cached_property, lru_cache
19from typing import TYPE_CHECKING, ClassVar, Literal, NoReturn, TypeVar, overload
21from astroid import bases, util
22from astroid.const import IS_PYPY, PY38, PY39_PLUS, PYPY_7_3_11_PLUS
23from astroid.context import (
24 CallContext,
25 InferenceContext,
26 bind_context_to_node,
27 copy_context,
28)
29from astroid.exceptions import (
30 AstroidBuildingError,
31 AstroidTypeError,
32 AttributeInferenceError,
33 DuplicateBasesError,
34 InconsistentMroError,
35 InferenceError,
36 MroError,
37 StatementMissing,
38 TooManyLevelsError,
39)
40from astroid.interpreter.dunder_lookup import lookup
41from astroid.interpreter.objectmodel import ClassModel, FunctionModel, ModuleModel
42from astroid.manager import AstroidManager
43from astroid.nodes import Arguments, Const, NodeNG, Unknown, _base_nodes, node_classes
44from astroid.nodes.scoped_nodes.mixin import ComprehensionScope, LocalsDictNodeNG
45from astroid.nodes.scoped_nodes.utils import builtin_lookup
46from astroid.nodes.utils import Position
47from astroid.typing import InferBinaryOp, InferenceResult, SuccessfulInferenceResult
49if TYPE_CHECKING:
50 from astroid import nodes
53ITER_METHODS = ("__iter__", "__getitem__")
54EXCEPTION_BASE_CLASSES = frozenset({"Exception", "BaseException"})
55BUILTIN_DESCRIPTORS = frozenset(
56 {"classmethod", "staticmethod", "builtins.classmethod", "builtins.staticmethod"}
57)
59_T = TypeVar("_T")
62def _c3_merge(sequences, cls, context):
63 """Merges MROs in *sequences* to a single MRO using the C3 algorithm.
65 Adapted from http://www.python.org/download/releases/2.3/mro/.
67 """
68 result = []
69 while True:
70 sequences = [s for s in sequences if s] # purge empty sequences
71 if not sequences:
72 return result
73 for s1 in sequences: # find merge candidates among seq heads
74 candidate = s1[0]
75 for s2 in sequences:
76 if candidate in s2[1:]:
77 candidate = None
78 break # reject the current head, it appears later
79 else:
80 break
81 if not candidate:
82 # Show all the remaining bases, which were considered as
83 # candidates for the next mro sequence.
84 raise InconsistentMroError(
85 message="Cannot create a consistent method resolution order "
86 "for MROs {mros} of class {cls!r}.",
87 mros=sequences,
88 cls=cls,
89 context=context,
90 )
92 result.append(candidate)
93 # remove the chosen candidate
94 for seq in sequences:
95 if seq[0] == candidate:
96 del seq[0]
97 return None
100def clean_typing_generic_mro(sequences: list[list[ClassDef]]) -> None:
101 """A class can inherit from typing.Generic directly, as base,
102 and as base of bases. The merged MRO must however only contain the last entry.
103 To prepare for _c3_merge, remove some typing.Generic entries from
104 sequences if multiple are present.
106 This method will check if Generic is in inferred_bases and also
107 part of bases_mro. If true, remove it from inferred_bases
108 as well as its entry the bases_mro.
110 Format sequences: [[self]] + bases_mro + [inferred_bases]
111 """
112 bases_mro = sequences[1:-1]
113 inferred_bases = sequences[-1]
114 # Check if Generic is part of inferred_bases
115 for i, base in enumerate(inferred_bases):
116 if base.qname() == "typing.Generic":
117 position_in_inferred_bases = i
118 break
119 else:
120 return
121 # Check if also part of bases_mro
122 # Ignore entry for typing.Generic
123 for i, seq in enumerate(bases_mro):
124 if i == position_in_inferred_bases:
125 continue
126 if any(base.qname() == "typing.Generic" for base in seq):
127 break
128 else:
129 return
130 # Found multiple Generics in mro, remove entry from inferred_bases
131 # and the corresponding one from bases_mro
132 inferred_bases.pop(position_in_inferred_bases)
133 bases_mro.pop(position_in_inferred_bases)
136def clean_duplicates_mro(
137 sequences: Iterable[Iterable[ClassDef]],
138 cls: ClassDef,
139 context: InferenceContext | None,
140) -> Iterable[Iterable[ClassDef]]:
141 for sequence in sequences:
142 seen = set()
143 for node in sequence:
144 lineno_and_qname = (node.lineno, node.qname())
145 if lineno_and_qname in seen:
146 raise DuplicateBasesError(
147 message="Duplicates found in MROs {mros} for {cls!r}.",
148 mros=sequences,
149 cls=cls,
150 context=context,
151 )
152 seen.add(lineno_and_qname)
153 return sequences
156def function_to_method(n, klass):
157 if isinstance(n, FunctionDef):
158 if n.type == "classmethod":
159 return bases.BoundMethod(n, klass)
160 if n.type == "property":
161 return n
162 if n.type != "staticmethod":
163 return bases.UnboundMethod(n)
164 return n
167class Module(LocalsDictNodeNG):
168 """Class representing an :class:`ast.Module` node.
170 >>> import astroid
171 >>> node = astroid.extract_node('import astroid')
172 >>> node
173 <Import l.1 at 0x7f23b2e4e5c0>
174 >>> node.parent
175 <Module l.0 at 0x7f23b2e4eda0>
176 """
178 _astroid_fields = ("doc_node", "body")
180 doc_node: Const | None
181 """The doc node associated with this node."""
183 # attributes below are set by the builder module or by raw factories
185 file_bytes: str | bytes | None = None
186 """The string/bytes that this ast was built from."""
188 file_encoding: str | None = None
189 """The encoding of the source file.
191 This is used to get unicode out of a source file.
192 Python 2 only.
193 """
195 special_attributes = ModuleModel()
196 """The names of special attributes that this module has."""
198 # names of module attributes available through the global scope
199 scope_attrs = {"__name__", "__doc__", "__file__", "__path__", "__package__"}
200 """The names of module attributes available through the global scope."""
202 _other_fields = (
203 "name",
204 "file",
205 "path",
206 "package",
207 "pure_python",
208 "future_imports",
209 )
210 _other_other_fields = ("locals", "globals")
212 def __init__(
213 self,
214 name: str,
215 file: str | None = None,
216 path: Sequence[str] | None = None,
217 package: bool = False,
218 pure_python: bool = True,
219 ) -> None:
220 self.name = name
221 """The name of the module."""
223 self.file = file
224 """The path to the file that this ast has been extracted from.
226 This will be ``None`` when the representation has been built from a
227 built-in module.
228 """
230 self.path = path
232 self.package = package
233 """Whether the node represents a package or a module."""
235 self.pure_python = pure_python
236 """Whether the ast was built from source."""
238 self.globals: dict[str, list[SuccessfulInferenceResult]]
239 """A map of the name of a global variable to the node defining the global."""
241 self.locals = self.globals = {}
242 """A map of the name of a local variable to the node defining the local."""
244 self.body: list[node_classes.NodeNG] = []
245 """The contents of the module."""
247 self.future_imports: set[str] = set()
248 """The imports from ``__future__``."""
250 super().__init__(
251 lineno=0, parent=None, col_offset=0, end_lineno=None, end_col_offset=None
252 )
254 # pylint: enable=redefined-builtin
256 def postinit(
257 self, body: list[node_classes.NodeNG], *, doc_node: Const | None = None
258 ):
259 self.body = body
260 self.doc_node = doc_node
262 def _get_stream(self):
263 if self.file_bytes is not None:
264 return io.BytesIO(self.file_bytes)
265 if self.file is not None:
266 # pylint: disable=consider-using-with
267 stream = open(self.file, "rb")
268 return stream
269 return None
271 def stream(self):
272 """Get a stream to the underlying file or bytes.
274 :type: file or io.BytesIO or None
275 """
276 return self._get_stream()
278 def block_range(self, lineno: int) -> tuple[int, int]:
279 """Get a range from where this node starts to where this node ends.
281 :param lineno: Unused.
283 :returns: The range of line numbers that this node belongs to.
284 """
285 return self.fromlineno, self.tolineno
287 def scope_lookup(
288 self, node: node_classes.LookupMixIn, name: str, offset: int = 0
289 ) -> tuple[LocalsDictNodeNG, list[node_classes.NodeNG]]:
290 """Lookup where the given variable is assigned.
292 :param node: The node to look for assignments up to.
293 Any assignments after the given node are ignored.
295 :param name: The name of the variable to find assignments for.
297 :param offset: The line offset to filter statements up to.
299 :returns: This scope node and the list of assignments associated to the
300 given name according to the scope where it has been found (locals,
301 globals or builtin).
302 """
303 if name in self.scope_attrs and name not in self.locals:
304 try:
305 return self, self.getattr(name)
306 except AttributeInferenceError:
307 return self, []
308 return self._scope_lookup(node, name, offset)
310 def pytype(self) -> Literal["builtins.module"]:
311 """Get the name of the type that this node represents.
313 :returns: The name of the type.
314 """
315 return "builtins.module"
317 def display_type(self) -> str:
318 """A human readable type of this node.
320 :returns: The type of this node.
321 :rtype: str
322 """
323 return "Module"
325 def getattr(
326 self, name, context: InferenceContext | None = None, ignore_locals=False
327 ):
328 if not name:
329 raise AttributeInferenceError(target=self, attribute=name, context=context)
331 result = []
332 name_in_locals = name in self.locals
334 if name in self.special_attributes and not ignore_locals and not name_in_locals:
335 result = [self.special_attributes.lookup(name)]
336 elif not ignore_locals and name_in_locals:
337 result = self.locals[name]
338 elif self.package:
339 try:
340 result = [self.import_module(name, relative_only=True)]
341 except (AstroidBuildingError, SyntaxError) as exc:
342 raise AttributeInferenceError(
343 target=self, attribute=name, context=context
344 ) from exc
345 result = [n for n in result if not isinstance(n, node_classes.DelName)]
346 if result:
347 return result
348 raise AttributeInferenceError(target=self, attribute=name, context=context)
350 def igetattr(
351 self, name: str, context: InferenceContext | None = None
352 ) -> Iterator[InferenceResult]:
353 """Infer the possible values of the given variable.
355 :param name: The name of the variable to infer.
357 :returns: The inferred possible values.
358 """
359 # set lookup name since this is necessary to infer on import nodes for
360 # instance
361 context = copy_context(context)
362 context.lookupname = name
363 try:
364 return bases._infer_stmts(self.getattr(name, context), context, frame=self)
365 except AttributeInferenceError as error:
366 raise InferenceError(
367 str(error), target=self, attribute=name, context=context
368 ) from error
370 def fully_defined(self) -> bool:
371 """Check if this module has been build from a .py file.
373 If so, the module contains a complete representation,
374 including the code.
376 :returns: Whether the module has been built from a .py file.
377 """
378 return self.file is not None and self.file.endswith(".py")
380 @overload
381 def statement(self, *, future: None = ...) -> Module:
382 ...
384 @overload
385 def statement(self, *, future: Literal[True]) -> NoReturn:
386 ...
388 def statement(self, *, future: Literal[None, True] = None) -> Module | NoReturn:
389 """The first parent node, including self, marked as statement node.
391 When called on a :class:`Module` with the future parameter this raises an error.
393 TODO: Deprecate the future parameter and only raise StatementMissing
395 :raises StatementMissing: If no self has no parent attribute and future is True
396 """
397 if future:
398 raise StatementMissing(target=self)
399 warnings.warn(
400 "In astroid 3.0.0 NodeNG.statement() will return either a nodes.Statement "
401 "or raise a StatementMissing exception. nodes.Module will no longer be "
402 "considered a statement. This behaviour can already be triggered "
403 "by passing 'future=True' to a statement() call.",
404 DeprecationWarning,
405 stacklevel=2,
406 )
407 return self
409 def previous_sibling(self):
410 """The previous sibling statement.
412 :returns: The previous sibling statement node.
413 :rtype: NodeNG or None
414 """
416 def next_sibling(self):
417 """The next sibling statement node.
419 :returns: The next sibling statement node.
420 :rtype: NodeNG or None
421 """
423 _absolute_import_activated = True
425 def absolute_import_activated(self) -> bool:
426 """Whether :pep:`328` absolute import behaviour has been enabled.
428 :returns: Whether :pep:`328` has been enabled.
429 """
430 return self._absolute_import_activated
432 def import_module(
433 self,
434 modname: str,
435 relative_only: bool = False,
436 level: int | None = None,
437 use_cache: bool = True,
438 ) -> Module:
439 """Get the ast for a given module as if imported from this module.
441 :param modname: The name of the module to "import".
443 :param relative_only: Whether to only consider relative imports.
445 :param level: The level of relative import.
447 :param use_cache: Whether to use the astroid_cache of modules.
449 :returns: The imported module ast.
450 """
451 if relative_only and level is None:
452 level = 0
453 absmodname = self.relative_to_absolute_name(modname, level)
455 try:
456 return AstroidManager().ast_from_module_name(
457 absmodname, use_cache=use_cache
458 )
459 except AstroidBuildingError:
460 # we only want to import a sub module or package of this module,
461 # skip here
462 if relative_only:
463 raise
464 return AstroidManager().ast_from_module_name(modname)
466 def relative_to_absolute_name(self, modname: str, level: int | None) -> str:
467 """Get the absolute module name for a relative import.
469 The relative import can be implicit or explicit.
471 :param modname: The module name to convert.
473 :param level: The level of relative import.
475 :returns: The absolute module name.
477 :raises TooManyLevelsError: When the relative import refers to a
478 module too far above this one.
479 """
480 # XXX this returns non sens when called on an absolute import
481 # like 'pylint.checkers.astroid.utils'
482 # XXX doesn't return absolute name if self.name isn't absolute name
483 if self.absolute_import_activated() and level is None:
484 return modname
485 if level:
486 if self.package:
487 level = level - 1
488 package_name = self.name.rsplit(".", level)[0]
489 elif (
490 self.path
491 and not os.path.exists(os.path.dirname(self.path[0]) + "/__init__.py")
492 and os.path.exists(
493 os.path.dirname(self.path[0]) + "/" + modname.split(".")[0]
494 )
495 ):
496 level = level - 1
497 package_name = ""
498 else:
499 package_name = self.name.rsplit(".", level)[0]
500 if level and self.name.count(".") < level:
501 raise TooManyLevelsError(level=level, name=self.name)
503 elif self.package:
504 package_name = self.name
505 else:
506 package_name = self.name.rsplit(".", 1)[0]
508 if package_name:
509 if not modname:
510 return package_name
511 return f"{package_name}.{modname}"
512 return modname
514 def wildcard_import_names(self):
515 """The list of imported names when this module is 'wildcard imported'.
517 It doesn't include the '__builtins__' name which is added by the
518 current CPython implementation of wildcard imports.
520 :returns: The list of imported names.
521 :rtype: list(str)
522 """
523 # We separate the different steps of lookup in try/excepts
524 # to avoid catching too many Exceptions
525 default = [name for name in self.keys() if not name.startswith("_")]
526 try:
527 all_values = self["__all__"]
528 except KeyError:
529 return default
531 try:
532 explicit = next(all_values.assigned_stmts())
533 except (InferenceError, StopIteration):
534 return default
535 except AttributeError:
536 # not an assignment node
537 # XXX infer?
538 return default
540 # Try our best to detect the exported name.
541 inferred = []
542 try:
543 explicit = next(explicit.infer())
544 except (InferenceError, StopIteration):
545 return default
546 if not isinstance(explicit, (node_classes.Tuple, node_classes.List)):
547 return default
549 def str_const(node) -> bool:
550 return isinstance(node, node_classes.Const) and isinstance(node.value, str)
552 for node in explicit.elts:
553 if str_const(node):
554 inferred.append(node.value)
555 else:
556 try:
557 inferred_node = next(node.infer())
558 except (InferenceError, StopIteration):
559 continue
560 if str_const(inferred_node):
561 inferred.append(inferred_node.value)
562 return inferred
564 def public_names(self):
565 """The list of the names that are publicly available in this module.
567 :returns: The list of public names.
568 :rtype: list(str)
569 """
570 return [name for name in self.keys() if not name.startswith("_")]
572 def bool_value(self, context: InferenceContext | None = None) -> bool:
573 """Determine the boolean value of this node.
575 :returns: The boolean value of this node.
576 For a :class:`Module` this is always ``True``.
577 """
578 return True
580 def get_children(self):
581 yield from self.body
583 def frame(self: _T, *, future: Literal[None, True] = None) -> _T:
584 """The node's frame node.
586 A frame node is a :class:`Module`, :class:`FunctionDef`,
587 :class:`ClassDef` or :class:`Lambda`.
589 :returns: The node itself.
590 """
591 return self
594class GeneratorExp(ComprehensionScope):
595 """Class representing an :class:`ast.GeneratorExp` node.
597 >>> import astroid
598 >>> node = astroid.extract_node('(thing for thing in things if thing)')
599 >>> node
600 <GeneratorExp l.1 at 0x7f23b2e4e400>
601 """
603 _astroid_fields = ("elt", "generators")
604 _other_other_fields = ("locals",)
605 elt: NodeNG
606 """The element that forms the output of the expression."""
608 def __init__(
609 self,
610 lineno: int,
611 col_offset: int,
612 parent: NodeNG,
613 *,
614 end_lineno: int | None,
615 end_col_offset: int | None,
616 ) -> None:
617 self.locals = {}
618 """A map of the name of a local variable to the node defining the local."""
620 self.generators: list[nodes.Comprehension] = []
621 """The generators that are looped through."""
623 super().__init__(
624 lineno=lineno,
625 col_offset=col_offset,
626 end_lineno=end_lineno,
627 end_col_offset=end_col_offset,
628 parent=parent,
629 )
631 def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]) -> None:
632 self.elt = elt
633 self.generators = generators
635 def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
636 """Determine the boolean value of this node.
638 :returns: The boolean value of this node.
639 For a :class:`GeneratorExp` this is always ``True``.
640 """
641 return True
643 def get_children(self):
644 yield self.elt
646 yield from self.generators
649class DictComp(ComprehensionScope):
650 """Class representing an :class:`ast.DictComp` node.
652 >>> import astroid
653 >>> node = astroid.extract_node('{k:v for k, v in things if k > v}')
654 >>> node
655 <DictComp l.1 at 0x7f23b2e41d68>
656 """
658 _astroid_fields = ("key", "value", "generators")
659 _other_other_fields = ("locals",)
660 key: NodeNG
661 """What produces the keys."""
663 value: NodeNG
664 """What produces the values."""
666 def __init__(
667 self,
668 lineno: int,
669 col_offset: int,
670 parent: NodeNG,
671 *,
672 end_lineno: int | None,
673 end_col_offset: int | None,
674 ) -> None:
675 self.locals = {}
676 """A map of the name of a local variable to the node defining the local."""
678 super().__init__(
679 lineno=lineno,
680 col_offset=col_offset,
681 end_lineno=end_lineno,
682 end_col_offset=end_col_offset,
683 parent=parent,
684 )
686 def postinit(
687 self, key: NodeNG, value: NodeNG, generators: list[nodes.Comprehension]
688 ) -> None:
689 self.key = key
690 self.value = value
691 self.generators = generators
693 def bool_value(self, context: InferenceContext | None = None):
694 """Determine the boolean value of this node.
696 :returns: The boolean value of this node.
697 For a :class:`DictComp` this is always :class:`Uninferable`.
698 :rtype: Uninferable
699 """
700 return util.Uninferable
702 def get_children(self):
703 yield self.key
704 yield self.value
706 yield from self.generators
709class SetComp(ComprehensionScope):
710 """Class representing an :class:`ast.SetComp` node.
712 >>> import astroid
713 >>> node = astroid.extract_node('{thing for thing in things if thing}')
714 >>> node
715 <SetComp l.1 at 0x7f23b2e41898>
716 """
718 _astroid_fields = ("elt", "generators")
719 _other_other_fields = ("locals",)
720 elt: NodeNG
721 """The element that forms the output of the expression."""
723 def __init__(
724 self,
725 lineno: int,
726 col_offset: int,
727 parent: NodeNG,
728 *,
729 end_lineno: int | None,
730 end_col_offset: int | None,
731 ) -> None:
732 self.locals = {}
733 """A map of the name of a local variable to the node defining the local."""
735 self.generators: list[nodes.Comprehension] = []
736 """The generators that are looped through."""
738 super().__init__(
739 lineno=lineno,
740 col_offset=col_offset,
741 end_lineno=end_lineno,
742 end_col_offset=end_col_offset,
743 parent=parent,
744 )
746 def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]) -> None:
747 self.elt = elt
748 self.generators = generators
750 def bool_value(self, context: InferenceContext | None = None):
751 """Determine the boolean value of this node.
753 :returns: The boolean value of this node.
754 For a :class:`SetComp` this is always :class:`Uninferable`.
755 :rtype: Uninferable
756 """
757 return util.Uninferable
759 def get_children(self):
760 yield self.elt
762 yield from self.generators
765class ListComp(ComprehensionScope):
766 """Class representing an :class:`ast.ListComp` node.
768 >>> import astroid
769 >>> node = astroid.extract_node('[thing for thing in things if thing]')
770 >>> node
771 <ListComp l.1 at 0x7f23b2e418d0>
772 """
774 _astroid_fields = ("elt", "generators")
775 _other_other_fields = ("locals",)
777 elt: NodeNG
778 """The element that forms the output of the expression."""
780 def __init__(
781 self,
782 lineno: int,
783 col_offset: int,
784 parent: NodeNG,
785 *,
786 end_lineno: int | None,
787 end_col_offset: int | None,
788 ) -> None:
789 self.locals = {}
790 """A map of the name of a local variable to the node defining it."""
792 self.generators: list[nodes.Comprehension] = []
793 """The generators that are looped through."""
795 super().__init__(
796 lineno=lineno,
797 col_offset=col_offset,
798 end_lineno=end_lineno,
799 end_col_offset=end_col_offset,
800 parent=parent,
801 )
803 def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]):
804 self.elt = elt
805 self.generators = generators
807 def bool_value(self, context: InferenceContext | None = None):
808 """Determine the boolean value of this node.
810 :returns: The boolean value of this node.
811 For a :class:`ListComp` this is always :class:`Uninferable`.
812 :rtype: Uninferable
813 """
814 return util.Uninferable
816 def get_children(self):
817 yield self.elt
819 yield from self.generators
822def _infer_decorator_callchain(node):
823 """Detect decorator call chaining and see if the end result is a
824 static or a classmethod.
825 """
826 if not isinstance(node, FunctionDef):
827 return None
828 if not node.parent:
829 return None
830 try:
831 result = next(node.infer_call_result(node.parent), None)
832 except InferenceError:
833 return None
834 if isinstance(result, bases.Instance):
835 result = result._proxied
836 if isinstance(result, ClassDef):
837 if result.is_subtype_of("builtins.classmethod"):
838 return "classmethod"
839 if result.is_subtype_of("builtins.staticmethod"):
840 return "staticmethod"
841 if isinstance(result, FunctionDef):
842 if not result.decorators:
843 return None
844 # Determine if this function is decorated with one of the builtin descriptors we want.
845 for decorator in result.decorators.nodes:
846 if isinstance(decorator, node_classes.Name):
847 if decorator.name in BUILTIN_DESCRIPTORS:
848 return decorator.name
849 if (
850 isinstance(decorator, node_classes.Attribute)
851 and isinstance(decorator.expr, node_classes.Name)
852 and decorator.expr.name == "builtins"
853 and decorator.attrname in BUILTIN_DESCRIPTORS
854 ):
855 return decorator.attrname
856 return None
859class Lambda(_base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG):
860 """Class representing an :class:`ast.Lambda` node.
862 >>> import astroid
863 >>> node = astroid.extract_node('lambda arg: arg + 1')
864 >>> node
865 <Lambda.<lambda> l.1 at 0x7f23b2e41518>
866 """
868 _astroid_fields: ClassVar[tuple[str, ...]] = ("args", "body")
869 _other_other_fields: ClassVar[tuple[str, ...]] = ("locals",)
870 name = "<lambda>"
871 is_lambda = True
872 special_attributes = FunctionModel()
873 """The names of special attributes that this function has."""
875 args: Arguments
876 """The arguments that the function takes."""
878 body: NodeNG
879 """The contents of the function body."""
881 def implicit_parameters(self) -> Literal[0]:
882 return 0
884 @property
885 def type(self) -> Literal["method", "function"]:
886 """Whether this is a method or function.
888 :returns: 'method' if this is a method, 'function' otherwise.
889 """
890 if self.args.arguments and self.args.arguments[0].name == "self":
891 if isinstance(self.parent.scope(), ClassDef):
892 return "method"
893 return "function"
895 def __init__(
896 self,
897 lineno: int,
898 col_offset: int,
899 parent: NodeNG,
900 *,
901 end_lineno: int | None,
902 end_col_offset: int | None,
903 ):
904 self.locals = {}
905 """A map of the name of a local variable to the node defining it."""
907 self.instance_attrs: dict[str, list[NodeNG]] = {}
909 super().__init__(
910 lineno=lineno,
911 col_offset=col_offset,
912 end_lineno=end_lineno,
913 end_col_offset=end_col_offset,
914 parent=parent,
915 )
917 def postinit(self, args: Arguments, body: NodeNG) -> None:
918 self.args = args
919 self.body = body
921 def pytype(self) -> Literal["builtins.instancemethod", "builtins.function"]:
922 """Get the name of the type that this node represents.
924 :returns: The name of the type.
925 """
926 if "method" in self.type:
927 return "builtins.instancemethod"
928 return "builtins.function"
930 def display_type(self) -> str:
931 """A human readable type of this node.
933 :returns: The type of this node.
934 :rtype: str
935 """
936 if "method" in self.type:
937 return "Method"
938 return "Function"
940 def callable(self) -> Literal[True]:
941 """Whether this node defines something that is callable.
943 :returns: Whether this defines something that is callable
944 For a :class:`Lambda` this is always ``True``.
945 """
946 return True
948 def argnames(self) -> list[str]:
949 """Get the names of each of the arguments, including that
950 of the collections of variable-length arguments ("args", "kwargs",
951 etc.), as well as positional-only and keyword-only arguments.
953 :returns: The names of the arguments.
954 :rtype: list(str)
955 """
956 if self.args.arguments: # maybe None with builtin functions
957 names = _rec_get_names(self.args.arguments)
958 else:
959 names = []
960 if self.args.vararg:
961 names.append(self.args.vararg)
962 names += [elt.name for elt in self.args.kwonlyargs]
963 if self.args.kwarg:
964 names.append(self.args.kwarg)
965 return names
967 def infer_call_result(
968 self,
969 caller: SuccessfulInferenceResult | None,
970 context: InferenceContext | None = None,
971 ) -> Iterator[InferenceResult]:
972 """Infer what the function returns when called."""
973 return self.body.infer(context)
975 def scope_lookup(
976 self, node: node_classes.LookupMixIn, name: str, offset: int = 0
977 ) -> tuple[LocalsDictNodeNG, list[NodeNG]]:
978 """Lookup where the given names is assigned.
980 :param node: The node to look for assignments up to.
981 Any assignments after the given node are ignored.
983 :param name: The name to find assignments for.
985 :param offset: The line offset to filter statements up to.
987 :returns: This scope node and the list of assignments associated to the
988 given name according to the scope where it has been found (locals,
989 globals or builtin).
990 """
991 if (self.args.defaults and node in self.args.defaults) or (
992 self.args.kw_defaults and node in self.args.kw_defaults
993 ):
994 frame = self.parent.frame(future=True)
995 # line offset to avoid that def func(f=func) resolve the default
996 # value to the defined function
997 offset = -1
998 else:
999 # check this is not used in function decorators
1000 frame = self
1001 return frame._scope_lookup(node, name, offset)
1003 def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
1004 """Determine the boolean value of this node.
1006 :returns: The boolean value of this node.
1007 For a :class:`Lambda` this is always ``True``.
1008 """
1009 return True
1011 def get_children(self):
1012 yield self.args
1013 yield self.body
1015 def frame(self: _T, *, future: Literal[None, True] = None) -> _T:
1016 """The node's frame node.
1018 A frame node is a :class:`Module`, :class:`FunctionDef`,
1019 :class:`ClassDef` or :class:`Lambda`.
1021 :returns: The node itself.
1022 """
1023 return self
1025 def getattr(
1026 self, name: str, context: InferenceContext | None = None
1027 ) -> list[NodeNG]:
1028 if not name:
1029 raise AttributeInferenceError(target=self, attribute=name, context=context)
1031 found_attrs = []
1032 if name in self.instance_attrs:
1033 found_attrs = self.instance_attrs[name]
1034 if name in self.special_attributes:
1035 found_attrs.append(self.special_attributes.lookup(name))
1036 if found_attrs:
1037 return found_attrs
1038 raise AttributeInferenceError(target=self, attribute=name)
1041class FunctionDef(
1042 _base_nodes.MultiLineBlockNode,
1043 _base_nodes.FilterStmtsBaseNode,
1044 _base_nodes.Statement,
1045 LocalsDictNodeNG,
1046):
1047 """Class representing an :class:`ast.FunctionDef`.
1049 >>> import astroid
1050 >>> node = astroid.extract_node('''
1051 ... def my_func(arg):
1052 ... return arg + 1
1053 ... ''')
1054 >>> node
1055 <FunctionDef.my_func l.2 at 0x7f23b2e71e10>
1056 """
1058 _astroid_fields = ("decorators", "args", "returns", "doc_node", "body")
1059 _multi_line_block_fields = ("body",)
1060 returns = None
1062 decorators: node_classes.Decorators | None
1063 """The decorators that are applied to this method or function."""
1065 doc_node: Const | None
1066 """The doc node associated with this node."""
1068 args: Arguments
1069 """The arguments that the function takes."""
1071 is_function = True
1072 """Whether this node indicates a function.
1074 For a :class:`FunctionDef` this is always ``True``.
1076 :type: bool
1077 """
1078 type_annotation = None
1079 """If present, this will contain the type annotation passed by a type comment
1081 :type: NodeNG or None
1082 """
1083 type_comment_args = None
1084 """
1085 If present, this will contain the type annotation for arguments
1086 passed by a type comment
1087 """
1088 type_comment_returns = None
1089 """If present, this will contain the return type annotation, passed by a type comment"""
1090 # attributes below are set by the builder module or by raw factories
1091 _other_fields = ("name", "position")
1092 _other_other_fields = (
1093 "locals",
1094 "_type",
1095 "type_comment_returns",
1096 "type_comment_args",
1097 )
1098 _type = None
1100 name = "<functiondef>"
1102 is_lambda = True
1104 special_attributes = FunctionModel()
1105 """The names of special attributes that this function has."""
1107 def __init__(
1108 self,
1109 name: str,
1110 lineno: int,
1111 col_offset: int,
1112 parent: NodeNG,
1113 *,
1114 end_lineno: int | None,
1115 end_col_offset: int | None,
1116 ) -> None:
1117 self.name = name
1118 """The name of the function."""
1120 self.locals = {}
1121 """A map of the name of a local variable to the node defining it."""
1123 self.body: list[NodeNG] = []
1124 """The contents of the function body."""
1126 self.instance_attrs: dict[str, list[NodeNG]] = {}
1128 super().__init__(
1129 lineno=lineno,
1130 col_offset=col_offset,
1131 end_lineno=end_lineno,
1132 end_col_offset=end_col_offset,
1133 parent=parent,
1134 )
1135 if parent and not isinstance(parent, Unknown):
1136 frame = parent.frame(future=True)
1137 frame.set_local(name, self)
1139 def postinit(
1140 self,
1141 args: Arguments,
1142 body: list[NodeNG],
1143 decorators: node_classes.Decorators | None = None,
1144 returns=None,
1145 type_comment_returns=None,
1146 type_comment_args=None,
1147 *,
1148 position: Position | None = None,
1149 doc_node: Const | None = None,
1150 ):
1151 """Do some setup after initialisation.
1153 :param args: The arguments that the function takes.
1155 :param body: The contents of the function body.
1157 :param decorators: The decorators that are applied to this
1158 method or function.
1159 :params type_comment_returns:
1160 The return type annotation passed via a type comment.
1161 :params type_comment_args:
1162 The args type annotation passed via a type comment.
1163 :params position:
1164 Position of function keyword(s) and name.
1165 :param doc_node:
1166 The doc node associated with this node.
1167 """
1168 self.args = args
1169 self.body = body
1170 self.decorators = decorators
1171 self.returns = returns
1172 self.type_comment_returns = type_comment_returns
1173 self.type_comment_args = type_comment_args
1174 self.position = position
1175 self.doc_node = doc_node
1177 @cached_property
1178 def extra_decorators(self) -> list[node_classes.Call]:
1179 """The extra decorators that this function can have.
1181 Additional decorators are considered when they are used as
1182 assignments, as in ``method = staticmethod(method)``.
1183 The property will return all the callables that are used for
1184 decoration.
1185 """
1186 frame = self.parent.frame(future=True)
1187 if not isinstance(frame, ClassDef):
1188 return []
1190 decorators: list[node_classes.Call] = []
1191 for assign in frame._assign_nodes_in_scope:
1192 if isinstance(assign.value, node_classes.Call) and isinstance(
1193 assign.value.func, node_classes.Name
1194 ):
1195 for assign_node in assign.targets:
1196 if not isinstance(assign_node, node_classes.AssignName):
1197 # Support only `name = callable(name)`
1198 continue
1200 if assign_node.name != self.name:
1201 # Interested only in the assignment nodes that
1202 # decorates the current method.
1203 continue
1204 try:
1205 meth = frame[self.name]
1206 except KeyError:
1207 continue
1208 else:
1209 # Must be a function and in the same frame as the
1210 # original method.
1211 if (
1212 isinstance(meth, FunctionDef)
1213 and assign_node.frame(future=True) == frame
1214 ):
1215 decorators.append(assign.value)
1216 return decorators
1218 def pytype(self) -> Literal["builtins.instancemethod", "builtins.function"]:
1219 """Get the name of the type that this node represents.
1221 :returns: The name of the type.
1222 """
1223 if "method" in self.type:
1224 return "builtins.instancemethod"
1225 return "builtins.function"
1227 def display_type(self) -> str:
1228 """A human readable type of this node.
1230 :returns: The type of this node.
1231 :rtype: str
1232 """
1233 if "method" in self.type:
1234 return "Method"
1235 return "Function"
1237 def callable(self) -> Literal[True]:
1238 return True
1240 def argnames(self) -> list[str]:
1241 """Get the names of each of the arguments, including that
1242 of the collections of variable-length arguments ("args", "kwargs",
1243 etc.), as well as positional-only and keyword-only arguments.
1245 :returns: The names of the arguments.
1246 :rtype: list(str)
1247 """
1248 if self.args.arguments: # maybe None with builtin functions
1249 names = _rec_get_names(self.args.arguments)
1250 else:
1251 names = []
1252 if self.args.vararg:
1253 names.append(self.args.vararg)
1254 names += [elt.name for elt in self.args.kwonlyargs]
1255 if self.args.kwarg:
1256 names.append(self.args.kwarg)
1257 return names
1259 def getattr(
1260 self, name: str, context: InferenceContext | None = None
1261 ) -> list[NodeNG]:
1262 if not name:
1263 raise AttributeInferenceError(target=self, attribute=name, context=context)
1265 found_attrs = []
1266 if name in self.instance_attrs:
1267 found_attrs = self.instance_attrs[name]
1268 if name in self.special_attributes:
1269 found_attrs.append(self.special_attributes.lookup(name))
1270 if found_attrs:
1271 return found_attrs
1272 raise AttributeInferenceError(target=self, attribute=name)
1274 @cached_property
1275 def type(self) -> str: # pylint: disable=too-many-return-statements # noqa: C901
1276 """The function type for this node.
1278 Possible values are: method, function, staticmethod, classmethod.
1279 """
1280 for decorator in self.extra_decorators:
1281 if decorator.func.name in BUILTIN_DESCRIPTORS:
1282 return decorator.func.name
1284 frame = self.parent.frame(future=True)
1285 type_name = "function"
1286 if isinstance(frame, ClassDef):
1287 if self.name == "__new__":
1288 return "classmethod"
1289 if self.name == "__init_subclass__":
1290 return "classmethod"
1291 if self.name == "__class_getitem__":
1292 return "classmethod"
1294 type_name = "method"
1296 if not self.decorators:
1297 return type_name
1299 for node in self.decorators.nodes:
1300 if isinstance(node, node_classes.Name):
1301 if node.name in BUILTIN_DESCRIPTORS:
1302 return node.name
1303 if (
1304 isinstance(node, node_classes.Attribute)
1305 and isinstance(node.expr, node_classes.Name)
1306 and node.expr.name == "builtins"
1307 and node.attrname in BUILTIN_DESCRIPTORS
1308 ):
1309 return node.attrname
1311 if isinstance(node, node_classes.Call):
1312 # Handle the following case:
1313 # @some_decorator(arg1, arg2)
1314 # def func(...)
1315 #
1316 try:
1317 current = next(node.func.infer())
1318 except (InferenceError, StopIteration):
1319 continue
1320 _type = _infer_decorator_callchain(current)
1321 if _type is not None:
1322 return _type
1324 try:
1325 for inferred in node.infer():
1326 # Check to see if this returns a static or a class method.
1327 _type = _infer_decorator_callchain(inferred)
1328 if _type is not None:
1329 return _type
1331 if not isinstance(inferred, ClassDef):
1332 continue
1333 for ancestor in inferred.ancestors():
1334 if not isinstance(ancestor, ClassDef):
1335 continue
1336 if ancestor.is_subtype_of("builtins.classmethod"):
1337 return "classmethod"
1338 if ancestor.is_subtype_of("builtins.staticmethod"):
1339 return "staticmethod"
1340 except InferenceError:
1341 pass
1342 return type_name
1344 @cached_property
1345 def fromlineno(self) -> int:
1346 """The first line that this node appears on in the source code.
1348 Can also return 0 if the line can not be determined.
1349 """
1350 # lineno is the line number of the first decorator, we want the def
1351 # statement lineno. Similar to 'ClassDef.fromlineno'
1352 lineno = self.lineno or 0
1353 if self.decorators is not None:
1354 lineno += sum(
1355 node.tolineno - (node.lineno or 0) + 1 for node in self.decorators.nodes
1356 )
1358 return lineno or 0
1360 @cached_property
1361 def blockstart_tolineno(self):
1362 """The line on which the beginning of this block ends.
1364 :type: int
1365 """
1366 return self.args.tolineno
1368 def implicit_parameters(self) -> Literal[0, 1]:
1369 return 1 if self.is_bound() else 0
1371 def block_range(self, lineno: int) -> tuple[int, int]:
1372 """Get a range from the given line number to where this node ends.
1374 :param lineno: Unused.
1376 :returns: The range of line numbers that this node belongs to,
1377 """
1378 return self.fromlineno, self.tolineno
1380 def igetattr(
1381 self, name: str, context: InferenceContext | None = None
1382 ) -> Iterator[InferenceResult]:
1383 """Inferred getattr, which returns an iterator of inferred statements."""
1384 try:
1385 return bases._infer_stmts(self.getattr(name, context), context, frame=self)
1386 except AttributeInferenceError as error:
1387 raise InferenceError(
1388 str(error), target=self, attribute=name, context=context
1389 ) from error
1391 def is_method(self) -> bool:
1392 """Check if this function node represents a method.
1394 :returns: Whether this is a method.
1395 """
1396 # check we are defined in a ClassDef, because this is usually expected
1397 # (e.g. pylint...) when is_method() return True
1398 return self.type != "function" and isinstance(
1399 self.parent.frame(future=True), ClassDef
1400 )
1402 def decoratornames(self, context: InferenceContext | None = None) -> set[str]:
1403 """Get the qualified names of each of the decorators on this function.
1405 :param context:
1406 An inference context that can be passed to inference functions
1407 :returns: The names of the decorators.
1408 """
1409 result = set()
1410 decoratornodes = []
1411 if self.decorators is not None:
1412 decoratornodes += self.decorators.nodes
1413 decoratornodes += self.extra_decorators
1414 for decnode in decoratornodes:
1415 try:
1416 for infnode in decnode.infer(context=context):
1417 result.add(infnode.qname())
1418 except InferenceError:
1419 continue
1420 return result
1422 def is_bound(self) -> bool:
1423 """Check if the function is bound to an instance or class.
1425 :returns: Whether the function is bound to an instance or class.
1426 """
1427 return self.type in {"method", "classmethod"}
1429 def is_abstract(self, pass_is_abstract=True, any_raise_is_abstract=False) -> bool:
1430 """Check if the method is abstract.
1432 A method is considered abstract if any of the following is true:
1433 * The only statement is 'raise NotImplementedError'
1434 * The only statement is 'raise <SomeException>' and any_raise_is_abstract is True
1435 * The only statement is 'pass' and pass_is_abstract is True
1436 * The method is annotated with abc.astractproperty/abc.abstractmethod
1438 :returns: Whether the method is abstract.
1439 """
1440 if self.decorators:
1441 for node in self.decorators.nodes:
1442 try:
1443 inferred = next(node.infer())
1444 except (InferenceError, StopIteration):
1445 continue
1446 if inferred and inferred.qname() in {
1447 "abc.abstractproperty",
1448 "abc.abstractmethod",
1449 }:
1450 return True
1452 for child_node in self.body:
1453 if isinstance(child_node, node_classes.Raise):
1454 if any_raise_is_abstract:
1455 return True
1456 if child_node.raises_not_implemented():
1457 return True
1458 return pass_is_abstract and isinstance(child_node, node_classes.Pass)
1459 # empty function is the same as function with a single "pass" statement
1460 if pass_is_abstract:
1461 return True
1463 return False
1465 def is_generator(self) -> bool:
1466 """Check if this is a generator function.
1468 :returns: Whether this is a generator function.
1469 """
1470 return bool(next(self._get_yield_nodes_skip_lambdas(), False))
1472 def infer_yield_result(self, context: InferenceContext | None = None):
1473 """Infer what the function yields when called
1475 :returns: What the function yields
1476 :rtype: iterable(NodeNG or Uninferable) or None
1477 """
1478 # pylint: disable=not-an-iterable
1479 # https://github.com/pylint-dev/astroid/issues/1015
1480 for yield_ in self.nodes_of_class(node_classes.Yield):
1481 if yield_.value is None:
1482 const = node_classes.Const(None)
1483 const.parent = yield_
1484 const.lineno = yield_.lineno
1485 yield const
1486 elif yield_.scope() == self:
1487 yield from yield_.value.infer(context=context)
1489 def infer_call_result(
1490 self,
1491 caller: SuccessfulInferenceResult | None,
1492 context: InferenceContext | None = None,
1493 ) -> Iterator[InferenceResult]:
1494 """Infer what the function returns when called."""
1495 if self.is_generator():
1496 if isinstance(self, AsyncFunctionDef):
1497 generator_cls: type[bases.Generator] = bases.AsyncGenerator
1498 else:
1499 generator_cls = bases.Generator
1500 result = generator_cls(self, generator_initial_context=context)
1501 yield result
1502 return
1503 # This is really a gigantic hack to work around metaclass generators
1504 # that return transient class-generating functions. Pylint's AST structure
1505 # cannot handle a base class object that is only used for calling __new__,
1506 # but does not contribute to the inheritance structure itself. We inject
1507 # a fake class into the hierarchy here for several well-known metaclass
1508 # generators, and filter it out later.
1509 if (
1510 self.name == "with_metaclass"
1511 and caller is not None
1512 and self.args.args
1513 and len(self.args.args) == 1
1514 and self.args.vararg is not None
1515 ):
1516 if isinstance(caller.args, Arguments):
1517 assert caller.args.args is not None
1518 metaclass = next(caller.args.args[0].infer(context), None)
1519 elif isinstance(caller.args, list):
1520 metaclass = next(caller.args[0].infer(context), None)
1521 else:
1522 raise TypeError( # pragma: no cover
1523 f"caller.args was neither Arguments nor list; got {type(caller.args)}"
1524 )
1525 if isinstance(metaclass, ClassDef):
1526 try:
1527 class_bases = [
1528 # Find the first non-None inferred base value
1529 next(
1530 b
1531 for b in arg.infer(
1532 context=context.clone() if context else context
1533 )
1534 if not (isinstance(b, Const) and b.value is None)
1535 )
1536 for arg in caller.args[1:]
1537 ]
1538 except StopIteration as e:
1539 raise InferenceError(node=caller.args[1:], context=context) from e
1540 new_class = ClassDef(
1541 name="temporary_class",
1542 lineno=0,
1543 col_offset=0,
1544 end_lineno=0,
1545 end_col_offset=0,
1546 parent=self,
1547 )
1548 new_class.hide = True
1549 new_class.postinit(
1550 bases=[
1551 base
1552 for base in class_bases
1553 if not isinstance(base, util.UninferableBase)
1554 ],
1555 body=[],
1556 decorators=None,
1557 metaclass=metaclass,
1558 )
1559 yield new_class
1560 return
1561 returns = self._get_return_nodes_skip_functions()
1563 first_return = next(returns, None)
1564 if not first_return:
1565 if self.body:
1566 if self.is_abstract(pass_is_abstract=True, any_raise_is_abstract=True):
1567 yield util.Uninferable
1568 else:
1569 yield node_classes.Const(None)
1570 return
1572 raise InferenceError("The function does not have any return statements")
1574 for returnnode in itertools.chain((first_return,), returns):
1575 if returnnode.value is None:
1576 yield node_classes.Const(None)
1577 else:
1578 try:
1579 yield from returnnode.value.infer(context)
1580 except InferenceError:
1581 yield util.Uninferable
1583 def bool_value(self, context: InferenceContext | None = None) -> bool:
1584 """Determine the boolean value of this node.
1586 :returns: The boolean value of this node.
1587 For a :class:`FunctionDef` this is always ``True``.
1588 """
1589 return True
1591 def get_children(self):
1592 if self.decorators is not None:
1593 yield self.decorators
1595 yield self.args
1597 if self.returns is not None:
1598 yield self.returns
1600 yield from self.body
1602 def scope_lookup(
1603 self, node: node_classes.LookupMixIn, name: str, offset: int = 0
1604 ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]:
1605 """Lookup where the given name is assigned."""
1606 if name == "__class__":
1607 # __class__ is an implicit closure reference created by the compiler
1608 # if any methods in a class body refer to either __class__ or super.
1609 # In our case, we want to be able to look it up in the current scope
1610 # when `__class__` is being used.
1611 frame = self.parent.frame(future=True)
1612 if isinstance(frame, ClassDef):
1613 return self, [frame]
1615 if (self.args.defaults and node in self.args.defaults) or (
1616 self.args.kw_defaults and node in self.args.kw_defaults
1617 ):
1618 frame = self.parent.frame(future=True)
1619 # line offset to avoid that def func(f=func) resolve the default
1620 # value to the defined function
1621 offset = -1
1622 else:
1623 # check this is not used in function decorators
1624 frame = self
1625 return frame._scope_lookup(node, name, offset)
1627 def frame(self: _T, *, future: Literal[None, True] = None) -> _T:
1628 """The node's frame node.
1630 A frame node is a :class:`Module`, :class:`FunctionDef`,
1631 :class:`ClassDef` or :class:`Lambda`.
1633 :returns: The node itself.
1634 """
1635 return self
1638class AsyncFunctionDef(FunctionDef):
1639 """Class representing an :class:`ast.FunctionDef` node.
1641 A :class:`AsyncFunctionDef` is an asynchronous function
1642 created with the `async` keyword.
1644 >>> import astroid
1645 >>> node = astroid.extract_node('''
1646 async def func(things):
1647 async for thing in things:
1648 print(thing)
1649 ''')
1650 >>> node
1651 <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8>
1652 >>> node.body[0]
1653 <AsyncFor l.3 at 0x7f23b2e417b8>
1654 """
1657def _rec_get_names(args, names: list[str] | None = None) -> list[str]:
1658 """return a list of all argument names"""
1659 if names is None:
1660 names = []
1661 for arg in args:
1662 if isinstance(arg, node_classes.Tuple):
1663 _rec_get_names(arg.elts, names)
1664 else:
1665 names.append(arg.name)
1666 return names
1669def _is_metaclass(klass, seen=None) -> bool:
1670 """Return if the given class can be
1671 used as a metaclass.
1672 """
1673 if klass.name == "type":
1674 return True
1675 if seen is None:
1676 seen = set()
1677 for base in klass.bases:
1678 try:
1679 for baseobj in base.infer():
1680 baseobj_name = baseobj.qname()
1681 if baseobj_name in seen:
1682 continue
1684 seen.add(baseobj_name)
1685 if isinstance(baseobj, bases.Instance):
1686 # not abstract
1687 return False
1688 if baseobj is klass:
1689 continue
1690 if not isinstance(baseobj, ClassDef):
1691 continue
1692 if baseobj._type == "metaclass":
1693 return True
1694 if _is_metaclass(baseobj, seen):
1695 return True
1696 except InferenceError:
1697 continue
1698 return False
1701def _class_type(klass, ancestors=None):
1702 """return a ClassDef node type to differ metaclass and exception
1703 from 'regular' classes
1704 """
1705 # XXX we have to store ancestors in case we have an ancestor loop
1706 if klass._type is not None:
1707 return klass._type
1708 if _is_metaclass(klass):
1709 klass._type = "metaclass"
1710 elif klass.name.endswith("Exception"):
1711 klass._type = "exception"
1712 else:
1713 if ancestors is None:
1714 ancestors = set()
1715 klass_name = klass.qname()
1716 if klass_name in ancestors:
1717 # XXX we are in loop ancestors, and have found no type
1718 klass._type = "class"
1719 return "class"
1720 ancestors.add(klass_name)
1721 for base in klass.ancestors(recurs=False):
1722 name = _class_type(base, ancestors)
1723 if name != "class":
1724 if name == "metaclass" and not _is_metaclass(klass):
1725 # don't propagate it if the current class
1726 # can't be a metaclass
1727 continue
1728 klass._type = base.type
1729 break
1730 if klass._type is None:
1731 klass._type = "class"
1732 return klass._type
1735def get_wrapping_class(node):
1736 """Get the class that wraps the given node.
1738 We consider that a class wraps a node if the class
1739 is a parent for the said node.
1741 :returns: The class that wraps the given node
1742 :rtype: ClassDef or None
1743 """
1745 klass = node.frame(future=True)
1746 while klass is not None and not isinstance(klass, ClassDef):
1747 if klass.parent is None:
1748 klass = None
1749 else:
1750 klass = klass.parent.frame(future=True)
1751 return klass
1754class ClassDef(
1755 _base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG, _base_nodes.Statement
1756):
1757 """Class representing an :class:`ast.ClassDef` node.
1759 >>> import astroid
1760 >>> node = astroid.extract_node('''
1761 class Thing:
1762 def my_meth(self, arg):
1763 return arg + self.offset
1764 ''')
1765 >>> node
1766 <ClassDef.Thing l.2 at 0x7f23b2e9e748>
1767 """
1769 # some of the attributes below are set by the builder module or
1770 # by a raw factories
1772 # a dictionary of class instances attributes
1773 _astroid_fields = ("decorators", "bases", "keywords", "doc_node", "body") # name
1775 decorators = None
1776 """The decorators that are applied to this class.
1778 :type: Decorators or None
1779 """
1780 special_attributes = ClassModel()
1781 """The names of special attributes that this class has.
1783 :type: objectmodel.ClassModel
1784 """
1786 _type = None
1787 _metaclass: NodeNG | None = None
1788 _metaclass_hack = False
1789 hide = False
1790 type = property(
1791 _class_type,
1792 doc=(
1793 "The class type for this node.\n\n"
1794 "Possible values are: class, metaclass, exception.\n\n"
1795 ":type: str"
1796 ),
1797 )
1798 _other_fields = ("name", "is_dataclass", "position")
1799 _other_other_fields = ("locals", "_newstyle")
1800 _newstyle: bool | None = None
1802 def __init__(
1803 self,
1804 name: str,
1805 lineno: int,
1806 col_offset: int,
1807 parent: NodeNG,
1808 *,
1809 end_lineno: int | None,
1810 end_col_offset: int | None,
1811 ) -> None:
1812 self.instance_attrs: dict[str, NodeNG] = {}
1813 self.locals = {}
1814 """A map of the name of a local variable to the node defining it."""
1816 self.keywords: list[node_classes.Keyword] = []
1817 """The keywords given to the class definition.
1819 This is usually for :pep:`3115` style metaclass declaration.
1820 """
1822 self.bases: list[SuccessfulInferenceResult] = []
1823 """What the class inherits from."""
1825 self.body: list[NodeNG] = []
1826 """The contents of the class body."""
1828 self.name = name
1829 """The name of the class."""
1831 self.decorators = None
1832 """The decorators that are applied to this class."""
1834 self.doc_node: Const | None = None
1835 """The doc node associated with this node."""
1837 self.is_dataclass: bool = False
1838 """Whether this class is a dataclass."""
1840 super().__init__(
1841 lineno=lineno,
1842 col_offset=col_offset,
1843 end_lineno=end_lineno,
1844 end_col_offset=end_col_offset,
1845 parent=parent,
1846 )
1847 if parent and not isinstance(parent, Unknown):
1848 parent.frame(future=True).set_local(name, self)
1850 for local_name, node in self.implicit_locals():
1851 self.add_local_node(node, local_name)
1853 infer_binary_op: ClassVar[InferBinaryOp[ClassDef]]
1855 def implicit_parameters(self) -> Literal[1]:
1856 return 1
1858 def implicit_locals(self):
1859 """Get implicitly defined class definition locals.
1861 :returns: the the name and Const pair for each local
1862 :rtype: tuple(tuple(str, node_classes.Const), ...)
1863 """
1864 locals_ = (("__module__", self.special_attributes.attr___module__),)
1865 # __qualname__ is defined in PEP3155
1866 locals_ += (("__qualname__", self.special_attributes.attr___qualname__),)
1867 return locals_
1869 # pylint: disable=redefined-outer-name
1870 def postinit(
1871 self,
1872 bases: list[SuccessfulInferenceResult],
1873 body: list[NodeNG],
1874 decorators: node_classes.Decorators | None,
1875 newstyle: bool | None = None,
1876 metaclass: NodeNG | None = None,
1877 keywords: list[node_classes.Keyword] | None = None,
1878 *,
1879 position: Position | None = None,
1880 doc_node: Const | None = None,
1881 ) -> None:
1882 if keywords is not None:
1883 self.keywords = keywords
1884 self.bases = bases
1885 self.body = body
1886 self.decorators = decorators
1887 self._newstyle = newstyle
1888 self._metaclass = metaclass
1889 self.position = position
1890 self.doc_node = doc_node
1892 def _newstyle_impl(self, context: InferenceContext | None = None):
1893 if context is None:
1894 context = InferenceContext()
1895 if self._newstyle is not None:
1896 return self._newstyle
1897 for base in self.ancestors(recurs=False, context=context):
1898 if base._newstyle_impl(context):
1899 self._newstyle = True
1900 break
1901 klass = self.declared_metaclass()
1902 # could be any callable, we'd need to infer the result of klass(name,
1903 # bases, dict). punt if it's not a class node.
1904 if klass is not None and isinstance(klass, ClassDef):
1905 self._newstyle = klass._newstyle_impl(context)
1906 if self._newstyle is None:
1907 self._newstyle = False
1908 return self._newstyle
1910 _newstyle = None
1911 newstyle = property(
1912 _newstyle_impl,
1913 doc=("Whether this is a new style class or not\n\n" ":type: bool or None"),
1914 )
1916 @cached_property
1917 def fromlineno(self) -> int:
1918 """The first line that this node appears on in the source code.
1920 Can also return 0 if the line can not be determined.
1921 """
1922 if IS_PYPY and PY38 and not PYPY_7_3_11_PLUS:
1923 # For Python < 3.8 the lineno is the line number of the first decorator.
1924 # We want the class statement lineno. Similar to 'FunctionDef.fromlineno'
1925 # PyPy (3.8): Fixed with version v7.3.11
1926 lineno = self.lineno or 0
1927 if self.decorators is not None:
1928 lineno += sum(
1929 node.tolineno - (node.lineno or 0) + 1
1930 for node in self.decorators.nodes
1931 )
1933 return lineno or 0
1934 return super().fromlineno
1936 @cached_property
1937 def blockstart_tolineno(self):
1938 """The line on which the beginning of this block ends.
1940 :type: int
1941 """
1942 if self.bases:
1943 return self.bases[-1].tolineno
1945 return self.fromlineno
1947 def block_range(self, lineno: int) -> tuple[int, int]:
1948 """Get a range from the given line number to where this node ends.
1950 :param lineno: Unused.
1952 :returns: The range of line numbers that this node belongs to,
1953 """
1954 return self.fromlineno, self.tolineno
1956 def pytype(self) -> Literal["builtins.type", "builtins.classobj"]:
1957 """Get the name of the type that this node represents.
1959 :returns: The name of the type.
1960 """
1961 if self.newstyle:
1962 return "builtins.type"
1963 return "builtins.classobj"
1965 def display_type(self) -> str:
1966 """A human readable type of this node.
1968 :returns: The type of this node.
1969 :rtype: str
1970 """
1971 return "Class"
1973 def callable(self) -> bool:
1974 """Whether this node defines something that is callable.
1976 :returns: Whether this defines something that is callable.
1977 For a :class:`ClassDef` this is always ``True``.
1978 """
1979 return True
1981 def is_subtype_of(self, type_name, context: InferenceContext | None = None) -> bool:
1982 """Whether this class is a subtype of the given type.
1984 :param type_name: The name of the type of check against.
1985 :type type_name: str
1987 :returns: Whether this class is a subtype of the given type.
1988 """
1989 if self.qname() == type_name:
1990 return True
1992 return any(anc.qname() == type_name for anc in self.ancestors(context=context))
1994 def _infer_type_call(self, caller, context):
1995 try:
1996 name_node = next(caller.args[0].infer(context))
1997 except StopIteration as e:
1998 raise InferenceError(node=caller.args[0], context=context) from e
1999 if isinstance(name_node, node_classes.Const) and isinstance(
2000 name_node.value, str
2001 ):
2002 name = name_node.value
2003 else:
2004 return util.Uninferable
2006 result = ClassDef(
2007 name,
2008 lineno=0,
2009 col_offset=0,
2010 end_lineno=0,
2011 end_col_offset=0,
2012 parent=Unknown(),
2013 )
2015 # Get the bases of the class.
2016 try:
2017 class_bases = next(caller.args[1].infer(context))
2018 except StopIteration as e:
2019 raise InferenceError(node=caller.args[1], context=context) from e
2020 if isinstance(class_bases, (node_classes.Tuple, node_classes.List)):
2021 bases = []
2022 for base in class_bases.itered():
2023 inferred = next(base.infer(context=context), None)
2024 if inferred:
2025 bases.append(
2026 node_classes.EvaluatedObject(original=base, value=inferred)
2027 )
2028 result.bases = bases
2029 else:
2030 # There is currently no AST node that can represent an 'unknown'
2031 # node (Uninferable is not an AST node), therefore we simply return Uninferable here
2032 # although we know at least the name of the class.
2033 return util.Uninferable
2035 # Get the members of the class
2036 try:
2037 members = next(caller.args[2].infer(context))
2038 except (InferenceError, StopIteration):
2039 members = None
2041 if members and isinstance(members, node_classes.Dict):
2042 for attr, value in members.items:
2043 if isinstance(attr, node_classes.Const) and isinstance(attr.value, str):
2044 result.locals[attr.value] = [value]
2046 result.parent = caller.parent
2047 return result
2049 def infer_call_result(
2050 self,
2051 caller: SuccessfulInferenceResult | None,
2052 context: InferenceContext | None = None,
2053 ) -> Iterator[InferenceResult]:
2054 """infer what a class is returning when called"""
2055 if self.is_subtype_of("builtins.type", context) and len(caller.args) == 3:
2056 result = self._infer_type_call(caller, context)
2057 yield result
2058 return
2060 dunder_call = None
2061 try:
2062 metaclass = self.metaclass(context=context)
2063 if metaclass is not None:
2064 # Only get __call__ if it's defined locally for the metaclass.
2065 # Otherwise we will find ObjectModel.__call__ which will
2066 # return an instance of the metaclass. Instantiating the class is
2067 # handled later.
2068 if "__call__" in metaclass.locals:
2069 dunder_call = next(metaclass.igetattr("__call__", context))
2070 except (AttributeInferenceError, StopIteration):
2071 pass
2073 if dunder_call and dunder_call.qname() != "builtins.type.__call__":
2074 # Call type.__call__ if not set metaclass
2075 # (since type is the default metaclass)
2076 context = bind_context_to_node(context, self)
2077 context.callcontext.callee = dunder_call
2078 yield from dunder_call.infer_call_result(caller, context)
2079 else:
2080 yield self.instantiate_class()
2082 def scope_lookup(
2083 self, node: node_classes.LookupMixIn, name: str, offset: int = 0
2084 ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]:
2085 """Lookup where the given name is assigned.
2087 :param node: The node to look for assignments up to.
2088 Any assignments after the given node are ignored.
2090 :param name: The name to find assignments for.
2092 :param offset: The line offset to filter statements up to.
2094 :returns: This scope node and the list of assignments associated to the
2095 given name according to the scope where it has been found (locals,
2096 globals or builtin).
2097 """
2098 # If the name looks like a builtin name, just try to look
2099 # into the upper scope of this class. We might have a
2100 # decorator that it's poorly named after a builtin object
2101 # inside this class.
2102 lookup_upper_frame = (
2103 isinstance(node.parent, node_classes.Decorators)
2104 and name in AstroidManager().builtins_module
2105 )
2106 if (
2107 any(node == base or base.parent_of(node) for base in self.bases)
2108 or lookup_upper_frame
2109 ):
2110 # Handle the case where we have either a name
2111 # in the bases of a class, which exists before
2112 # the actual definition or the case where we have
2113 # a Getattr node, with that name.
2114 #
2115 # name = ...
2116 # class A(name):
2117 # def name(self): ...
2118 #
2119 # import name
2120 # class A(name.Name):
2121 # def name(self): ...
2123 frame = self.parent.frame(future=True)
2124 # line offset to avoid that class A(A) resolve the ancestor to
2125 # the defined class
2126 offset = -1
2127 else:
2128 frame = self
2129 return frame._scope_lookup(node, name, offset)
2131 @property
2132 def basenames(self):
2133 """The names of the parent classes
2135 Names are given in the order they appear in the class definition.
2137 :type: list(str)
2138 """
2139 return [bnode.as_string() for bnode in self.bases]
2141 def ancestors(
2142 self, recurs: bool = True, context: InferenceContext | None = None
2143 ) -> Generator[ClassDef, None, None]:
2144 """Iterate over the base classes in prefixed depth first order.
2146 :param recurs: Whether to recurse or return direct ancestors only.
2148 :returns: The base classes
2149 """
2150 # FIXME: should be possible to choose the resolution order
2151 # FIXME: inference make infinite loops possible here
2152 yielded = {self}
2153 if context is None:
2154 context = InferenceContext()
2155 if not self.bases and self.qname() != "builtins.object":
2156 yield builtin_lookup("object")[1][0]
2157 return
2159 for stmt in self.bases:
2160 with context.restore_path():
2161 try:
2162 for baseobj in stmt.infer(context):
2163 if not isinstance(baseobj, ClassDef):
2164 if isinstance(baseobj, bases.Instance):
2165 baseobj = baseobj._proxied
2166 else:
2167 continue
2168 if not baseobj.hide:
2169 if baseobj in yielded:
2170 continue
2171 yielded.add(baseobj)
2172 yield baseobj
2173 if not recurs:
2174 continue
2175 for grandpa in baseobj.ancestors(recurs=True, context=context):
2176 if grandpa is self:
2177 # This class is the ancestor of itself.
2178 break
2179 if grandpa in yielded:
2180 continue
2181 yielded.add(grandpa)
2182 yield grandpa
2183 except InferenceError:
2184 continue
2186 def local_attr_ancestors(self, name, context: InferenceContext | None = None):
2187 """Iterate over the parents that define the given name.
2189 :param name: The name to find definitions for.
2190 :type name: str
2192 :returns: The parents that define the given name.
2193 :rtype: iterable(NodeNG)
2194 """
2195 # Look up in the mro if we can. This will result in the
2196 # attribute being looked up just as Python does it.
2197 try:
2198 ancestors: Iterable[ClassDef] = self.mro(context)[1:]
2199 except MroError:
2200 # Fallback to use ancestors, we can't determine
2201 # a sane MRO.
2202 ancestors = self.ancestors(context=context)
2203 for astroid in ancestors:
2204 if name in astroid:
2205 yield astroid
2207 def instance_attr_ancestors(self, name, context: InferenceContext | None = None):
2208 """Iterate over the parents that define the given name as an attribute.
2210 :param name: The name to find definitions for.
2211 :type name: str
2213 :returns: The parents that define the given name as
2214 an instance attribute.
2215 :rtype: iterable(NodeNG)
2216 """
2217 for astroid in self.ancestors(context=context):
2218 if name in astroid.instance_attrs:
2219 yield astroid
2221 def has_base(self, node) -> bool:
2222 """Whether this class directly inherits from the given node.
2224 :param node: The node to check for.
2225 :type node: NodeNG
2227 :returns: Whether this class directly inherits from the given node.
2228 """
2229 return node in self.bases
2231 def local_attr(self, name, context: InferenceContext | None = None):
2232 """Get the list of assign nodes associated to the given name.
2234 Assignments are looked for in both this class and in parents.
2236 :returns: The list of assignments to the given name.
2237 :rtype: list(NodeNG)
2239 :raises AttributeInferenceError: If no attribute with this name
2240 can be found in this class or parent classes.
2241 """
2242 result = []
2243 if name in self.locals:
2244 result = self.locals[name]
2245 else:
2246 class_node = next(self.local_attr_ancestors(name, context), None)
2247 if class_node:
2248 result = class_node.locals[name]
2249 result = [n for n in result if not isinstance(n, node_classes.DelAttr)]
2250 if result:
2251 return result
2252 raise AttributeInferenceError(target=self, attribute=name, context=context)
2254 def instance_attr(self, name, context: InferenceContext | None = None):
2255 """Get the list of nodes associated to the given attribute name.
2257 Assignments are looked for in both this class and in parents.
2259 :returns: The list of assignments to the given name.
2260 :rtype: list(NodeNG)
2262 :raises AttributeInferenceError: If no attribute with this name
2263 can be found in this class or parent classes.
2264 """
2265 # Return a copy, so we don't modify self.instance_attrs,
2266 # which could lead to infinite loop.
2267 values = list(self.instance_attrs.get(name, []))
2268 # get all values from parents
2269 for class_node in self.instance_attr_ancestors(name, context):
2270 values += class_node.instance_attrs[name]
2271 values = [n for n in values if not isinstance(n, node_classes.DelAttr)]
2272 if values:
2273 return values
2274 raise AttributeInferenceError(target=self, attribute=name, context=context)
2276 def instantiate_class(self) -> bases.Instance:
2277 """Get an :class:`Instance` of the :class:`ClassDef` node.
2279 :returns: An :class:`Instance` of the :class:`ClassDef` node
2280 """
2281 from astroid import objects # pylint: disable=import-outside-toplevel
2283 try:
2284 if any(cls.name in EXCEPTION_BASE_CLASSES for cls in self.mro()):
2285 # Subclasses of exceptions can be exception instances
2286 return objects.ExceptionInstance(self)
2287 except MroError:
2288 pass
2289 return bases.Instance(self)
2291 def getattr(
2292 self,
2293 name: str,
2294 context: InferenceContext | None = None,
2295 class_context: bool = True,
2296 ) -> list[SuccessfulInferenceResult]:
2297 """Get an attribute from this class, using Python's attribute semantic.
2299 This method doesn't look in the :attr:`instance_attrs` dictionary
2300 since it is done by an :class:`Instance` proxy at inference time.
2301 It may return an :class:`Uninferable` object if
2302 the attribute has not been
2303 found, but a ``__getattr__`` or ``__getattribute__`` method is defined.
2304 If ``class_context`` is given, then it is considered that the
2305 attribute is accessed from a class context,
2306 e.g. ClassDef.attribute, otherwise it might have been accessed
2307 from an instance as well. If ``class_context`` is used in that
2308 case, then a lookup in the implicit metaclass and the explicit
2309 metaclass will be done.
2311 :param name: The attribute to look for.
2313 :param class_context: Whether the attribute can be accessed statically.
2315 :returns: The attribute.
2317 :raises AttributeInferenceError: If the attribute cannot be inferred.
2318 """
2319 if not name:
2320 raise AttributeInferenceError(target=self, attribute=name, context=context)
2322 # don't modify the list in self.locals!
2323 values: list[SuccessfulInferenceResult] = list(self.locals.get(name, []))
2324 for classnode in self.ancestors(recurs=True, context=context):
2325 values += classnode.locals.get(name, [])
2327 if name in self.special_attributes and class_context and not values:
2328 result = [self.special_attributes.lookup(name)]
2329 if name == "__bases__":
2330 # Need special treatment, since they are mutable
2331 # and we need to return all the values.
2332 result += values
2333 return result
2335 if class_context:
2336 values += self._metaclass_lookup_attribute(name, context)
2338 # Remove AnnAssigns without value, which are not attributes in the purest sense.
2339 for value in values.copy():
2340 if isinstance(value, node_classes.AssignName):
2341 stmt = value.statement(future=True)
2342 if isinstance(stmt, node_classes.AnnAssign) and stmt.value is None:
2343 values.pop(values.index(value))
2345 if not values:
2346 raise AttributeInferenceError(target=self, attribute=name, context=context)
2348 return values
2350 @lru_cache(maxsize=1024) # noqa
2351 def _metaclass_lookup_attribute(self, name, context):
2352 """Search the given name in the implicit and the explicit metaclass."""
2353 attrs = set()
2354 implicit_meta = self.implicit_metaclass()
2355 context = copy_context(context)
2356 metaclass = self.metaclass(context=context)
2357 for cls in (implicit_meta, metaclass):
2358 if cls and cls != self and isinstance(cls, ClassDef):
2359 cls_attributes = self._get_attribute_from_metaclass(cls, name, context)
2360 attrs.update(set(cls_attributes))
2361 return attrs
2363 def _get_attribute_from_metaclass(self, cls, name, context):
2364 from astroid import objects # pylint: disable=import-outside-toplevel
2366 try:
2367 attrs = cls.getattr(name, context=context, class_context=True)
2368 except AttributeInferenceError:
2369 return
2371 for attr in bases._infer_stmts(attrs, context, frame=cls):
2372 if not isinstance(attr, FunctionDef):
2373 yield attr
2374 continue
2376 if isinstance(attr, objects.Property):
2377 yield attr
2378 continue
2379 if attr.type == "classmethod":
2380 # If the method is a classmethod, then it will
2381 # be bound to the metaclass, not to the class
2382 # from where the attribute is retrieved.
2383 # get_wrapping_class could return None, so just
2384 # default to the current class.
2385 frame = get_wrapping_class(attr) or self
2386 yield bases.BoundMethod(attr, frame)
2387 elif attr.type == "staticmethod":
2388 yield attr
2389 else:
2390 yield bases.BoundMethod(attr, self)
2392 def igetattr(
2393 self,
2394 name: str,
2395 context: InferenceContext | None = None,
2396 class_context: bool = True,
2397 ) -> Iterator[InferenceResult]:
2398 """Infer the possible values of the given variable.
2400 :param name: The name of the variable to infer.
2402 :returns: The inferred possible values.
2403 """
2404 from astroid import objects # pylint: disable=import-outside-toplevel
2406 # set lookup name since this is necessary to infer on import nodes for
2407 # instance
2408 context = copy_context(context)
2409 context.lookupname = name
2411 metaclass = self.metaclass(context=context)
2412 try:
2413 attributes = self.getattr(name, context, class_context=class_context)
2414 # If we have more than one attribute, make sure that those starting from
2415 # the second one are from the same scope. This is to account for modifications
2416 # to the attribute happening *after* the attribute's definition (e.g. AugAssigns on lists)
2417 if len(attributes) > 1:
2418 first_attr, attributes = attributes[0], attributes[1:]
2419 first_scope = first_attr.scope()
2420 attributes = [first_attr] + [
2421 attr
2422 for attr in attributes
2423 if attr.parent and attr.parent.scope() == first_scope
2424 ]
2426 for inferred in bases._infer_stmts(attributes, context, frame=self):
2427 # yield Uninferable object instead of descriptors when necessary
2428 if not isinstance(inferred, node_classes.Const) and isinstance(
2429 inferred, bases.Instance
2430 ):
2431 try:
2432 inferred._proxied.getattr("__get__", context)
2433 except AttributeInferenceError:
2434 yield inferred
2435 else:
2436 yield util.Uninferable
2437 elif isinstance(inferred, objects.Property):
2438 function = inferred.function
2439 if not class_context:
2440 # Through an instance so we can solve the property
2441 yield from function.infer_call_result(
2442 caller=self, context=context
2443 )
2444 # If we're in a class context, we need to determine if the property
2445 # was defined in the metaclass (a derived class must be a subclass of
2446 # the metaclass of all its bases), in which case we can resolve the
2447 # property. If not, i.e. the property is defined in some base class
2448 # instead, then we return the property object
2449 elif metaclass and function.parent.scope() is metaclass:
2450 # Resolve a property as long as it is not accessed through
2451 # the class itself.
2452 yield from function.infer_call_result(
2453 caller=self, context=context
2454 )
2455 else:
2456 yield inferred
2457 else:
2458 yield function_to_method(inferred, self)
2459 except AttributeInferenceError as error:
2460 if not name.startswith("__") and self.has_dynamic_getattr(context):
2461 # class handle some dynamic attributes, return a Uninferable object
2462 yield util.Uninferable
2463 else:
2464 raise InferenceError(
2465 str(error), target=self, attribute=name, context=context
2466 ) from error
2468 def has_dynamic_getattr(self, context: InferenceContext | None = None) -> bool:
2469 """Check if the class has a custom __getattr__ or __getattribute__.
2471 If any such method is found and it is not from
2472 builtins, nor from an extension module, then the function
2473 will return True.
2475 :returns: Whether the class has a custom __getattr__ or __getattribute__.
2476 """
2478 def _valid_getattr(node):
2479 root = node.root()
2480 return root.name != "builtins" and getattr(root, "pure_python", None)
2482 try:
2483 return _valid_getattr(self.getattr("__getattr__", context)[0])
2484 except AttributeInferenceError:
2485 # if self.newstyle: XXX cause an infinite recursion error
2486 try:
2487 getattribute = self.getattr("__getattribute__", context)[0]
2488 return _valid_getattr(getattribute)
2489 except AttributeInferenceError:
2490 pass
2491 return False
2493 def getitem(self, index, context: InferenceContext | None = None):
2494 """Return the inference of a subscript.
2496 This is basically looking up the method in the metaclass and calling it.
2498 :returns: The inferred value of a subscript to this class.
2499 :rtype: NodeNG
2501 :raises AstroidTypeError: If this class does not define a
2502 ``__getitem__`` method.
2503 """
2504 try:
2505 methods = lookup(self, "__getitem__")
2506 except AttributeInferenceError as exc:
2507 if isinstance(self, ClassDef):
2508 # subscripting a class definition may be
2509 # achieved thanks to __class_getitem__ method
2510 # which is a classmethod defined in the class
2511 # that supports subscript and not in the metaclass
2512 try:
2513 methods = self.getattr("__class_getitem__")
2514 # Here it is assumed that the __class_getitem__ node is
2515 # a FunctionDef. One possible improvement would be to deal
2516 # with more generic inference.
2517 except AttributeInferenceError:
2518 raise AstroidTypeError(node=self, context=context) from exc
2519 else:
2520 raise AstroidTypeError(node=self, context=context) from exc
2522 method = methods[0]
2524 # Create a new callcontext for providing index as an argument.
2525 new_context = bind_context_to_node(context, self)
2526 new_context.callcontext = CallContext(args=[index], callee=method)
2528 try:
2529 return next(method.infer_call_result(self, new_context), util.Uninferable)
2530 except AttributeError:
2531 # Starting with python3.9, builtin types list, dict etc...
2532 # are subscriptable thanks to __class_getitem___ classmethod.
2533 # However in such case the method is bound to an EmptyNode and
2534 # EmptyNode doesn't have infer_call_result method yielding to
2535 # AttributeError
2536 if (
2537 isinstance(method, node_classes.EmptyNode)
2538 and self.pytype() == "builtins.type"
2539 and PY39_PLUS
2540 ):
2541 return self
2542 raise
2543 except InferenceError:
2544 return util.Uninferable
2546 def methods(self):
2547 """Iterate over all of the method defined in this class and its parents.
2549 :returns: The methods defined on the class.
2550 :rtype: iterable(FunctionDef)
2551 """
2552 done = {}
2553 for astroid in itertools.chain(iter((self,)), self.ancestors()):
2554 for meth in astroid.mymethods():
2555 if meth.name in done:
2556 continue
2557 done[meth.name] = None
2558 yield meth
2560 def mymethods(self):
2561 """Iterate over all of the method defined in this class only.
2563 :returns: The methods defined on the class.
2564 :rtype: iterable(FunctionDef)
2565 """
2566 for member in self.values():
2567 if isinstance(member, FunctionDef):
2568 yield member
2570 def implicit_metaclass(self):
2571 """Get the implicit metaclass of the current class.
2573 For newstyle classes, this will return an instance of builtins.type.
2574 For oldstyle classes, it will simply return None, since there's
2575 no implicit metaclass there.
2577 :returns: The metaclass.
2578 :rtype: builtins.type or None
2579 """
2580 if self.newstyle:
2581 return builtin_lookup("type")[1][0]
2582 return None
2584 def declared_metaclass(
2585 self, context: InferenceContext | None = None
2586 ) -> SuccessfulInferenceResult | None:
2587 """Return the explicit declared metaclass for the current class.
2589 An explicit declared metaclass is defined
2590 either by passing the ``metaclass`` keyword argument
2591 in the class definition line (Python 3) or (Python 2) by
2592 having a ``__metaclass__`` class attribute, or if there are
2593 no explicit bases but there is a global ``__metaclass__`` variable.
2595 :returns: The metaclass of this class,
2596 or None if one could not be found.
2597 """
2598 for base in self.bases:
2599 try:
2600 for baseobj in base.infer(context=context):
2601 if isinstance(baseobj, ClassDef) and baseobj.hide:
2602 self._metaclass = baseobj._metaclass
2603 self._metaclass_hack = True
2604 break
2605 except InferenceError:
2606 pass
2608 if self._metaclass:
2609 # Expects this from Py3k TreeRebuilder
2610 try:
2611 return next(
2612 node
2613 for node in self._metaclass.infer(context=context)
2614 if not isinstance(node, util.UninferableBase)
2615 )
2616 except (InferenceError, StopIteration):
2617 return None
2619 return None
2621 def _find_metaclass(
2622 self, seen: set[ClassDef] | None = None, context: InferenceContext | None = None
2623 ) -> SuccessfulInferenceResult | None:
2624 if seen is None:
2625 seen = set()
2626 seen.add(self)
2628 klass = self.declared_metaclass(context=context)
2629 if klass is None:
2630 for parent in self.ancestors(context=context):
2631 if parent not in seen:
2632 klass = parent._find_metaclass(seen)
2633 if klass is not None:
2634 break
2635 return klass
2637 def metaclass(
2638 self, context: InferenceContext | None = None
2639 ) -> SuccessfulInferenceResult | None:
2640 """Get the metaclass of this class.
2642 If this class does not define explicitly a metaclass,
2643 then the first defined metaclass in ancestors will be used
2644 instead.
2646 :returns: The metaclass of this class.
2647 """
2648 return self._find_metaclass(context=context)
2650 def has_metaclass_hack(self):
2651 return self._metaclass_hack
2653 def _islots(self):
2654 """Return an iterator with the inferred slots."""
2655 if "__slots__" not in self.locals:
2656 return None
2657 for slots in self.igetattr("__slots__"):
2658 # check if __slots__ is a valid type
2659 for meth in ITER_METHODS:
2660 try:
2661 slots.getattr(meth)
2662 break
2663 except AttributeInferenceError:
2664 continue
2665 else:
2666 continue
2668 if isinstance(slots, node_classes.Const):
2669 # a string. Ignore the following checks,
2670 # but yield the node, only if it has a value
2671 if slots.value:
2672 yield slots
2673 continue
2674 if not hasattr(slots, "itered"):
2675 # we can't obtain the values, maybe a .deque?
2676 continue
2678 if isinstance(slots, node_classes.Dict):
2679 values = [item[0] for item in slots.items]
2680 else:
2681 values = slots.itered()
2682 if isinstance(values, util.UninferableBase):
2683 continue
2684 if not values:
2685 # Stop the iteration, because the class
2686 # has an empty list of slots.
2687 return values
2689 for elt in values:
2690 try:
2691 for inferred in elt.infer():
2692 if not isinstance(
2693 inferred, node_classes.Const
2694 ) or not isinstance(inferred.value, str):
2695 continue
2696 if not inferred.value:
2697 continue
2698 yield inferred
2699 except InferenceError:
2700 continue
2702 return None
2704 def _slots(self):
2705 if not self.newstyle:
2706 raise NotImplementedError(
2707 "The concept of slots is undefined for old-style classes."
2708 )
2710 slots = self._islots()
2711 try:
2712 first = next(slots)
2713 except StopIteration as exc:
2714 # The class doesn't have a __slots__ definition or empty slots.
2715 if exc.args and exc.args[0] not in ("", None):
2716 return exc.args[0]
2717 return None
2718 return [first, *slots]
2720 # Cached, because inferring them all the time is expensive
2721 @cached_property
2722 def _all_slots(self):
2723 """Get all the slots for this node.
2725 :returns: The names of slots for this class.
2726 If the class doesn't define any slot, through the ``__slots__``
2727 variable, then this function will return a None.
2728 Also, it will return None in the case the slots were not inferred.
2729 :rtype: list(str) or None
2730 """
2732 def grouped_slots(
2733 mro: list[ClassDef],
2734 ) -> Iterator[node_classes.NodeNG | None]:
2735 for cls in mro:
2736 # Not interested in object, since it can't have slots.
2737 if cls.qname() == "builtins.object":
2738 continue
2739 try:
2740 cls_slots = cls._slots()
2741 except NotImplementedError:
2742 continue
2743 if cls_slots is not None:
2744 yield from cls_slots
2745 else:
2746 yield None
2748 if not self.newstyle:
2749 raise NotImplementedError(
2750 "The concept of slots is undefined for old-style classes."
2751 )
2753 try:
2754 mro = self.mro()
2755 except MroError as e:
2756 raise NotImplementedError(
2757 "Cannot get slots while parsing mro fails."
2758 ) from e
2760 slots = list(grouped_slots(mro))
2761 if not all(slot is not None for slot in slots):
2762 return None
2764 return sorted(set(slots), key=lambda item: item.value)
2766 def slots(self):
2767 return self._all_slots
2769 def _inferred_bases(self, context: InferenceContext | None = None):
2770 # Similar with .ancestors, but the difference is when one base is inferred,
2771 # only the first object is wanted. That's because
2772 # we aren't interested in superclasses, as in the following
2773 # example:
2774 #
2775 # class SomeSuperClass(object): pass
2776 # class SomeClass(SomeSuperClass): pass
2777 # class Test(SomeClass): pass
2778 #
2779 # Inferring SomeClass from the Test's bases will give
2780 # us both SomeClass and SomeSuperClass, but we are interested
2781 # only in SomeClass.
2783 if context is None:
2784 context = InferenceContext()
2785 if not self.bases and self.qname() != "builtins.object":
2786 yield builtin_lookup("object")[1][0]
2787 return
2789 for stmt in self.bases:
2790 try:
2791 # Find the first non-None inferred base value
2792 baseobj = next(
2793 b
2794 for b in stmt.infer(context=context.clone())
2795 if not (isinstance(b, Const) and b.value is None)
2796 )
2797 except (InferenceError, StopIteration):
2798 continue
2799 if isinstance(baseobj, bases.Instance):
2800 baseobj = baseobj._proxied
2801 if not isinstance(baseobj, ClassDef):
2802 continue
2803 if not baseobj.hide:
2804 yield baseobj
2805 else:
2806 yield from baseobj.bases
2808 def _compute_mro(self, context: InferenceContext | None = None):
2809 if self.qname() == "builtins.object":
2810 return [self]
2812 inferred_bases = list(self._inferred_bases(context=context))
2813 bases_mro = []
2814 for base in inferred_bases:
2815 if base is self:
2816 continue
2818 try:
2819 mro = base._compute_mro(context=context)
2820 bases_mro.append(mro)
2821 except NotImplementedError:
2822 # Some classes have in their ancestors both newstyle and
2823 # old style classes. For these we can't retrieve the .mro,
2824 # although in Python it's possible, since the class we are
2825 # currently working is in fact new style.
2826 # So, we fallback to ancestors here.
2827 ancestors = list(base.ancestors(context=context))
2828 bases_mro.append(ancestors)
2830 unmerged_mro = [[self], *bases_mro, inferred_bases]
2831 unmerged_mro = clean_duplicates_mro(unmerged_mro, self, context)
2832 clean_typing_generic_mro(unmerged_mro)
2833 return _c3_merge(unmerged_mro, self, context)
2835 def mro(self, context: InferenceContext | None = None) -> list[ClassDef]:
2836 """Get the method resolution order, using C3 linearization.
2838 :returns: The list of ancestors, sorted by the mro.
2839 :rtype: list(NodeNG)
2840 :raises DuplicateBasesError: Duplicate bases in the same class base
2841 :raises InconsistentMroError: A class' MRO is inconsistent
2842 """
2843 return self._compute_mro(context=context)
2845 def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
2846 """Determine the boolean value of this node.
2848 :returns: The boolean value of this node.
2849 For a :class:`ClassDef` this is always ``True``.
2850 """
2851 return True
2853 def get_children(self):
2854 if self.decorators is not None:
2855 yield self.decorators
2857 yield from self.bases
2858 if self.keywords is not None:
2859 yield from self.keywords
2860 yield from self.body
2862 @cached_property
2863 def _assign_nodes_in_scope(self):
2864 children_assign_nodes = (
2865 child_node._assign_nodes_in_scope for child_node in self.body
2866 )
2867 return list(itertools.chain.from_iterable(children_assign_nodes))
2869 def frame(self: _T, *, future: Literal[None, True] = None) -> _T:
2870 """The node's frame node.
2872 A frame node is a :class:`Module`, :class:`FunctionDef`,
2873 :class:`ClassDef` or :class:`Lambda`.
2875 :returns: The node itself.
2876 """
2877 return self