1# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr> 2# Copyright (c) 2014-2020 Claudiu Popa <pcmanticore@gmail.com> 3# Copyright (c) 2014 Google, Inc. 4# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com> 5# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com> 6# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru> 7# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com> 8# Copyright (c) 2017-2018 Ashley Whetter <ashley@awhetter.co.uk> 9# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com> 10# Copyright (c) 2017 rr- <rr-@sakuya.pl> 11# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com> 12# Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi> 13# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com> 14# Copyright (c) 2018 HoverHell <hoverhell@gmail.com> 15# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com> 16# Copyright (c) 2020-2021 hippo91 <guillaume.peillex@gmail.com> 17# Copyright (c) 2020 Vilnis Termanis <vilnis.termanis@iotics.com> 18# Copyright (c) 2020 Ram Rachum <ram@rachum.com> 19# Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com> 20# Copyright (c) 2021 David Liu <david@cs.toronto.edu> 21# Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com> 22# Copyright (c) 2021 doranid <ddandd@gmail.com> 23 24# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html 25# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE 26 27"""this module contains a set of functions to handle python protocols for nodes 28where it makes sense. 29""" 30 31import collections 32import itertools 33import operator as operator_mod 34import sys 35from typing import Generator, Optional 36 37from astroid import arguments, bases, decorators, helpers, nodes, util 38from astroid.const import Context 39from astroid.context import InferenceContext, copy_context 40from astroid.exceptions import ( 41 AstroidIndexError, 42 AstroidTypeError, 43 AttributeInferenceError, 44 InferenceError, 45 NoDefault, 46) 47from astroid.nodes import node_classes 48 49if sys.version_info >= (3, 8): 50 from typing import Literal 51else: 52 from typing_extensions import Literal 53 54raw_building = util.lazy_import("raw_building") 55objects = util.lazy_import("objects") 56 57 58def _reflected_name(name): 59 return "__r" + name[2:] 60 61 62def _augmented_name(name): 63 return "__i" + name[2:] 64 65 66_CONTEXTLIB_MGR = "contextlib.contextmanager" 67BIN_OP_METHOD = { 68 "+": "__add__", 69 "-": "__sub__", 70 "/": "__truediv__", 71 "//": "__floordiv__", 72 "*": "__mul__", 73 "**": "__pow__", 74 "%": "__mod__", 75 "&": "__and__", 76 "|": "__or__", 77 "^": "__xor__", 78 "<<": "__lshift__", 79 ">>": "__rshift__", 80 "@": "__matmul__", 81} 82 83REFLECTED_BIN_OP_METHOD = { 84 key: _reflected_name(value) for (key, value) in BIN_OP_METHOD.items() 85} 86AUGMENTED_OP_METHOD = { 87 key + "=": _augmented_name(value) for (key, value) in BIN_OP_METHOD.items() 88} 89 90UNARY_OP_METHOD = { 91 "+": "__pos__", 92 "-": "__neg__", 93 "~": "__invert__", 94 "not": None, # XXX not '__nonzero__' 95} 96_UNARY_OPERATORS = { 97 "+": operator_mod.pos, 98 "-": operator_mod.neg, 99 "~": operator_mod.invert, 100 "not": operator_mod.not_, 101} 102 103 104def _infer_unary_op(obj, op): 105 func = _UNARY_OPERATORS[op] 106 value = func(obj) 107 return nodes.const_factory(value) 108 109 110nodes.Tuple.infer_unary_op = lambda self, op: _infer_unary_op(tuple(self.elts), op) 111nodes.List.infer_unary_op = lambda self, op: _infer_unary_op(self.elts, op) 112nodes.Set.infer_unary_op = lambda self, op: _infer_unary_op(set(self.elts), op) 113nodes.Const.infer_unary_op = lambda self, op: _infer_unary_op(self.value, op) 114nodes.Dict.infer_unary_op = lambda self, op: _infer_unary_op(dict(self.items), op) 115 116# Binary operations 117 118BIN_OP_IMPL = { 119 "+": lambda a, b: a + b, 120 "-": lambda a, b: a - b, 121 "/": lambda a, b: a / b, 122 "//": lambda a, b: a // b, 123 "*": lambda a, b: a * b, 124 "**": lambda a, b: a ** b, 125 "%": lambda a, b: a % b, 126 "&": lambda a, b: a & b, 127 "|": lambda a, b: a | b, 128 "^": lambda a, b: a ^ b, 129 "<<": lambda a, b: a << b, 130 ">>": lambda a, b: a >> b, 131 "@": operator_mod.matmul, 132} 133for _KEY, _IMPL in list(BIN_OP_IMPL.items()): 134 BIN_OP_IMPL[_KEY + "="] = _IMPL 135 136 137@decorators.yes_if_nothing_inferred 138def const_infer_binary_op(self, opnode, operator, other, context, _): 139 not_implemented = nodes.Const(NotImplemented) 140 if isinstance(other, nodes.Const): 141 try: 142 impl = BIN_OP_IMPL[operator] 143 try: 144 yield nodes.const_factory(impl(self.value, other.value)) 145 except TypeError: 146 # ArithmeticError is not enough: float >> float is a TypeError 147 yield not_implemented 148 except Exception: # pylint: disable=broad-except 149 yield util.Uninferable 150 except TypeError: 151 yield not_implemented 152 elif isinstance(self.value, str) and operator == "%": 153 # TODO(cpopa): implement string interpolation later on. 154 yield util.Uninferable 155 else: 156 yield not_implemented 157 158 159nodes.Const.infer_binary_op = const_infer_binary_op 160 161 162def _multiply_seq_by_int(self, opnode, other, context): 163 node = self.__class__(parent=opnode) 164 filtered_elts = ( 165 helpers.safe_infer(elt, context) or util.Uninferable 166 for elt in self.elts 167 if elt is not util.Uninferable 168 ) 169 node.elts = list(filtered_elts) * other.value 170 return node 171 172 173def _filter_uninferable_nodes(elts, context): 174 for elt in elts: 175 if elt is util.Uninferable: 176 yield nodes.Unknown() 177 else: 178 for inferred in elt.infer(context): 179 if inferred is not util.Uninferable: 180 yield inferred 181 else: 182 yield nodes.Unknown() 183 184 185@decorators.yes_if_nothing_inferred 186def tl_infer_binary_op(self, opnode, operator, other, context, method): 187 not_implemented = nodes.Const(NotImplemented) 188 if isinstance(other, self.__class__) and operator == "+": 189 node = self.__class__(parent=opnode) 190 node.elts = list( 191 itertools.chain( 192 _filter_uninferable_nodes(self.elts, context), 193 _filter_uninferable_nodes(other.elts, context), 194 ) 195 ) 196 yield node 197 elif isinstance(other, nodes.Const) and operator == "*": 198 if not isinstance(other.value, int): 199 yield not_implemented 200 return 201 yield _multiply_seq_by_int(self, opnode, other, context) 202 elif isinstance(other, bases.Instance) and operator == "*": 203 # Verify if the instance supports __index__. 204 as_index = helpers.class_instance_as_index(other) 205 if not as_index: 206 yield util.Uninferable 207 else: 208 yield _multiply_seq_by_int(self, opnode, as_index, context) 209 else: 210 yield not_implemented 211 212 213nodes.Tuple.infer_binary_op = tl_infer_binary_op 214nodes.List.infer_binary_op = tl_infer_binary_op 215 216 217@decorators.yes_if_nothing_inferred 218def instance_class_infer_binary_op(self, opnode, operator, other, context, method): 219 return method.infer_call_result(self, context) 220 221 222bases.Instance.infer_binary_op = instance_class_infer_binary_op 223nodes.ClassDef.infer_binary_op = instance_class_infer_binary_op 224 225 226# assignment ################################################################## 227 228"""the assigned_stmts method is responsible to return the assigned statement 229(e.g. not inferred) according to the assignment type. 230 231The `assign_path` argument is used to record the lhs path of the original node. 232For instance if we want assigned statements for 'c' in 'a, (b,c)', assign_path 233will be [1, 1] once arrived to the Assign node. 234 235The `context` argument is the current inference context which should be given 236to any intermediary inference necessary. 237""" 238 239 240def _resolve_looppart(parts, assign_path, context): 241 """recursive function to resolve multiple assignments on loops""" 242 assign_path = assign_path[:] 243 index = assign_path.pop(0) 244 for part in parts: 245 if part is util.Uninferable: 246 continue 247 if not hasattr(part, "itered"): 248 continue 249 try: 250 itered = part.itered() 251 except TypeError: 252 continue 253 for stmt in itered: 254 index_node = nodes.Const(index) 255 try: 256 assigned = stmt.getitem(index_node, context) 257 except (AttributeError, AstroidTypeError, AstroidIndexError): 258 continue 259 if not assign_path: 260 # we achieved to resolved the assignment path, 261 # don't infer the last part 262 yield assigned 263 elif assigned is util.Uninferable: 264 break 265 else: 266 # we are not yet on the last part of the path 267 # search on each possibly inferred value 268 try: 269 yield from _resolve_looppart( 270 assigned.infer(context), assign_path, context 271 ) 272 except InferenceError: 273 break 274 275 276@decorators.raise_if_nothing_inferred 277def for_assigned_stmts(self, node=None, context=None, assign_path=None): 278 if isinstance(self, nodes.AsyncFor) or getattr(self, "is_async", False): 279 # Skip inferring of async code for now 280 return dict(node=self, unknown=node, assign_path=assign_path, context=context) 281 if assign_path is None: 282 for lst in self.iter.infer(context): 283 if isinstance(lst, (nodes.Tuple, nodes.List)): 284 yield from lst.elts 285 else: 286 yield from _resolve_looppart(self.iter.infer(context), assign_path, context) 287 return dict(node=self, unknown=node, assign_path=assign_path, context=context) 288 289 290nodes.For.assigned_stmts = for_assigned_stmts 291nodes.Comprehension.assigned_stmts = for_assigned_stmts 292 293 294def sequence_assigned_stmts(self, node=None, context=None, assign_path=None): 295 if assign_path is None: 296 assign_path = [] 297 try: 298 index = self.elts.index(node) 299 except ValueError as exc: 300 raise InferenceError( 301 "Tried to retrieve a node {node!r} which does not exist", 302 node=self, 303 assign_path=assign_path, 304 context=context, 305 ) from exc 306 307 assign_path.insert(0, index) 308 return self.parent.assigned_stmts( 309 node=self, context=context, assign_path=assign_path 310 ) 311 312 313nodes.Tuple.assigned_stmts = sequence_assigned_stmts 314nodes.List.assigned_stmts = sequence_assigned_stmts 315 316 317def assend_assigned_stmts(self, node=None, context=None, assign_path=None): 318 return self.parent.assigned_stmts(node=self, context=context) 319 320 321nodes.AssignName.assigned_stmts = assend_assigned_stmts 322nodes.AssignAttr.assigned_stmts = assend_assigned_stmts 323 324 325def _arguments_infer_argname(self, name, context): 326 # arguments information may be missing, in which case we can't do anything 327 # more 328 if not (self.arguments or self.vararg or self.kwarg): 329 yield util.Uninferable 330 return 331 332 functype = self.parent.type 333 # first argument of instance/class method 334 if ( 335 self.arguments 336 and getattr(self.arguments[0], "name", None) == name 337 and functype != "staticmethod" 338 ): 339 cls = self.parent.parent.scope() 340 is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == "metaclass" 341 # If this is a metaclass, then the first argument will always 342 # be the class, not an instance. 343 if context.boundnode and isinstance(context.boundnode, bases.Instance): 344 cls = context.boundnode._proxied 345 if is_metaclass or functype == "classmethod": 346 yield cls 347 return 348 if functype == "method": 349 yield cls.instantiate_class() 350 return 351 352 if context and context.callcontext: 353 callee = context.callcontext.callee 354 while hasattr(callee, "_proxied"): 355 callee = callee._proxied 356 if getattr(callee, "name", None) == self.parent.name: 357 call_site = arguments.CallSite(context.callcontext, context.extra_context) 358 yield from call_site.infer_argument(self.parent, name, context) 359 return 360 361 if name == self.vararg: 362 vararg = nodes.const_factory(()) 363 vararg.parent = self 364 if not self.arguments and self.parent.name == "__init__": 365 cls = self.parent.parent.scope() 366 vararg.elts = [cls.instantiate_class()] 367 yield vararg 368 return 369 if name == self.kwarg: 370 kwarg = nodes.const_factory({}) 371 kwarg.parent = self 372 yield kwarg 373 return 374 # if there is a default value, yield it. And then yield Uninferable to reflect 375 # we can't guess given argument value 376 try: 377 context = copy_context(context) 378 yield from self.default_value(name).infer(context) 379 yield util.Uninferable 380 except NoDefault: 381 yield util.Uninferable 382 383 384def arguments_assigned_stmts(self, node=None, context=None, assign_path=None): 385 if context.callcontext: 386 callee = context.callcontext.callee 387 while hasattr(callee, "_proxied"): 388 callee = callee._proxied 389 else: 390 callee = None 391 if ( 392 context.callcontext 393 and node 394 and getattr(callee, "name", None) == node.frame().name 395 ): 396 # reset call context/name 397 callcontext = context.callcontext 398 context = copy_context(context) 399 context.callcontext = None 400 args = arguments.CallSite(callcontext, context=context) 401 return args.infer_argument(self.parent, node.name, context) 402 return _arguments_infer_argname(self, node.name, context) 403 404 405nodes.Arguments.assigned_stmts = arguments_assigned_stmts 406 407 408@decorators.raise_if_nothing_inferred 409def assign_assigned_stmts(self, node=None, context=None, assign_path=None): 410 if not assign_path: 411 yield self.value 412 return None 413 yield from _resolve_assignment_parts( 414 self.value.infer(context), assign_path, context 415 ) 416 417 return dict(node=self, unknown=node, assign_path=assign_path, context=context) 418 419 420def assign_annassigned_stmts(self, node=None, context=None, assign_path=None): 421 for inferred in assign_assigned_stmts(self, node, context, assign_path): 422 if inferred is None: 423 yield util.Uninferable 424 else: 425 yield inferred 426 427 428nodes.Assign.assigned_stmts = assign_assigned_stmts 429nodes.AnnAssign.assigned_stmts = assign_annassigned_stmts 430nodes.AugAssign.assigned_stmts = assign_assigned_stmts 431 432 433def _resolve_assignment_parts(parts, assign_path, context): 434 """recursive function to resolve multiple assignments""" 435 assign_path = assign_path[:] 436 index = assign_path.pop(0) 437 for part in parts: 438 assigned = None 439 if isinstance(part, nodes.Dict): 440 # A dictionary in an iterating context 441 try: 442 assigned, _ = part.items[index] 443 except IndexError: 444 return 445 446 elif hasattr(part, "getitem"): 447 index_node = nodes.Const(index) 448 try: 449 assigned = part.getitem(index_node, context) 450 except (AstroidTypeError, AstroidIndexError): 451 return 452 453 if not assigned: 454 return 455 456 if not assign_path: 457 # we achieved to resolved the assignment path, don't infer the 458 # last part 459 yield assigned 460 elif assigned is util.Uninferable: 461 return 462 else: 463 # we are not yet on the last part of the path search on each 464 # possibly inferred value 465 try: 466 yield from _resolve_assignment_parts( 467 assigned.infer(context), assign_path, context 468 ) 469 except InferenceError: 470 return 471 472 473@decorators.raise_if_nothing_inferred 474def excepthandler_assigned_stmts(self, node=None, context=None, assign_path=None): 475 for assigned in node_classes.unpack_infer(self.type): 476 if isinstance(assigned, nodes.ClassDef): 477 assigned = objects.ExceptionInstance(assigned) 478 479 yield assigned 480 return dict(node=self, unknown=node, assign_path=assign_path, context=context) 481 482 483nodes.ExceptHandler.assigned_stmts = excepthandler_assigned_stmts 484 485 486def _infer_context_manager(self, mgr, context): 487 try: 488 inferred = next(mgr.infer(context=context)) 489 except StopIteration as e: 490 raise InferenceError(node=mgr) from e 491 if isinstance(inferred, bases.Generator): 492 # Check if it is decorated with contextlib.contextmanager. 493 func = inferred.parent 494 if not func.decorators: 495 raise InferenceError( 496 "No decorators found on inferred generator %s", node=func 497 ) 498 499 for decorator_node in func.decorators.nodes: 500 decorator = next(decorator_node.infer(context=context), None) 501 if isinstance(decorator, nodes.FunctionDef): 502 if decorator.qname() == _CONTEXTLIB_MGR: 503 break 504 else: 505 # It doesn't interest us. 506 raise InferenceError(node=func) 507 try: 508 yield next(inferred.infer_yield_types()) 509 except StopIteration as e: 510 raise InferenceError(node=func) from e 511 512 elif isinstance(inferred, bases.Instance): 513 try: 514 enter = next(inferred.igetattr("__enter__", context=context)) 515 except (InferenceError, AttributeInferenceError, StopIteration) as exc: 516 raise InferenceError(node=inferred) from exc 517 if not isinstance(enter, bases.BoundMethod): 518 raise InferenceError(node=enter) 519 yield from enter.infer_call_result(self, context) 520 else: 521 raise InferenceError(node=mgr) 522 523 524@decorators.raise_if_nothing_inferred 525def with_assigned_stmts(self, node=None, context=None, assign_path=None): 526 """Infer names and other nodes from a *with* statement. 527 528 This enables only inference for name binding in a *with* statement. 529 For instance, in the following code, inferring `func` will return 530 the `ContextManager` class, not whatever ``__enter__`` returns. 531 We are doing this intentionally, because we consider that the context 532 manager result is whatever __enter__ returns and what it is binded 533 using the ``as`` keyword. 534 535 class ContextManager(object): 536 def __enter__(self): 537 return 42 538 with ContextManager() as f: 539 pass 540 541 # ContextManager().infer() will return ContextManager 542 # f.infer() will return 42. 543 544 Arguments: 545 self: nodes.With 546 node: The target of the assignment, `as (a, b)` in `with foo as (a, b)`. 547 context: Inference context used for caching already inferred objects 548 assign_path: 549 A list of indices, where each index specifies what item to fetch from 550 the inference results. 551 """ 552 try: 553 mgr = next(mgr for (mgr, vars) in self.items if vars == node) 554 except StopIteration: 555 return None 556 if assign_path is None: 557 yield from _infer_context_manager(self, mgr, context) 558 else: 559 for result in _infer_context_manager(self, mgr, context): 560 # Walk the assign_path and get the item at the final index. 561 obj = result 562 for index in assign_path: 563 if not hasattr(obj, "elts"): 564 raise InferenceError( 565 "Wrong type ({targets!r}) for {node!r} assignment", 566 node=self, 567 targets=node, 568 assign_path=assign_path, 569 context=context, 570 ) 571 try: 572 obj = obj.elts[index] 573 except IndexError as exc: 574 raise InferenceError( 575 "Tried to infer a nonexistent target with index {index} " 576 "in {node!r}.", 577 node=self, 578 targets=node, 579 assign_path=assign_path, 580 context=context, 581 ) from exc 582 except TypeError as exc: 583 raise InferenceError( 584 "Tried to unpack a non-iterable value " "in {node!r}.", 585 node=self, 586 targets=node, 587 assign_path=assign_path, 588 context=context, 589 ) from exc 590 yield obj 591 return dict(node=self, unknown=node, assign_path=assign_path, context=context) 592 593 594nodes.With.assigned_stmts = with_assigned_stmts 595 596 597@decorators.raise_if_nothing_inferred 598def named_expr_assigned_stmts(self, node, context=None, assign_path=None): 599 """Infer names and other nodes from an assignment expression""" 600 if self.target == node: 601 yield from self.value.infer(context=context) 602 else: 603 raise InferenceError( 604 "Cannot infer NamedExpr node {node!r}", 605 node=self, 606 assign_path=assign_path, 607 context=context, 608 ) 609 610 611nodes.NamedExpr.assigned_stmts = named_expr_assigned_stmts 612 613 614@decorators.yes_if_nothing_inferred 615def starred_assigned_stmts(self, node=None, context=None, assign_path=None): 616 """ 617 Arguments: 618 self: nodes.Starred 619 node: a node related to the current underlying Node. 620 context: Inference context used for caching already inferred objects 621 assign_path: 622 A list of indices, where each index specifies what item to fetch from 623 the inference results. 624 """ 625 # pylint: disable=too-many-locals,too-many-statements 626 def _determine_starred_iteration_lookups(starred, target, lookups): 627 # Determine the lookups for the rhs of the iteration 628 itered = target.itered() 629 for index, element in enumerate(itered): 630 if ( 631 isinstance(element, nodes.Starred) 632 and element.value.name == starred.value.name 633 ): 634 lookups.append((index, len(itered))) 635 break 636 if isinstance(element, nodes.Tuple): 637 lookups.append((index, len(element.itered()))) 638 _determine_starred_iteration_lookups(starred, element, lookups) 639 640 stmt = self.statement() 641 if not isinstance(stmt, (nodes.Assign, nodes.For)): 642 raise InferenceError( 643 "Statement {stmt!r} enclosing {node!r} " "must be an Assign or For node.", 644 node=self, 645 stmt=stmt, 646 unknown=node, 647 context=context, 648 ) 649 650 if context is None: 651 context = InferenceContext() 652 653 if isinstance(stmt, nodes.Assign): 654 value = stmt.value 655 lhs = stmt.targets[0] 656 657 if sum(1 for _ in lhs.nodes_of_class(nodes.Starred)) > 1: 658 raise InferenceError( 659 "Too many starred arguments in the " " assignment targets {lhs!r}.", 660 node=self, 661 targets=lhs, 662 unknown=node, 663 context=context, 664 ) 665 666 try: 667 rhs = next(value.infer(context)) 668 except (InferenceError, StopIteration): 669 yield util.Uninferable 670 return 671 if rhs is util.Uninferable or not hasattr(rhs, "itered"): 672 yield util.Uninferable 673 return 674 675 try: 676 elts = collections.deque(rhs.itered()) 677 except TypeError: 678 yield util.Uninferable 679 return 680 681 # Unpack iteratively the values from the rhs of the assignment, 682 # until the find the starred node. What will remain will 683 # be the list of values which the Starred node will represent 684 # This is done in two steps, from left to right to remove 685 # anything before the starred node and from right to left 686 # to remove anything after the starred node. 687 688 for index, left_node in enumerate(lhs.elts): 689 if not isinstance(left_node, nodes.Starred): 690 if not elts: 691 break 692 elts.popleft() 693 continue 694 lhs_elts = collections.deque(reversed(lhs.elts[index:])) 695 for right_node in lhs_elts: 696 if not isinstance(right_node, nodes.Starred): 697 if not elts: 698 break 699 elts.pop() 700 continue 701 702 # We're done unpacking. 703 packed = nodes.List( 704 ctx=Context.Store, 705 parent=self, 706 lineno=lhs.lineno, 707 col_offset=lhs.col_offset, 708 ) 709 packed.postinit(elts=list(elts)) 710 yield packed 711 break 712 713 if isinstance(stmt, nodes.For): 714 try: 715 inferred_iterable = next(stmt.iter.infer(context=context)) 716 except (InferenceError, StopIteration): 717 yield util.Uninferable 718 return 719 if inferred_iterable is util.Uninferable or not hasattr( 720 inferred_iterable, "itered" 721 ): 722 yield util.Uninferable 723 return 724 try: 725 itered = inferred_iterable.itered() 726 except TypeError: 727 yield util.Uninferable 728 return 729 730 target = stmt.target 731 732 if not isinstance(target, nodes.Tuple): 733 raise InferenceError( 734 "Could not make sense of this, the target must be a tuple", 735 context=context, 736 ) 737 738 lookups = [] 739 _determine_starred_iteration_lookups(self, target, lookups) 740 if not lookups: 741 raise InferenceError( 742 "Could not make sense of this, needs at least a lookup", context=context 743 ) 744 745 # Make the last lookup a slice, since that what we want for a Starred node 746 last_element_index, last_element_length = lookups[-1] 747 is_starred_last = last_element_index == (last_element_length - 1) 748 749 lookup_slice = slice( 750 last_element_index, 751 None if is_starred_last else (last_element_length - last_element_index), 752 ) 753 lookups[-1] = lookup_slice 754 755 for element in itered: 756 757 # We probably want to infer the potential values *for each* element in an 758 # iterable, but we can't infer a list of all values, when only a list of 759 # step values are expected: 760 # 761 # for a, *b in [...]: 762 # b 763 # 764 # *b* should now point to just the elements at that particular iteration step, 765 # which astroid can't know about. 766 767 found_element = None 768 for lookup in lookups: 769 if not hasattr(element, "itered"): 770 break 771 if not isinstance(lookup, slice): 772 # Grab just the index, not the whole length 773 lookup = lookup[0] 774 try: 775 itered_inner_element = element.itered() 776 element = itered_inner_element[lookup] 777 except IndexError: 778 break 779 except TypeError: 780 # Most likely the itered() call failed, cannot make sense of this 781 yield util.Uninferable 782 return 783 else: 784 found_element = element 785 786 unpacked = nodes.List( 787 ctx=Context.Store, 788 parent=self, 789 lineno=self.lineno, 790 col_offset=self.col_offset, 791 ) 792 unpacked.postinit(elts=found_element or []) 793 yield unpacked 794 return 795 796 yield util.Uninferable 797 798 799nodes.Starred.assigned_stmts = starred_assigned_stmts 800 801 802@decorators.yes_if_nothing_inferred 803def match_mapping_assigned_stmts( 804 self: nodes.MatchMapping, 805 node: nodes.AssignName, 806 context: Optional[InferenceContext] = None, 807 assign_path: Literal[None] = None, 808) -> Generator[nodes.NodeNG, None, None]: 809 """Return empty generator (return -> raises StopIteration) so inferred value 810 is Uninferable. 811 """ 812 return 813 yield 814 815 816nodes.MatchMapping.assigned_stmts = match_mapping_assigned_stmts 817 818 819@decorators.yes_if_nothing_inferred 820def match_star_assigned_stmts( 821 self: nodes.MatchStar, 822 node: nodes.AssignName, 823 context: Optional[InferenceContext] = None, 824 assign_path: Literal[None] = None, 825) -> Generator[nodes.NodeNG, None, None]: 826 """Return empty generator (return -> raises StopIteration) so inferred value 827 is Uninferable. 828 """ 829 return 830 yield 831 832 833nodes.MatchStar.assigned_stmts = match_star_assigned_stmts 834 835 836@decorators.yes_if_nothing_inferred 837def match_as_assigned_stmts( 838 self: nodes.MatchAs, 839 node: nodes.AssignName, 840 context: Optional[InferenceContext] = None, 841 assign_path: Literal[None] = None, 842) -> Generator[nodes.NodeNG, None, None]: 843 """Infer MatchAs as the Match subject if it's the only MatchCase pattern 844 else raise StopIteration to yield Uninferable. 845 """ 846 if ( 847 isinstance(self.parent, nodes.MatchCase) 848 and isinstance(self.parent.parent, nodes.Match) 849 and self.pattern is None 850 ): 851 yield self.parent.parent.subject 852 853 854nodes.MatchAs.assigned_stmts = match_as_assigned_stmts 855