Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/astroid/protocols.py: 38%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
5"""This module contains a set of functions to handle python protocols for nodes
6where it makes sense.
7"""
9from __future__ import annotations
11import collections
12import itertools
13import operator as operator_mod
14from collections.abc import Callable, Generator, Iterator, Sequence
15from typing import TYPE_CHECKING, Any, TypeVar
17from astroid import bases, decorators, nodes, util
18from astroid.builder import extract_node
19from astroid.const import Context
20from astroid.context import InferenceContext, copy_context
21from astroid.exceptions import (
22 AstroidIndexError,
23 AstroidTypeError,
24 AttributeInferenceError,
25 InferenceError,
26 NoDefault,
27)
28from astroid.nodes import node_classes
29from astroid.typing import (
30 ConstFactoryResult,
31 InferenceResult,
32 SuccessfulInferenceResult,
33)
35if TYPE_CHECKING:
36 _TupleListNodeT = TypeVar("_TupleListNodeT", nodes.Tuple, nodes.List)
38_CONTEXTLIB_MGR = "contextlib.contextmanager"
40_UNARY_OPERATORS: dict[str, Callable[[Any], Any]] = {
41 "+": operator_mod.pos,
42 "-": operator_mod.neg,
43 "~": operator_mod.invert,
44 "not": operator_mod.not_,
45}
48def _infer_unary_op(obj: Any, op: str) -> ConstFactoryResult:
49 """Perform unary operation on `obj`, unless it is `NotImplemented`.
51 Can raise TypeError if operation is unsupported.
52 """
53 if obj is NotImplemented:
54 value = obj
55 else:
56 func = _UNARY_OPERATORS[op]
57 value = func(obj)
58 return nodes.const_factory(value)
61def tuple_infer_unary_op(self, op):
62 return _infer_unary_op(tuple(self.elts), op)
65def list_infer_unary_op(self, op):
66 return _infer_unary_op(self.elts, op)
69def set_infer_unary_op(self, op):
70 return _infer_unary_op(set(self.elts), op)
73def const_infer_unary_op(self, op):
74 return _infer_unary_op(self.value, op)
77def dict_infer_unary_op(self, op):
78 return _infer_unary_op(dict(self.items), op)
81# Binary operations
83BIN_OP_IMPL = {
84 "+": lambda a, b: a + b,
85 "-": lambda a, b: a - b,
86 "/": lambda a, b: a / b,
87 "//": lambda a, b: a // b,
88 "*": lambda a, b: a * b,
89 "**": lambda a, b: a**b,
90 "%": lambda a, b: a % b,
91 "&": lambda a, b: a & b,
92 "|": lambda a, b: a | b,
93 "^": lambda a, b: a ^ b,
94 "<<": lambda a, b: a << b,
95 ">>": lambda a, b: a >> b,
96 "@": operator_mod.matmul,
97}
98for _KEY, _IMPL in list(BIN_OP_IMPL.items()):
99 BIN_OP_IMPL[_KEY + "="] = _IMPL
102def _old_style_format_too_large(template: str | bytes, values: object) -> bool:
103 """Whether ``template % values`` would exceed the 1e8 size cap.
105 Walks the conversion specifiers the way CPython parses them, resolving
106 ``*`` width/precision fields from the positional arguments.
107 """
108 if isinstance(template, bytes):
109 template = template.decode("latin-1")
110 if isinstance(values, dict):
111 args: tuple[object, ...] = ()
112 elif isinstance(values, tuple):
113 args = values
114 else:
115 args = (values,)
116 index = 0 # next positional argument
117 i, end = 0, len(template)
118 while i < end:
119 if template[i] != "%":
120 i += 1
121 continue
122 i += 1
123 if i < end and template[i] == "(":
124 # Skip the mapping key, counting nested parentheses like CPython.
125 depth, i = 1, i + 1
126 while i < end and depth:
127 if template[i] == "(":
128 depth += 1
129 elif template[i] == ")":
130 depth -= 1
131 i += 1
132 while i < end and template[i] in "#0- +":
133 i += 1
134 for dot in ("", "."): # width, then precision
135 if dot:
136 if i >= end or template[i] != ".":
137 break
138 i += 1
139 if i < end and template[i] == "*":
140 i += 1
141 if index < len(args):
142 star = args[index]
143 index += 1
144 # A negative width means left-justify with that magnitude.
145 if isinstance(star, int) and abs(star) > 1e8:
146 return True
147 else:
148 start = i
149 while i < end and "0" <= template[i] <= "9":
150 i += 1
151 digits = template[start:i]
152 # len > 9 already exceeds the cap and avoids int() choking.
153 if digits and (len(digits) > 9 or int(digits) > 1e8):
154 return True
155 while i < end and template[i] in "hlL":
156 i += 1
157 if i < end:
158 if template[i] != "%":
159 index += 1 # the conversion itself consumes an argument
160 i += 1
161 return False
164@decorators.yes_if_nothing_inferred
165def const_infer_binary_op(
166 self: nodes.Const,
167 opnode: nodes.AugAssign | nodes.BinOp,
168 operator: str,
169 other: InferenceResult,
170 context: InferenceContext,
171 _: SuccessfulInferenceResult,
172) -> Generator[ConstFactoryResult | util.UninferableBase]:
173 not_implemented = nodes.Const(NotImplemented)
174 if isinstance(other, nodes.Const):
175 if (
176 operator == "**"
177 and isinstance(self.value, (int, float))
178 and isinstance(other.value, (int, float))
179 and (self.value > 1e5 or other.value > 1e5)
180 ):
181 yield not_implemented
182 return
183 # Repeating a str/bytes by a large count ("x" * n or n * "x") would
184 # eagerly build the whole object, the same way list/tuple repetition
185 # is bounded in _multiply_seq_by_int. Don't materialize it.
186 if operator == "*":
187 sequence, count = self.value, other.value
188 if isinstance(sequence, int) and isinstance(count, (str, bytes)):
189 sequence, count = count, sequence
190 if (
191 isinstance(sequence, (str, bytes))
192 and isinstance(count, int)
193 and len(sequence) * count > 1e8
194 ):
195 yield util.Uninferable
196 return
197 # Left-shifting by a large amount builds an enormous int, the integer
198 # analog of the ** guard above.
199 if (
200 operator == "<<"
201 and isinstance(self.value, int)
202 and isinstance(other.value, int)
203 and other.value > 1e8
204 ):
205 yield util.Uninferable
206 return
207 # Don't materialize an overly large str/bytes concatenation.
208 if (
209 operator == "+"
210 and isinstance(self.value, (str, bytes))
211 and isinstance(other.value, type(self.value))
212 and len(self.value) + len(other.value) > 1e8
213 ):
214 yield util.Uninferable
215 return
216 # An oversized width or precision ("%1000000000d" % 1) would
217 # materialize the whole interpolation, like the repetition above.
218 if (
219 operator in {"%", "%="}
220 and isinstance(self.value, (str, bytes))
221 and _old_style_format_too_large(self.value, other.value)
222 ):
223 yield util.Uninferable
224 return
225 try:
226 impl = BIN_OP_IMPL[operator]
227 try:
228 yield nodes.const_factory(impl(self.value, other.value))
229 except TypeError:
230 # ArithmeticError is not enough: float >> float is a TypeError
231 yield not_implemented
232 except Exception: # pylint: disable=broad-except
233 yield util.Uninferable
234 except TypeError:
235 yield not_implemented
236 elif isinstance(self.value, str) and operator == "%":
237 # TODO(cpopa): implement string interpolation later on.
238 yield util.Uninferable
239 else:
240 yield not_implemented
243def _multiply_seq_by_int(
244 self: _TupleListNodeT,
245 opnode: nodes.AugAssign | nodes.BinOp,
246 value: int,
247 context: InferenceContext,
248) -> _TupleListNodeT:
249 node = self.__class__(parent=opnode)
250 if not (value > 0 and self.elts):
251 node.elts = []
252 return node
253 if len(self.elts) * value > 1e8:
254 node.elts = [util.Uninferable]
255 return node
256 filtered_elts = (
257 util.safe_infer(elt, context) or util.Uninferable
258 for elt in self.elts
259 if not isinstance(elt, util.UninferableBase)
260 )
261 node.elts = list(filtered_elts) * value
262 return node
265def _filter_uninferable_nodes(
266 elts: Sequence[InferenceResult], context: InferenceContext
267) -> Iterator[SuccessfulInferenceResult]:
268 for elt in elts:
269 if isinstance(elt, util.UninferableBase):
270 yield node_classes.UNATTACHED_UNKNOWN
271 else:
272 for inferred in elt.infer(context):
273 if not isinstance(inferred, util.UninferableBase):
274 yield inferred
275 else:
276 yield node_classes.UNATTACHED_UNKNOWN
279@decorators.yes_if_nothing_inferred
280def tl_infer_binary_op(
281 self: _TupleListNodeT,
282 opnode: nodes.AugAssign | nodes.BinOp,
283 operator: str,
284 other: InferenceResult,
285 context: InferenceContext,
286 method: SuccessfulInferenceResult,
287) -> Generator[_TupleListNodeT | nodes.Const | util.UninferableBase]:
288 """Infer a binary operation on a tuple or list.
290 The instance on which the binary operation is performed is a tuple
291 or list. This refers to the left-hand side of the operation, so:
292 'tuple() + 1' or '[] + A()'
293 """
294 from astroid import helpers # pylint: disable=import-outside-toplevel
296 # For tuples and list the boundnode is no longer the tuple or list instance
297 context.boundnode = None
298 not_implemented = nodes.Const(NotImplemented)
299 if isinstance(other, self.__class__) and operator == "+":
300 # Don't build (and infer every element of) an overly large sequence.
301 if len(self.elts) + len(other.elts) > 1e8:
302 yield util.Uninferable
303 return
304 node = self.__class__(parent=opnode)
305 node.elts = list(
306 itertools.chain(
307 _filter_uninferable_nodes(self.elts, context),
308 _filter_uninferable_nodes(other.elts, context),
309 )
310 )
311 yield node
312 elif isinstance(other, nodes.Const) and operator == "*":
313 if not isinstance(other.value, int):
314 yield not_implemented
315 return
316 yield _multiply_seq_by_int(self, opnode, other.value, context)
317 elif isinstance(other, bases.Instance) and operator == "*":
318 # Verify if the instance supports __index__.
319 as_index = helpers.class_instance_as_index(other)
320 if not as_index:
321 yield util.Uninferable
322 elif not isinstance(as_index.value, int): # pragma: no cover
323 # already checked by class_instance_as_index() but faster than casting
324 raise AssertionError("Please open a bug report.")
325 else:
326 yield _multiply_seq_by_int(self, opnode, as_index.value, context)
327 else:
328 yield not_implemented
331@decorators.yes_if_nothing_inferred
332def instance_class_infer_binary_op(
333 self: nodes.ClassDef,
334 opnode: nodes.AugAssign | nodes.BinOp,
335 operator: str,
336 other: InferenceResult,
337 context: InferenceContext,
338 method: SuccessfulInferenceResult,
339) -> Generator[InferenceResult]:
340 return method.infer_call_result(self, context)
343# assignment ##################################################################
344# pylint: disable-next=pointless-string-statement
345"""The assigned_stmts method is responsible to return the assigned statement
346(e.g. not inferred) according to the assignment type.
348The `assign_path` argument is used to record the lhs path of the original node.
349For instance if we want assigned statements for 'c' in 'a, (b,c)', assign_path
350will be [1, 1] once arrived to the Assign node.
352The `context` argument is the current inference context which should be given
353to any intermediary inference necessary.
354"""
357def _resolve_looppart(parts, assign_path, context):
358 """Recursive function to resolve multiple assignments on loops."""
359 assign_path = assign_path[:]
360 index = assign_path.pop(0)
361 for part in parts:
362 if isinstance(part, util.UninferableBase):
363 continue
364 if not hasattr(part, "itered"):
365 continue
366 try:
367 itered = part.itered()
368 except TypeError:
369 continue
370 try:
371 if isinstance(itered[index], (nodes.Const, nodes.Name)):
372 itered = [part]
373 except IndexError:
374 pass
375 for stmt in itered:
376 index_node = nodes.Const(index)
377 try:
378 assigned = stmt.getitem(index_node, context)
379 except (AttributeError, AstroidTypeError, AstroidIndexError):
380 continue
381 if not assign_path:
382 # we achieved to resolved the assignment path,
383 # don't infer the last part
384 yield assigned
385 elif isinstance(assigned, util.UninferableBase):
386 break
387 else:
388 # we are not yet on the last part of the path
389 # search on each possibly inferred value
390 try:
391 yield from _resolve_looppart(
392 assigned.infer(context), assign_path, context
393 )
394 except InferenceError:
395 break
398@decorators.raise_if_nothing_inferred
399def for_assigned_stmts(
400 self: nodes.For | nodes.Comprehension,
401 node: node_classes.AssignedStmtsPossibleNode = None,
402 context: InferenceContext | None = None,
403 assign_path: list[int] | None = None,
404) -> Any:
405 if isinstance(self, nodes.AsyncFor) or getattr(self, "is_async", False):
406 # Skip inferring of async code for now
407 return {
408 "node": self,
409 "unknown": node,
410 "assign_path": assign_path,
411 "context": context,
412 }
413 if assign_path is None:
414 for lst in self.iter.infer(context):
415 if isinstance(lst, (nodes.Tuple, nodes.List)):
416 yield from lst.elts
417 else:
418 yield from _resolve_looppart(self.iter.infer(context), assign_path, context)
419 return {
420 "node": self,
421 "unknown": node,
422 "assign_path": assign_path,
423 "context": context,
424 }
427def sequence_assigned_stmts(
428 self: nodes.Tuple | nodes.List,
429 node: node_classes.AssignedStmtsPossibleNode = None,
430 context: InferenceContext | None = None,
431 assign_path: list[int] | None = None,
432) -> Any:
433 if assign_path is None:
434 assign_path = []
435 try:
436 index = self.elts.index(node) # type: ignore[arg-type]
437 except ValueError as exc:
438 raise InferenceError(
439 "Tried to retrieve a node {node!r} which does not exist",
440 node=self,
441 assign_path=assign_path,
442 context=context,
443 ) from exc
445 assign_path.insert(0, index)
446 return self.parent.assigned_stmts(
447 node=self, context=context, assign_path=assign_path
448 )
451def assend_assigned_stmts(
452 self: nodes.AssignName | nodes.AssignAttr,
453 node: node_classes.AssignedStmtsPossibleNode = None,
454 context: InferenceContext | None = None,
455 assign_path: list[int] | None = None,
456) -> Any:
457 return self.parent.assigned_stmts(node=self, context=context)
460def _arguments_infer_argname(
461 self, name: str | None, context: InferenceContext | None
462) -> Generator[InferenceResult]:
463 # arguments information may be missing, in which case we can't do anything
464 # more
465 from astroid import arguments # pylint: disable=import-outside-toplevel
467 if not self.arguments:
468 yield util.Uninferable
469 return
471 args = [arg for arg in self.arguments if arg.name not in [self.vararg, self.kwarg]]
472 functype = self.parent.type
473 # first argument of instance/class method
474 if (
475 args
476 and getattr(self.arguments[0], "name", None) == name
477 and functype != "staticmethod"
478 ):
479 cls = self.parent.parent.scope()
480 is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == "metaclass"
481 # If this is a metaclass, then the first argument will always
482 # be the class, not an instance.
483 if (
484 context
485 and context.boundnode
486 and isinstance(context.boundnode, bases.Instance)
487 ):
488 bound_cls = context.boundnode._proxied
489 if not isinstance(cls, nodes.ClassDef) or bound_cls.is_subtype_of(
490 cls.qname()
491 ):
492 cls = bound_cls
493 if is_metaclass or functype == "classmethod":
494 yield cls
495 return
496 if functype == "method":
497 yield cls.instantiate_class()
498 return
500 if context and context.callcontext:
501 callee = context.callcontext.callee
502 while hasattr(callee, "_proxied"):
503 callee = callee._proxied
504 if getattr(callee, "name", None) == self.parent.name:
505 call_site = arguments.CallSite(context.callcontext, context.extra_context)
506 yield from call_site.infer_argument(self.parent, name, context)
507 return
509 if name == self.vararg:
510 vararg = nodes.const_factory(())
511 vararg.parent = self
512 if not args and self.parent.name == "__init__":
513 cls = self.parent.parent.scope()
514 vararg.elts = [cls.instantiate_class()]
515 yield vararg
516 return
517 if name == self.kwarg:
518 kwarg = nodes.const_factory({})
519 kwarg.parent = self
520 yield kwarg
521 return
522 # if there is a default value, yield it. And then yield Uninferable to reflect
523 # we can't guess given argument value
524 try:
525 context = copy_context(context)
526 yield from self.default_value(name).infer(context)
527 yield util.Uninferable
528 except NoDefault:
529 yield util.Uninferable
532def arguments_assigned_stmts(
533 self: nodes.Arguments,
534 node: node_classes.AssignedStmtsPossibleNode = None,
535 context: InferenceContext | None = None,
536 assign_path: list[int] | None = None,
537) -> Any:
538 from astroid import arguments # pylint: disable=import-outside-toplevel
540 try:
541 node_name = node.name # type: ignore[union-attr]
542 except AttributeError:
543 # Added to handle edge cases where node.name is not defined.
544 # https://github.com/pylint-dev/astroid/pull/1644#discussion_r901545816
545 node_name = None # pragma: no cover
547 if context and context.callcontext:
548 callee = context.callcontext.callee
549 while hasattr(callee, "_proxied"):
550 callee = callee._proxied
551 else:
552 return _arguments_infer_argname(self, node_name, context)
553 if node and getattr(callee, "name", None) == node.frame().name:
554 # reset call context/name
555 callcontext = context.callcontext
556 context = copy_context(context)
557 context.callcontext = None
558 args = arguments.CallSite(callcontext, context=context)
559 return args.infer_argument(self.parent, node_name, context)
560 return _arguments_infer_argname(self, node_name, context)
563@decorators.raise_if_nothing_inferred
564def assign_assigned_stmts(
565 self: nodes.AugAssign | nodes.Assign | nodes.AnnAssign | nodes.TypeAlias,
566 node: node_classes.AssignedStmtsPossibleNode = None,
567 context: InferenceContext | None = None,
568 assign_path: list[int] | None = None,
569) -> Any:
570 if not assign_path:
571 yield self.value
572 return None
573 yield from _resolve_assignment_parts(
574 self.value.infer(context), assign_path, context
575 )
577 return {
578 "node": self,
579 "unknown": node,
580 "assign_path": assign_path,
581 "context": context,
582 }
585def assign_annassigned_stmts(
586 self: nodes.AnnAssign,
587 node: node_classes.AssignedStmtsPossibleNode = None,
588 context: InferenceContext | None = None,
589 assign_path: list[int] | None = None,
590) -> Any:
591 for inferred in assign_assigned_stmts(self, node, context, assign_path):
592 if inferred is None:
593 yield util.Uninferable
594 else:
595 yield inferred
598def _resolve_assignment_parts(parts, assign_path, context):
599 """Recursive function to resolve multiple assignments."""
600 assign_path = assign_path[:]
601 index = assign_path.pop(0)
602 for part in parts:
603 assigned = None
604 if isinstance(part, nodes.Dict):
605 # A dictionary in an iterating context
606 try:
607 assigned, _ = part.items[index]
608 except IndexError:
609 return
611 elif hasattr(part, "getitem"):
612 index_node = nodes.Const(index)
613 try:
614 assigned = part.getitem(index_node, context)
615 except (AstroidTypeError, AstroidIndexError):
616 return
618 if not assigned:
619 return
621 if not assign_path:
622 # we achieved to resolved the assignment path, don't infer the
623 # last part
624 yield assigned
625 elif isinstance(assigned, util.UninferableBase):
626 return
627 else:
628 # we are not yet on the last part of the path search on each
629 # possibly inferred value
630 try:
631 yield from _resolve_assignment_parts(
632 assigned.infer(context), assign_path, context
633 )
634 except InferenceError:
635 return
638@decorators.raise_if_nothing_inferred
639def excepthandler_assigned_stmts(
640 self: nodes.ExceptHandler,
641 node: node_classes.AssignedStmtsPossibleNode = None,
642 context: InferenceContext | None = None,
643 assign_path: list[int] | None = None,
644) -> Any:
645 from astroid import objects # pylint: disable=import-outside-toplevel
647 def _generate_assigned():
648 for assigned in node_classes.unpack_infer(self.type):
649 if isinstance(assigned, nodes.ClassDef):
650 assigned = objects.ExceptionInstance(assigned)
652 yield assigned
654 if isinstance(self.parent, node_classes.TryStar):
655 # except * handler has assigned ExceptionGroup with caught
656 # exceptions under exceptions attribute
657 # pylint: disable-next=stop-iteration-return
658 eg = next(node_classes.unpack_infer(extract_node("""
659from builtins import ExceptionGroup
660ExceptionGroup
661""")))
662 assigned = objects.ExceptionInstance(eg)
663 assigned.instance_attrs["exceptions"] = [
664 nodes.Tuple.from_elements(_generate_assigned())
665 ]
666 yield assigned
667 else:
668 yield from _generate_assigned()
669 return {
670 "node": self,
671 "unknown": node,
672 "assign_path": assign_path,
673 "context": context,
674 }
677def _infer_context_manager(self, mgr, context):
678 try:
679 inferred = next(mgr.infer(context=context))
680 except StopIteration as e:
681 raise InferenceError(node=mgr) from e
682 if isinstance(inferred, bases.Generator):
683 # Check if it is decorated with contextlib.contextmanager.
684 func = inferred.parent
685 if not func.decorators:
686 raise InferenceError(
687 "No decorators found on inferred generator %s", node=func
688 )
690 for decorator_node in func.decorators.nodes:
691 decorator = next(decorator_node.infer(context=context), None)
692 if isinstance(decorator, nodes.FunctionDef):
693 if decorator.qname() == _CONTEXTLIB_MGR:
694 break
695 else:
696 # It doesn't interest us.
697 raise InferenceError(node=func)
698 try:
699 yield next(inferred.infer_yield_types())
700 except StopIteration as e:
701 raise InferenceError(node=func) from e
703 elif isinstance(inferred, bases.Instance):
704 try:
705 enter = next(inferred.igetattr("__enter__", context=context))
706 except (InferenceError, AttributeInferenceError, StopIteration) as exc:
707 raise InferenceError(node=inferred) from exc
708 if not isinstance(enter, bases.BoundMethod):
709 raise InferenceError(node=enter)
710 yield from enter.infer_call_result(self, context)
711 else:
712 raise InferenceError(node=mgr)
715@decorators.raise_if_nothing_inferred
716def with_assigned_stmts(
717 self: nodes.With,
718 node: node_classes.AssignedStmtsPossibleNode = None,
719 context: InferenceContext | None = None,
720 assign_path: list[int] | None = None,
721) -> Any:
722 """Infer names and other nodes from a *with* statement.
724 This enables only inference for name binding in a *with* statement.
725 For instance, in the following code, inferring `func` will return
726 the `ContextManager` class, not whatever ``__enter__`` returns.
727 We are doing this intentionally, because we consider that the context
728 manager result is whatever __enter__ returns and what it is binded
729 using the ``as`` keyword.
731 class ContextManager(object):
732 def __enter__(self):
733 return 42
734 with ContextManager() as f:
735 pass
737 # ContextManager().infer() will return ContextManager
738 # f.infer() will return 42.
740 Arguments:
741 self: nodes.With
742 node: The target of the assignment, `as (a, b)` in `with foo as (a, b)`.
743 context: Inference context used for caching already inferred objects
744 assign_path:
745 A list of indices, where each index specifies what item to fetch from
746 the inference results.
747 """
748 try:
749 mgr = next(mgr for (mgr, vars) in self.items if vars == node)
750 except StopIteration:
751 return None
752 if assign_path is None:
753 yield from _infer_context_manager(self, mgr, context)
754 else:
755 for result in _infer_context_manager(self, mgr, context):
756 # Walk the assign_path and get the item at the final index.
757 obj = result
758 for index in assign_path:
759 if not hasattr(obj, "elts"):
760 raise InferenceError(
761 "Wrong type ({targets!r}) for {node!r} assignment",
762 node=self,
763 targets=node,
764 assign_path=assign_path,
765 context=context,
766 )
767 try:
768 obj = obj.elts[index]
769 except IndexError as exc:
770 raise InferenceError(
771 "Tried to infer a nonexistent target with index {index} "
772 "in {node!r}.",
773 node=self,
774 targets=node,
775 assign_path=assign_path,
776 context=context,
777 ) from exc
778 except TypeError as exc:
779 raise InferenceError(
780 "Tried to unpack a non-iterable value in {node!r}.",
781 node=self,
782 targets=node,
783 assign_path=assign_path,
784 context=context,
785 ) from exc
786 yield obj
787 return {
788 "node": self,
789 "unknown": node,
790 "assign_path": assign_path,
791 "context": context,
792 }
795@decorators.raise_if_nothing_inferred
796def named_expr_assigned_stmts(
797 self: nodes.NamedExpr,
798 node: node_classes.AssignedStmtsPossibleNode,
799 context: InferenceContext | None = None,
800 assign_path: list[int] | None = None,
801) -> Any:
802 """Infer names and other nodes from an assignment expression."""
803 if self.target == node:
804 yield from self.value.infer(context=context)
805 else:
806 raise InferenceError(
807 "Cannot infer NamedExpr node {node!r}",
808 node=self,
809 assign_path=assign_path,
810 context=context,
811 )
814@decorators.yes_if_nothing_inferred
815def starred_assigned_stmts( # noqa: C901
816 self: nodes.Starred,
817 node: node_classes.AssignedStmtsPossibleNode = None,
818 context: InferenceContext | None = None,
819 assign_path: list[int] | None = None,
820) -> Any:
821 """
822 Arguments:
823 self: nodes.Starred
824 node: a node related to the current underlying Node.
825 context: Inference context used for caching already inferred objects
826 assign_path:
827 A list of indices, where each index specifies what item to fetch from
828 the inference results.
829 """
831 # pylint: disable = too-many-locals, too-many-statements, too-many-branches
833 def _determine_starred_iteration_lookups(
834 starred: nodes.Starred, target: nodes.Tuple, lookups: list[tuple[int, int]]
835 ) -> None:
836 # Determine the lookups for the rhs of the iteration
837 itered = target.itered()
838 for index, element in enumerate(itered):
839 if isinstance(element, nodes.Starred) and element is starred:
840 lookups.append((index, len(itered)))
841 break
842 if isinstance(element, nodes.Tuple):
843 lookups.append((index, len(element.itered())))
844 _determine_starred_iteration_lookups(starred, element, lookups)
846 stmt = self.statement()
847 if not isinstance(stmt, (nodes.Assign, nodes.For)):
848 raise InferenceError(
849 "Statement {stmt!r} enclosing {node!r} must be an Assign or For node.",
850 node=self,
851 stmt=stmt,
852 unknown=node,
853 context=context,
854 )
856 if context is None:
857 context = InferenceContext()
859 if isinstance(stmt, nodes.Assign):
860 value = stmt.value
861 lhs = stmt.targets[0]
862 if not isinstance(lhs, nodes.BaseContainer):
863 yield util.Uninferable
864 return
866 if sum(1 for _ in lhs.nodes_of_class(nodes.Starred)) > 1:
867 raise InferenceError(
868 "Too many starred arguments in the assignment targets {lhs!r}.",
869 node=self,
870 targets=lhs,
871 unknown=node,
872 context=context,
873 )
875 try:
876 rhs = next(value.infer(context))
877 except (InferenceError, StopIteration):
878 yield util.Uninferable
879 return
880 if isinstance(rhs, util.UninferableBase) or not hasattr(rhs, "itered"):
881 yield util.Uninferable
882 return
884 try:
885 elts = collections.deque(rhs.itered()) # type: ignore[union-attr]
886 except TypeError:
887 yield util.Uninferable
888 return
890 # Unpack iteratively the values from the rhs of the assignment,
891 # until the find the starred node. What will remain will
892 # be the list of values which the Starred node will represent
893 # This is done in two steps, from left to right to remove
894 # anything before the starred node and from right to left
895 # to remove anything after the starred node.
897 for index, left_node in enumerate(lhs.elts):
898 if not isinstance(left_node, nodes.Starred):
899 if not elts:
900 break
901 elts.popleft()
902 continue
903 lhs_elts = collections.deque(reversed(lhs.elts[index:]))
904 for right_node in lhs_elts:
905 if not isinstance(right_node, nodes.Starred):
906 if not elts:
907 break
908 elts.pop()
909 continue
911 # We're done unpacking.
912 packed = nodes.List(
913 ctx=Context.Store,
914 parent=self,
915 lineno=lhs.lineno,
916 col_offset=lhs.col_offset,
917 )
918 packed.postinit(elts=list(elts))
919 yield packed
920 break
922 if isinstance(stmt, nodes.For):
923 try:
924 inferred_iterable = next(stmt.iter.infer(context=context))
925 except (InferenceError, StopIteration):
926 yield util.Uninferable
927 return
928 if isinstance(inferred_iterable, util.UninferableBase) or not hasattr(
929 inferred_iterable, "itered"
930 ):
931 yield util.Uninferable
932 return
933 try:
934 itered = inferred_iterable.itered() # type: ignore[union-attr]
935 except TypeError:
936 yield util.Uninferable
937 return
939 target = stmt.target
941 if not isinstance(target, nodes.Tuple):
942 raise InferenceError(
943 f"Could not make sense of this, the target must be a tuple, not {type(target)!r}",
944 context=context,
945 )
947 lookups: list[tuple[int, int]] = []
948 _determine_starred_iteration_lookups(self, target, lookups)
949 if not lookups:
950 raise InferenceError(
951 "Could not make sense of this, needs at least a lookup", context=context
952 )
954 # Make the last lookup a slice, since that what we want for a Starred node
955 last_element_index, last_element_length = lookups[-1]
956 is_starred_last = last_element_index == (last_element_length - 1)
958 # The elements after the starred one are counted back from the end of the
959 # iterable, whose length is unrelated to the target's.
960 lookup_slice = slice(
961 last_element_index,
962 (
963 None
964 if is_starred_last
965 else -(last_element_length - last_element_index - 1)
966 ),
967 )
968 last_lookup = lookup_slice
970 for element in itered:
971 # We probably want to infer the potential values *for each* element in an
972 # iterable, but we can't infer a list of all values, when only a list of
973 # step values are expected:
974 #
975 # for a, *b in [...]:
976 # b
977 #
978 # *b* should now point to just the elements at that particular iteration step,
979 # which astroid can't know about.
981 found_element = None
982 for index, lookup in enumerate(lookups):
983 if not hasattr(element, "itered"):
984 break
985 if index + 1 is len(lookups):
986 cur_lookup: slice | int = last_lookup
987 else:
988 # Grab just the index, not the whole length
989 cur_lookup = lookup[0]
990 try:
991 itered_inner_element = element.itered()
992 element = itered_inner_element[cur_lookup]
993 except IndexError:
994 break
995 except TypeError:
996 # Most likely the itered() call failed, cannot make sense of this
997 yield util.Uninferable
998 return
999 else:
1000 found_element = element
1002 unpacked = nodes.List(
1003 ctx=Context.Store,
1004 parent=self,
1005 lineno=self.lineno,
1006 col_offset=self.col_offset,
1007 )
1008 unpacked.postinit(elts=found_element or [])
1009 yield unpacked
1010 return
1012 yield util.Uninferable
1015@decorators.yes_if_nothing_inferred
1016def match_mapping_assigned_stmts(
1017 self: nodes.MatchMapping,
1018 node: nodes.AssignName,
1019 context: InferenceContext | None = None,
1020 assign_path: None = None,
1021) -> Generator[nodes.NodeNG]:
1022 """Return empty generator (return -> raises StopIteration) so inferred value
1023 is Uninferable.
1024 """
1025 return
1026 yield
1029@decorators.yes_if_nothing_inferred
1030def match_star_assigned_stmts(
1031 self: nodes.MatchStar,
1032 node: nodes.AssignName,
1033 context: InferenceContext | None = None,
1034 assign_path: None = None,
1035) -> Generator[nodes.NodeNG]:
1036 """Return empty generator (return -> raises StopIteration) so inferred value
1037 is Uninferable.
1038 """
1039 return
1040 yield
1043@decorators.yes_if_nothing_inferred
1044def match_as_assigned_stmts(
1045 self: nodes.MatchAs,
1046 node: nodes.AssignName,
1047 context: InferenceContext | None = None,
1048 assign_path: None = None,
1049) -> Generator[nodes.NodeNG]:
1050 """Infer MatchAs as the Match subject if it's the only MatchCase pattern
1051 else raise StopIteration to yield Uninferable.
1052 """
1053 if (
1054 isinstance(self.parent, nodes.MatchCase)
1055 and isinstance(self.parent.parent, nodes.Match)
1056 and self.pattern is None
1057 ):
1058 yield self.parent.parent.subject
1061@decorators.yes_if_nothing_inferred
1062def generic_type_assigned_stmts(
1063 self: nodes.TypeVar | nodes.TypeVarTuple | nodes.ParamSpec,
1064 node: nodes.AssignName,
1065 context: InferenceContext | None = None,
1066 assign_path: None = None,
1067) -> Generator[nodes.NodeNG]:
1068 """Return the type parameter node itself so inference doesn't fail
1069 when evaluating __class_getitem__ and so that the node's type is
1070 preserved for downstream checks (e.g. TypeVarTuple starred handling).
1071 """
1072 yield self