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