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"""Module for some node classes. More nodes in scoped_nodes.py"""
6
7from __future__ import annotations
8
9import abc
10import ast
11import itertools
12import operator
13import sys
14import typing
15import warnings
16from collections.abc import Generator, Iterable, Iterator, Mapping
17from functools import cached_property
18from typing import (
19 TYPE_CHECKING,
20 Any,
21 Callable,
22 ClassVar,
23 Literal,
24 Optional,
25 Union,
26)
27
28from astroid import decorators, protocols, util
29from astroid.bases import Instance, _infer_stmts
30from astroid.const import _EMPTY_OBJECT_MARKER, Context
31from astroid.context import CallContext, InferenceContext, copy_context
32from astroid.exceptions import (
33 AstroidBuildingError,
34 AstroidError,
35 AstroidIndexError,
36 AstroidTypeError,
37 AstroidValueError,
38 AttributeInferenceError,
39 InferenceError,
40 NameInferenceError,
41 NoDefault,
42 ParentMissingError,
43 _NonDeducibleTypeHierarchy,
44)
45from astroid.interpreter import dunder_lookup
46from astroid.manager import AstroidManager
47from astroid.nodes import _base_nodes
48from astroid.nodes.const import OP_PRECEDENCE
49from astroid.nodes.node_ng import NodeNG
50from astroid.typing import (
51 ConstFactoryResult,
52 InferenceErrorInfo,
53 InferenceResult,
54 SuccessfulInferenceResult,
55)
56
57if sys.version_info >= (3, 11):
58 from typing import Self
59else:
60 from typing_extensions import Self
61
62if TYPE_CHECKING:
63 from astroid import nodes
64 from astroid.nodes import LocalsDictNodeNG
65
66
67def _is_const(value) -> bool:
68 return isinstance(value, tuple(CONST_CLS))
69
70
71_NodesT = typing.TypeVar("_NodesT", bound=NodeNG)
72_BadOpMessageT = typing.TypeVar("_BadOpMessageT", bound=util.BadOperationMessage)
73
74AssignedStmtsPossibleNode = Union["List", "Tuple", "AssignName", "AssignAttr", None]
75AssignedStmtsCall = Callable[
76 [
77 _NodesT,
78 AssignedStmtsPossibleNode,
79 Optional[InferenceContext],
80 Optional[typing.List[int]],
81 ],
82 Any,
83]
84InferBinaryOperation = Callable[
85 [_NodesT, Optional[InferenceContext]],
86 typing.Generator[Union[InferenceResult, _BadOpMessageT], None, None],
87]
88InferLHS = Callable[
89 [_NodesT, Optional[InferenceContext]],
90 typing.Generator[InferenceResult, None, Optional[InferenceErrorInfo]],
91]
92InferUnaryOp = Callable[[_NodesT, str], ConstFactoryResult]
93
94
95@decorators.raise_if_nothing_inferred
96def unpack_infer(stmt, context: InferenceContext | None = None):
97 """recursively generate nodes inferred by the given statement.
98 If the inferred value is a list or a tuple, recurse on the elements
99 """
100 if isinstance(stmt, (List, Tuple)):
101 for elt in stmt.elts:
102 if elt is util.Uninferable:
103 yield elt
104 continue
105 yield from unpack_infer(elt, context)
106 return {"node": stmt, "context": context}
107 # if inferred is a final node, return it and stop
108 inferred = next(stmt.infer(context), util.Uninferable)
109 if inferred is stmt:
110 yield inferred
111 return {"node": stmt, "context": context}
112 # else, infer recursively, except Uninferable object that should be returned as is
113 for inferred in stmt.infer(context):
114 if isinstance(inferred, util.UninferableBase):
115 yield inferred
116 else:
117 yield from unpack_infer(inferred, context)
118
119 return {"node": stmt, "context": context}
120
121
122def are_exclusive(stmt1, stmt2, exceptions: list[str] | None = None) -> bool:
123 """return true if the two given statements are mutually exclusive
124
125 `exceptions` may be a list of exception names. If specified, discard If
126 branches and check one of the statement is in an exception handler catching
127 one of the given exceptions.
128
129 algorithm :
130 1) index stmt1's parents
131 2) climb among stmt2's parents until we find a common parent
132 3) if the common parent is a If or Try statement, look if nodes are
133 in exclusive branches
134 """
135 # index stmt1's parents
136 stmt1_parents = {}
137 children = {}
138 previous = stmt1
139 for node in stmt1.node_ancestors():
140 stmt1_parents[node] = 1
141 children[node] = previous
142 previous = node
143 # climb among stmt2's parents until we find a common parent
144 previous = stmt2
145 for node in stmt2.node_ancestors():
146 if node in stmt1_parents:
147 # if the common parent is a If or Try statement, look if
148 # nodes are in exclusive branches
149 if isinstance(node, If) and exceptions is None:
150 c2attr, c2node = node.locate_child(previous)
151 c1attr, c1node = node.locate_child(children[node])
152 if "test" in (c1attr, c2attr):
153 # If any node is `If.test`, then it must be inclusive with
154 # the other node (`If.body` and `If.orelse`)
155 return False
156 if c1attr != c2attr:
157 # different `If` branches (`If.body` and `If.orelse`)
158 return True
159 elif isinstance(node, Try):
160 c2attr, c2node = node.locate_child(previous)
161 c1attr, c1node = node.locate_child(children[node])
162 if c1node is not c2node:
163 first_in_body_caught_by_handlers = (
164 c2attr == "handlers"
165 and c1attr == "body"
166 and previous.catch(exceptions)
167 )
168 second_in_body_caught_by_handlers = (
169 c2attr == "body"
170 and c1attr == "handlers"
171 and children[node].catch(exceptions)
172 )
173 first_in_else_other_in_handlers = (
174 c2attr == "handlers" and c1attr == "orelse"
175 )
176 second_in_else_other_in_handlers = (
177 c2attr == "orelse" and c1attr == "handlers"
178 )
179 if any(
180 (
181 first_in_body_caught_by_handlers,
182 second_in_body_caught_by_handlers,
183 first_in_else_other_in_handlers,
184 second_in_else_other_in_handlers,
185 )
186 ):
187 return True
188 elif c2attr == "handlers" and c1attr == "handlers":
189 return previous is not children[node]
190 return False
191 previous = node
192 return False
193
194
195# getitem() helpers.
196
197_SLICE_SENTINEL = object()
198
199
200def _slice_value(index, context: InferenceContext | None = None):
201 """Get the value of the given slice index."""
202
203 if isinstance(index, Const):
204 if isinstance(index.value, (int, type(None))):
205 return index.value
206 elif index is None:
207 return None
208 else:
209 # Try to infer what the index actually is.
210 # Since we can't return all the possible values,
211 # we'll stop at the first possible value.
212 try:
213 inferred = next(index.infer(context=context))
214 except (InferenceError, StopIteration):
215 pass
216 else:
217 if isinstance(inferred, Const):
218 if isinstance(inferred.value, (int, type(None))):
219 return inferred.value
220
221 # Use a sentinel, because None can be a valid
222 # value that this function can return,
223 # as it is the case for unspecified bounds.
224 return _SLICE_SENTINEL
225
226
227def _infer_slice(node, context: InferenceContext | None = None):
228 lower = _slice_value(node.lower, context)
229 upper = _slice_value(node.upper, context)
230 step = _slice_value(node.step, context)
231 if all(elem is not _SLICE_SENTINEL for elem in (lower, upper, step)):
232 return slice(lower, upper, step)
233
234 raise AstroidTypeError(
235 message="Could not infer slice used in subscript",
236 node=node,
237 index=node.parent,
238 context=context,
239 )
240
241
242def _container_getitem(instance, elts, index, context: InferenceContext | None = None):
243 """Get a slice or an item, using the given *index*, for the given sequence."""
244 try:
245 if isinstance(index, Slice):
246 index_slice = _infer_slice(index, context=context)
247 new_cls = instance.__class__()
248 new_cls.elts = elts[index_slice]
249 new_cls.parent = instance.parent
250 return new_cls
251 if isinstance(index, Const):
252 return elts[index.value]
253 except ValueError as exc:
254 raise AstroidValueError(
255 message="Slice {index!r} cannot index container",
256 node=instance,
257 index=index,
258 context=context,
259 ) from exc
260 except IndexError as exc:
261 raise AstroidIndexError(
262 message="Index {index!s} out of range",
263 node=instance,
264 index=index,
265 context=context,
266 ) from exc
267 except TypeError as exc:
268 raise AstroidTypeError(
269 message="Type error {error!r}", node=instance, index=index, context=context
270 ) from exc
271
272 raise AstroidTypeError(f"Could not use {index} as subscript index")
273
274
275class BaseContainer(_base_nodes.ParentAssignNode, Instance, metaclass=abc.ABCMeta):
276 """Base class for Set, FrozenSet, Tuple and List."""
277
278 _astroid_fields = ("elts",)
279
280 def __init__(
281 self,
282 lineno: int | None,
283 col_offset: int | None,
284 parent: NodeNG | None,
285 *,
286 end_lineno: int | None,
287 end_col_offset: int | None,
288 ) -> None:
289 self.elts: list[SuccessfulInferenceResult] = []
290 """The elements in the node."""
291
292 super().__init__(
293 lineno=lineno,
294 col_offset=col_offset,
295 end_lineno=end_lineno,
296 end_col_offset=end_col_offset,
297 parent=parent,
298 )
299
300 def postinit(self, elts: list[SuccessfulInferenceResult]) -> None:
301 self.elts = elts
302
303 @classmethod
304 def from_elements(cls, elts: Iterable[Any]) -> Self:
305 """Create a node of this type from the given list of elements.
306
307 :param elts: The list of elements that the node should contain.
308
309 :returns: A new node containing the given elements.
310 """
311 node = cls(
312 lineno=None,
313 col_offset=None,
314 parent=None,
315 end_lineno=None,
316 end_col_offset=None,
317 )
318 node.elts = [const_factory(e) if _is_const(e) else e for e in elts]
319 return node
320
321 def itered(self):
322 """An iterator over the elements this node contains.
323
324 :returns: The contents of this node.
325 :rtype: iterable(NodeNG)
326 """
327 return self.elts
328
329 def bool_value(self, context: InferenceContext | None = None) -> bool:
330 """Determine the boolean value of this node.
331
332 :returns: The boolean value of this node.
333 """
334 return bool(self.elts)
335
336 @abc.abstractmethod
337 def pytype(self) -> str:
338 """Get the name of the type that this node represents.
339
340 :returns: The name of the type.
341 """
342
343 def get_children(self):
344 yield from self.elts
345
346 @decorators.raise_if_nothing_inferred
347 def _infer(
348 self,
349 context: InferenceContext | None = None,
350 **kwargs: Any,
351 ) -> Iterator[Self]:
352 has_starred_named_expr = any(
353 isinstance(e, (Starred, NamedExpr)) for e in self.elts
354 )
355 if has_starred_named_expr:
356 values = self._infer_sequence_helper(context)
357 new_seq = type(self)(
358 lineno=self.lineno,
359 col_offset=self.col_offset,
360 parent=self.parent,
361 end_lineno=self.end_lineno,
362 end_col_offset=self.end_col_offset,
363 )
364 new_seq.postinit(values)
365
366 yield new_seq
367 else:
368 yield self
369
370 def _infer_sequence_helper(
371 self, context: InferenceContext | None = None
372 ) -> list[SuccessfulInferenceResult]:
373 """Infer all values based on BaseContainer.elts."""
374 values = []
375
376 for elt in self.elts:
377 if isinstance(elt, Starred):
378 starred = util.safe_infer(elt.value, context)
379 if not starred:
380 raise InferenceError(node=self, context=context)
381 if not hasattr(starred, "elts"):
382 raise InferenceError(node=self, context=context)
383 # TODO: fresh context?
384 values.extend(starred._infer_sequence_helper(context))
385 elif isinstance(elt, NamedExpr):
386 value = util.safe_infer(elt.value, context)
387 if not value:
388 raise InferenceError(node=self, context=context)
389 values.append(value)
390 else:
391 values.append(elt)
392 return values
393
394
395# Name classes
396
397
398class AssignName(
399 _base_nodes.NoChildrenNode,
400 _base_nodes.LookupMixIn,
401 _base_nodes.ParentAssignNode,
402):
403 """Variation of :class:`ast.Assign` representing assignment to a name.
404
405 An :class:`AssignName` is the name of something that is assigned to.
406 This includes variables defined in a function signature or in a loop.
407
408 >>> import astroid
409 >>> node = astroid.extract_node('variable = range(10)')
410 >>> node
411 <Assign l.1 at 0x7effe1db8550>
412 >>> list(node.get_children())
413 [<AssignName.variable l.1 at 0x7effe1db8748>, <Call l.1 at 0x7effe1db8630>]
414 >>> list(node.get_children())[0].as_string()
415 'variable'
416 """
417
418 _other_fields = ("name",)
419
420 def __init__(
421 self,
422 name: str,
423 lineno: int,
424 col_offset: int,
425 parent: NodeNG,
426 *,
427 end_lineno: int | None,
428 end_col_offset: int | None,
429 ) -> None:
430 self.name = name
431 """The name that is assigned to."""
432
433 super().__init__(
434 lineno=lineno,
435 col_offset=col_offset,
436 end_lineno=end_lineno,
437 end_col_offset=end_col_offset,
438 parent=parent,
439 )
440
441 assigned_stmts = protocols.assend_assigned_stmts
442 """Returns the assigned statement (non inferred) according to the assignment type.
443 See astroid/protocols.py for actual implementation.
444 """
445
446 @decorators.raise_if_nothing_inferred
447 @decorators.path_wrapper
448 def _infer(
449 self, context: InferenceContext | None = None, **kwargs: Any
450 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
451 """Infer an AssignName: need to inspect the RHS part of the
452 assign node.
453 """
454 if isinstance(self.parent, AugAssign):
455 return self.parent.infer(context)
456
457 stmts = list(self.assigned_stmts(context=context))
458 return _infer_stmts(stmts, context)
459
460 @decorators.raise_if_nothing_inferred
461 def infer_lhs(
462 self, context: InferenceContext | None = None, **kwargs: Any
463 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
464 """Infer a Name: use name lookup rules.
465
466 Same implementation as Name._infer."""
467 # pylint: disable=import-outside-toplevel
468 from astroid.constraint import get_constraints
469 from astroid.helpers import _higher_function_scope
470
471 frame, stmts = self.lookup(self.name)
472 if not stmts:
473 # Try to see if the name is enclosed in a nested function
474 # and use the higher (first function) scope for searching.
475 parent_function = _higher_function_scope(self.scope())
476 if parent_function:
477 _, stmts = parent_function.lookup(self.name)
478
479 if not stmts:
480 raise NameInferenceError(
481 name=self.name, scope=self.scope(), context=context
482 )
483 context = copy_context(context)
484 context.lookupname = self.name
485 context.constraints[self.name] = get_constraints(self, frame)
486
487 return _infer_stmts(stmts, context, frame)
488
489
490class DelName(
491 _base_nodes.NoChildrenNode, _base_nodes.LookupMixIn, _base_nodes.ParentAssignNode
492):
493 """Variation of :class:`ast.Delete` representing deletion of a name.
494
495 A :class:`DelName` is the name of something that is deleted.
496
497 >>> import astroid
498 >>> node = astroid.extract_node("del variable #@")
499 >>> list(node.get_children())
500 [<DelName.variable l.1 at 0x7effe1da4d30>]
501 >>> list(node.get_children())[0].as_string()
502 'variable'
503 """
504
505 _other_fields = ("name",)
506
507 def __init__(
508 self,
509 name: str,
510 lineno: int,
511 col_offset: int,
512 parent: NodeNG,
513 *,
514 end_lineno: int | None,
515 end_col_offset: int | None,
516 ) -> None:
517 self.name = name
518 """The name that is being deleted."""
519
520 super().__init__(
521 lineno=lineno,
522 col_offset=col_offset,
523 end_lineno=end_lineno,
524 end_col_offset=end_col_offset,
525 parent=parent,
526 )
527
528
529class Name(_base_nodes.LookupMixIn, _base_nodes.NoChildrenNode):
530 """Class representing an :class:`ast.Name` node.
531
532 A :class:`Name` node is something that is named, but not covered by
533 :class:`AssignName` or :class:`DelName`.
534
535 >>> import astroid
536 >>> node = astroid.extract_node('range(10)')
537 >>> node
538 <Call l.1 at 0x7effe1db8710>
539 >>> list(node.get_children())
540 [<Name.range l.1 at 0x7effe1db86a0>, <Const.int l.1 at 0x7effe1db8518>]
541 >>> list(node.get_children())[0].as_string()
542 'range'
543 """
544
545 _other_fields = ("name",)
546
547 def __init__(
548 self,
549 name: str,
550 lineno: int,
551 col_offset: int,
552 parent: NodeNG,
553 *,
554 end_lineno: int | None,
555 end_col_offset: int | None,
556 ) -> None:
557 self.name = name
558 """The name that this node refers to."""
559
560 super().__init__(
561 lineno=lineno,
562 col_offset=col_offset,
563 end_lineno=end_lineno,
564 end_col_offset=end_col_offset,
565 parent=parent,
566 )
567
568 def _get_name_nodes(self):
569 yield self
570
571 for child_node in self.get_children():
572 yield from child_node._get_name_nodes()
573
574 @decorators.raise_if_nothing_inferred
575 @decorators.path_wrapper
576 def _infer(
577 self, context: InferenceContext | None = None, **kwargs: Any
578 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
579 """Infer a Name: use name lookup rules
580
581 Same implementation as AssignName._infer_lhs."""
582 # pylint: disable=import-outside-toplevel
583 from astroid.constraint import get_constraints
584 from astroid.helpers import _higher_function_scope
585
586 frame, stmts = self.lookup(self.name)
587 if not stmts:
588 # Try to see if the name is enclosed in a nested function
589 # and use the higher (first function) scope for searching.
590 parent_function = _higher_function_scope(self.scope())
591 if parent_function:
592 _, stmts = parent_function.lookup(self.name)
593
594 if not stmts:
595 raise NameInferenceError(
596 name=self.name, scope=self.scope(), context=context
597 )
598 context = copy_context(context)
599 context.lookupname = self.name
600 context.constraints[self.name] = get_constraints(self, frame)
601
602 return _infer_stmts(stmts, context, frame)
603
604
605DEPRECATED_ARGUMENT_DEFAULT = "DEPRECATED_ARGUMENT_DEFAULT"
606
607
608class Arguments(
609 _base_nodes.AssignTypeNode
610): # pylint: disable=too-many-instance-attributes
611 """Class representing an :class:`ast.arguments` node.
612
613 An :class:`Arguments` node represents that arguments in a
614 function definition.
615
616 >>> import astroid
617 >>> node = astroid.extract_node('def foo(bar): pass')
618 >>> node
619 <FunctionDef.foo l.1 at 0x7effe1db8198>
620 >>> node.args
621 <Arguments l.1 at 0x7effe1db82e8>
622 """
623
624 # Python 3.4+ uses a different approach regarding annotations,
625 # each argument is a new class, _ast.arg, which exposes an
626 # 'annotation' attribute. In astroid though, arguments are exposed
627 # as is in the Arguments node and the only way to expose annotations
628 # is by using something similar with Python 3.3:
629 # - we expose 'varargannotation' and 'kwargannotation' of annotations
630 # of varargs and kwargs.
631 # - we expose 'annotation', a list with annotations for
632 # for each normal argument. If an argument doesn't have an
633 # annotation, its value will be None.
634 _astroid_fields = (
635 "args",
636 "defaults",
637 "kwonlyargs",
638 "posonlyargs",
639 "posonlyargs_annotations",
640 "kw_defaults",
641 "annotations",
642 "varargannotation",
643 "kwargannotation",
644 "kwonlyargs_annotations",
645 "type_comment_args",
646 "type_comment_kwonlyargs",
647 "type_comment_posonlyargs",
648 )
649
650 _other_fields = ("vararg", "kwarg")
651
652 args: list[AssignName] | None
653 """The names of the required arguments.
654
655 Can be None if the associated function does not have a retrievable
656 signature and the arguments are therefore unknown.
657 This can happen with (builtin) functions implemented in C that have
658 incomplete signature information.
659 """
660
661 defaults: list[NodeNG] | None
662 """The default values for arguments that can be passed positionally."""
663
664 kwonlyargs: list[AssignName]
665 """The keyword arguments that cannot be passed positionally."""
666
667 posonlyargs: list[AssignName]
668 """The arguments that can only be passed positionally."""
669
670 kw_defaults: list[NodeNG | None] | None
671 """The default values for keyword arguments that cannot be passed positionally."""
672
673 annotations: list[NodeNG | None]
674 """The type annotations of arguments that can be passed positionally."""
675
676 posonlyargs_annotations: list[NodeNG | None]
677 """The type annotations of arguments that can only be passed positionally."""
678
679 kwonlyargs_annotations: list[NodeNG | None]
680 """The type annotations of arguments that cannot be passed positionally."""
681
682 type_comment_args: list[NodeNG | None]
683 """The type annotation, passed by a type comment, of each argument.
684
685 If an argument does not have a type comment,
686 the value for that argument will be None.
687 """
688
689 type_comment_kwonlyargs: list[NodeNG | None]
690 """The type annotation, passed by a type comment, of each keyword only argument.
691
692 If an argument does not have a type comment,
693 the value for that argument will be None.
694 """
695
696 type_comment_posonlyargs: list[NodeNG | None]
697 """The type annotation, passed by a type comment, of each positional argument.
698
699 If an argument does not have a type comment,
700 the value for that argument will be None.
701 """
702
703 varargannotation: NodeNG | None
704 """The type annotation for the variable length arguments."""
705
706 kwargannotation: NodeNG | None
707 """The type annotation for the variable length keyword arguments."""
708
709 vararg_node: AssignName | None
710 """The node for variable length arguments"""
711
712 kwarg_node: AssignName | None
713 """The node for variable keyword arguments"""
714
715 def __init__(
716 self,
717 vararg: str | None,
718 kwarg: str | None,
719 parent: NodeNG,
720 vararg_node: AssignName | None = None,
721 kwarg_node: AssignName | None = None,
722 ) -> None:
723 """Almost all attributes can be None for living objects where introspection failed."""
724 super().__init__(
725 parent=parent,
726 lineno=None,
727 col_offset=None,
728 end_lineno=None,
729 end_col_offset=None,
730 )
731
732 self.vararg = vararg
733 """The name of the variable length arguments."""
734
735 self.kwarg = kwarg
736 """The name of the variable length keyword arguments."""
737
738 self.vararg_node = vararg_node
739 self.kwarg_node = kwarg_node
740
741 # pylint: disable=too-many-arguments
742 def postinit(
743 self,
744 args: list[AssignName] | None,
745 defaults: list[NodeNG] | None,
746 kwonlyargs: list[AssignName],
747 kw_defaults: list[NodeNG | None] | None,
748 annotations: list[NodeNG | None],
749 posonlyargs: list[AssignName],
750 kwonlyargs_annotations: list[NodeNG | None],
751 posonlyargs_annotations: list[NodeNG | None],
752 varargannotation: NodeNG | None = None,
753 kwargannotation: NodeNG | None = None,
754 type_comment_args: list[NodeNG | None] | None = None,
755 type_comment_kwonlyargs: list[NodeNG | None] | None = None,
756 type_comment_posonlyargs: list[NodeNG | None] | None = None,
757 ) -> None:
758 self.args = args
759 self.defaults = defaults
760 self.kwonlyargs = kwonlyargs
761 self.posonlyargs = posonlyargs
762 self.kw_defaults = kw_defaults
763 self.annotations = annotations
764 self.kwonlyargs_annotations = kwonlyargs_annotations
765 self.posonlyargs_annotations = posonlyargs_annotations
766
767 # Parameters that got added later and need a default
768 self.varargannotation = varargannotation
769 self.kwargannotation = kwargannotation
770 if type_comment_args is None:
771 type_comment_args = []
772 self.type_comment_args = type_comment_args
773 if type_comment_kwonlyargs is None:
774 type_comment_kwonlyargs = []
775 self.type_comment_kwonlyargs = type_comment_kwonlyargs
776 if type_comment_posonlyargs is None:
777 type_comment_posonlyargs = []
778 self.type_comment_posonlyargs = type_comment_posonlyargs
779
780 assigned_stmts = protocols.arguments_assigned_stmts
781 """Returns the assigned statement (non inferred) according to the assignment type.
782 See astroid/protocols.py for actual implementation.
783 """
784
785 def _infer_name(self, frame, name):
786 if self.parent is frame:
787 return name
788 return None
789
790 @cached_property
791 def fromlineno(self) -> int:
792 """The first line that this node appears on in the source code.
793
794 Can also return 0 if the line can not be determined.
795 """
796 lineno = super().fromlineno
797 return max(lineno, self.parent.fromlineno or 0)
798
799 @cached_property
800 def arguments(self):
801 """Get all the arguments for this node. This includes:
802 * Positional only arguments
803 * Positional arguments
804 * Keyword arguments
805 * Variable arguments (.e.g *args)
806 * Variable keyword arguments (e.g **kwargs)
807 """
808 retval = list(itertools.chain((self.posonlyargs or ()), (self.args or ())))
809 if self.vararg_node:
810 retval.append(self.vararg_node)
811 retval += self.kwonlyargs or ()
812 if self.kwarg_node:
813 retval.append(self.kwarg_node)
814
815 return retval
816
817 def format_args(self, *, skippable_names: set[str] | None = None) -> str:
818 """Get the arguments formatted as string.
819
820 :returns: The formatted arguments.
821 :rtype: str
822 """
823 result = []
824 positional_only_defaults = []
825 positional_or_keyword_defaults = self.defaults
826 if self.defaults:
827 args = self.args or []
828 positional_or_keyword_defaults = self.defaults[-len(args) :]
829 positional_only_defaults = self.defaults[: len(self.defaults) - len(args)]
830
831 if self.posonlyargs:
832 result.append(
833 _format_args(
834 self.posonlyargs,
835 positional_only_defaults,
836 self.posonlyargs_annotations,
837 skippable_names=skippable_names,
838 )
839 )
840 result.append("/")
841 if self.args:
842 result.append(
843 _format_args(
844 self.args,
845 positional_or_keyword_defaults,
846 getattr(self, "annotations", None),
847 skippable_names=skippable_names,
848 )
849 )
850 if self.vararg:
851 result.append(f"*{self.vararg}")
852 if self.kwonlyargs:
853 if not self.vararg:
854 result.append("*")
855 result.append(
856 _format_args(
857 self.kwonlyargs,
858 self.kw_defaults,
859 self.kwonlyargs_annotations,
860 skippable_names=skippable_names,
861 )
862 )
863 if self.kwarg:
864 result.append(f"**{self.kwarg}")
865 return ", ".join(result)
866
867 def _get_arguments_data(
868 self,
869 ) -> tuple[
870 dict[str, tuple[str | None, str | None]],
871 dict[str, tuple[str | None, str | None]],
872 ]:
873 """Get the arguments as dictionary with information about typing and defaults.
874
875 The return tuple contains a dictionary for positional and keyword arguments with their typing
876 and their default value, if any.
877 The method follows a similar order as format_args but instead of formatting into a string it
878 returns the data that is used to do so.
879 """
880 pos_only: dict[str, tuple[str | None, str | None]] = {}
881 kw_only: dict[str, tuple[str | None, str | None]] = {}
882
883 # Setup and match defaults with arguments
884 positional_only_defaults = []
885 positional_or_keyword_defaults = self.defaults
886 if self.defaults:
887 args = self.args or []
888 positional_or_keyword_defaults = self.defaults[-len(args) :]
889 positional_only_defaults = self.defaults[: len(self.defaults) - len(args)]
890
891 for index, posonly in enumerate(self.posonlyargs):
892 annotation, default = self.posonlyargs_annotations[index], None
893 if annotation is not None:
894 annotation = annotation.as_string()
895 if positional_only_defaults:
896 default = positional_only_defaults[index].as_string()
897 pos_only[posonly.name] = (annotation, default)
898
899 for index, arg in enumerate(self.args):
900 annotation, default = self.annotations[index], None
901 if annotation is not None:
902 annotation = annotation.as_string()
903 if positional_or_keyword_defaults:
904 defaults_offset = len(self.args) - len(positional_or_keyword_defaults)
905 default_index = index - defaults_offset
906 if (
907 default_index > -1
908 and positional_or_keyword_defaults[default_index] is not None
909 ):
910 default = positional_or_keyword_defaults[default_index].as_string()
911 pos_only[arg.name] = (annotation, default)
912
913 if self.vararg:
914 annotation = self.varargannotation
915 if annotation is not None:
916 annotation = annotation.as_string()
917 pos_only[self.vararg] = (annotation, None)
918
919 for index, kwarg in enumerate(self.kwonlyargs):
920 annotation = self.kwonlyargs_annotations[index]
921 if annotation is not None:
922 annotation = annotation.as_string()
923 default = self.kw_defaults[index]
924 if default is not None:
925 default = default.as_string()
926 kw_only[kwarg.name] = (annotation, default)
927
928 if self.kwarg:
929 annotation = self.kwargannotation
930 if annotation is not None:
931 annotation = annotation.as_string()
932 kw_only[self.kwarg] = (annotation, None)
933
934 return pos_only, kw_only
935
936 def default_value(self, argname):
937 """Get the default value for an argument.
938
939 :param argname: The name of the argument to get the default value for.
940 :type argname: str
941
942 :raises NoDefault: If there is no default value defined for the
943 given argument.
944 """
945 args = [
946 arg for arg in self.arguments if arg.name not in [self.vararg, self.kwarg]
947 ]
948
949 index = _find_arg(argname, self.kwonlyargs)[0]
950 if (index is not None) and (len(self.kw_defaults) > index):
951 if self.kw_defaults[index] is not None:
952 return self.kw_defaults[index]
953 raise NoDefault(func=self.parent, name=argname)
954
955 index = _find_arg(argname, args)[0]
956 if index is not None:
957 idx = index - (len(args) - len(self.defaults) - len(self.kw_defaults))
958 if idx >= 0:
959 return self.defaults[idx]
960
961 raise NoDefault(func=self.parent, name=argname)
962
963 def is_argument(self, name) -> bool:
964 """Check if the given name is defined in the arguments.
965
966 :param name: The name to check for.
967 :type name: str
968
969 :returns: Whether the given name is defined in the arguments,
970 """
971 if name == self.vararg:
972 return True
973 if name == self.kwarg:
974 return True
975 return self.find_argname(name)[1] is not None
976
977 def find_argname(self, argname, rec=DEPRECATED_ARGUMENT_DEFAULT):
978 """Get the index and :class:`AssignName` node for given name.
979
980 :param argname: The name of the argument to search for.
981 :type argname: str
982
983 :returns: The index and node for the argument.
984 :rtype: tuple(str or None, AssignName or None)
985 """
986 if rec != DEPRECATED_ARGUMENT_DEFAULT: # pragma: no cover
987 warnings.warn(
988 "The rec argument will be removed in astroid 3.1.",
989 DeprecationWarning,
990 stacklevel=2,
991 )
992 if self.arguments:
993 index, argument = _find_arg(argname, self.arguments)
994 if argument:
995 return index, argument
996 return None, None
997
998 def get_children(self):
999 yield from self.posonlyargs or ()
1000
1001 for elt in self.posonlyargs_annotations:
1002 if elt is not None:
1003 yield elt
1004
1005 yield from self.args or ()
1006
1007 if self.defaults is not None:
1008 yield from self.defaults
1009 yield from self.kwonlyargs
1010
1011 for elt in self.kw_defaults or ():
1012 if elt is not None:
1013 yield elt
1014
1015 for elt in self.annotations:
1016 if elt is not None:
1017 yield elt
1018
1019 if self.varargannotation is not None:
1020 yield self.varargannotation
1021
1022 if self.kwargannotation is not None:
1023 yield self.kwargannotation
1024
1025 for elt in self.kwonlyargs_annotations:
1026 if elt is not None:
1027 yield elt
1028
1029 @decorators.raise_if_nothing_inferred
1030 def _infer(
1031 self: nodes.Arguments, context: InferenceContext | None = None, **kwargs: Any
1032 ) -> Generator[InferenceResult, None, None]:
1033 # pylint: disable-next=import-outside-toplevel
1034 from astroid.protocols import _arguments_infer_argname
1035
1036 if context is None or context.lookupname is None:
1037 raise InferenceError(node=self, context=context)
1038 return _arguments_infer_argname(self, context.lookupname, context)
1039
1040
1041def _find_arg(argname, args):
1042 for i, arg in enumerate(args):
1043 if arg.name == argname:
1044 return i, arg
1045 return None, None
1046
1047
1048def _format_args(
1049 args, defaults=None, annotations=None, skippable_names: set[str] | None = None
1050) -> str:
1051 if skippable_names is None:
1052 skippable_names = set()
1053 values = []
1054 if args is None:
1055 return ""
1056 if annotations is None:
1057 annotations = []
1058 if defaults is not None:
1059 default_offset = len(args) - len(defaults)
1060 else:
1061 default_offset = None
1062 packed = itertools.zip_longest(args, annotations)
1063 for i, (arg, annotation) in enumerate(packed):
1064 if arg.name in skippable_names:
1065 continue
1066 if isinstance(arg, Tuple):
1067 values.append(f"({_format_args(arg.elts)})")
1068 else:
1069 argname = arg.name
1070 default_sep = "="
1071 if annotation is not None:
1072 argname += ": " + annotation.as_string()
1073 default_sep = " = "
1074 values.append(argname)
1075
1076 if default_offset is not None and i >= default_offset:
1077 if defaults[i - default_offset] is not None:
1078 values[-1] += default_sep + defaults[i - default_offset].as_string()
1079 return ", ".join(values)
1080
1081
1082def _infer_attribute(
1083 node: nodes.AssignAttr | nodes.Attribute,
1084 context: InferenceContext | None = None,
1085 **kwargs: Any,
1086) -> Generator[InferenceResult, None, InferenceErrorInfo]:
1087 """Infer an AssignAttr/Attribute node by using getattr on the associated object."""
1088 # pylint: disable=import-outside-toplevel
1089 from astroid.constraint import get_constraints
1090 from astroid.nodes import ClassDef
1091
1092 for owner in node.expr.infer(context):
1093 if isinstance(owner, util.UninferableBase):
1094 yield owner
1095 continue
1096
1097 context = copy_context(context)
1098 old_boundnode = context.boundnode
1099 try:
1100 context.boundnode = owner
1101 if isinstance(owner, (ClassDef, Instance)):
1102 frame = owner if isinstance(owner, ClassDef) else owner._proxied
1103 context.constraints[node.attrname] = get_constraints(node, frame=frame)
1104 if node.attrname == "argv" and owner.name == "sys":
1105 # sys.argv will never be inferable during static analysis
1106 # It's value would be the args passed to the linter itself
1107 yield util.Uninferable
1108 else:
1109 yield from owner.igetattr(node.attrname, context)
1110 except (
1111 AttributeInferenceError,
1112 InferenceError,
1113 AttributeError,
1114 ):
1115 pass
1116 finally:
1117 context.boundnode = old_boundnode
1118 return InferenceErrorInfo(node=node, context=context)
1119
1120
1121class AssignAttr(_base_nodes.LookupMixIn, _base_nodes.ParentAssignNode):
1122 """Variation of :class:`ast.Assign` representing assignment to an attribute.
1123
1124 >>> import astroid
1125 >>> node = astroid.extract_node('self.attribute = range(10)')
1126 >>> node
1127 <Assign l.1 at 0x7effe1d521d0>
1128 >>> list(node.get_children())
1129 [<AssignAttr.attribute l.1 at 0x7effe1d52320>, <Call l.1 at 0x7effe1d522e8>]
1130 >>> list(node.get_children())[0].as_string()
1131 'self.attribute'
1132 """
1133
1134 expr: NodeNG
1135
1136 _astroid_fields = ("expr",)
1137 _other_fields = ("attrname",)
1138
1139 def __init__(
1140 self,
1141 attrname: str,
1142 lineno: int,
1143 col_offset: int,
1144 parent: NodeNG,
1145 *,
1146 end_lineno: int | None,
1147 end_col_offset: int | None,
1148 ) -> None:
1149 self.attrname = attrname
1150 """The name of the attribute being assigned to."""
1151
1152 super().__init__(
1153 lineno=lineno,
1154 col_offset=col_offset,
1155 end_lineno=end_lineno,
1156 end_col_offset=end_col_offset,
1157 parent=parent,
1158 )
1159
1160 def postinit(self, expr: NodeNG) -> None:
1161 self.expr = expr
1162
1163 assigned_stmts = protocols.assend_assigned_stmts
1164 """Returns the assigned statement (non inferred) according to the assignment type.
1165 See astroid/protocols.py for actual implementation.
1166 """
1167
1168 def get_children(self):
1169 yield self.expr
1170
1171 @decorators.raise_if_nothing_inferred
1172 @decorators.path_wrapper
1173 def _infer(
1174 self, context: InferenceContext | None = None, **kwargs: Any
1175 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
1176 """Infer an AssignAttr: need to inspect the RHS part of the
1177 assign node.
1178 """
1179 if isinstance(self.parent, AugAssign):
1180 return self.parent.infer(context)
1181
1182 stmts = list(self.assigned_stmts(context=context))
1183 return _infer_stmts(stmts, context)
1184
1185 @decorators.raise_if_nothing_inferred
1186 @decorators.path_wrapper
1187 def infer_lhs(
1188 self, context: InferenceContext | None = None, **kwargs: Any
1189 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
1190 return _infer_attribute(self, context, **kwargs)
1191
1192
1193class Assert(_base_nodes.Statement):
1194 """Class representing an :class:`ast.Assert` node.
1195
1196 An :class:`Assert` node represents an assert statement.
1197
1198 >>> import astroid
1199 >>> node = astroid.extract_node('assert len(things) == 10, "Not enough things"')
1200 >>> node
1201 <Assert l.1 at 0x7effe1d527b8>
1202 """
1203
1204 _astroid_fields = ("test", "fail")
1205
1206 test: NodeNG
1207 """The test that passes or fails the assertion."""
1208
1209 fail: NodeNG | None
1210 """The message shown when the assertion fails."""
1211
1212 def postinit(self, test: NodeNG, fail: NodeNG | None) -> None:
1213 self.fail = fail
1214 self.test = test
1215
1216 def get_children(self):
1217 yield self.test
1218
1219 if self.fail is not None:
1220 yield self.fail
1221
1222
1223class Assign(_base_nodes.AssignTypeNode, _base_nodes.Statement):
1224 """Class representing an :class:`ast.Assign` node.
1225
1226 An :class:`Assign` is a statement where something is explicitly
1227 asssigned to.
1228
1229 >>> import astroid
1230 >>> node = astroid.extract_node('variable = range(10)')
1231 >>> node
1232 <Assign l.1 at 0x7effe1db8550>
1233 """
1234
1235 targets: list[NodeNG]
1236 """What is being assigned to."""
1237
1238 value: NodeNG
1239 """The value being assigned to the variables."""
1240
1241 type_annotation: NodeNG | None
1242 """If present, this will contain the type annotation passed by a type comment"""
1243
1244 _astroid_fields = ("targets", "value")
1245 _other_other_fields = ("type_annotation",)
1246
1247 def postinit(
1248 self,
1249 targets: list[NodeNG],
1250 value: NodeNG,
1251 type_annotation: NodeNG | None,
1252 ) -> None:
1253 self.targets = targets
1254 self.value = value
1255 self.type_annotation = type_annotation
1256
1257 assigned_stmts = protocols.assign_assigned_stmts
1258 """Returns the assigned statement (non inferred) according to the assignment type.
1259 See astroid/protocols.py for actual implementation.
1260 """
1261
1262 def get_children(self):
1263 yield from self.targets
1264
1265 yield self.value
1266
1267 @cached_property
1268 def _assign_nodes_in_scope(self) -> list[nodes.Assign]:
1269 return [self, *self.value._assign_nodes_in_scope]
1270
1271 def _get_yield_nodes_skip_functions(self):
1272 yield from self.value._get_yield_nodes_skip_functions()
1273
1274 def _get_yield_nodes_skip_lambdas(self):
1275 yield from self.value._get_yield_nodes_skip_lambdas()
1276
1277
1278class AnnAssign(_base_nodes.AssignTypeNode, _base_nodes.Statement):
1279 """Class representing an :class:`ast.AnnAssign` node.
1280
1281 An :class:`AnnAssign` is an assignment with a type annotation.
1282
1283 >>> import astroid
1284 >>> node = astroid.extract_node('variable: List[int] = range(10)')
1285 >>> node
1286 <AnnAssign l.1 at 0x7effe1d4c630>
1287 """
1288
1289 _astroid_fields = ("target", "annotation", "value")
1290 _other_fields = ("simple",)
1291
1292 target: Name | Attribute | Subscript
1293 """What is being assigned to."""
1294
1295 annotation: NodeNG
1296 """The type annotation of what is being assigned to."""
1297
1298 value: NodeNG | None
1299 """The value being assigned to the variables."""
1300
1301 simple: int
1302 """Whether :attr:`target` is a pure name or a complex statement."""
1303
1304 def postinit(
1305 self,
1306 target: Name | Attribute | Subscript,
1307 annotation: NodeNG,
1308 simple: int,
1309 value: NodeNG | None,
1310 ) -> None:
1311 self.target = target
1312 self.annotation = annotation
1313 self.value = value
1314 self.simple = simple
1315
1316 assigned_stmts = protocols.assign_annassigned_stmts
1317 """Returns the assigned statement (non inferred) according to the assignment type.
1318 See astroid/protocols.py for actual implementation.
1319 """
1320
1321 def get_children(self):
1322 yield self.target
1323 yield self.annotation
1324
1325 if self.value is not None:
1326 yield self.value
1327
1328
1329class AugAssign(
1330 _base_nodes.AssignTypeNode, _base_nodes.OperatorNode, _base_nodes.Statement
1331):
1332 """Class representing an :class:`ast.AugAssign` node.
1333
1334 An :class:`AugAssign` is an assignment paired with an operator.
1335
1336 >>> import astroid
1337 >>> node = astroid.extract_node('variable += 1')
1338 >>> node
1339 <AugAssign l.1 at 0x7effe1db4d68>
1340 """
1341
1342 _astroid_fields = ("target", "value")
1343 _other_fields = ("op",)
1344
1345 target: Name | Attribute | Subscript
1346 """What is being assigned to."""
1347
1348 value: NodeNG
1349 """The value being assigned to the variable."""
1350
1351 def __init__(
1352 self,
1353 op: str,
1354 lineno: int,
1355 col_offset: int,
1356 parent: NodeNG,
1357 *,
1358 end_lineno: int | None,
1359 end_col_offset: int | None,
1360 ) -> None:
1361 self.op = op
1362 """The operator that is being combined with the assignment.
1363
1364 This includes the equals sign.
1365 """
1366
1367 super().__init__(
1368 lineno=lineno,
1369 col_offset=col_offset,
1370 end_lineno=end_lineno,
1371 end_col_offset=end_col_offset,
1372 parent=parent,
1373 )
1374
1375 def postinit(self, target: Name | Attribute | Subscript, value: NodeNG) -> None:
1376 self.target = target
1377 self.value = value
1378
1379 assigned_stmts = protocols.assign_assigned_stmts
1380 """Returns the assigned statement (non inferred) according to the assignment type.
1381 See astroid/protocols.py for actual implementation.
1382 """
1383
1384 def type_errors(
1385 self, context: InferenceContext | None = None
1386 ) -> list[util.BadBinaryOperationMessage]:
1387 """Get a list of type errors which can occur during inference.
1388
1389 Each TypeError is represented by a :class:`BadBinaryOperationMessage` ,
1390 which holds the original exception.
1391
1392 If any inferred result is uninferable, an empty list is returned.
1393 """
1394 bad = []
1395 try:
1396 for result in self._infer_augassign(context=context):
1397 if result is util.Uninferable:
1398 raise InferenceError
1399 if isinstance(result, util.BadBinaryOperationMessage):
1400 bad.append(result)
1401 except InferenceError:
1402 return []
1403 return bad
1404
1405 def get_children(self):
1406 yield self.target
1407 yield self.value
1408
1409 def _get_yield_nodes_skip_functions(self):
1410 """An AugAssign node can contain a Yield node in the value"""
1411 yield from self.value._get_yield_nodes_skip_functions()
1412 yield from super()._get_yield_nodes_skip_functions()
1413
1414 def _get_yield_nodes_skip_lambdas(self):
1415 """An AugAssign node can contain a Yield node in the value"""
1416 yield from self.value._get_yield_nodes_skip_lambdas()
1417 yield from super()._get_yield_nodes_skip_lambdas()
1418
1419 def _infer_augassign(
1420 self, context: InferenceContext | None = None
1421 ) -> Generator[InferenceResult | util.BadBinaryOperationMessage, None, None]:
1422 """Inference logic for augmented binary operations."""
1423 context = context or InferenceContext()
1424
1425 rhs_context = context.clone()
1426
1427 lhs_iter = self.target.infer_lhs(context=context)
1428 rhs_iter = self.value.infer(context=rhs_context)
1429
1430 for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
1431 if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
1432 # Don't know how to process this.
1433 yield util.Uninferable
1434 return
1435
1436 try:
1437 yield from self._infer_binary_operation(
1438 left=lhs,
1439 right=rhs,
1440 binary_opnode=self,
1441 context=context,
1442 flow_factory=self._get_aug_flow,
1443 )
1444 except _NonDeducibleTypeHierarchy:
1445 yield util.Uninferable
1446
1447 @decorators.raise_if_nothing_inferred
1448 @decorators.path_wrapper
1449 def _infer(
1450 self: nodes.AugAssign, context: InferenceContext | None = None, **kwargs: Any
1451 ) -> Generator[InferenceResult, None, None]:
1452 return self._filter_operation_errors(
1453 self._infer_augassign, context, util.BadBinaryOperationMessage
1454 )
1455
1456
1457class BinOp(_base_nodes.OperatorNode):
1458 """Class representing an :class:`ast.BinOp` node.
1459
1460 A :class:`BinOp` node is an application of a binary operator.
1461
1462 >>> import astroid
1463 >>> node = astroid.extract_node('a + b')
1464 >>> node
1465 <BinOp l.1 at 0x7f23b2e8cfd0>
1466 """
1467
1468 _astroid_fields = ("left", "right")
1469 _other_fields = ("op",)
1470
1471 left: NodeNG
1472 """What is being applied to the operator on the left side."""
1473
1474 right: NodeNG
1475 """What is being applied to the operator on the right side."""
1476
1477 def __init__(
1478 self,
1479 op: str,
1480 lineno: int,
1481 col_offset: int,
1482 parent: NodeNG,
1483 *,
1484 end_lineno: int | None,
1485 end_col_offset: int | None,
1486 ) -> None:
1487 self.op = op
1488 """The operator."""
1489
1490 super().__init__(
1491 lineno=lineno,
1492 col_offset=col_offset,
1493 end_lineno=end_lineno,
1494 end_col_offset=end_col_offset,
1495 parent=parent,
1496 )
1497
1498 def postinit(self, left: NodeNG, right: NodeNG) -> None:
1499 self.left = left
1500 self.right = right
1501
1502 def type_errors(
1503 self, context: InferenceContext | None = None
1504 ) -> list[util.BadBinaryOperationMessage]:
1505 """Get a list of type errors which can occur during inference.
1506
1507 Each TypeError is represented by a :class:`BadBinaryOperationMessage`,
1508 which holds the original exception.
1509
1510 If any inferred result is uninferable, an empty list is returned.
1511 """
1512 bad = []
1513 try:
1514 for result in self._infer_binop(context=context):
1515 if result is util.Uninferable:
1516 raise InferenceError
1517 if isinstance(result, util.BadBinaryOperationMessage):
1518 bad.append(result)
1519 except InferenceError:
1520 return []
1521 return bad
1522
1523 def get_children(self):
1524 yield self.left
1525 yield self.right
1526
1527 def op_precedence(self):
1528 return OP_PRECEDENCE[self.op]
1529
1530 def op_left_associative(self) -> bool:
1531 # 2**3**4 == 2**(3**4)
1532 return self.op != "**"
1533
1534 def _infer_binop(
1535 self, context: InferenceContext | None = None, **kwargs: Any
1536 ) -> Generator[InferenceResult, None, None]:
1537 """Binary operation inference logic."""
1538 left = self.left
1539 right = self.right
1540
1541 # we use two separate contexts for evaluating lhs and rhs because
1542 # 1. evaluating lhs may leave some undesired entries in context.path
1543 # which may not let us infer right value of rhs
1544 context = context or InferenceContext()
1545 lhs_context = copy_context(context)
1546 rhs_context = copy_context(context)
1547 lhs_iter = left.infer(context=lhs_context)
1548 rhs_iter = right.infer(context=rhs_context)
1549 for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
1550 if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
1551 # Don't know how to process this.
1552 yield util.Uninferable
1553 return
1554
1555 try:
1556 yield from self._infer_binary_operation(
1557 lhs, rhs, self, context, self._get_binop_flow
1558 )
1559 except _NonDeducibleTypeHierarchy:
1560 yield util.Uninferable
1561
1562 @decorators.yes_if_nothing_inferred
1563 @decorators.path_wrapper
1564 def _infer(
1565 self: nodes.BinOp, context: InferenceContext | None = None, **kwargs: Any
1566 ) -> Generator[InferenceResult, None, None]:
1567 return self._filter_operation_errors(
1568 self._infer_binop, context, util.BadBinaryOperationMessage
1569 )
1570
1571
1572class BoolOp(NodeNG):
1573 """Class representing an :class:`ast.BoolOp` node.
1574
1575 A :class:`BoolOp` is an application of a boolean operator.
1576
1577 >>> import astroid
1578 >>> node = astroid.extract_node('a and b')
1579 >>> node
1580 <BinOp l.1 at 0x7f23b2e71c50>
1581 """
1582
1583 _astroid_fields = ("values",)
1584 _other_fields = ("op",)
1585
1586 def __init__(
1587 self,
1588 op: str,
1589 lineno: int | None = None,
1590 col_offset: int | None = None,
1591 parent: NodeNG | None = None,
1592 *,
1593 end_lineno: int | None = None,
1594 end_col_offset: int | None = None,
1595 ) -> None:
1596 """
1597 :param op: The operator.
1598
1599 :param lineno: The line that this node appears on in the source code.
1600
1601 :param col_offset: The column that this node appears on in the
1602 source code.
1603
1604 :param parent: The parent node in the syntax tree.
1605
1606 :param end_lineno: The last line this node appears on in the source code.
1607
1608 :param end_col_offset: The end column this node appears on in the
1609 source code. Note: This is after the last symbol.
1610 """
1611 self.op: str = op
1612 """The operator."""
1613
1614 self.values: list[NodeNG] = []
1615 """The values being applied to the operator."""
1616
1617 super().__init__(
1618 lineno=lineno,
1619 col_offset=col_offset,
1620 end_lineno=end_lineno,
1621 end_col_offset=end_col_offset,
1622 parent=parent,
1623 )
1624
1625 def postinit(self, values: list[NodeNG] | None = None) -> None:
1626 """Do some setup after initialisation.
1627
1628 :param values: The values being applied to the operator.
1629 """
1630 if values is not None:
1631 self.values = values
1632
1633 def get_children(self):
1634 yield from self.values
1635
1636 def op_precedence(self):
1637 return OP_PRECEDENCE[self.op]
1638
1639 @decorators.raise_if_nothing_inferred
1640 @decorators.path_wrapper
1641 def _infer(
1642 self: nodes.BoolOp, context: InferenceContext | None = None, **kwargs: Any
1643 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
1644 """Infer a boolean operation (and / or / not).
1645
1646 The function will calculate the boolean operation
1647 for all pairs generated through inference for each component
1648 node.
1649 """
1650 values = self.values
1651 if self.op == "or":
1652 predicate = operator.truth
1653 else:
1654 predicate = operator.not_
1655
1656 try:
1657 inferred_values = [value.infer(context=context) for value in values]
1658 except InferenceError:
1659 yield util.Uninferable
1660 return None
1661
1662 for pair in itertools.product(*inferred_values):
1663 if any(isinstance(item, util.UninferableBase) for item in pair):
1664 # Can't infer the final result, just yield Uninferable.
1665 yield util.Uninferable
1666 continue
1667
1668 bool_values = [item.bool_value() for item in pair]
1669 if any(isinstance(item, util.UninferableBase) for item in bool_values):
1670 # Can't infer the final result, just yield Uninferable.
1671 yield util.Uninferable
1672 continue
1673
1674 # Since the boolean operations are short circuited operations,
1675 # this code yields the first value for which the predicate is True
1676 # and if no value respected the predicate, then the last value will
1677 # be returned (or Uninferable if there was no last value).
1678 # This is conforming to the semantics of `and` and `or`:
1679 # 1 and 0 -> 1
1680 # 0 and 1 -> 0
1681 # 1 or 0 -> 1
1682 # 0 or 1 -> 1
1683 value = util.Uninferable
1684 for value, bool_value in zip(pair, bool_values):
1685 if predicate(bool_value):
1686 yield value
1687 break
1688 else:
1689 yield value
1690
1691 return InferenceErrorInfo(node=self, context=context)
1692
1693
1694class Break(_base_nodes.NoChildrenNode, _base_nodes.Statement):
1695 """Class representing an :class:`ast.Break` node.
1696
1697 >>> import astroid
1698 >>> node = astroid.extract_node('break')
1699 >>> node
1700 <Break l.1 at 0x7f23b2e9e5c0>
1701 """
1702
1703
1704class Call(NodeNG):
1705 """Class representing an :class:`ast.Call` node.
1706
1707 A :class:`Call` node is a call to a function, method, etc.
1708
1709 >>> import astroid
1710 >>> node = astroid.extract_node('function()')
1711 >>> node
1712 <Call l.1 at 0x7f23b2e71eb8>
1713 """
1714
1715 _astroid_fields = ("func", "args", "keywords")
1716
1717 func: NodeNG
1718 """What is being called."""
1719
1720 args: list[NodeNG]
1721 """The positional arguments being given to the call."""
1722
1723 keywords: list[Keyword]
1724 """The keyword arguments being given to the call."""
1725
1726 def postinit(
1727 self, func: NodeNG, args: list[NodeNG], keywords: list[Keyword]
1728 ) -> None:
1729 self.func = func
1730 self.args = args
1731 self.keywords = keywords
1732
1733 @property
1734 def starargs(self) -> list[Starred]:
1735 """The positional arguments that unpack something."""
1736 return [arg for arg in self.args if isinstance(arg, Starred)]
1737
1738 @property
1739 def kwargs(self) -> list[Keyword]:
1740 """The keyword arguments that unpack something."""
1741 return [keyword for keyword in self.keywords if keyword.arg is None]
1742
1743 def get_children(self):
1744 yield self.func
1745
1746 yield from self.args
1747
1748 yield from self.keywords
1749
1750 @decorators.raise_if_nothing_inferred
1751 @decorators.path_wrapper
1752 def _infer(
1753 self, context: InferenceContext | None = None, **kwargs: Any
1754 ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
1755 """Infer a Call node by trying to guess what the function returns."""
1756 callcontext = copy_context(context)
1757 callcontext.boundnode = None
1758 if context is not None:
1759 callcontext.extra_context = self._populate_context_lookup(context.clone())
1760
1761 for callee in self.func.infer(context):
1762 if isinstance(callee, util.UninferableBase):
1763 yield callee
1764 continue
1765 try:
1766 if hasattr(callee, "infer_call_result"):
1767 callcontext.callcontext = CallContext(
1768 args=self.args, keywords=self.keywords, callee=callee
1769 )
1770 yield from callee.infer_call_result(
1771 caller=self, context=callcontext
1772 )
1773 except InferenceError:
1774 continue
1775 return InferenceErrorInfo(node=self, context=context)
1776
1777 def _populate_context_lookup(self, context: InferenceContext | None):
1778 """Allows context to be saved for later for inference inside a function."""
1779 context_lookup: dict[InferenceResult, InferenceContext] = {}
1780 if context is None:
1781 return context_lookup
1782 for arg in self.args:
1783 if isinstance(arg, Starred):
1784 context_lookup[arg.value] = context
1785 else:
1786 context_lookup[arg] = context
1787 keywords = self.keywords if self.keywords is not None else []
1788 for keyword in keywords:
1789 context_lookup[keyword.value] = context
1790 return context_lookup
1791
1792
1793COMPARE_OPS: dict[str, Callable[[Any, Any], bool]] = {
1794 "==": operator.eq,
1795 "!=": operator.ne,
1796 "<": operator.lt,
1797 "<=": operator.le,
1798 ">": operator.gt,
1799 ">=": operator.ge,
1800 "in": lambda a, b: a in b,
1801 "not in": lambda a, b: a not in b,
1802}
1803UNINFERABLE_OPS = {
1804 "is",
1805 "is not",
1806}
1807
1808
1809class Compare(NodeNG):
1810 """Class representing an :class:`ast.Compare` node.
1811
1812 A :class:`Compare` node indicates a comparison.
1813
1814 >>> import astroid
1815 >>> node = astroid.extract_node('a <= b <= c')
1816 >>> node
1817 <Compare l.1 at 0x7f23b2e9e6d8>
1818 >>> node.ops
1819 [('<=', <Name.b l.1 at 0x7f23b2e9e2b0>), ('<=', <Name.c l.1 at 0x7f23b2e9e390>)]
1820 """
1821
1822 _astroid_fields = ("left", "ops")
1823
1824 left: NodeNG
1825 """The value at the left being applied to a comparison operator."""
1826
1827 ops: list[tuple[str, NodeNG]]
1828 """The remainder of the operators and their relevant right hand value."""
1829
1830 def postinit(self, left: NodeNG, ops: list[tuple[str, NodeNG]]) -> None:
1831 self.left = left
1832 self.ops = ops
1833
1834 def get_children(self):
1835 """Get the child nodes below this node.
1836
1837 Overridden to handle the tuple fields and skip returning the operator
1838 strings.
1839
1840 :returns: The children.
1841 :rtype: iterable(NodeNG)
1842 """
1843 yield self.left
1844 for _, comparator in self.ops:
1845 yield comparator # we don't want the 'op'
1846
1847 def last_child(self):
1848 """An optimized version of list(get_children())[-1]
1849
1850 :returns: The last child.
1851 :rtype: NodeNG
1852 """
1853 # XXX maybe if self.ops:
1854 return self.ops[-1][1]
1855 # return self.left
1856
1857 # TODO: move to util?
1858 @staticmethod
1859 def _to_literal(node: SuccessfulInferenceResult) -> Any:
1860 # Can raise SyntaxError or ValueError from ast.literal_eval
1861 # Can raise AttributeError from node.as_string() as not all nodes have a visitor
1862 # Is this the stupidest idea or the simplest idea?
1863 return ast.literal_eval(node.as_string())
1864
1865 def _do_compare(
1866 self,
1867 left_iter: Iterable[InferenceResult],
1868 op: str,
1869 right_iter: Iterable[InferenceResult],
1870 ) -> bool | util.UninferableBase:
1871 """
1872 If all possible combinations are either True or False, return that:
1873 >>> _do_compare([1, 2], '<=', [3, 4])
1874 True
1875 >>> _do_compare([1, 2], '==', [3, 4])
1876 False
1877
1878 If any item is uninferable, or if some combinations are True and some
1879 are False, return Uninferable:
1880 >>> _do_compare([1, 3], '<=', [2, 4])
1881 util.Uninferable
1882 """
1883 retval: bool | None = None
1884 if op in UNINFERABLE_OPS:
1885 return util.Uninferable
1886 op_func = COMPARE_OPS[op]
1887
1888 for left, right in itertools.product(left_iter, right_iter):
1889 if isinstance(left, util.UninferableBase) or isinstance(
1890 right, util.UninferableBase
1891 ):
1892 return util.Uninferable
1893
1894 try:
1895 left, right = self._to_literal(left), self._to_literal(right)
1896 except (SyntaxError, ValueError, AttributeError):
1897 return util.Uninferable
1898
1899 try:
1900 expr = op_func(left, right)
1901 except TypeError as exc:
1902 raise AstroidTypeError from exc
1903
1904 if retval is None:
1905 retval = expr
1906 elif retval != expr:
1907 return util.Uninferable
1908 # (or both, but "True | False" is basically the same)
1909
1910 assert retval is not None
1911 return retval # it was all the same value
1912
1913 def _infer(
1914 self, context: InferenceContext | None = None, **kwargs: Any
1915 ) -> Generator[nodes.Const | util.UninferableBase, None, None]:
1916 """Chained comparison inference logic."""
1917 retval: bool | util.UninferableBase = True
1918
1919 ops = self.ops
1920 left_node = self.left
1921 lhs = list(left_node.infer(context=context))
1922 # should we break early if first element is uninferable?
1923 for op, right_node in ops:
1924 # eagerly evaluate rhs so that values can be re-used as lhs
1925 rhs = list(right_node.infer(context=context))
1926 try:
1927 retval = self._do_compare(lhs, op, rhs)
1928 except AstroidTypeError:
1929 retval = util.Uninferable
1930 break
1931 if retval is not True:
1932 break # short-circuit
1933 lhs = rhs # continue
1934 if retval is util.Uninferable:
1935 yield retval # type: ignore[misc]
1936 else:
1937 yield Const(retval)
1938
1939
1940class Comprehension(NodeNG):
1941 """Class representing an :class:`ast.comprehension` node.
1942
1943 A :class:`Comprehension` indicates the loop inside any type of
1944 comprehension including generator expressions.
1945
1946 >>> import astroid
1947 >>> node = astroid.extract_node('[x for x in some_values]')
1948 >>> list(node.get_children())
1949 [<Name.x l.1 at 0x7f23b2e352b0>, <Comprehension l.1 at 0x7f23b2e35320>]
1950 >>> list(node.get_children())[1].as_string()
1951 'for x in some_values'
1952 """
1953
1954 _astroid_fields = ("target", "iter", "ifs")
1955 _other_fields = ("is_async",)
1956
1957 optional_assign = True
1958 """Whether this node optionally assigns a variable."""
1959
1960 target: NodeNG
1961 """What is assigned to by the comprehension."""
1962
1963 iter: NodeNG
1964 """What is iterated over by the comprehension."""
1965
1966 ifs: list[NodeNG]
1967 """The contents of any if statements that filter the comprehension."""
1968
1969 is_async: bool
1970 """Whether this is an asynchronous comprehension or not."""
1971
1972 def postinit(
1973 self,
1974 target: NodeNG,
1975 iter: NodeNG, # pylint: disable = redefined-builtin
1976 ifs: list[NodeNG],
1977 is_async: bool,
1978 ) -> None:
1979 self.target = target
1980 self.iter = iter
1981 self.ifs = ifs
1982 self.is_async = is_async
1983
1984 assigned_stmts = protocols.for_assigned_stmts
1985 """Returns the assigned statement (non inferred) according to the assignment type.
1986 See astroid/protocols.py for actual implementation.
1987 """
1988
1989 def assign_type(self):
1990 """The type of assignment that this node performs.
1991
1992 :returns: The assignment type.
1993 :rtype: NodeNG
1994 """
1995 return self
1996
1997 def _get_filtered_stmts(
1998 self, lookup_node, node, stmts, mystmt: _base_nodes.Statement | None
1999 ):
2000 """method used in filter_stmts"""
2001 if self is mystmt:
2002 if isinstance(lookup_node, (Const, Name)):
2003 return [lookup_node], True
2004
2005 elif self.statement() is mystmt:
2006 # original node's statement is the assignment, only keeps
2007 # current node (gen exp, list comp)
2008
2009 return [node], True
2010
2011 return stmts, False
2012
2013 def get_children(self):
2014 yield self.target
2015 yield self.iter
2016
2017 yield from self.ifs
2018
2019
2020class Const(_base_nodes.NoChildrenNode, Instance):
2021 """Class representing any constant including num, str, bool, None, bytes.
2022
2023 >>> import astroid
2024 >>> node = astroid.extract_node('(5, "This is a string.", True, None, b"bytes")')
2025 >>> node
2026 <Tuple.tuple l.1 at 0x7f23b2e358d0>
2027 >>> list(node.get_children())
2028 [<Const.int l.1 at 0x7f23b2e35940>,
2029 <Const.str l.1 at 0x7f23b2e35978>,
2030 <Const.bool l.1 at 0x7f23b2e359b0>,
2031 <Const.NoneType l.1 at 0x7f23b2e359e8>,
2032 <Const.bytes l.1 at 0x7f23b2e35a20>]
2033 """
2034
2035 _other_fields = ("value", "kind")
2036
2037 def __init__(
2038 self,
2039 value: Any,
2040 lineno: int | None = None,
2041 col_offset: int | None = None,
2042 parent: NodeNG | None = None,
2043 kind: str | None = None,
2044 *,
2045 end_lineno: int | None = None,
2046 end_col_offset: int | None = None,
2047 ) -> None:
2048 """
2049 :param value: The value that the constant represents.
2050
2051 :param lineno: The line that this node appears on in the source code.
2052
2053 :param col_offset: The column that this node appears on in the
2054 source code.
2055
2056 :param parent: The parent node in the syntax tree.
2057
2058 :param kind: The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only.
2059
2060 :param end_lineno: The last line this node appears on in the source code.
2061
2062 :param end_col_offset: The end column this node appears on in the
2063 source code. Note: This is after the last symbol.
2064 """
2065 self.value: Any = value
2066 """The value that the constant represents."""
2067
2068 self.kind: str | None = kind # can be None
2069 """"The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only."""
2070
2071 super().__init__(
2072 lineno=lineno,
2073 col_offset=col_offset,
2074 end_lineno=end_lineno,
2075 end_col_offset=end_col_offset,
2076 parent=parent,
2077 )
2078
2079 Instance.__init__(self, None)
2080
2081 infer_unary_op = protocols.const_infer_unary_op
2082 infer_binary_op = protocols.const_infer_binary_op
2083
2084 def __getattr__(self, name):
2085 # This is needed because of Proxy's __getattr__ method.
2086 # Calling object.__new__ on this class without calling
2087 # __init__ would result in an infinite loop otherwise
2088 # since __getattr__ is called when an attribute doesn't
2089 # exist and self._proxied indirectly calls self.value
2090 # and Proxy __getattr__ calls self.value
2091 if name == "value":
2092 raise AttributeError
2093 return super().__getattr__(name)
2094
2095 def getitem(self, index, context: InferenceContext | None = None):
2096 """Get an item from this node if subscriptable.
2097
2098 :param index: The node to use as a subscript index.
2099 :type index: Const or Slice
2100
2101 :raises AstroidTypeError: When the given index cannot be used as a
2102 subscript index, or if this node is not subscriptable.
2103 """
2104 if isinstance(index, Const):
2105 index_value = index.value
2106 elif isinstance(index, Slice):
2107 index_value = _infer_slice(index, context=context)
2108
2109 else:
2110 raise AstroidTypeError(
2111 f"Could not use type {type(index)} as subscript index"
2112 )
2113
2114 try:
2115 if isinstance(self.value, (str, bytes)):
2116 return Const(self.value[index_value])
2117 except ValueError as exc:
2118 raise AstroidValueError(
2119 f"Could not index {self.value!r} with {index_value!r}"
2120 ) from exc
2121 except IndexError as exc:
2122 raise AstroidIndexError(
2123 message="Index {index!r} out of range",
2124 node=self,
2125 index=index,
2126 context=context,
2127 ) from exc
2128 except TypeError as exc:
2129 raise AstroidTypeError(
2130 message="Type error {error!r}", node=self, index=index, context=context
2131 ) from exc
2132
2133 raise AstroidTypeError(f"{self!r} (value={self.value})")
2134
2135 def has_dynamic_getattr(self) -> bool:
2136 """Check if the node has a custom __getattr__ or __getattribute__.
2137
2138 :returns: Whether the class has a custom __getattr__ or __getattribute__.
2139 For a :class:`Const` this is always ``False``.
2140 """
2141 return False
2142
2143 def itered(self):
2144 """An iterator over the elements this node contains.
2145
2146 :returns: The contents of this node.
2147 :rtype: iterable(Const)
2148
2149 :raises TypeError: If this node does not represent something that is iterable.
2150 """
2151 if isinstance(self.value, str):
2152 return [const_factory(elem) for elem in self.value]
2153 raise TypeError(f"Cannot iterate over type {type(self.value)!r}")
2154
2155 def pytype(self) -> str:
2156 """Get the name of the type that this node represents.
2157
2158 :returns: The name of the type.
2159 """
2160 return self._proxied.qname()
2161
2162 def bool_value(self, context: InferenceContext | None = None):
2163 """Determine the boolean value of this node.
2164
2165 :returns: The boolean value of this node.
2166 :rtype: bool
2167 """
2168 return bool(self.value)
2169
2170 def _infer(
2171 self, context: InferenceContext | None = None, **kwargs: Any
2172 ) -> Iterator[Const]:
2173 yield self
2174
2175
2176class Continue(_base_nodes.NoChildrenNode, _base_nodes.Statement):
2177 """Class representing an :class:`ast.Continue` node.
2178
2179 >>> import astroid
2180 >>> node = astroid.extract_node('continue')
2181 >>> node
2182 <Continue l.1 at 0x7f23b2e35588>
2183 """
2184
2185
2186class Decorators(NodeNG):
2187 """A node representing a list of decorators.
2188
2189 A :class:`Decorators` is the decorators that are applied to
2190 a method or function.
2191
2192 >>> import astroid
2193 >>> node = astroid.extract_node('''
2194 @property
2195 def my_property(self):
2196 return 3
2197 ''')
2198 >>> node
2199 <FunctionDef.my_property l.2 at 0x7f23b2e35d30>
2200 >>> list(node.get_children())[0]
2201 <Decorators l.1 at 0x7f23b2e35d68>
2202 """
2203
2204 _astroid_fields = ("nodes",)
2205
2206 nodes: list[NodeNG]
2207 """The decorators that this node contains."""
2208
2209 def postinit(self, nodes: list[NodeNG]) -> None:
2210 self.nodes = nodes
2211
2212 def scope(self) -> LocalsDictNodeNG:
2213 """The first parent node defining a new scope.
2214 These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.
2215
2216 :returns: The first parent scope node.
2217 """
2218 # skip the function node to go directly to the upper level scope
2219 if not self.parent:
2220 raise ParentMissingError(target=self)
2221 if not self.parent.parent:
2222 raise ParentMissingError(target=self.parent)
2223 return self.parent.parent.scope()
2224
2225 def get_children(self):
2226 yield from self.nodes
2227
2228
2229class DelAttr(_base_nodes.ParentAssignNode):
2230 """Variation of :class:`ast.Delete` representing deletion of an attribute.
2231
2232 >>> import astroid
2233 >>> node = astroid.extract_node('del self.attr')
2234 >>> node
2235 <Delete l.1 at 0x7f23b2e35f60>
2236 >>> list(node.get_children())[0]
2237 <DelAttr.attr l.1 at 0x7f23b2e411d0>
2238 """
2239
2240 _astroid_fields = ("expr",)
2241 _other_fields = ("attrname",)
2242
2243 expr: NodeNG
2244 """The name that this node represents."""
2245
2246 def __init__(
2247 self,
2248 attrname: str,
2249 lineno: int,
2250 col_offset: int,
2251 parent: NodeNG,
2252 *,
2253 end_lineno: int | None,
2254 end_col_offset: int | None,
2255 ) -> None:
2256 self.attrname = attrname
2257 """The name of the attribute that is being deleted."""
2258
2259 super().__init__(
2260 lineno=lineno,
2261 col_offset=col_offset,
2262 end_lineno=end_lineno,
2263 end_col_offset=end_col_offset,
2264 parent=parent,
2265 )
2266
2267 def postinit(self, expr: NodeNG) -> None:
2268 self.expr = expr
2269
2270 def get_children(self):
2271 yield self.expr
2272
2273
2274class Delete(_base_nodes.AssignTypeNode, _base_nodes.Statement):
2275 """Class representing an :class:`ast.Delete` node.
2276
2277 A :class:`Delete` is a ``del`` statement this is deleting something.
2278
2279 >>> import astroid
2280 >>> node = astroid.extract_node('del self.attr')
2281 >>> node
2282 <Delete l.1 at 0x7f23b2e35f60>
2283 """
2284
2285 _astroid_fields = ("targets",)
2286
2287 def __init__(
2288 self,
2289 lineno: int,
2290 col_offset: int,
2291 parent: NodeNG,
2292 *,
2293 end_lineno: int | None,
2294 end_col_offset: int | None,
2295 ) -> None:
2296 self.targets: list[NodeNG] = []
2297 """What is being deleted."""
2298
2299 super().__init__(
2300 lineno=lineno,
2301 col_offset=col_offset,
2302 end_lineno=end_lineno,
2303 end_col_offset=end_col_offset,
2304 parent=parent,
2305 )
2306
2307 def postinit(self, targets: list[NodeNG]) -> None:
2308 self.targets = targets
2309
2310 def get_children(self):
2311 yield from self.targets
2312
2313
2314class Dict(NodeNG, Instance):
2315 """Class representing an :class:`ast.Dict` node.
2316
2317 A :class:`Dict` is a dictionary that is created with ``{}`` syntax.
2318
2319 >>> import astroid
2320 >>> node = astroid.extract_node('{1: "1"}')
2321 >>> node
2322 <Dict.dict l.1 at 0x7f23b2e35cc0>
2323 """
2324
2325 _astroid_fields = ("items",)
2326
2327 def __init__(
2328 self,
2329 lineno: int | None,
2330 col_offset: int | None,
2331 parent: NodeNG | None,
2332 *,
2333 end_lineno: int | None,
2334 end_col_offset: int | None,
2335 ) -> None:
2336 self.items: list[tuple[InferenceResult, InferenceResult]] = []
2337 """The key-value pairs contained in the dictionary."""
2338
2339 super().__init__(
2340 lineno=lineno,
2341 col_offset=col_offset,
2342 end_lineno=end_lineno,
2343 end_col_offset=end_col_offset,
2344 parent=parent,
2345 )
2346
2347 def postinit(self, items: list[tuple[InferenceResult, InferenceResult]]) -> None:
2348 """Do some setup after initialisation.
2349
2350 :param items: The key-value pairs contained in the dictionary.
2351 """
2352 self.items = items
2353
2354 infer_unary_op = protocols.dict_infer_unary_op
2355
2356 def pytype(self) -> Literal["builtins.dict"]:
2357 """Get the name of the type that this node represents.
2358
2359 :returns: The name of the type.
2360 """
2361 return "builtins.dict"
2362
2363 def get_children(self):
2364 """Get the key and value nodes below this node.
2365
2366 Children are returned in the order that they are defined in the source
2367 code, key first then the value.
2368
2369 :returns: The children.
2370 :rtype: iterable(NodeNG)
2371 """
2372 for key, value in self.items:
2373 yield key
2374 yield value
2375
2376 def last_child(self):
2377 """An optimized version of list(get_children())[-1]
2378
2379 :returns: The last child, or None if no children exist.
2380 :rtype: NodeNG or None
2381 """
2382 if self.items:
2383 return self.items[-1][1]
2384 return None
2385
2386 def itered(self):
2387 """An iterator over the keys this node contains.
2388
2389 :returns: The keys of this node.
2390 :rtype: iterable(NodeNG)
2391 """
2392 return [key for (key, _) in self.items]
2393
2394 def getitem(
2395 self, index: Const | Slice, context: InferenceContext | None = None
2396 ) -> NodeNG:
2397 """Get an item from this node.
2398
2399 :param index: The node to use as a subscript index.
2400
2401 :raises AstroidTypeError: When the given index cannot be used as a
2402 subscript index, or if this node is not subscriptable.
2403 :raises AstroidIndexError: If the given index does not exist in the
2404 dictionary.
2405 """
2406 for key, value in self.items:
2407 # TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}.
2408 if isinstance(key, DictUnpack):
2409 inferred_value = util.safe_infer(value, context)
2410 if not isinstance(inferred_value, Dict):
2411 continue
2412
2413 try:
2414 return inferred_value.getitem(index, context)
2415 except (AstroidTypeError, AstroidIndexError):
2416 continue
2417
2418 for inferredkey in key.infer(context):
2419 if isinstance(inferredkey, util.UninferableBase):
2420 continue
2421 if isinstance(inferredkey, Const) and isinstance(index, Const):
2422 if inferredkey.value == index.value:
2423 return value
2424
2425 raise AstroidIndexError(index)
2426
2427 def bool_value(self, context: InferenceContext | None = None):
2428 """Determine the boolean value of this node.
2429
2430 :returns: The boolean value of this node.
2431 :rtype: bool
2432 """
2433 return bool(self.items)
2434
2435 def _infer(
2436 self, context: InferenceContext | None = None, **kwargs: Any
2437 ) -> Iterator[nodes.Dict]:
2438 if not any(isinstance(k, DictUnpack) for k, _ in self.items):
2439 yield self
2440 else:
2441 items = self._infer_map(context)
2442 new_seq = type(self)(
2443 lineno=self.lineno,
2444 col_offset=self.col_offset,
2445 parent=self.parent,
2446 end_lineno=self.end_lineno,
2447 end_col_offset=self.end_col_offset,
2448 )
2449 new_seq.postinit(list(items.items()))
2450 yield new_seq
2451
2452 @staticmethod
2453 def _update_with_replacement(
2454 lhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult],
2455 rhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult],
2456 ) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]:
2457 """Delete nodes that equate to duplicate keys.
2458
2459 Since an astroid node doesn't 'equal' another node with the same value,
2460 this function uses the as_string method to make sure duplicate keys
2461 don't get through
2462
2463 Note that both the key and the value are astroid nodes
2464
2465 Fixes issue with DictUnpack causing duplicate keys
2466 in inferred Dict items
2467
2468 :param lhs_dict: Dictionary to 'merge' nodes into
2469 :param rhs_dict: Dictionary with nodes to pull from
2470 :return : merged dictionary of nodes
2471 """
2472 combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items())
2473 # Overwrite keys which have the same string values
2474 string_map = {key.as_string(): (key, value) for key, value in combined_dict}
2475 # Return to dictionary
2476 return dict(string_map.values())
2477
2478 def _infer_map(
2479 self, context: InferenceContext | None
2480 ) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]:
2481 """Infer all values based on Dict.items."""
2482 values: dict[SuccessfulInferenceResult, SuccessfulInferenceResult] = {}
2483 for name, value in self.items:
2484 if isinstance(name, DictUnpack):
2485 double_starred = util.safe_infer(value, context)
2486 if not double_starred:
2487 raise InferenceError
2488 if not isinstance(double_starred, Dict):
2489 raise InferenceError(node=self, context=context)
2490 unpack_items = double_starred._infer_map(context)
2491 values = self._update_with_replacement(values, unpack_items)
2492 else:
2493 key = util.safe_infer(name, context=context)
2494 safe_value = util.safe_infer(value, context=context)
2495 if any(not elem for elem in (key, safe_value)):
2496 raise InferenceError(node=self, context=context)
2497 # safe_value is SuccessfulInferenceResult as bool(Uninferable) == False
2498 values = self._update_with_replacement(values, {key: safe_value})
2499 return values
2500
2501
2502class Expr(_base_nodes.Statement):
2503 """Class representing an :class:`ast.Expr` node.
2504
2505 An :class:`Expr` is any expression that does not have its value used or
2506 stored.
2507
2508 >>> import astroid
2509 >>> node = astroid.extract_node('method()')
2510 >>> node
2511 <Call l.1 at 0x7f23b2e352b0>
2512 >>> node.parent
2513 <Expr l.1 at 0x7f23b2e35278>
2514 """
2515
2516 _astroid_fields = ("value",)
2517
2518 value: NodeNG
2519 """What the expression does."""
2520
2521 def postinit(self, value: NodeNG) -> None:
2522 self.value = value
2523
2524 def get_children(self):
2525 yield self.value
2526
2527 def _get_yield_nodes_skip_functions(self):
2528 if not self.value.is_function:
2529 yield from self.value._get_yield_nodes_skip_functions()
2530
2531 def _get_yield_nodes_skip_lambdas(self):
2532 if not self.value.is_lambda:
2533 yield from self.value._get_yield_nodes_skip_lambdas()
2534
2535
2536class EmptyNode(_base_nodes.NoChildrenNode):
2537 """Holds an arbitrary object in the :attr:`LocalsDictNodeNG.locals`."""
2538
2539 object = None
2540
2541 def __init__(
2542 self,
2543 lineno: None = None,
2544 col_offset: None = None,
2545 parent: None = None,
2546 *,
2547 end_lineno: None = None,
2548 end_col_offset: None = None,
2549 ) -> None:
2550 super().__init__(
2551 lineno=lineno,
2552 col_offset=col_offset,
2553 end_lineno=end_lineno,
2554 end_col_offset=end_col_offset,
2555 parent=parent,
2556 )
2557
2558 def has_underlying_object(self) -> bool:
2559 return self.object is not None and self.object is not _EMPTY_OBJECT_MARKER
2560
2561 @decorators.raise_if_nothing_inferred
2562 @decorators.path_wrapper
2563 def _infer(
2564 self, context: InferenceContext | None = None, **kwargs: Any
2565 ) -> Generator[InferenceResult, None, None]:
2566 if not self.has_underlying_object():
2567 yield util.Uninferable
2568 else:
2569 try:
2570 yield from AstroidManager().infer_ast_from_something(
2571 self.object, context=context
2572 )
2573 except AstroidError:
2574 yield util.Uninferable
2575
2576
2577class ExceptHandler(
2578 _base_nodes.MultiLineBlockNode, _base_nodes.AssignTypeNode, _base_nodes.Statement
2579):
2580 """Class representing an :class:`ast.ExceptHandler`. node.
2581
2582 An :class:`ExceptHandler` is an ``except`` block on a try-except.
2583
2584 >>> import astroid
2585 >>> node = astroid.extract_node('''
2586 try:
2587 do_something()
2588 except Exception as error:
2589 print("Error!")
2590 ''')
2591 >>> node
2592 <Try l.2 at 0x7f23b2e9d908>
2593 >>> node.handlers
2594 [<ExceptHandler l.4 at 0x7f23b2e9e860>]
2595 """
2596
2597 _astroid_fields = ("type", "name", "body")
2598 _multi_line_block_fields = ("body",)
2599
2600 type: NodeNG | None
2601 """The types that the block handles."""
2602
2603 name: AssignName | None
2604 """The name that the caught exception is assigned to."""
2605
2606 body: list[NodeNG]
2607 """The contents of the block."""
2608
2609 assigned_stmts = protocols.excepthandler_assigned_stmts
2610 """Returns the assigned statement (non inferred) according to the assignment type.
2611 See astroid/protocols.py for actual implementation.
2612 """
2613
2614 def postinit(
2615 self,
2616 type: NodeNG | None, # pylint: disable = redefined-builtin
2617 name: AssignName | None,
2618 body: list[NodeNG],
2619 ) -> None:
2620 self.type = type
2621 self.name = name
2622 self.body = body
2623
2624 def get_children(self):
2625 if self.type is not None:
2626 yield self.type
2627
2628 if self.name is not None:
2629 yield self.name
2630
2631 yield from self.body
2632
2633 @cached_property
2634 def blockstart_tolineno(self):
2635 """The line on which the beginning of this block ends.
2636
2637 :type: int
2638 """
2639 if self.name:
2640 return self.name.tolineno
2641 if self.type:
2642 return self.type.tolineno
2643 return self.lineno
2644
2645 def catch(self, exceptions: list[str] | None) -> bool:
2646 """Check if this node handles any of the given
2647
2648 :param exceptions: The names of the exceptions to check for.
2649 """
2650 if self.type is None or exceptions is None:
2651 return True
2652 return any(node.name in exceptions for node in self.type._get_name_nodes())
2653
2654
2655class For(
2656 _base_nodes.MultiLineWithElseBlockNode,
2657 _base_nodes.AssignTypeNode,
2658 _base_nodes.Statement,
2659):
2660 """Class representing an :class:`ast.For` node.
2661
2662 >>> import astroid
2663 >>> node = astroid.extract_node('for thing in things: print(thing)')
2664 >>> node
2665 <For l.1 at 0x7f23b2e8cf28>
2666 """
2667
2668 _astroid_fields = ("target", "iter", "body", "orelse")
2669 _other_other_fields = ("type_annotation",)
2670 _multi_line_block_fields = ("body", "orelse")
2671
2672 optional_assign = True
2673 """Whether this node optionally assigns a variable.
2674
2675 This is always ``True`` for :class:`For` nodes.
2676 """
2677
2678 target: NodeNG
2679 """What the loop assigns to."""
2680
2681 iter: NodeNG
2682 """What the loop iterates over."""
2683
2684 body: list[NodeNG]
2685 """The contents of the body of the loop."""
2686
2687 orelse: list[NodeNG]
2688 """The contents of the ``else`` block of the loop."""
2689
2690 type_annotation: NodeNG | None
2691 """If present, this will contain the type annotation passed by a type comment"""
2692
2693 def postinit(
2694 self,
2695 target: NodeNG,
2696 iter: NodeNG, # pylint: disable = redefined-builtin
2697 body: list[NodeNG],
2698 orelse: list[NodeNG],
2699 type_annotation: NodeNG | None,
2700 ) -> None:
2701 self.target = target
2702 self.iter = iter
2703 self.body = body
2704 self.orelse = orelse
2705 self.type_annotation = type_annotation
2706
2707 assigned_stmts = protocols.for_assigned_stmts
2708 """Returns the assigned statement (non inferred) according to the assignment type.
2709 See astroid/protocols.py for actual implementation.
2710 """
2711
2712 @cached_property
2713 def blockstart_tolineno(self):
2714 """The line on which the beginning of this block ends.
2715
2716 :type: int
2717 """
2718 return self.iter.tolineno
2719
2720 def get_children(self):
2721 yield self.target
2722 yield self.iter
2723
2724 yield from self.body
2725 yield from self.orelse
2726
2727
2728class AsyncFor(For):
2729 """Class representing an :class:`ast.AsyncFor` node.
2730
2731 An :class:`AsyncFor` is an asynchronous :class:`For` built with
2732 the ``async`` keyword.
2733
2734 >>> import astroid
2735 >>> node = astroid.extract_node('''
2736 async def func(things):
2737 async for thing in things:
2738 print(thing)
2739 ''')
2740 >>> node
2741 <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8>
2742 >>> node.body[0]
2743 <AsyncFor l.3 at 0x7f23b2e417b8>
2744 """
2745
2746
2747class Await(NodeNG):
2748 """Class representing an :class:`ast.Await` node.
2749
2750 An :class:`Await` is the ``await`` keyword.
2751
2752 >>> import astroid
2753 >>> node = astroid.extract_node('''
2754 async def func(things):
2755 await other_func()
2756 ''')
2757 >>> node
2758 <AsyncFunctionDef.func l.2 at 0x7f23b2e41748>
2759 >>> node.body[0]
2760 <Expr l.3 at 0x7f23b2e419e8>
2761 >>> list(node.body[0].get_children())[0]
2762 <Await l.3 at 0x7f23b2e41a20>
2763 """
2764
2765 _astroid_fields = ("value",)
2766
2767 value: NodeNG
2768 """What to wait for."""
2769
2770 def postinit(self, value: NodeNG) -> None:
2771 self.value = value
2772
2773 def get_children(self):
2774 yield self.value
2775
2776
2777class ImportFrom(_base_nodes.ImportNode):
2778 """Class representing an :class:`ast.ImportFrom` node.
2779
2780 >>> import astroid
2781 >>> node = astroid.extract_node('from my_package import my_module')
2782 >>> node
2783 <ImportFrom l.1 at 0x7f23b2e415c0>
2784 """
2785
2786 _other_fields = ("modname", "names", "level")
2787
2788 def __init__(
2789 self,
2790 fromname: str | None,
2791 names: list[tuple[str, str | None]],
2792 level: int | None = 0,
2793 lineno: int | None = None,
2794 col_offset: int | None = None,
2795 parent: NodeNG | None = None,
2796 *,
2797 end_lineno: int | None = None,
2798 end_col_offset: int | None = None,
2799 ) -> None:
2800 """
2801 :param fromname: The module that is being imported from.
2802
2803 :param names: What is being imported from the module.
2804
2805 :param level: The level of relative import.
2806
2807 :param lineno: The line that this node appears on in the source code.
2808
2809 :param col_offset: The column that this node appears on in the
2810 source code.
2811
2812 :param parent: The parent node in the syntax tree.
2813
2814 :param end_lineno: The last line this node appears on in the source code.
2815
2816 :param end_col_offset: The end column this node appears on in the
2817 source code. Note: This is after the last symbol.
2818 """
2819 self.modname: str | None = fromname # can be None
2820 """The module that is being imported from.
2821
2822 This is ``None`` for relative imports.
2823 """
2824
2825 self.names: list[tuple[str, str | None]] = names
2826 """What is being imported from the module.
2827
2828 Each entry is a :class:`tuple` of the name being imported,
2829 and the alias that the name is assigned to (if any).
2830 """
2831
2832 # TODO When is 'level' None?
2833 self.level: int | None = level # can be None
2834 """The level of relative import.
2835
2836 Essentially this is the number of dots in the import.
2837 This is always 0 for absolute imports.
2838 """
2839
2840 super().__init__(
2841 lineno=lineno,
2842 col_offset=col_offset,
2843 end_lineno=end_lineno,
2844 end_col_offset=end_col_offset,
2845 parent=parent,
2846 )
2847
2848 @decorators.raise_if_nothing_inferred
2849 @decorators.path_wrapper
2850 def _infer(
2851 self,
2852 context: InferenceContext | None = None,
2853 asname: bool = True,
2854 **kwargs: Any,
2855 ) -> Generator[InferenceResult, None, None]:
2856 """Infer a ImportFrom node: return the imported module/object."""
2857 context = context or InferenceContext()
2858 name = context.lookupname
2859 if name is None:
2860 raise InferenceError(node=self, context=context)
2861 if asname:
2862 try:
2863 name = self.real_name(name)
2864 except AttributeInferenceError as exc:
2865 # See https://github.com/pylint-dev/pylint/issues/4692
2866 raise InferenceError(node=self, context=context) from exc
2867 try:
2868 module = self.do_import_module()
2869 except AstroidBuildingError as exc:
2870 raise InferenceError(node=self, context=context) from exc
2871
2872 try:
2873 context = copy_context(context)
2874 context.lookupname = name
2875 stmts = module.getattr(name, ignore_locals=module is self.root())
2876 return _infer_stmts(stmts, context)
2877 except AttributeInferenceError as error:
2878 raise InferenceError(
2879 str(error), target=self, attribute=name, context=context
2880 ) from error
2881
2882
2883class Attribute(NodeNG):
2884 """Class representing an :class:`ast.Attribute` node."""
2885
2886 expr: NodeNG
2887
2888 _astroid_fields = ("expr",)
2889 _other_fields = ("attrname",)
2890
2891 def __init__(
2892 self,
2893 attrname: str,
2894 lineno: int,
2895 col_offset: int,
2896 parent: NodeNG,
2897 *,
2898 end_lineno: int | None,
2899 end_col_offset: int | None,
2900 ) -> None:
2901 self.attrname = attrname
2902 """The name of the attribute."""
2903
2904 super().__init__(
2905 lineno=lineno,
2906 col_offset=col_offset,
2907 end_lineno=end_lineno,
2908 end_col_offset=end_col_offset,
2909 parent=parent,
2910 )
2911
2912 def postinit(self, expr: NodeNG) -> None:
2913 self.expr = expr
2914
2915 def get_children(self):
2916 yield self.expr
2917
2918 @decorators.raise_if_nothing_inferred
2919 @decorators.path_wrapper
2920 def _infer(
2921 self, context: InferenceContext | None = None, **kwargs: Any
2922 ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
2923 return _infer_attribute(self, context, **kwargs)
2924
2925
2926class Global(_base_nodes.NoChildrenNode, _base_nodes.Statement):
2927 """Class representing an :class:`ast.Global` node.
2928
2929 >>> import astroid
2930 >>> node = astroid.extract_node('global a_global')
2931 >>> node
2932 <Global l.1 at 0x7f23b2e9de10>
2933 """
2934
2935 _other_fields = ("names",)
2936
2937 def __init__(
2938 self,
2939 names: list[str],
2940 lineno: int | None = None,
2941 col_offset: int | None = None,
2942 parent: NodeNG | None = None,
2943 *,
2944 end_lineno: int | None = None,
2945 end_col_offset: int | None = None,
2946 ) -> None:
2947 """
2948 :param names: The names being declared as global.
2949
2950 :param lineno: The line that this node appears on in the source code.
2951
2952 :param col_offset: The column that this node appears on in the
2953 source code.
2954
2955 :param parent: The parent node in the syntax tree.
2956
2957 :param end_lineno: The last line this node appears on in the source code.
2958
2959 :param end_col_offset: The end column this node appears on in the
2960 source code. Note: This is after the last symbol.
2961 """
2962 self.names: list[str] = names
2963 """The names being declared as global."""
2964
2965 super().__init__(
2966 lineno=lineno,
2967 col_offset=col_offset,
2968 end_lineno=end_lineno,
2969 end_col_offset=end_col_offset,
2970 parent=parent,
2971 )
2972
2973 def _infer_name(self, frame, name):
2974 return name
2975
2976 @decorators.raise_if_nothing_inferred
2977 @decorators.path_wrapper
2978 def _infer(
2979 self, context: InferenceContext | None = None, **kwargs: Any
2980 ) -> Generator[InferenceResult, None, None]:
2981 if context is None or context.lookupname is None:
2982 raise InferenceError(node=self, context=context)
2983 try:
2984 # pylint: disable-next=no-member
2985 return _infer_stmts(self.root().getattr(context.lookupname), context)
2986 except AttributeInferenceError as error:
2987 raise InferenceError(
2988 str(error), target=self, attribute=context.lookupname, context=context
2989 ) from error
2990
2991
2992class If(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
2993 """Class representing an :class:`ast.If` node.
2994
2995 >>> import astroid
2996 >>> node = astroid.extract_node('if condition: print(True)')
2997 >>> node
2998 <If l.1 at 0x7f23b2e9dd30>
2999 """
3000
3001 _astroid_fields = ("test", "body", "orelse")
3002 _multi_line_block_fields = ("body", "orelse")
3003
3004 test: NodeNG
3005 """The condition that the statement tests."""
3006
3007 body: list[NodeNG]
3008 """The contents of the block."""
3009
3010 orelse: list[NodeNG]
3011 """The contents of the ``else`` block."""
3012
3013 def postinit(self, test: NodeNG, body: list[NodeNG], orelse: list[NodeNG]) -> None:
3014 self.test = test
3015 self.body = body
3016 self.orelse = orelse
3017
3018 @cached_property
3019 def blockstart_tolineno(self):
3020 """The line on which the beginning of this block ends.
3021
3022 :type: int
3023 """
3024 return self.test.tolineno
3025
3026 def block_range(self, lineno: int) -> tuple[int, int]:
3027 """Get a range from the given line number to where this node ends.
3028
3029 :param lineno: The line number to start the range at.
3030
3031 :returns: The range of line numbers that this node belongs to,
3032 starting at the given line number.
3033 """
3034 if lineno == self.body[0].fromlineno:
3035 return lineno, lineno
3036 if lineno <= self.body[-1].tolineno:
3037 return lineno, self.body[-1].tolineno
3038 return self._elsed_block_range(lineno, self.orelse, self.body[0].fromlineno - 1)
3039
3040 def get_children(self):
3041 yield self.test
3042
3043 yield from self.body
3044 yield from self.orelse
3045
3046 def has_elif_block(self):
3047 return len(self.orelse) == 1 and isinstance(self.orelse[0], If)
3048
3049 def _get_yield_nodes_skip_functions(self):
3050 """An If node can contain a Yield node in the test"""
3051 yield from self.test._get_yield_nodes_skip_functions()
3052 yield from super()._get_yield_nodes_skip_functions()
3053
3054 def _get_yield_nodes_skip_lambdas(self):
3055 """An If node can contain a Yield node in the test"""
3056 yield from self.test._get_yield_nodes_skip_lambdas()
3057 yield from super()._get_yield_nodes_skip_lambdas()
3058
3059
3060class IfExp(NodeNG):
3061 """Class representing an :class:`ast.IfExp` node.
3062 >>> import astroid
3063 >>> node = astroid.extract_node('value if condition else other')
3064 >>> node
3065 <IfExp l.1 at 0x7f23b2e9dbe0>
3066 """
3067
3068 _astroid_fields = ("test", "body", "orelse")
3069
3070 test: NodeNG
3071 """The condition that the statement tests."""
3072
3073 body: NodeNG
3074 """The contents of the block."""
3075
3076 orelse: NodeNG
3077 """The contents of the ``else`` block."""
3078
3079 def postinit(self, test: NodeNG, body: NodeNG, orelse: NodeNG) -> None:
3080 self.test = test
3081 self.body = body
3082 self.orelse = orelse
3083
3084 def get_children(self):
3085 yield self.test
3086 yield self.body
3087 yield self.orelse
3088
3089 def op_left_associative(self) -> Literal[False]:
3090 # `1 if True else 2 if False else 3` is parsed as
3091 # `1 if True else (2 if False else 3)`
3092 return False
3093
3094 @decorators.raise_if_nothing_inferred
3095 def _infer(
3096 self, context: InferenceContext | None = None, **kwargs: Any
3097 ) -> Generator[InferenceResult, None, None]:
3098 """Support IfExp inference.
3099
3100 If we can't infer the truthiness of the condition, we default
3101 to inferring both branches. Otherwise, we infer either branch
3102 depending on the condition.
3103 """
3104 both_branches = False
3105 # We use two separate contexts for evaluating lhs and rhs because
3106 # evaluating lhs may leave some undesired entries in context.path
3107 # which may not let us infer right value of rhs.
3108
3109 context = context or InferenceContext()
3110 lhs_context = copy_context(context)
3111 rhs_context = copy_context(context)
3112 try:
3113 test = next(self.test.infer(context=context.clone()))
3114 except (InferenceError, StopIteration):
3115 both_branches = True
3116 else:
3117 if not isinstance(test, util.UninferableBase):
3118 if test.bool_value():
3119 yield from self.body.infer(context=lhs_context)
3120 else:
3121 yield from self.orelse.infer(context=rhs_context)
3122 else:
3123 both_branches = True
3124 if both_branches:
3125 yield from self.body.infer(context=lhs_context)
3126 yield from self.orelse.infer(context=rhs_context)
3127
3128
3129class Import(_base_nodes.ImportNode):
3130 """Class representing an :class:`ast.Import` node.
3131 >>> import astroid
3132 >>> node = astroid.extract_node('import astroid')
3133 >>> node
3134 <Import l.1 at 0x7f23b2e4e5c0>
3135 """
3136
3137 _other_fields = ("names",)
3138
3139 def __init__(
3140 self,
3141 names: list[tuple[str, str | None]],
3142 lineno: int | None = None,
3143 col_offset: int | None = None,
3144 parent: NodeNG | None = None,
3145 *,
3146 end_lineno: int | None = None,
3147 end_col_offset: int | None = None,
3148 ) -> None:
3149 """
3150 :param names: The names being imported.
3151
3152 :param lineno: The line that this node appears on in the source code.
3153
3154 :param col_offset: The column that this node appears on in the
3155 source code.
3156
3157 :param parent: The parent node in the syntax tree.
3158
3159 :param end_lineno: The last line this node appears on in the source code.
3160
3161 :param end_col_offset: The end column this node appears on in the
3162 source code. Note: This is after the last symbol.
3163 """
3164 self.names: list[tuple[str, str | None]] = names
3165 """The names being imported.
3166
3167 Each entry is a :class:`tuple` of the name being imported,
3168 and the alias that the name is assigned to (if any).
3169 """
3170
3171 super().__init__(
3172 lineno=lineno,
3173 col_offset=col_offset,
3174 end_lineno=end_lineno,
3175 end_col_offset=end_col_offset,
3176 parent=parent,
3177 )
3178
3179 @decorators.raise_if_nothing_inferred
3180 @decorators.path_wrapper
3181 def _infer(
3182 self,
3183 context: InferenceContext | None = None,
3184 asname: bool = True,
3185 **kwargs: Any,
3186 ) -> Generator[nodes.Module, None, None]:
3187 """Infer an Import node: return the imported module/object."""
3188 context = context or InferenceContext()
3189 name = context.lookupname
3190 if name is None:
3191 raise InferenceError(node=self, context=context)
3192
3193 try:
3194 if asname:
3195 yield self.do_import_module(self.real_name(name))
3196 else:
3197 yield self.do_import_module(name)
3198 except AstroidBuildingError as exc:
3199 raise InferenceError(node=self, context=context) from exc
3200
3201
3202class Keyword(NodeNG):
3203 """Class representing an :class:`ast.keyword` node.
3204
3205 >>> import astroid
3206 >>> node = astroid.extract_node('function(a_kwarg=True)')
3207 >>> node
3208 <Call l.1 at 0x7f23b2e9e320>
3209 >>> node.keywords
3210 [<Keyword l.1 at 0x7f23b2e9e9b0>]
3211 """
3212
3213 _astroid_fields = ("value",)
3214 _other_fields = ("arg",)
3215
3216 value: NodeNG
3217 """The value being assigned to the keyword argument."""
3218
3219 def __init__(
3220 self,
3221 arg: str | None,
3222 lineno: int | None,
3223 col_offset: int | None,
3224 parent: NodeNG,
3225 *,
3226 end_lineno: int | None,
3227 end_col_offset: int | None,
3228 ) -> None:
3229 self.arg = arg
3230 """The argument being assigned to."""
3231
3232 super().__init__(
3233 lineno=lineno,
3234 col_offset=col_offset,
3235 end_lineno=end_lineno,
3236 end_col_offset=end_col_offset,
3237 parent=parent,
3238 )
3239
3240 def postinit(self, value: NodeNG) -> None:
3241 self.value = value
3242
3243 def get_children(self):
3244 yield self.value
3245
3246
3247class List(BaseContainer):
3248 """Class representing an :class:`ast.List` node.
3249
3250 >>> import astroid
3251 >>> node = astroid.extract_node('[1, 2, 3]')
3252 >>> node
3253 <List.list l.1 at 0x7f23b2e9e128>
3254 """
3255
3256 _other_fields = ("ctx",)
3257
3258 def __init__(
3259 self,
3260 ctx: Context | None = None,
3261 lineno: int | None = None,
3262 col_offset: int | None = None,
3263 parent: NodeNG | None = None,
3264 *,
3265 end_lineno: int | None = None,
3266 end_col_offset: int | None = None,
3267 ) -> None:
3268 """
3269 :param ctx: Whether the list is assigned to or loaded from.
3270
3271 :param lineno: The line that this node appears on in the source code.
3272
3273 :param col_offset: The column that this node appears on in the
3274 source code.
3275
3276 :param parent: The parent node in the syntax tree.
3277
3278 :param end_lineno: The last line this node appears on in the source code.
3279
3280 :param end_col_offset: The end column this node appears on in the
3281 source code. Note: This is after the last symbol.
3282 """
3283 self.ctx: Context | None = ctx
3284 """Whether the list is assigned to or loaded from."""
3285
3286 super().__init__(
3287 lineno=lineno,
3288 col_offset=col_offset,
3289 end_lineno=end_lineno,
3290 end_col_offset=end_col_offset,
3291 parent=parent,
3292 )
3293
3294 assigned_stmts = protocols.sequence_assigned_stmts
3295 """Returns the assigned statement (non inferred) according to the assignment type.
3296 See astroid/protocols.py for actual implementation.
3297 """
3298
3299 infer_unary_op = protocols.list_infer_unary_op
3300 infer_binary_op = protocols.tl_infer_binary_op
3301
3302 def pytype(self) -> Literal["builtins.list"]:
3303 """Get the name of the type that this node represents.
3304
3305 :returns: The name of the type.
3306 """
3307 return "builtins.list"
3308
3309 def getitem(self, index, context: InferenceContext | None = None):
3310 """Get an item from this node.
3311
3312 :param index: The node to use as a subscript index.
3313 :type index: Const or Slice
3314 """
3315 return _container_getitem(self, self.elts, index, context=context)
3316
3317
3318class Nonlocal(_base_nodes.NoChildrenNode, _base_nodes.Statement):
3319 """Class representing an :class:`ast.Nonlocal` node.
3320
3321 >>> import astroid
3322 >>> node = astroid.extract_node('''
3323 def function():
3324 nonlocal var
3325 ''')
3326 >>> node
3327 <FunctionDef.function l.2 at 0x7f23b2e9e208>
3328 >>> node.body[0]
3329 <Nonlocal l.3 at 0x7f23b2e9e908>
3330 """
3331
3332 _other_fields = ("names",)
3333
3334 def __init__(
3335 self,
3336 names: list[str],
3337 lineno: int | None = None,
3338 col_offset: int | None = None,
3339 parent: NodeNG | None = None,
3340 *,
3341 end_lineno: int | None = None,
3342 end_col_offset: int | None = None,
3343 ) -> None:
3344 """
3345 :param names: The names being declared as not local.
3346
3347 :param lineno: The line that this node appears on in the source code.
3348
3349 :param col_offset: The column that this node appears on in the
3350 source code.
3351
3352 :param parent: The parent node in the syntax tree.
3353
3354 :param end_lineno: The last line this node appears on in the source code.
3355
3356 :param end_col_offset: The end column this node appears on in the
3357 source code. Note: This is after the last symbol.
3358 """
3359 self.names: list[str] = names
3360 """The names being declared as not local."""
3361
3362 super().__init__(
3363 lineno=lineno,
3364 col_offset=col_offset,
3365 end_lineno=end_lineno,
3366 end_col_offset=end_col_offset,
3367 parent=parent,
3368 )
3369
3370 def _infer_name(self, frame, name):
3371 return name
3372
3373
3374class ParamSpec(_base_nodes.AssignTypeNode):
3375 """Class representing a :class:`ast.ParamSpec` node.
3376
3377 >>> import astroid
3378 >>> node = astroid.extract_node('type Alias[**P] = Callable[P, int]')
3379 >>> node.type_params[0]
3380 <ParamSpec l.1 at 0x7f23b2e4e198>
3381 """
3382
3383 _astroid_fields = ("name",)
3384
3385 name: AssignName
3386
3387 def __init__(
3388 self,
3389 lineno: int,
3390 col_offset: int,
3391 parent: NodeNG,
3392 *,
3393 end_lineno: int,
3394 end_col_offset: int,
3395 ) -> None:
3396 super().__init__(
3397 lineno=lineno,
3398 col_offset=col_offset,
3399 end_lineno=end_lineno,
3400 end_col_offset=end_col_offset,
3401 parent=parent,
3402 )
3403
3404 def postinit(self, *, name: AssignName) -> None:
3405 self.name = name
3406
3407 def _infer(
3408 self, context: InferenceContext | None = None, **kwargs: Any
3409 ) -> Iterator[ParamSpec]:
3410 yield self
3411
3412 assigned_stmts = protocols.generic_type_assigned_stmts
3413 """Returns the assigned statement (non inferred) according to the assignment type.
3414 See astroid/protocols.py for actual implementation.
3415 """
3416
3417
3418class Pass(_base_nodes.NoChildrenNode, _base_nodes.Statement):
3419 """Class representing an :class:`ast.Pass` node.
3420
3421 >>> import astroid
3422 >>> node = astroid.extract_node('pass')
3423 >>> node
3424 <Pass l.1 at 0x7f23b2e9e748>
3425 """
3426
3427
3428class Raise(_base_nodes.Statement):
3429 """Class representing an :class:`ast.Raise` node.
3430
3431 >>> import astroid
3432 >>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")')
3433 >>> node
3434 <Raise l.1 at 0x7f23b2e9e828>
3435 """
3436
3437 _astroid_fields = ("exc", "cause")
3438
3439 exc: NodeNG | None
3440 """What is being raised."""
3441
3442 cause: NodeNG | None
3443 """The exception being used to raise this one."""
3444
3445 def postinit(
3446 self,
3447 exc: NodeNG | None,
3448 cause: NodeNG | None,
3449 ) -> None:
3450 self.exc = exc
3451 self.cause = cause
3452
3453 def raises_not_implemented(self) -> bool:
3454 """Check if this node raises a :class:`NotImplementedError`.
3455
3456 :returns: Whether this node raises a :class:`NotImplementedError`.
3457 """
3458 if not self.exc:
3459 return False
3460 return any(
3461 name.name == "NotImplementedError" for name in self.exc._get_name_nodes()
3462 )
3463
3464 def get_children(self):
3465 if self.exc is not None:
3466 yield self.exc
3467
3468 if self.cause is not None:
3469 yield self.cause
3470
3471
3472class Return(_base_nodes.Statement):
3473 """Class representing an :class:`ast.Return` node.
3474
3475 >>> import astroid
3476 >>> node = astroid.extract_node('return True')
3477 >>> node
3478 <Return l.1 at 0x7f23b8211908>
3479 """
3480
3481 _astroid_fields = ("value",)
3482
3483 value: NodeNG | None
3484 """The value being returned."""
3485
3486 def postinit(self, value: NodeNG | None) -> None:
3487 self.value = value
3488
3489 def get_children(self):
3490 if self.value is not None:
3491 yield self.value
3492
3493 def is_tuple_return(self):
3494 return isinstance(self.value, Tuple)
3495
3496 def _get_return_nodes_skip_functions(self):
3497 yield self
3498
3499
3500class Set(BaseContainer):
3501 """Class representing an :class:`ast.Set` node.
3502
3503 >>> import astroid
3504 >>> node = astroid.extract_node('{1, 2, 3}')
3505 >>> node
3506 <Set.set l.1 at 0x7f23b2e71d68>
3507 """
3508
3509 infer_unary_op = protocols.set_infer_unary_op
3510
3511 def pytype(self) -> Literal["builtins.set"]:
3512 """Get the name of the type that this node represents.
3513
3514 :returns: The name of the type.
3515 """
3516 return "builtins.set"
3517
3518
3519class Slice(NodeNG):
3520 """Class representing an :class:`ast.Slice` node.
3521
3522 >>> import astroid
3523 >>> node = astroid.extract_node('things[1:3]')
3524 >>> node
3525 <Subscript l.1 at 0x7f23b2e71f60>
3526 >>> node.slice
3527 <Slice l.1 at 0x7f23b2e71e80>
3528 """
3529
3530 _astroid_fields = ("lower", "upper", "step")
3531
3532 lower: NodeNG | None
3533 """The lower index in the slice."""
3534
3535 upper: NodeNG | None
3536 """The upper index in the slice."""
3537
3538 step: NodeNG | None
3539 """The step to take between indexes."""
3540
3541 def postinit(
3542 self,
3543 lower: NodeNG | None,
3544 upper: NodeNG | None,
3545 step: NodeNG | None,
3546 ) -> None:
3547 self.lower = lower
3548 self.upper = upper
3549 self.step = step
3550
3551 def _wrap_attribute(self, attr):
3552 """Wrap the empty attributes of the Slice in a Const node."""
3553 if not attr:
3554 const = const_factory(attr)
3555 const.parent = self
3556 return const
3557 return attr
3558
3559 @cached_property
3560 def _proxied(self) -> nodes.ClassDef:
3561 builtins = AstroidManager().builtins_module
3562 return builtins.getattr("slice")[0]
3563
3564 def pytype(self) -> Literal["builtins.slice"]:
3565 """Get the name of the type that this node represents.
3566
3567 :returns: The name of the type.
3568 """
3569 return "builtins.slice"
3570
3571 def display_type(self) -> Literal["Slice"]:
3572 """A human readable type of this node.
3573
3574 :returns: The type of this node.
3575 """
3576 return "Slice"
3577
3578 def igetattr(
3579 self, attrname: str, context: InferenceContext | None = None
3580 ) -> Iterator[SuccessfulInferenceResult]:
3581 """Infer the possible values of the given attribute on the slice.
3582
3583 :param attrname: The name of the attribute to infer.
3584
3585 :returns: The inferred possible values.
3586 """
3587 if attrname == "start":
3588 yield self._wrap_attribute(self.lower)
3589 elif attrname == "stop":
3590 yield self._wrap_attribute(self.upper)
3591 elif attrname == "step":
3592 yield self._wrap_attribute(self.step)
3593 else:
3594 yield from self.getattr(attrname, context=context)
3595
3596 def getattr(self, attrname, context: InferenceContext | None = None):
3597 return self._proxied.getattr(attrname, context)
3598
3599 def get_children(self):
3600 if self.lower is not None:
3601 yield self.lower
3602
3603 if self.upper is not None:
3604 yield self.upper
3605
3606 if self.step is not None:
3607 yield self.step
3608
3609 def _infer(
3610 self, context: InferenceContext | None = None, **kwargs: Any
3611 ) -> Iterator[Slice]:
3612 yield self
3613
3614
3615class Starred(_base_nodes.ParentAssignNode):
3616 """Class representing an :class:`ast.Starred` node.
3617
3618 >>> import astroid
3619 >>> node = astroid.extract_node('*args')
3620 >>> node
3621 <Starred l.1 at 0x7f23b2e41978>
3622 """
3623
3624 _astroid_fields = ("value",)
3625 _other_fields = ("ctx",)
3626
3627 value: NodeNG
3628 """What is being unpacked."""
3629
3630 def __init__(
3631 self,
3632 ctx: Context,
3633 lineno: int,
3634 col_offset: int,
3635 parent: NodeNG,
3636 *,
3637 end_lineno: int | None,
3638 end_col_offset: int | None,
3639 ) -> None:
3640 self.ctx = ctx
3641 """Whether the starred item is assigned to or loaded from."""
3642
3643 super().__init__(
3644 lineno=lineno,
3645 col_offset=col_offset,
3646 end_lineno=end_lineno,
3647 end_col_offset=end_col_offset,
3648 parent=parent,
3649 )
3650
3651 def postinit(self, value: NodeNG) -> None:
3652 self.value = value
3653
3654 assigned_stmts = protocols.starred_assigned_stmts
3655 """Returns the assigned statement (non inferred) according to the assignment type.
3656 See astroid/protocols.py for actual implementation.
3657 """
3658
3659 def get_children(self):
3660 yield self.value
3661
3662
3663class Subscript(NodeNG):
3664 """Class representing an :class:`ast.Subscript` node.
3665
3666 >>> import astroid
3667 >>> node = astroid.extract_node('things[1:3]')
3668 >>> node
3669 <Subscript l.1 at 0x7f23b2e71f60>
3670 """
3671
3672 _SUBSCRIPT_SENTINEL = object()
3673 _astroid_fields = ("value", "slice")
3674 _other_fields = ("ctx",)
3675
3676 value: NodeNG
3677 """What is being indexed."""
3678
3679 slice: NodeNG
3680 """The slice being used to lookup."""
3681
3682 def __init__(
3683 self,
3684 ctx: Context,
3685 lineno: int,
3686 col_offset: int,
3687 parent: NodeNG,
3688 *,
3689 end_lineno: int | None,
3690 end_col_offset: int | None,
3691 ) -> None:
3692 self.ctx = ctx
3693 """Whether the subscripted item is assigned to or loaded from."""
3694
3695 super().__init__(
3696 lineno=lineno,
3697 col_offset=col_offset,
3698 end_lineno=end_lineno,
3699 end_col_offset=end_col_offset,
3700 parent=parent,
3701 )
3702
3703 # pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
3704 def postinit(self, value: NodeNG, slice: NodeNG) -> None:
3705 self.value = value
3706 self.slice = slice
3707
3708 def get_children(self):
3709 yield self.value
3710 yield self.slice
3711
3712 def _infer_subscript(
3713 self, context: InferenceContext | None = None, **kwargs: Any
3714 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
3715 """Inference for subscripts.
3716
3717 We're understanding if the index is a Const
3718 or a slice, passing the result of inference
3719 to the value's `getitem` method, which should
3720 handle each supported index type accordingly.
3721 """
3722 from astroid import helpers # pylint: disable=import-outside-toplevel
3723
3724 found_one = False
3725 for value in self.value.infer(context):
3726 if isinstance(value, util.UninferableBase):
3727 yield util.Uninferable
3728 return None
3729 for index in self.slice.infer(context):
3730 if isinstance(index, util.UninferableBase):
3731 yield util.Uninferable
3732 return None
3733
3734 # Try to deduce the index value.
3735 index_value = self._SUBSCRIPT_SENTINEL
3736 if value.__class__ == Instance:
3737 index_value = index
3738 elif index.__class__ == Instance:
3739 instance_as_index = helpers.class_instance_as_index(index)
3740 if instance_as_index:
3741 index_value = instance_as_index
3742 else:
3743 index_value = index
3744
3745 if index_value is self._SUBSCRIPT_SENTINEL:
3746 raise InferenceError(node=self, context=context)
3747
3748 try:
3749 assigned = value.getitem(index_value, context)
3750 except (
3751 AstroidTypeError,
3752 AstroidIndexError,
3753 AstroidValueError,
3754 AttributeInferenceError,
3755 AttributeError,
3756 ) as exc:
3757 raise InferenceError(node=self, context=context) from exc
3758
3759 # Prevent inferring if the inferred subscript
3760 # is the same as the original subscripted object.
3761 if self is assigned or isinstance(assigned, util.UninferableBase):
3762 yield util.Uninferable
3763 return None
3764 yield from assigned.infer(context)
3765 found_one = True
3766
3767 if found_one:
3768 return InferenceErrorInfo(node=self, context=context)
3769 return None
3770
3771 @decorators.raise_if_nothing_inferred
3772 @decorators.path_wrapper
3773 def _infer(self, context: InferenceContext | None = None, **kwargs: Any):
3774 return self._infer_subscript(context, **kwargs)
3775
3776 @decorators.raise_if_nothing_inferred
3777 def infer_lhs(self, context: InferenceContext | None = None, **kwargs: Any):
3778 return self._infer_subscript(context, **kwargs)
3779
3780
3781class Try(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
3782 """Class representing a :class:`ast.Try` node.
3783
3784 >>> import astroid
3785 >>> node = astroid.extract_node('''
3786 try:
3787 do_something()
3788 except Exception as error:
3789 print("Error!")
3790 finally:
3791 print("Cleanup!")
3792 ''')
3793 >>> node
3794 <Try l.2 at 0x7f23b2e41d68>
3795 """
3796
3797 _astroid_fields = ("body", "handlers", "orelse", "finalbody")
3798 _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody")
3799
3800 def __init__(
3801 self,
3802 *,
3803 lineno: int,
3804 col_offset: int,
3805 end_lineno: int,
3806 end_col_offset: int,
3807 parent: NodeNG,
3808 ) -> None:
3809 """
3810 :param lineno: The line that this node appears on in the source code.
3811
3812 :param col_offset: The column that this node appears on in the
3813 source code.
3814
3815 :param parent: The parent node in the syntax tree.
3816
3817 :param end_lineno: The last line this node appears on in the source code.
3818
3819 :param end_col_offset: The end column this node appears on in the
3820 source code. Note: This is after the last symbol.
3821 """
3822 self.body: list[NodeNG] = []
3823 """The contents of the block to catch exceptions from."""
3824
3825 self.handlers: list[ExceptHandler] = []
3826 """The exception handlers."""
3827
3828 self.orelse: list[NodeNG] = []
3829 """The contents of the ``else`` block."""
3830
3831 self.finalbody: list[NodeNG] = []
3832 """The contents of the ``finally`` block."""
3833
3834 super().__init__(
3835 lineno=lineno,
3836 col_offset=col_offset,
3837 end_lineno=end_lineno,
3838 end_col_offset=end_col_offset,
3839 parent=parent,
3840 )
3841
3842 def postinit(
3843 self,
3844 *,
3845 body: list[NodeNG],
3846 handlers: list[ExceptHandler],
3847 orelse: list[NodeNG],
3848 finalbody: list[NodeNG],
3849 ) -> None:
3850 """Do some setup after initialisation.
3851
3852 :param body: The contents of the block to catch exceptions from.
3853
3854 :param handlers: The exception handlers.
3855
3856 :param orelse: The contents of the ``else`` block.
3857
3858 :param finalbody: The contents of the ``finally`` block.
3859 """
3860 self.body = body
3861 self.handlers = handlers
3862 self.orelse = orelse
3863 self.finalbody = finalbody
3864
3865 def _infer_name(self, frame, name):
3866 return name
3867
3868 def block_range(self, lineno: int) -> tuple[int, int]:
3869 """Get a range from a given line number to where this node ends."""
3870 if lineno == self.fromlineno:
3871 return lineno, lineno
3872 if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno:
3873 # Inside try body - return from lineno till end of try body
3874 return lineno, self.body[-1].tolineno
3875 for exhandler in self.handlers:
3876 if exhandler.type and lineno == exhandler.type.fromlineno:
3877 return lineno, lineno
3878 if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
3879 return lineno, exhandler.body[-1].tolineno
3880 if self.orelse:
3881 if self.orelse[0].fromlineno - 1 == lineno:
3882 return lineno, lineno
3883 if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno:
3884 return lineno, self.orelse[-1].tolineno
3885 if self.finalbody:
3886 if self.finalbody[0].fromlineno - 1 == lineno:
3887 return lineno, lineno
3888 if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno:
3889 return lineno, self.finalbody[-1].tolineno
3890 return lineno, self.tolineno
3891
3892 def get_children(self):
3893 yield from self.body
3894 yield from self.handlers
3895 yield from self.orelse
3896 yield from self.finalbody
3897
3898
3899class TryStar(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
3900 """Class representing an :class:`ast.TryStar` node."""
3901
3902 _astroid_fields = ("body", "handlers", "orelse", "finalbody")
3903 _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody")
3904
3905 def __init__(
3906 self,
3907 *,
3908 lineno: int | None = None,
3909 col_offset: int | None = None,
3910 end_lineno: int | None = None,
3911 end_col_offset: int | None = None,
3912 parent: NodeNG | None = None,
3913 ) -> None:
3914 """
3915 :param lineno: The line that this node appears on in the source code.
3916 :param col_offset: The column that this node appears on in the
3917 source code.
3918 :param parent: The parent node in the syntax tree.
3919 :param end_lineno: The last line this node appears on in the source code.
3920 :param end_col_offset: The end column this node appears on in the
3921 source code. Note: This is after the last symbol.
3922 """
3923 self.body: list[NodeNG] = []
3924 """The contents of the block to catch exceptions from."""
3925
3926 self.handlers: list[ExceptHandler] = []
3927 """The exception handlers."""
3928
3929 self.orelse: list[NodeNG] = []
3930 """The contents of the ``else`` block."""
3931
3932 self.finalbody: list[NodeNG] = []
3933 """The contents of the ``finally`` block."""
3934
3935 super().__init__(
3936 lineno=lineno,
3937 col_offset=col_offset,
3938 end_lineno=end_lineno,
3939 end_col_offset=end_col_offset,
3940 parent=parent,
3941 )
3942
3943 def postinit(
3944 self,
3945 *,
3946 body: list[NodeNG] | None = None,
3947 handlers: list[ExceptHandler] | None = None,
3948 orelse: list[NodeNG] | None = None,
3949 finalbody: list[NodeNG] | None = None,
3950 ) -> None:
3951 """Do some setup after initialisation.
3952 :param body: The contents of the block to catch exceptions from.
3953 :param handlers: The exception handlers.
3954 :param orelse: The contents of the ``else`` block.
3955 :param finalbody: The contents of the ``finally`` block.
3956 """
3957 if body:
3958 self.body = body
3959 if handlers:
3960 self.handlers = handlers
3961 if orelse:
3962 self.orelse = orelse
3963 if finalbody:
3964 self.finalbody = finalbody
3965
3966 def _infer_name(self, frame, name):
3967 return name
3968
3969 def block_range(self, lineno: int) -> tuple[int, int]:
3970 """Get a range from a given line number to where this node ends."""
3971 if lineno == self.fromlineno:
3972 return lineno, lineno
3973 if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno:
3974 # Inside try body - return from lineno till end of try body
3975 return lineno, self.body[-1].tolineno
3976 for exhandler in self.handlers:
3977 if exhandler.type and lineno == exhandler.type.fromlineno:
3978 return lineno, lineno
3979 if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
3980 return lineno, exhandler.body[-1].tolineno
3981 if self.orelse:
3982 if self.orelse[0].fromlineno - 1 == lineno:
3983 return lineno, lineno
3984 if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno:
3985 return lineno, self.orelse[-1].tolineno
3986 if self.finalbody:
3987 if self.finalbody[0].fromlineno - 1 == lineno:
3988 return lineno, lineno
3989 if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno:
3990 return lineno, self.finalbody[-1].tolineno
3991 return lineno, self.tolineno
3992
3993 def get_children(self):
3994 yield from self.body
3995 yield from self.handlers
3996 yield from self.orelse
3997 yield from self.finalbody
3998
3999
4000class Tuple(BaseContainer):
4001 """Class representing an :class:`ast.Tuple` node.
4002
4003 >>> import astroid
4004 >>> node = astroid.extract_node('(1, 2, 3)')
4005 >>> node
4006 <Tuple.tuple l.1 at 0x7f23b2e41780>
4007 """
4008
4009 _other_fields = ("ctx",)
4010
4011 def __init__(
4012 self,
4013 ctx: Context | None = None,
4014 lineno: int | None = None,
4015 col_offset: int | None = None,
4016 parent: NodeNG | None = None,
4017 *,
4018 end_lineno: int | None = None,
4019 end_col_offset: int | None = None,
4020 ) -> None:
4021 """
4022 :param ctx: Whether the tuple is assigned to or loaded from.
4023
4024 :param lineno: The line that this node appears on in the source code.
4025
4026 :param col_offset: The column that this node appears on in the
4027 source code.
4028
4029 :param parent: The parent node in the syntax tree.
4030
4031 :param end_lineno: The last line this node appears on in the source code.
4032
4033 :param end_col_offset: The end column this node appears on in the
4034 source code. Note: This is after the last symbol.
4035 """
4036 self.ctx: Context | None = ctx
4037 """Whether the tuple is assigned to or loaded from."""
4038
4039 super().__init__(
4040 lineno=lineno,
4041 col_offset=col_offset,
4042 end_lineno=end_lineno,
4043 end_col_offset=end_col_offset,
4044 parent=parent,
4045 )
4046
4047 assigned_stmts = protocols.sequence_assigned_stmts
4048 """Returns the assigned statement (non inferred) according to the assignment type.
4049 See astroid/protocols.py for actual implementation.
4050 """
4051
4052 infer_unary_op = protocols.tuple_infer_unary_op
4053 infer_binary_op = protocols.tl_infer_binary_op
4054
4055 def pytype(self) -> Literal["builtins.tuple"]:
4056 """Get the name of the type that this node represents.
4057
4058 :returns: The name of the type.
4059 """
4060 return "builtins.tuple"
4061
4062 def getitem(self, index, context: InferenceContext | None = None):
4063 """Get an item from this node.
4064
4065 :param index: The node to use as a subscript index.
4066 :type index: Const or Slice
4067 """
4068 return _container_getitem(self, self.elts, index, context=context)
4069
4070
4071class TypeAlias(_base_nodes.AssignTypeNode, _base_nodes.Statement):
4072 """Class representing a :class:`ast.TypeAlias` node.
4073
4074 >>> import astroid
4075 >>> node = astroid.extract_node('type Point = tuple[float, float]')
4076 >>> node
4077 <TypeAlias l.1 at 0x7f23b2e4e198>
4078 """
4079
4080 _astroid_fields = ("name", "type_params", "value")
4081
4082 name: AssignName
4083 type_params: list[TypeVar | ParamSpec | TypeVarTuple]
4084 value: NodeNG
4085
4086 def __init__(
4087 self,
4088 lineno: int,
4089 col_offset: int,
4090 parent: NodeNG,
4091 *,
4092 end_lineno: int,
4093 end_col_offset: int,
4094 ) -> None:
4095 super().__init__(
4096 lineno=lineno,
4097 col_offset=col_offset,
4098 end_lineno=end_lineno,
4099 end_col_offset=end_col_offset,
4100 parent=parent,
4101 )
4102
4103 def postinit(
4104 self,
4105 *,
4106 name: AssignName,
4107 type_params: list[TypeVar | ParamSpec | TypeVarTuple],
4108 value: NodeNG,
4109 ) -> None:
4110 self.name = name
4111 self.type_params = type_params
4112 self.value = value
4113
4114 def _infer(
4115 self, context: InferenceContext | None = None, **kwargs: Any
4116 ) -> Iterator[TypeAlias]:
4117 yield self
4118
4119 assigned_stmts: ClassVar[
4120 Callable[
4121 [
4122 TypeAlias,
4123 AssignName,
4124 InferenceContext | None,
4125 None,
4126 ],
4127 Generator[NodeNG, None, None],
4128 ]
4129 ] = protocols.assign_assigned_stmts
4130
4131
4132class TypeVar(_base_nodes.AssignTypeNode):
4133 """Class representing a :class:`ast.TypeVar` node.
4134
4135 >>> import astroid
4136 >>> node = astroid.extract_node('type Point[T] = tuple[float, float]')
4137 >>> node.type_params[0]
4138 <TypeVar l.1 at 0x7f23b2e4e198>
4139 """
4140
4141 _astroid_fields = ("name", "bound")
4142
4143 name: AssignName
4144 bound: NodeNG | None
4145
4146 def __init__(
4147 self,
4148 lineno: int,
4149 col_offset: int,
4150 parent: NodeNG,
4151 *,
4152 end_lineno: int,
4153 end_col_offset: int,
4154 ) -> None:
4155 super().__init__(
4156 lineno=lineno,
4157 col_offset=col_offset,
4158 end_lineno=end_lineno,
4159 end_col_offset=end_col_offset,
4160 parent=parent,
4161 )
4162
4163 def postinit(self, *, name: AssignName, bound: NodeNG | None) -> None:
4164 self.name = name
4165 self.bound = bound
4166
4167 def _infer(
4168 self, context: InferenceContext | None = None, **kwargs: Any
4169 ) -> Iterator[TypeVar]:
4170 yield self
4171
4172 assigned_stmts = protocols.generic_type_assigned_stmts
4173 """Returns the assigned statement (non inferred) according to the assignment type.
4174 See astroid/protocols.py for actual implementation.
4175 """
4176
4177
4178class TypeVarTuple(_base_nodes.AssignTypeNode):
4179 """Class representing a :class:`ast.TypeVarTuple` node.
4180
4181 >>> import astroid
4182 >>> node = astroid.extract_node('type Alias[*Ts] = tuple[*Ts]')
4183 >>> node.type_params[0]
4184 <TypeVarTuple l.1 at 0x7f23b2e4e198>
4185 """
4186
4187 _astroid_fields = ("name",)
4188
4189 name: AssignName
4190
4191 def __init__(
4192 self,
4193 lineno: int,
4194 col_offset: int,
4195 parent: NodeNG,
4196 *,
4197 end_lineno: int,
4198 end_col_offset: int,
4199 ) -> None:
4200 super().__init__(
4201 lineno=lineno,
4202 col_offset=col_offset,
4203 end_lineno=end_lineno,
4204 end_col_offset=end_col_offset,
4205 parent=parent,
4206 )
4207
4208 def postinit(self, *, name: AssignName) -> None:
4209 self.name = name
4210
4211 def _infer(
4212 self, context: InferenceContext | None = None, **kwargs: Any
4213 ) -> Iterator[TypeVarTuple]:
4214 yield self
4215
4216 assigned_stmts = protocols.generic_type_assigned_stmts
4217 """Returns the assigned statement (non inferred) according to the assignment type.
4218 See astroid/protocols.py for actual implementation.
4219 """
4220
4221
4222UNARY_OP_METHOD = {
4223 "+": "__pos__",
4224 "-": "__neg__",
4225 "~": "__invert__",
4226 "not": None, # XXX not '__nonzero__'
4227}
4228
4229
4230class UnaryOp(_base_nodes.OperatorNode):
4231 """Class representing an :class:`ast.UnaryOp` node.
4232
4233 >>> import astroid
4234 >>> node = astroid.extract_node('-5')
4235 >>> node
4236 <UnaryOp l.1 at 0x7f23b2e4e198>
4237 """
4238
4239 _astroid_fields = ("operand",)
4240 _other_fields = ("op",)
4241
4242 operand: NodeNG
4243 """What the unary operator is applied to."""
4244
4245 def __init__(
4246 self,
4247 op: str,
4248 lineno: int,
4249 col_offset: int,
4250 parent: NodeNG,
4251 *,
4252 end_lineno: int | None,
4253 end_col_offset: int | None,
4254 ) -> None:
4255 self.op = op
4256 """The operator."""
4257
4258 super().__init__(
4259 lineno=lineno,
4260 col_offset=col_offset,
4261 end_lineno=end_lineno,
4262 end_col_offset=end_col_offset,
4263 parent=parent,
4264 )
4265
4266 def postinit(self, operand: NodeNG) -> None:
4267 self.operand = operand
4268
4269 def type_errors(
4270 self, context: InferenceContext | None = None
4271 ) -> list[util.BadUnaryOperationMessage]:
4272 """Get a list of type errors which can occur during inference.
4273
4274 Each TypeError is represented by a :class:`BadUnaryOperationMessage`,
4275 which holds the original exception.
4276
4277 If any inferred result is uninferable, an empty list is returned.
4278 """
4279 bad = []
4280 try:
4281 for result in self._infer_unaryop(context=context):
4282 if result is util.Uninferable:
4283 raise InferenceError
4284 if isinstance(result, util.BadUnaryOperationMessage):
4285 bad.append(result)
4286 except InferenceError:
4287 return []
4288 return bad
4289
4290 def get_children(self):
4291 yield self.operand
4292
4293 def op_precedence(self):
4294 if self.op == "not":
4295 return OP_PRECEDENCE[self.op]
4296
4297 return super().op_precedence()
4298
4299 def _infer_unaryop(
4300 self: nodes.UnaryOp, context: InferenceContext | None = None, **kwargs: Any
4301 ) -> Generator[
4302 InferenceResult | util.BadUnaryOperationMessage, None, InferenceErrorInfo
4303 ]:
4304 """Infer what an UnaryOp should return when evaluated."""
4305 from astroid.nodes import ClassDef # pylint: disable=import-outside-toplevel
4306
4307 for operand in self.operand.infer(context):
4308 try:
4309 yield operand.infer_unary_op(self.op)
4310 except TypeError as exc:
4311 # The operand doesn't support this operation.
4312 yield util.BadUnaryOperationMessage(operand, self.op, exc)
4313 except AttributeError as exc:
4314 meth = UNARY_OP_METHOD[self.op]
4315 if meth is None:
4316 # `not node`. Determine node's boolean
4317 # value and negate its result, unless it is
4318 # Uninferable, which will be returned as is.
4319 bool_value = operand.bool_value()
4320 if not isinstance(bool_value, util.UninferableBase):
4321 yield const_factory(not bool_value)
4322 else:
4323 yield util.Uninferable
4324 else:
4325 if not isinstance(operand, (Instance, ClassDef)):
4326 # The operation was used on something which
4327 # doesn't support it.
4328 yield util.BadUnaryOperationMessage(operand, self.op, exc)
4329 continue
4330
4331 try:
4332 try:
4333 methods = dunder_lookup.lookup(operand, meth)
4334 except AttributeInferenceError:
4335 yield util.BadUnaryOperationMessage(operand, self.op, exc)
4336 continue
4337
4338 meth = methods[0]
4339 inferred = next(meth.infer(context=context), None)
4340 if (
4341 isinstance(inferred, util.UninferableBase)
4342 or not inferred.callable()
4343 ):
4344 continue
4345
4346 context = copy_context(context)
4347 context.boundnode = operand
4348 context.callcontext = CallContext(args=[], callee=inferred)
4349
4350 call_results = inferred.infer_call_result(self, context=context)
4351 result = next(call_results, None)
4352 if result is None:
4353 # Failed to infer, return the same type.
4354 yield operand
4355 else:
4356 yield result
4357 except AttributeInferenceError as inner_exc:
4358 # The unary operation special method was not found.
4359 yield util.BadUnaryOperationMessage(operand, self.op, inner_exc)
4360 except InferenceError:
4361 yield util.Uninferable
4362
4363 @decorators.raise_if_nothing_inferred
4364 @decorators.path_wrapper
4365 def _infer(
4366 self: nodes.UnaryOp, context: InferenceContext | None = None, **kwargs: Any
4367 ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
4368 """Infer what an UnaryOp should return when evaluated."""
4369 yield from self._filter_operation_errors(
4370 self._infer_unaryop, context, util.BadUnaryOperationMessage
4371 )
4372 return InferenceErrorInfo(node=self, context=context)
4373
4374
4375class While(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
4376 """Class representing an :class:`ast.While` node.
4377
4378 >>> import astroid
4379 >>> node = astroid.extract_node('''
4380 while condition():
4381 print("True")
4382 ''')
4383 >>> node
4384 <While l.2 at 0x7f23b2e4e390>
4385 """
4386
4387 _astroid_fields = ("test", "body", "orelse")
4388 _multi_line_block_fields = ("body", "orelse")
4389
4390 test: NodeNG
4391 """The condition that the loop tests."""
4392
4393 body: list[NodeNG]
4394 """The contents of the loop."""
4395
4396 orelse: list[NodeNG]
4397 """The contents of the ``else`` block."""
4398
4399 def postinit(
4400 self,
4401 test: NodeNG,
4402 body: list[NodeNG],
4403 orelse: list[NodeNG],
4404 ) -> None:
4405 self.test = test
4406 self.body = body
4407 self.orelse = orelse
4408
4409 @cached_property
4410 def blockstart_tolineno(self):
4411 """The line on which the beginning of this block ends.
4412
4413 :type: int
4414 """
4415 return self.test.tolineno
4416
4417 def block_range(self, lineno: int) -> tuple[int, int]:
4418 """Get a range from the given line number to where this node ends.
4419
4420 :param lineno: The line number to start the range at.
4421
4422 :returns: The range of line numbers that this node belongs to,
4423 starting at the given line number.
4424 """
4425 return self._elsed_block_range(lineno, self.orelse)
4426
4427 def get_children(self):
4428 yield self.test
4429
4430 yield from self.body
4431 yield from self.orelse
4432
4433 def _get_yield_nodes_skip_functions(self):
4434 """A While node can contain a Yield node in the test"""
4435 yield from self.test._get_yield_nodes_skip_functions()
4436 yield from super()._get_yield_nodes_skip_functions()
4437
4438 def _get_yield_nodes_skip_lambdas(self):
4439 """A While node can contain a Yield node in the test"""
4440 yield from self.test._get_yield_nodes_skip_lambdas()
4441 yield from super()._get_yield_nodes_skip_lambdas()
4442
4443
4444class With(
4445 _base_nodes.MultiLineWithElseBlockNode,
4446 _base_nodes.AssignTypeNode,
4447 _base_nodes.Statement,
4448):
4449 """Class representing an :class:`ast.With` node.
4450
4451 >>> import astroid
4452 >>> node = astroid.extract_node('''
4453 with open(file_path) as file_:
4454 print(file_.read())
4455 ''')
4456 >>> node
4457 <With l.2 at 0x7f23b2e4e710>
4458 """
4459
4460 _astroid_fields = ("items", "body")
4461 _other_other_fields = ("type_annotation",)
4462 _multi_line_block_fields = ("body",)
4463
4464 def __init__(
4465 self,
4466 lineno: int | None = None,
4467 col_offset: int | None = None,
4468 parent: NodeNG | None = None,
4469 *,
4470 end_lineno: int | None = None,
4471 end_col_offset: int | None = None,
4472 ) -> None:
4473 """
4474 :param lineno: The line that this node appears on in the source code.
4475
4476 :param col_offset: The column that this node appears on in the
4477 source code.
4478
4479 :param parent: The parent node in the syntax tree.
4480
4481 :param end_lineno: The last line this node appears on in the source code.
4482
4483 :param end_col_offset: The end column this node appears on in the
4484 source code. Note: This is after the last symbol.
4485 """
4486 self.items: list[tuple[NodeNG, NodeNG | None]] = []
4487 """The pairs of context managers and the names they are assigned to."""
4488
4489 self.body: list[NodeNG] = []
4490 """The contents of the ``with`` block."""
4491
4492 self.type_annotation: NodeNG | None = None # can be None
4493 """If present, this will contain the type annotation passed by a type comment"""
4494
4495 super().__init__(
4496 lineno=lineno,
4497 col_offset=col_offset,
4498 end_lineno=end_lineno,
4499 end_col_offset=end_col_offset,
4500 parent=parent,
4501 )
4502
4503 def postinit(
4504 self,
4505 items: list[tuple[NodeNG, NodeNG | None]] | None = None,
4506 body: list[NodeNG] | None = None,
4507 type_annotation: NodeNG | None = None,
4508 ) -> None:
4509 """Do some setup after initialisation.
4510
4511 :param items: The pairs of context managers and the names
4512 they are assigned to.
4513
4514 :param body: The contents of the ``with`` block.
4515 """
4516 if items is not None:
4517 self.items = items
4518 if body is not None:
4519 self.body = body
4520 self.type_annotation = type_annotation
4521
4522 assigned_stmts = protocols.with_assigned_stmts
4523 """Returns the assigned statement (non inferred) according to the assignment type.
4524 See astroid/protocols.py for actual implementation.
4525 """
4526
4527 @cached_property
4528 def blockstart_tolineno(self):
4529 """The line on which the beginning of this block ends.
4530
4531 :type: int
4532 """
4533 return self.items[-1][0].tolineno
4534
4535 def get_children(self):
4536 """Get the child nodes below this node.
4537
4538 :returns: The children.
4539 :rtype: iterable(NodeNG)
4540 """
4541 for expr, var in self.items:
4542 yield expr
4543 if var:
4544 yield var
4545 yield from self.body
4546
4547
4548class AsyncWith(With):
4549 """Asynchronous ``with`` built with the ``async`` keyword."""
4550
4551
4552class Yield(NodeNG):
4553 """Class representing an :class:`ast.Yield` node.
4554
4555 >>> import astroid
4556 >>> node = astroid.extract_node('yield True')
4557 >>> node
4558 <Yield l.1 at 0x7f23b2e4e5f8>
4559 """
4560
4561 _astroid_fields = ("value",)
4562
4563 value: NodeNG | None
4564 """The value to yield."""
4565
4566 def postinit(self, value: NodeNG | None) -> None:
4567 self.value = value
4568
4569 def get_children(self):
4570 if self.value is not None:
4571 yield self.value
4572
4573 def _get_yield_nodes_skip_functions(self):
4574 yield self
4575
4576 def _get_yield_nodes_skip_lambdas(self):
4577 yield self
4578
4579
4580class YieldFrom(Yield): # TODO value is required, not optional
4581 """Class representing an :class:`ast.YieldFrom` node."""
4582
4583
4584class DictUnpack(_base_nodes.NoChildrenNode):
4585 """Represents the unpacking of dicts into dicts using :pep:`448`."""
4586
4587
4588class FormattedValue(NodeNG):
4589 """Class representing an :class:`ast.FormattedValue` node.
4590
4591 Represents a :pep:`498` format string.
4592
4593 >>> import astroid
4594 >>> node = astroid.extract_node('f"Format {type_}"')
4595 >>> node
4596 <JoinedStr l.1 at 0x7f23b2e4ed30>
4597 >>> node.values
4598 [<Const.str l.1 at 0x7f23b2e4eda0>, <FormattedValue l.1 at 0x7f23b2e4edd8>]
4599 """
4600
4601 _astroid_fields = ("value", "format_spec")
4602 _other_fields = ("conversion",)
4603
4604 def __init__(
4605 self,
4606 lineno: int | None = None,
4607 col_offset: int | None = None,
4608 parent: NodeNG | None = None,
4609 *,
4610 end_lineno: int | None = None,
4611 end_col_offset: int | None = None,
4612 ) -> None:
4613 """
4614 :param lineno: The line that this node appears on in the source code.
4615
4616 :param col_offset: The column that this node appears on in the
4617 source code.
4618
4619 :param parent: The parent node in the syntax tree.
4620
4621 :param end_lineno: The last line this node appears on in the source code.
4622
4623 :param end_col_offset: The end column this node appears on in the
4624 source code. Note: This is after the last symbol.
4625 """
4626 self.value: NodeNG
4627 """The value to be formatted into the string."""
4628
4629 self.conversion: int
4630 """The type of formatting to be applied to the value.
4631
4632 .. seealso::
4633 :class:`ast.FormattedValue`
4634 """
4635
4636 self.format_spec: JoinedStr | None = None
4637 """The formatting to be applied to the value.
4638
4639 .. seealso::
4640 :class:`ast.FormattedValue`
4641 """
4642
4643 super().__init__(
4644 lineno=lineno,
4645 col_offset=col_offset,
4646 end_lineno=end_lineno,
4647 end_col_offset=end_col_offset,
4648 parent=parent,
4649 )
4650
4651 def postinit(
4652 self,
4653 *,
4654 value: NodeNG,
4655 conversion: int,
4656 format_spec: JoinedStr | None = None,
4657 ) -> None:
4658 """Do some setup after initialisation.
4659
4660 :param value: The value to be formatted into the string.
4661
4662 :param conversion: The type of formatting to be applied to the value.
4663
4664 :param format_spec: The formatting to be applied to the value.
4665 :type format_spec: JoinedStr or None
4666 """
4667 self.value = value
4668 self.conversion = conversion
4669 self.format_spec = format_spec
4670
4671 def get_children(self):
4672 yield self.value
4673
4674 if self.format_spec is not None:
4675 yield self.format_spec
4676
4677
4678class JoinedStr(NodeNG):
4679 """Represents a list of string expressions to be joined.
4680
4681 >>> import astroid
4682 >>> node = astroid.extract_node('f"Format {type_}"')
4683 >>> node
4684 <JoinedStr l.1 at 0x7f23b2e4ed30>
4685 """
4686
4687 _astroid_fields = ("values",)
4688
4689 def __init__(
4690 self,
4691 lineno: int | None = None,
4692 col_offset: int | None = None,
4693 parent: NodeNG | None = None,
4694 *,
4695 end_lineno: int | None = None,
4696 end_col_offset: int | None = None,
4697 ) -> None:
4698 """
4699 :param lineno: The line that this node appears on in the source code.
4700
4701 :param col_offset: The column that this node appears on in the
4702 source code.
4703
4704 :param parent: The parent node in the syntax tree.
4705
4706 :param end_lineno: The last line this node appears on in the source code.
4707
4708 :param end_col_offset: The end column this node appears on in the
4709 source code. Note: This is after the last symbol.
4710 """
4711 self.values: list[NodeNG] = []
4712 """The string expressions to be joined.
4713
4714 :type: list(FormattedValue or Const)
4715 """
4716
4717 super().__init__(
4718 lineno=lineno,
4719 col_offset=col_offset,
4720 end_lineno=end_lineno,
4721 end_col_offset=end_col_offset,
4722 parent=parent,
4723 )
4724
4725 def postinit(self, values: list[NodeNG] | None = None) -> None:
4726 """Do some setup after initialisation.
4727
4728 :param value: The string expressions to be joined.
4729
4730 :type: list(FormattedValue or Const)
4731 """
4732 if values is not None:
4733 self.values = values
4734
4735 def get_children(self):
4736 yield from self.values
4737
4738
4739class NamedExpr(_base_nodes.AssignTypeNode):
4740 """Represents the assignment from the assignment expression
4741
4742 >>> import astroid
4743 >>> module = astroid.parse('if a := 1: pass')
4744 >>> module.body[0].test
4745 <NamedExpr l.1 at 0x7f23b2e4ed30>
4746 """
4747
4748 _astroid_fields = ("target", "value")
4749
4750 optional_assign = True
4751 """Whether this node optionally assigns a variable.
4752
4753 Since NamedExpr are not always called they do not always assign."""
4754
4755 def __init__(
4756 self,
4757 lineno: int | None = None,
4758 col_offset: int | None = None,
4759 parent: NodeNG | None = None,
4760 *,
4761 end_lineno: int | None = None,
4762 end_col_offset: int | None = None,
4763 ) -> None:
4764 """
4765 :param lineno: The line that this node appears on in the source code.
4766
4767 :param col_offset: The column that this node appears on in the
4768 source code.
4769
4770 :param parent: The parent node in the syntax tree.
4771
4772 :param end_lineno: The last line this node appears on in the source code.
4773
4774 :param end_col_offset: The end column this node appears on in the
4775 source code. Note: This is after the last symbol.
4776 """
4777 self.target: NodeNG
4778 """The assignment target
4779
4780 :type: Name
4781 """
4782
4783 self.value: NodeNG
4784 """The value that gets assigned in the expression"""
4785
4786 super().__init__(
4787 lineno=lineno,
4788 col_offset=col_offset,
4789 end_lineno=end_lineno,
4790 end_col_offset=end_col_offset,
4791 parent=parent,
4792 )
4793
4794 def postinit(self, target: NodeNG, value: NodeNG) -> None:
4795 self.target = target
4796 self.value = value
4797
4798 assigned_stmts = protocols.named_expr_assigned_stmts
4799 """Returns the assigned statement (non inferred) according to the assignment type.
4800 See astroid/protocols.py for actual implementation.
4801 """
4802
4803 def frame(
4804 self, *, future: Literal[None, True] = None
4805 ) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda:
4806 """The first parent frame node.
4807
4808 A frame node is a :class:`Module`, :class:`FunctionDef`,
4809 or :class:`ClassDef`.
4810
4811 :returns: The first parent frame node.
4812 """
4813 if future is not None:
4814 warnings.warn(
4815 "The future arg will be removed in astroid 4.0.",
4816 DeprecationWarning,
4817 stacklevel=2,
4818 )
4819 if not self.parent:
4820 raise ParentMissingError(target=self)
4821
4822 # For certain parents NamedExpr evaluate to the scope of the parent
4823 if isinstance(self.parent, (Arguments, Keyword, Comprehension)):
4824 if not self.parent.parent:
4825 raise ParentMissingError(target=self.parent)
4826 if not self.parent.parent.parent:
4827 raise ParentMissingError(target=self.parent.parent)
4828 return self.parent.parent.parent.frame()
4829
4830 return self.parent.frame()
4831
4832 def scope(self) -> LocalsDictNodeNG:
4833 """The first parent node defining a new scope.
4834 These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.
4835
4836 :returns: The first parent scope node.
4837 """
4838 if not self.parent:
4839 raise ParentMissingError(target=self)
4840
4841 # For certain parents NamedExpr evaluate to the scope of the parent
4842 if isinstance(self.parent, (Arguments, Keyword, Comprehension)):
4843 if not self.parent.parent:
4844 raise ParentMissingError(target=self.parent)
4845 if not self.parent.parent.parent:
4846 raise ParentMissingError(target=self.parent.parent)
4847 return self.parent.parent.parent.scope()
4848
4849 return self.parent.scope()
4850
4851 def set_local(self, name: str, stmt: NodeNG) -> None:
4852 """Define that the given name is declared in the given statement node.
4853 NamedExpr's in Arguments, Keyword or Comprehension are evaluated in their
4854 parent's parent scope. So we add to their frame's locals.
4855
4856 .. seealso:: :meth:`scope`
4857
4858 :param name: The name that is being defined.
4859
4860 :param stmt: The statement that defines the given name.
4861 """
4862 self.frame().set_local(name, stmt)
4863
4864
4865class Unknown(_base_nodes.AssignTypeNode):
4866 """This node represents a node in a constructed AST where
4867 introspection is not possible. At the moment, it's only used in
4868 the args attribute of FunctionDef nodes where function signature
4869 introspection failed.
4870 """
4871
4872 name = "Unknown"
4873
4874 def __init__(
4875 self,
4876 lineno: None = None,
4877 col_offset: None = None,
4878 parent: None = None,
4879 *,
4880 end_lineno: None = None,
4881 end_col_offset: None = None,
4882 ) -> None:
4883 super().__init__(
4884 lineno=lineno,
4885 col_offset=col_offset,
4886 end_lineno=end_lineno,
4887 end_col_offset=end_col_offset,
4888 parent=parent,
4889 )
4890
4891 def qname(self) -> Literal["Unknown"]:
4892 return "Unknown"
4893
4894 def _infer(self, context: InferenceContext | None = None, **kwargs):
4895 """Inference on an Unknown node immediately terminates."""
4896 yield util.Uninferable
4897
4898
4899class EvaluatedObject(NodeNG):
4900 """Contains an object that has already been inferred
4901
4902 This class is useful to pre-evaluate a particular node,
4903 with the resulting class acting as the non-evaluated node.
4904 """
4905
4906 name = "EvaluatedObject"
4907 _astroid_fields = ("original",)
4908 _other_fields = ("value",)
4909
4910 def __init__(
4911 self, original: SuccessfulInferenceResult, value: InferenceResult
4912 ) -> None:
4913 self.original: SuccessfulInferenceResult = original
4914 """The original node that has already been evaluated"""
4915
4916 self.value: InferenceResult = value
4917 """The inferred value"""
4918
4919 super().__init__(
4920 lineno=self.original.lineno,
4921 col_offset=self.original.col_offset,
4922 parent=self.original.parent,
4923 end_lineno=self.original.end_lineno,
4924 end_col_offset=self.original.end_col_offset,
4925 )
4926
4927 def _infer(
4928 self, context: InferenceContext | None = None, **kwargs: Any
4929 ) -> Generator[NodeNG | util.UninferableBase, None, None]:
4930 yield self.value
4931
4932
4933# Pattern matching #######################################################
4934
4935
4936class Match(_base_nodes.Statement, _base_nodes.MultiLineBlockNode):
4937 """Class representing a :class:`ast.Match` node.
4938
4939 >>> import astroid
4940 >>> node = astroid.extract_node('''
4941 match x:
4942 case 200:
4943 ...
4944 case _:
4945 ...
4946 ''')
4947 >>> node
4948 <Match l.2 at 0x10c24e170>
4949 """
4950
4951 _astroid_fields = ("subject", "cases")
4952 _multi_line_block_fields = ("cases",)
4953
4954 def __init__(
4955 self,
4956 lineno: int | None = None,
4957 col_offset: int | None = None,
4958 parent: NodeNG | None = None,
4959 *,
4960 end_lineno: int | None = None,
4961 end_col_offset: int | None = None,
4962 ) -> None:
4963 self.subject: NodeNG
4964 self.cases: list[MatchCase]
4965 super().__init__(
4966 lineno=lineno,
4967 col_offset=col_offset,
4968 end_lineno=end_lineno,
4969 end_col_offset=end_col_offset,
4970 parent=parent,
4971 )
4972
4973 def postinit(
4974 self,
4975 *,
4976 subject: NodeNG,
4977 cases: list[MatchCase],
4978 ) -> None:
4979 self.subject = subject
4980 self.cases = cases
4981
4982
4983class Pattern(NodeNG):
4984 """Base class for all Pattern nodes."""
4985
4986
4987class MatchCase(_base_nodes.MultiLineBlockNode):
4988 """Class representing a :class:`ast.match_case` node.
4989
4990 >>> import astroid
4991 >>> node = astroid.extract_node('''
4992 match x:
4993 case 200:
4994 ...
4995 ''')
4996 >>> node.cases[0]
4997 <MatchCase l.3 at 0x10c24e590>
4998 """
4999
5000 _astroid_fields = ("pattern", "guard", "body")
5001 _multi_line_block_fields = ("body",)
5002
5003 lineno: None
5004 col_offset: None
5005 end_lineno: None
5006 end_col_offset: None
5007
5008 def __init__(self, *, parent: NodeNG | None = None) -> None:
5009 self.pattern: Pattern
5010 self.guard: NodeNG | None
5011 self.body: list[NodeNG]
5012 super().__init__(
5013 parent=parent,
5014 lineno=None,
5015 col_offset=None,
5016 end_lineno=None,
5017 end_col_offset=None,
5018 )
5019
5020 def postinit(
5021 self,
5022 *,
5023 pattern: Pattern,
5024 guard: NodeNG | None,
5025 body: list[NodeNG],
5026 ) -> None:
5027 self.pattern = pattern
5028 self.guard = guard
5029 self.body = body
5030
5031
5032class MatchValue(Pattern):
5033 """Class representing a :class:`ast.MatchValue` node.
5034
5035 >>> import astroid
5036 >>> node = astroid.extract_node('''
5037 match x:
5038 case 200:
5039 ...
5040 ''')
5041 >>> node.cases[0].pattern
5042 <MatchValue l.3 at 0x10c24e200>
5043 """
5044
5045 _astroid_fields = ("value",)
5046
5047 def __init__(
5048 self,
5049 lineno: int | None = None,
5050 col_offset: int | None = None,
5051 parent: NodeNG | None = None,
5052 *,
5053 end_lineno: int | None = None,
5054 end_col_offset: int | None = None,
5055 ) -> None:
5056 self.value: NodeNG
5057 super().__init__(
5058 lineno=lineno,
5059 col_offset=col_offset,
5060 end_lineno=end_lineno,
5061 end_col_offset=end_col_offset,
5062 parent=parent,
5063 )
5064
5065 def postinit(self, *, value: NodeNG) -> None:
5066 self.value = value
5067
5068
5069class MatchSingleton(Pattern):
5070 """Class representing a :class:`ast.MatchSingleton` node.
5071
5072 >>> import astroid
5073 >>> node = astroid.extract_node('''
5074 match x:
5075 case True:
5076 ...
5077 case False:
5078 ...
5079 case None:
5080 ...
5081 ''')
5082 >>> node.cases[0].pattern
5083 <MatchSingleton l.3 at 0x10c2282e0>
5084 >>> node.cases[1].pattern
5085 <MatchSingleton l.5 at 0x10c228af0>
5086 >>> node.cases[2].pattern
5087 <MatchSingleton l.7 at 0x10c229f90>
5088 """
5089
5090 _other_fields = ("value",)
5091
5092 def __init__(
5093 self,
5094 *,
5095 value: Literal[True, False, None],
5096 lineno: int | None = None,
5097 col_offset: int | None = None,
5098 end_lineno: int | None = None,
5099 end_col_offset: int | None = None,
5100 parent: NodeNG | None = None,
5101 ) -> None:
5102 self.value = value
5103 super().__init__(
5104 lineno=lineno,
5105 col_offset=col_offset,
5106 end_lineno=end_lineno,
5107 end_col_offset=end_col_offset,
5108 parent=parent,
5109 )
5110
5111
5112class MatchSequence(Pattern):
5113 """Class representing a :class:`ast.MatchSequence` node.
5114
5115 >>> import astroid
5116 >>> node = astroid.extract_node('''
5117 match x:
5118 case [1, 2]:
5119 ...
5120 case (1, 2, *_):
5121 ...
5122 ''')
5123 >>> node.cases[0].pattern
5124 <MatchSequence l.3 at 0x10ca80d00>
5125 >>> node.cases[1].pattern
5126 <MatchSequence l.5 at 0x10ca80b20>
5127 """
5128
5129 _astroid_fields = ("patterns",)
5130
5131 def __init__(
5132 self,
5133 lineno: int | None = None,
5134 col_offset: int | None = None,
5135 parent: NodeNG | None = None,
5136 *,
5137 end_lineno: int | None = None,
5138 end_col_offset: int | None = None,
5139 ) -> None:
5140 self.patterns: list[Pattern]
5141 super().__init__(
5142 lineno=lineno,
5143 col_offset=col_offset,
5144 end_lineno=end_lineno,
5145 end_col_offset=end_col_offset,
5146 parent=parent,
5147 )
5148
5149 def postinit(self, *, patterns: list[Pattern]) -> None:
5150 self.patterns = patterns
5151
5152
5153class MatchMapping(_base_nodes.AssignTypeNode, Pattern):
5154 """Class representing a :class:`ast.MatchMapping` node.
5155
5156 >>> import astroid
5157 >>> node = astroid.extract_node('''
5158 match x:
5159 case {1: "Hello", 2: "World", 3: _, **rest}:
5160 ...
5161 ''')
5162 >>> node.cases[0].pattern
5163 <MatchMapping l.3 at 0x10c8a8850>
5164 """
5165
5166 _astroid_fields = ("keys", "patterns", "rest")
5167
5168 def __init__(
5169 self,
5170 lineno: int | None = None,
5171 col_offset: int | None = None,
5172 parent: NodeNG | None = None,
5173 *,
5174 end_lineno: int | None = None,
5175 end_col_offset: int | None = None,
5176 ) -> None:
5177 self.keys: list[NodeNG]
5178 self.patterns: list[Pattern]
5179 self.rest: AssignName | None
5180 super().__init__(
5181 lineno=lineno,
5182 col_offset=col_offset,
5183 end_lineno=end_lineno,
5184 end_col_offset=end_col_offset,
5185 parent=parent,
5186 )
5187
5188 def postinit(
5189 self,
5190 *,
5191 keys: list[NodeNG],
5192 patterns: list[Pattern],
5193 rest: AssignName | None,
5194 ) -> None:
5195 self.keys = keys
5196 self.patterns = patterns
5197 self.rest = rest
5198
5199 assigned_stmts = protocols.match_mapping_assigned_stmts
5200 """Returns the assigned statement (non inferred) according to the assignment type.
5201 See astroid/protocols.py for actual implementation.
5202 """
5203
5204
5205class MatchClass(Pattern):
5206 """Class representing a :class:`ast.MatchClass` node.
5207
5208 >>> import astroid
5209 >>> node = astroid.extract_node('''
5210 match x:
5211 case Point2D(0, 0):
5212 ...
5213 case Point3D(x=0, y=0, z=0):
5214 ...
5215 ''')
5216 >>> node.cases[0].pattern
5217 <MatchClass l.3 at 0x10ca83940>
5218 >>> node.cases[1].pattern
5219 <MatchClass l.5 at 0x10ca80880>
5220 """
5221
5222 _astroid_fields = ("cls", "patterns", "kwd_patterns")
5223 _other_fields = ("kwd_attrs",)
5224
5225 def __init__(
5226 self,
5227 lineno: int | None = None,
5228 col_offset: int | None = None,
5229 parent: NodeNG | None = None,
5230 *,
5231 end_lineno: int | None = None,
5232 end_col_offset: int | None = None,
5233 ) -> None:
5234 self.cls: NodeNG
5235 self.patterns: list[Pattern]
5236 self.kwd_attrs: list[str]
5237 self.kwd_patterns: list[Pattern]
5238 super().__init__(
5239 lineno=lineno,
5240 col_offset=col_offset,
5241 end_lineno=end_lineno,
5242 end_col_offset=end_col_offset,
5243 parent=parent,
5244 )
5245
5246 def postinit(
5247 self,
5248 *,
5249 cls: NodeNG,
5250 patterns: list[Pattern],
5251 kwd_attrs: list[str],
5252 kwd_patterns: list[Pattern],
5253 ) -> None:
5254 self.cls = cls
5255 self.patterns = patterns
5256 self.kwd_attrs = kwd_attrs
5257 self.kwd_patterns = kwd_patterns
5258
5259
5260class MatchStar(_base_nodes.AssignTypeNode, Pattern):
5261 """Class representing a :class:`ast.MatchStar` node.
5262
5263 >>> import astroid
5264 >>> node = astroid.extract_node('''
5265 match x:
5266 case [1, *_]:
5267 ...
5268 ''')
5269 >>> node.cases[0].pattern.patterns[1]
5270 <MatchStar l.3 at 0x10ca809a0>
5271 """
5272
5273 _astroid_fields = ("name",)
5274
5275 def __init__(
5276 self,
5277 lineno: int | None = None,
5278 col_offset: int | None = None,
5279 parent: NodeNG | None = None,
5280 *,
5281 end_lineno: int | None = None,
5282 end_col_offset: int | None = None,
5283 ) -> None:
5284 self.name: AssignName | None
5285 super().__init__(
5286 lineno=lineno,
5287 col_offset=col_offset,
5288 end_lineno=end_lineno,
5289 end_col_offset=end_col_offset,
5290 parent=parent,
5291 )
5292
5293 def postinit(self, *, name: AssignName | None) -> None:
5294 self.name = name
5295
5296 assigned_stmts = protocols.match_star_assigned_stmts
5297 """Returns the assigned statement (non inferred) according to the assignment type.
5298 See astroid/protocols.py for actual implementation.
5299 """
5300
5301
5302class MatchAs(_base_nodes.AssignTypeNode, Pattern):
5303 """Class representing a :class:`ast.MatchAs` node.
5304
5305 >>> import astroid
5306 >>> node = astroid.extract_node('''
5307 match x:
5308 case [1, a]:
5309 ...
5310 case {'key': b}:
5311 ...
5312 case Point2D(0, 0) as c:
5313 ...
5314 case d:
5315 ...
5316 ''')
5317 >>> node.cases[0].pattern.patterns[1]
5318 <MatchAs l.3 at 0x10d0b2da0>
5319 >>> node.cases[1].pattern.patterns[0]
5320 <MatchAs l.5 at 0x10d0b2920>
5321 >>> node.cases[2].pattern
5322 <MatchAs l.7 at 0x10d0b06a0>
5323 >>> node.cases[3].pattern
5324 <MatchAs l.9 at 0x10d09b880>
5325 """
5326
5327 _astroid_fields = ("pattern", "name")
5328
5329 def __init__(
5330 self,
5331 lineno: int | None = None,
5332 col_offset: int | None = None,
5333 parent: NodeNG | None = None,
5334 *,
5335 end_lineno: int | None = None,
5336 end_col_offset: int | None = None,
5337 ) -> None:
5338 self.pattern: Pattern | None
5339 self.name: AssignName | None
5340 super().__init__(
5341 lineno=lineno,
5342 col_offset=col_offset,
5343 end_lineno=end_lineno,
5344 end_col_offset=end_col_offset,
5345 parent=parent,
5346 )
5347
5348 def postinit(
5349 self,
5350 *,
5351 pattern: Pattern | None,
5352 name: AssignName | None,
5353 ) -> None:
5354 self.pattern = pattern
5355 self.name = name
5356
5357 assigned_stmts = protocols.match_as_assigned_stmts
5358 """Returns the assigned statement (non inferred) according to the assignment type.
5359 See astroid/protocols.py for actual implementation.
5360 """
5361
5362
5363class MatchOr(Pattern):
5364 """Class representing a :class:`ast.MatchOr` node.
5365
5366 >>> import astroid
5367 >>> node = astroid.extract_node('''
5368 match x:
5369 case 400 | 401 | 402:
5370 ...
5371 ''')
5372 >>> node.cases[0].pattern
5373 <MatchOr l.3 at 0x10d0b0b50>
5374 """
5375
5376 _astroid_fields = ("patterns",)
5377
5378 def __init__(
5379 self,
5380 lineno: int | None = None,
5381 col_offset: int | None = None,
5382 parent: NodeNG | None = None,
5383 *,
5384 end_lineno: int | None = None,
5385 end_col_offset: int | None = None,
5386 ) -> None:
5387 self.patterns: list[Pattern]
5388 super().__init__(
5389 lineno=lineno,
5390 col_offset=col_offset,
5391 end_lineno=end_lineno,
5392 end_col_offset=end_col_offset,
5393 parent=parent,
5394 )
5395
5396 def postinit(self, *, patterns: list[Pattern]) -> None:
5397 self.patterns = patterns
5398
5399
5400# constants ##############################################################
5401
5402# The _proxied attribute of all container types (List, Tuple, etc.)
5403# are set during bootstrapping by _astroid_bootstrapping().
5404CONST_CLS: dict[type, type[NodeNG]] = {
5405 list: List,
5406 tuple: Tuple,
5407 dict: Dict,
5408 set: Set,
5409 type(None): Const,
5410 type(NotImplemented): Const,
5411 type(...): Const,
5412 bool: Const,
5413 int: Const,
5414 float: Const,
5415 complex: Const,
5416 str: Const,
5417 bytes: Const,
5418}
5419
5420
5421def _create_basic_elements(
5422 value: Iterable[Any], node: List | Set | Tuple
5423) -> list[NodeNG]:
5424 """Create a list of nodes to function as the elements of a new node."""
5425 elements: list[NodeNG] = []
5426 for element in value:
5427 element_node = const_factory(element)
5428 element_node.parent = node
5429 elements.append(element_node)
5430 return elements
5431
5432
5433def _create_dict_items(
5434 values: Mapping[Any, Any], node: Dict
5435) -> list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]]:
5436 """Create a list of node pairs to function as the items of a new dict node."""
5437 elements: list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]] = []
5438 for key, value in values.items():
5439 key_node = const_factory(key)
5440 key_node.parent = node
5441 value_node = const_factory(value)
5442 value_node.parent = node
5443 elements.append((key_node, value_node))
5444 return elements
5445
5446
5447def const_factory(value: Any) -> ConstFactoryResult:
5448 """Return an astroid node for a python value."""
5449 assert not isinstance(value, NodeNG)
5450
5451 # This only handles instances of the CONST types. Any
5452 # subclasses get inferred as EmptyNode.
5453 # TODO: See if we should revisit these with the normal builder.
5454 if value.__class__ not in CONST_CLS:
5455 node = EmptyNode()
5456 node.object = value
5457 return node
5458
5459 instance: List | Set | Tuple | Dict
5460 initializer_cls = CONST_CLS[value.__class__]
5461 if issubclass(initializer_cls, (List, Set, Tuple)):
5462 instance = initializer_cls(
5463 lineno=None,
5464 col_offset=None,
5465 parent=None,
5466 end_lineno=None,
5467 end_col_offset=None,
5468 )
5469 instance.postinit(_create_basic_elements(value, instance))
5470 return instance
5471 if issubclass(initializer_cls, Dict):
5472 instance = initializer_cls(
5473 lineno=None,
5474 col_offset=None,
5475 parent=None,
5476 end_lineno=None,
5477 end_col_offset=None,
5478 )
5479 instance.postinit(_create_dict_items(value, instance))
5480 return instance
5481 return Const(value)