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