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