1# Copyright (c) 2006-2007, 2009-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr> 2# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com> 3# Copyright (c) 2010 Daniel Harding <dharding@gmail.com> 4# Copyright (c) 2012-2014 Google, Inc. 5# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com> 6# Copyright (c) 2013-2020 Claudiu Popa <pcmanticore@gmail.com> 7# Copyright (c) 2014 Brett Cannon <brett@python.org> 8# Copyright (c) 2014 Ricardo Gemignani <ricardo.gemignani@gmail.com> 9# Copyright (c) 2014 Arun Persaud <arun@nubati.net> 10# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru> 11# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org> 12# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro> 13# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro> 14# Copyright (c) 2016, 2018-2019 Ashley Whetter <ashley@awhetter.co.uk> 15# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com> 16# Copyright (c) 2016-2017 Moises Lopez <moylop260@vauxoo.com> 17# Copyright (c) 2016 Brian C. Lane <bcl@redhat.com> 18# Copyright (c) 2017-2018, 2020 hippo91 <guillaume.peillex@gmail.com> 19# Copyright (c) 2017 ttenhoeve-aa <ttenhoeve@appannie.com> 20# Copyright (c) 2018 Alan Chan <achan961117@gmail.com> 21# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com> 22# Copyright (c) 2018 Yury Gribov <tetra2005@gmail.com> 23# Copyright (c) 2018 Caio Carrara <ccarrara@redhat.com> 24# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com> 25# Copyright (c) 2018 Bryce Guinta <bryce.guinta@protonmail.com> 26# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com> 27# Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi> 28# Copyright (c) 2018 Brian Shaginaw <brian.shaginaw@warbyparker.com> 29# Copyright (c) 2019-2021 Pierre Sassoulas <pierre.sassoulas@gmail.com> 30# Copyright (c) 2019 Matthijs Blom <19817960+MatthijsBlom@users.noreply.github.com> 31# Copyright (c) 2019 Djailla <bastien.vallet@gmail.com> 32# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com> 33# Copyright (c) 2019 Nathan Marrow <nmarrow@google.com> 34# Copyright (c) 2019 Svet <svet@hyperscience.com> 35# Copyright (c) 2019 Pascal Corpet <pcorpet@users.noreply.github.com> 36# Copyright (c) 2020 Batuhan Taskaya <batuhanosmantaskaya@gmail.com> 37# Copyright (c) 2020 Luigi <luigi.cristofolini@q-ctrl.com> 38# Copyright (c) 2020 ethan-leba <ethanleba5@gmail.com> 39# Copyright (c) 2020 Damien Baty <damien.baty@polyconseil.fr> 40# Copyright (c) 2020 Andrew Simmons <anjsimmo@gmail.com> 41# Copyright (c) 2020 Ram Rachum <ram@rachum.com> 42# Copyright (c) 2020 Slavfox <slavfoxman@gmail.com> 43# Copyright (c) 2020 Anthony Sottile <asottile@umich.edu> 44# Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> 45# Copyright (c) 2021 Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> 46# Copyright (c) 2021 Nick Drozd <nicholasdrozd@gmail.com> 47# Copyright (c) 2021 Arianna Y <92831762+areveny@users.noreply.github.com> 48# Copyright (c) 2021 Jaehoon Hwang <jaehoonhwang@users.noreply.github.com> 49# Copyright (c) 2021 Samuel FORESTIER <HorlogeSkynet@users.noreply.github.com> 50# Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com> 51# Copyright (c) 2021 bot <bot@noreply.github.com> 52# Copyright (c) 2021 David Liu <david@cs.toronto.edu> 53# Copyright (c) 2021 Matus Valo <matusvalo@users.noreply.github.com> 54# Copyright (c) 2021 Lorena B <46202743+lorena-b@users.noreply.github.com> 55# Copyright (c) 2021 yushao2 <36848472+yushao2@users.noreply.github.com> 56 57# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html 58# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE 59 60"""some functions that may be useful for various checkers 61""" 62import builtins 63import itertools 64import numbers 65import re 66import string 67from functools import lru_cache, partial 68from typing import ( 69 Callable, 70 Dict, 71 Iterable, 72 List, 73 Match, 74 Optional, 75 Set, 76 Tuple, 77 Type, 78 Union, 79) 80 81import _string 82import astroid 83import astroid.objects 84from astroid import TooManyLevelsError, nodes 85from astroid.context import InferenceContext 86 87COMP_NODE_TYPES = ( 88 nodes.ListComp, 89 nodes.SetComp, 90 nodes.DictComp, 91 nodes.GeneratorExp, 92) 93EXCEPTIONS_MODULE = "builtins" 94ABC_MODULES = {"abc", "_py_abc"} 95ABC_METHODS = { 96 "abc.abstractproperty", 97 "abc.abstractmethod", 98 "abc.abstractclassmethod", 99 "abc.abstractstaticmethod", 100} 101TYPING_PROTOCOLS = frozenset( 102 {"typing.Protocol", "typing_extensions.Protocol", ".Protocol"} 103) 104ITER_METHOD = "__iter__" 105AITER_METHOD = "__aiter__" 106NEXT_METHOD = "__next__" 107GETITEM_METHOD = "__getitem__" 108CLASS_GETITEM_METHOD = "__class_getitem__" 109SETITEM_METHOD = "__setitem__" 110DELITEM_METHOD = "__delitem__" 111CONTAINS_METHOD = "__contains__" 112KEYS_METHOD = "keys" 113 114# Dictionary which maps the number of expected parameters a 115# special method can have to a set of special methods. 116# The following keys are used to denote the parameters restrictions: 117# 118# * None: variable number of parameters 119# * number: exactly that number of parameters 120# * tuple: this are the odd ones. Basically it means that the function 121# can work with any number of arguments from that tuple, 122# although it's best to implement it in order to accept 123# all of them. 124_SPECIAL_METHODS_PARAMS = { 125 None: ("__new__", "__init__", "__call__"), 126 0: ( 127 "__del__", 128 "__repr__", 129 "__str__", 130 "__bytes__", 131 "__hash__", 132 "__bool__", 133 "__dir__", 134 "__len__", 135 "__length_hint__", 136 "__iter__", 137 "__reversed__", 138 "__neg__", 139 "__pos__", 140 "__abs__", 141 "__invert__", 142 "__complex__", 143 "__int__", 144 "__float__", 145 "__index__", 146 "__trunc__", 147 "__floor__", 148 "__ceil__", 149 "__enter__", 150 "__aenter__", 151 "__getnewargs_ex__", 152 "__getnewargs__", 153 "__getstate__", 154 "__reduce__", 155 "__copy__", 156 "__unicode__", 157 "__nonzero__", 158 "__await__", 159 "__aiter__", 160 "__anext__", 161 "__fspath__", 162 ), 163 1: ( 164 "__format__", 165 "__lt__", 166 "__le__", 167 "__eq__", 168 "__ne__", 169 "__gt__", 170 "__ge__", 171 "__getattr__", 172 "__getattribute__", 173 "__delattr__", 174 "__delete__", 175 "__instancecheck__", 176 "__subclasscheck__", 177 "__getitem__", 178 "__missing__", 179 "__delitem__", 180 "__contains__", 181 "__add__", 182 "__sub__", 183 "__mul__", 184 "__truediv__", 185 "__floordiv__", 186 "__rfloordiv__", 187 "__mod__", 188 "__divmod__", 189 "__lshift__", 190 "__rshift__", 191 "__and__", 192 "__xor__", 193 "__or__", 194 "__radd__", 195 "__rsub__", 196 "__rmul__", 197 "__rtruediv__", 198 "__rmod__", 199 "__rdivmod__", 200 "__rpow__", 201 "__rlshift__", 202 "__rrshift__", 203 "__rand__", 204 "__rxor__", 205 "__ror__", 206 "__iadd__", 207 "__isub__", 208 "__imul__", 209 "__itruediv__", 210 "__ifloordiv__", 211 "__imod__", 212 "__ilshift__", 213 "__irshift__", 214 "__iand__", 215 "__ixor__", 216 "__ior__", 217 "__ipow__", 218 "__setstate__", 219 "__reduce_ex__", 220 "__deepcopy__", 221 "__cmp__", 222 "__matmul__", 223 "__rmatmul__", 224 "__imatmul__", 225 "__div__", 226 ), 227 2: ("__setattr__", "__get__", "__set__", "__setitem__", "__set_name__"), 228 3: ("__exit__", "__aexit__"), 229 (0, 1): ("__round__",), 230 (1, 2): ("__pow__",), 231} 232 233SPECIAL_METHODS_PARAMS = { 234 name: params 235 for params, methods in _SPECIAL_METHODS_PARAMS.items() 236 for name in methods 237} 238PYMETHODS = set(SPECIAL_METHODS_PARAMS) 239 240SUBSCRIPTABLE_CLASSES_PEP585 = frozenset( 241 ( 242 "builtins.tuple", 243 "builtins.list", 244 "builtins.dict", 245 "builtins.set", 246 "builtins.frozenset", 247 "builtins.type", 248 "collections.deque", 249 "collections.defaultdict", 250 "collections.OrderedDict", 251 "collections.Counter", 252 "collections.ChainMap", 253 "_collections_abc.Awaitable", 254 "_collections_abc.Coroutine", 255 "_collections_abc.AsyncIterable", 256 "_collections_abc.AsyncIterator", 257 "_collections_abc.AsyncGenerator", 258 "_collections_abc.Iterable", 259 "_collections_abc.Iterator", 260 "_collections_abc.Generator", 261 "_collections_abc.Reversible", 262 "_collections_abc.Container", 263 "_collections_abc.Collection", 264 "_collections_abc.Callable", 265 "_collections_abc.Set", 266 "_collections_abc.MutableSet", 267 "_collections_abc.Mapping", 268 "_collections_abc.MutableMapping", 269 "_collections_abc.Sequence", 270 "_collections_abc.MutableSequence", 271 "_collections_abc.ByteString", 272 "_collections_abc.MappingView", 273 "_collections_abc.KeysView", 274 "_collections_abc.ItemsView", 275 "_collections_abc.ValuesView", 276 "contextlib.AbstractContextManager", 277 "contextlib.AbstractAsyncContextManager", 278 "re.Pattern", 279 "re.Match", 280 ) 281) 282 283 284class NoSuchArgumentError(Exception): 285 pass 286 287 288class InferredTypeError(Exception): 289 pass 290 291 292def is_inside_lambda(node: nodes.NodeNG) -> bool: 293 """Return whether the given node is inside a lambda""" 294 return any(isinstance(parent, nodes.Lambda) for parent in node.node_ancestors()) 295 296 297def get_all_elements( 298 node: nodes.NodeNG, 299) -> Iterable[nodes.NodeNG]: 300 """Recursively returns all atoms in nested lists and tuples.""" 301 if isinstance(node, (nodes.Tuple, nodes.List)): 302 for child in node.elts: 303 yield from get_all_elements(child) 304 else: 305 yield node 306 307 308def is_super(node: nodes.NodeNG) -> bool: 309 """return True if the node is referencing the "super" builtin function""" 310 if getattr(node, "name", None) == "super" and node.root().name == "builtins": 311 return True 312 return False 313 314 315def is_error(node: nodes.FunctionDef) -> bool: 316 """Return true if the given function node only raises an exception""" 317 return len(node.body) == 1 and isinstance(node.body[0], nodes.Raise) 318 319 320builtins = builtins.__dict__.copy() # type: ignore[assignment] 321SPECIAL_BUILTINS = ("__builtins__",) # '__path__', '__file__') 322 323 324def is_builtin_object(node: nodes.NodeNG) -> bool: 325 """Returns True if the given node is an object from the __builtin__ module.""" 326 return node and node.root().name == "builtins" 327 328 329def is_builtin(name: str) -> bool: 330 """return true if <name> could be considered as a builtin defined by python""" 331 return name in builtins or name in SPECIAL_BUILTINS # type: ignore[operator] 332 333 334def is_defined_in_scope( 335 var_node: nodes.NodeNG, 336 varname: str, 337 scope: nodes.NodeNG, 338) -> bool: 339 if isinstance(scope, nodes.If): 340 for node in scope.body: 341 if ( 342 isinstance(node, nodes.Assign) 343 and any( 344 isinstance(target, nodes.AssignName) and target.name == varname 345 for target in node.targets 346 ) 347 ) or (isinstance(node, nodes.Nonlocal) and varname in node.names): 348 return True 349 elif isinstance(scope, (COMP_NODE_TYPES, nodes.For)): 350 for ass_node in scope.nodes_of_class(nodes.AssignName): 351 if ass_node.name == varname: 352 return True 353 elif isinstance(scope, nodes.With): 354 for expr, ids in scope.items: 355 if expr.parent_of(var_node): 356 break 357 if ids and isinstance(ids, nodes.AssignName) and ids.name == varname: 358 return True 359 elif isinstance(scope, (nodes.Lambda, nodes.FunctionDef)): 360 if scope.args.is_argument(varname): 361 # If the name is found inside a default value 362 # of a function, then let the search continue 363 # in the parent's tree. 364 if scope.args.parent_of(var_node): 365 try: 366 scope.args.default_value(varname) 367 scope = scope.parent 368 is_defined_in_scope(var_node, varname, scope) 369 except astroid.NoDefault: 370 pass 371 return True 372 if getattr(scope, "name", None) == varname: 373 return True 374 elif isinstance(scope, nodes.ExceptHandler): 375 if isinstance(scope.name, nodes.AssignName): 376 ass_node = scope.name 377 if ass_node.name == varname: 378 return True 379 return False 380 381 382def is_defined_before(var_node: nodes.Name) -> bool: 383 """Check if the given variable node is defined before 384 385 Verify that the variable node is defined by a parent node 386 (list, set, dict, or generator comprehension, lambda) 387 or in a previous sibling node on the same line 388 (statement_defining ; statement_using). 389 """ 390 varname = var_node.name 391 for parent in var_node.node_ancestors(): 392 if is_defined_in_scope(var_node, varname, parent): 393 return True 394 # possibly multiple statements on the same line using semi colon separator 395 stmt = var_node.statement() 396 _node = stmt.previous_sibling() 397 lineno = stmt.fromlineno 398 while _node and _node.fromlineno == lineno: 399 for assign_node in _node.nodes_of_class(nodes.AssignName): 400 if assign_node.name == varname: 401 return True 402 for imp_node in _node.nodes_of_class((nodes.ImportFrom, nodes.Import)): 403 if varname in [name[1] or name[0] for name in imp_node.names]: 404 return True 405 _node = _node.previous_sibling() 406 return False 407 408 409def is_default_argument( 410 node: nodes.NodeNG, scope: Optional[nodes.NodeNG] = None 411) -> bool: 412 """return true if the given Name node is used in function or lambda 413 default argument's value 414 """ 415 if not scope: 416 scope = node.scope() 417 if isinstance(scope, (nodes.FunctionDef, nodes.Lambda)): 418 all_defaults = itertools.chain( 419 scope.args.defaults, (d for d in scope.args.kw_defaults if d is not None) 420 ) 421 return any( 422 default_name_node is node 423 for default_node in all_defaults 424 for default_name_node in default_node.nodes_of_class(nodes.Name) 425 ) 426 427 return False 428 429 430def is_func_decorator(node: nodes.NodeNG) -> bool: 431 """return true if the name is used in function decorator""" 432 for parent in node.node_ancestors(): 433 if isinstance(parent, nodes.Decorators): 434 return True 435 if parent.is_statement or isinstance( 436 parent, 437 ( 438 nodes.Lambda, 439 nodes.ComprehensionScope, 440 nodes.ListComp, 441 ), 442 ): 443 break 444 return False 445 446 447def is_ancestor_name(frame: nodes.ClassDef, node: nodes.NodeNG) -> bool: 448 """return whether `frame` is an astroid.Class node with `node` in the 449 subtree of its bases attribute 450 """ 451 if not isinstance(frame, nodes.ClassDef): 452 return False 453 return any(node in base.nodes_of_class(nodes.Name) for base in frame.bases) 454 455 456def is_being_called(node: nodes.NodeNG) -> bool: 457 """return True if node is the function being called in a Call node""" 458 return isinstance(node.parent, nodes.Call) and node.parent.func is node 459 460 461def assign_parent(node: nodes.NodeNG) -> nodes.NodeNG: 462 """return the higher parent which is not an AssignName, Tuple or List node""" 463 while node and isinstance(node, (nodes.AssignName, nodes.Tuple, nodes.List)): 464 node = node.parent 465 return node 466 467 468def overrides_a_method(class_node: nodes.ClassDef, name: str) -> bool: 469 """return True if <name> is a method overridden from an ancestor 470 which is not the base object class""" 471 for ancestor in class_node.ancestors(): 472 if ancestor.name == "object": 473 continue 474 if name in ancestor and isinstance(ancestor[name], nodes.FunctionDef): 475 return True 476 return False 477 478 479def check_messages(*messages: str) -> Callable: 480 """decorator to store messages that are handled by a checker method""" 481 482 def store_messages(func): 483 func.checks_msgs = messages 484 return func 485 486 return store_messages 487 488 489class IncompleteFormatString(Exception): 490 """A format string ended in the middle of a format specifier.""" 491 492 493class UnsupportedFormatCharacter(Exception): 494 """A format character in a format string is not one of the supported 495 format characters.""" 496 497 def __init__(self, index): 498 super().__init__(index) 499 self.index = index 500 501 502def parse_format_string( 503 format_string: str, 504) -> Tuple[Set[str], int, Dict[str, str], List[str]]: 505 """Parses a format string, returning a tuple of (keys, num_args), where keys 506 is the set of mapping keys in the format string, and num_args is the number 507 of arguments required by the format string. Raises 508 IncompleteFormatString or UnsupportedFormatCharacter if a 509 parse error occurs.""" 510 keys = set() 511 key_types = {} 512 pos_types = [] 513 num_args = 0 514 515 def next_char(i): 516 i += 1 517 if i == len(format_string): 518 raise IncompleteFormatString 519 return (i, format_string[i]) 520 521 i = 0 522 while i < len(format_string): 523 char = format_string[i] 524 if char == "%": 525 i, char = next_char(i) 526 # Parse the mapping key (optional). 527 key = None 528 if char == "(": 529 depth = 1 530 i, char = next_char(i) 531 key_start = i 532 while depth != 0: 533 if char == "(": 534 depth += 1 535 elif char == ")": 536 depth -= 1 537 i, char = next_char(i) 538 key_end = i - 1 539 key = format_string[key_start:key_end] 540 541 # Parse the conversion flags (optional). 542 while char in "#0- +": 543 i, char = next_char(i) 544 # Parse the minimum field width (optional). 545 if char == "*": 546 num_args += 1 547 i, char = next_char(i) 548 else: 549 while char in string.digits: 550 i, char = next_char(i) 551 # Parse the precision (optional). 552 if char == ".": 553 i, char = next_char(i) 554 if char == "*": 555 num_args += 1 556 i, char = next_char(i) 557 else: 558 while char in string.digits: 559 i, char = next_char(i) 560 # Parse the length modifier (optional). 561 if char in "hlL": 562 i, char = next_char(i) 563 # Parse the conversion type (mandatory). 564 flags = "diouxXeEfFgGcrs%a" 565 if char not in flags: 566 raise UnsupportedFormatCharacter(i) 567 if key: 568 keys.add(key) 569 key_types[key] = char 570 elif char != "%": 571 num_args += 1 572 pos_types.append(char) 573 i += 1 574 return keys, num_args, key_types, pos_types 575 576 577def split_format_field_names(format_string) -> Tuple[str, Iterable[Tuple[bool, str]]]: 578 try: 579 return _string.formatter_field_name_split(format_string) 580 except ValueError as e: 581 raise IncompleteFormatString() from e 582 583 584def collect_string_fields(format_string) -> Iterable[Optional[str]]: 585 """Given a format string, return an iterator 586 of all the valid format fields. It handles nested fields 587 as well. 588 """ 589 formatter = string.Formatter() 590 try: 591 parseiterator = formatter.parse(format_string) 592 for result in parseiterator: 593 if all(item is None for item in result[1:]): 594 # not a replacement format 595 continue 596 name = result[1] 597 nested = result[2] 598 yield name 599 if nested: 600 yield from collect_string_fields(nested) 601 except ValueError as exc: 602 # Probably the format string is invalid. 603 if exc.args[0].startswith("cannot switch from manual"): 604 # On Jython, parsing a string with both manual 605 # and automatic positions will fail with a ValueError, 606 # while on CPython it will simply return the fields, 607 # the validation being done in the interpreter (?). 608 # We're just returning two mixed fields in order 609 # to trigger the format-combined-specification check. 610 yield "" 611 yield "1" 612 return 613 raise IncompleteFormatString(format_string) from exc 614 615 616def parse_format_method_string( 617 format_string: str, 618) -> Tuple[List[Tuple[str, List[Tuple[bool, str]]]], int, int]: 619 """ 620 Parses a PEP 3101 format string, returning a tuple of 621 (keyword_arguments, implicit_pos_args_cnt, explicit_pos_args), 622 where keyword_arguments is the set of mapping keys in the format string, implicit_pos_args_cnt 623 is the number of arguments required by the format string and 624 explicit_pos_args is the number of arguments passed with the position. 625 """ 626 keyword_arguments = [] 627 implicit_pos_args_cnt = 0 628 explicit_pos_args = set() 629 for name in collect_string_fields(format_string): 630 if name and str(name).isdigit(): 631 explicit_pos_args.add(str(name)) 632 elif name: 633 keyname, fielditerator = split_format_field_names(name) 634 if isinstance(keyname, numbers.Number): 635 explicit_pos_args.add(str(keyname)) 636 try: 637 keyword_arguments.append((keyname, list(fielditerator))) 638 except ValueError as e: 639 raise IncompleteFormatString() from e 640 else: 641 implicit_pos_args_cnt += 1 642 return keyword_arguments, implicit_pos_args_cnt, len(explicit_pos_args) 643 644 645def is_attr_protected(attrname: str) -> bool: 646 """return True if attribute name is protected (start with _ and some other 647 details), False otherwise. 648 """ 649 return ( 650 attrname[0] == "_" 651 and attrname != "_" 652 and not (attrname.startswith("__") and attrname.endswith("__")) 653 ) 654 655 656def node_frame_class(node: nodes.NodeNG) -> Optional[nodes.ClassDef]: 657 """Return the class that is wrapping the given node 658 659 The function returns a class for a method node (or a staticmethod or a 660 classmethod), otherwise it returns `None`. 661 """ 662 klass = node.frame() 663 nodes_to_check = ( 664 nodes.NodeNG, 665 astroid.UnboundMethod, 666 astroid.BaseInstance, 667 ) 668 while ( 669 klass 670 and isinstance(klass, nodes_to_check) 671 and not isinstance(klass, nodes.ClassDef) 672 ): 673 if klass.parent is None: 674 return None 675 676 klass = klass.parent.frame() 677 678 return klass 679 680 681def get_outer_class(class_node: astroid.ClassDef) -> Optional[astroid.ClassDef]: 682 """Return the class that is the outer class of given (nested) class_node""" 683 parent_klass = class_node.parent.frame() 684 685 return parent_klass if isinstance(parent_klass, astroid.ClassDef) else None 686 687 688def is_attr_private(attrname: str) -> Optional[Match[str]]: 689 """Check that attribute name is private (at least two leading underscores, 690 at most one trailing underscore) 691 """ 692 regex = re.compile("^_{2,}.*[^_]+_?$") 693 return regex.match(attrname) 694 695 696def get_argument_from_call( 697 call_node: nodes.Call, position: Optional[int] = None, keyword: Optional[str] = None 698) -> nodes.Name: 699 """Returns the specified argument from a function call. 700 701 :param nodes.Call call_node: Node representing a function call to check. 702 :param int position: position of the argument. 703 :param str keyword: the keyword of the argument. 704 705 :returns: The node representing the argument, None if the argument is not found. 706 :rtype: nodes.Name 707 :raises ValueError: if both position and keyword are None. 708 :raises NoSuchArgumentError: if no argument at the provided position or with 709 the provided keyword. 710 """ 711 if position is None and keyword is None: 712 raise ValueError("Must specify at least one of: position or keyword.") 713 if position is not None: 714 try: 715 return call_node.args[position] 716 except IndexError: 717 pass 718 if keyword and call_node.keywords: 719 for arg in call_node.keywords: 720 if arg.arg == keyword: 721 return arg.value 722 723 raise NoSuchArgumentError 724 725 726def inherit_from_std_ex(node: nodes.NodeNG) -> bool: 727 """ 728 Return whether the given class node is subclass of 729 exceptions.Exception. 730 """ 731 ancestors = node.ancestors() if hasattr(node, "ancestors") else [] 732 return any( 733 ancestor.name in {"Exception", "BaseException"} 734 and ancestor.root().name == EXCEPTIONS_MODULE 735 for ancestor in itertools.chain([node], ancestors) 736 ) 737 738 739def error_of_type(handler: nodes.ExceptHandler, error_type) -> bool: 740 """ 741 Check if the given exception handler catches 742 the given error_type. 743 744 The *handler* parameter is a node, representing an ExceptHandler node. 745 The *error_type* can be an exception, such as AttributeError, 746 the name of an exception, or it can be a tuple of errors. 747 The function will return True if the handler catches any of the 748 given errors. 749 """ 750 751 def stringify_error(error): 752 if not isinstance(error, str): 753 return error.__name__ 754 return error 755 756 if not isinstance(error_type, tuple): 757 error_type = (error_type,) 758 expected_errors = {stringify_error(error) for error in error_type} 759 if not handler.type: 760 return False 761 return handler.catch(expected_errors) 762 763 764def decorated_with_property(node: nodes.FunctionDef) -> bool: 765 """Detect if the given function node is decorated with a property.""" 766 if not node.decorators: 767 return False 768 for decorator in node.decorators.nodes: 769 try: 770 if _is_property_decorator(decorator): 771 return True 772 except astroid.InferenceError: 773 pass 774 return False 775 776 777def _is_property_kind(node, *kinds): 778 if not isinstance(node, (astroid.UnboundMethod, nodes.FunctionDef)): 779 return False 780 if node.decorators: 781 for decorator in node.decorators.nodes: 782 if isinstance(decorator, nodes.Attribute) and decorator.attrname in kinds: 783 return True 784 return False 785 786 787def is_property_setter(node: nodes.FunctionDef) -> bool: 788 """Check if the given node is a property setter""" 789 return _is_property_kind(node, "setter") 790 791 792def is_property_deleter(node: nodes.FunctionDef) -> bool: 793 """Check if the given node is a property deleter""" 794 return _is_property_kind(node, "deleter") 795 796 797def is_property_setter_or_deleter(node: nodes.FunctionDef) -> bool: 798 """Check if the given node is either a property setter or a deleter""" 799 return _is_property_kind(node, "setter", "deleter") 800 801 802def _is_property_decorator(decorator: nodes.Name) -> bool: 803 for inferred in decorator.infer(): 804 if isinstance(inferred, nodes.ClassDef): 805 if inferred.qname() in {"builtins.property", "functools.cached_property"}: 806 return True 807 for ancestor in inferred.ancestors(): 808 if ancestor.name == "property" and ancestor.root().name == "builtins": 809 return True 810 elif isinstance(inferred, nodes.FunctionDef): 811 # If decorator is function, check if it has exactly one return 812 # and the return is itself a function decorated with property 813 returns: List[nodes.Return] = list( 814 inferred._get_return_nodes_skip_functions() 815 ) 816 if len(returns) == 1 and isinstance( 817 returns[0].value, (nodes.Name, nodes.Attribute) 818 ): 819 inferred = safe_infer(returns[0].value) 820 if ( 821 inferred 822 and isinstance(inferred, astroid.objects.Property) 823 and isinstance(inferred.function, nodes.FunctionDef) 824 ): 825 return decorated_with_property(inferred.function) 826 return False 827 828 829def decorated_with( 830 func: Union[ 831 nodes.ClassDef, nodes.FunctionDef, astroid.BoundMethod, astroid.UnboundMethod 832 ], 833 qnames: Iterable[str], 834) -> bool: 835 """Determine if the `func` node has a decorator with the qualified name `qname`.""" 836 decorators = func.decorators.nodes if func.decorators else [] 837 for decorator_node in decorators: 838 if isinstance(decorator_node, nodes.Call): 839 # We only want to infer the function name 840 decorator_node = decorator_node.func 841 try: 842 if any( 843 i.name in qnames or i.qname() in qnames 844 for i in decorator_node.infer() 845 if i is not None and i != astroid.Uninferable 846 ): 847 return True 848 except astroid.InferenceError: 849 continue 850 return False 851 852 853def uninferable_final_decorators( 854 node: nodes.Decorators, 855) -> List[Optional[Union[nodes.Attribute, nodes.Name]]]: 856 """Return a list of uninferable `typing.final` decorators in `node`. 857 858 This function is used to determine if the `typing.final` decorator is used 859 with an unsupported Python version; the decorator cannot be inferred when 860 using a Python version lower than 3.8. 861 """ 862 decorators = [] 863 for decorator in getattr(node, "nodes", []): 864 if isinstance(decorator, nodes.Attribute): 865 try: 866 import_node = decorator.expr.lookup(decorator.expr.name)[1][0] 867 except AttributeError: 868 continue 869 elif isinstance(decorator, nodes.Name): 870 import_node = decorator.lookup(decorator.name)[1][0] 871 else: 872 continue 873 874 if not isinstance(import_node, (astroid.Import, astroid.ImportFrom)): 875 continue 876 877 import_names = dict(import_node.names) 878 879 # from typing import final 880 is_from_import = ("final" in import_names) and import_node.modname == "typing" 881 # import typing 882 is_import = ("typing" in import_names) and getattr( 883 decorator, "attrname", None 884 ) == "final" 885 886 if (is_from_import or is_import) and safe_infer(decorator) in [ 887 astroid.Uninferable, 888 None, 889 ]: 890 decorators.append(decorator) 891 return decorators 892 893 894@lru_cache(maxsize=1024) 895def unimplemented_abstract_methods( 896 node: nodes.ClassDef, is_abstract_cb: nodes.FunctionDef = None 897) -> Dict[str, nodes.NodeNG]: 898 """ 899 Get the unimplemented abstract methods for the given *node*. 900 901 A method can be considered abstract if the callback *is_abstract_cb* 902 returns a ``True`` value. The check defaults to verifying that 903 a method is decorated with abstract methods. 904 The function will work only for new-style classes. For old-style 905 classes, it will simply return an empty dictionary. 906 For the rest of them, it will return a dictionary of abstract method 907 names and their inferred objects. 908 """ 909 if is_abstract_cb is None: 910 is_abstract_cb = partial(decorated_with, qnames=ABC_METHODS) 911 visited: Dict[str, nodes.NodeNG] = {} 912 try: 913 mro = reversed(node.mro()) 914 except NotImplementedError: 915 # Old style class, it will not have a mro. 916 return {} 917 except astroid.ResolveError: 918 # Probably inconsistent hierarchy, don'try 919 # to figure this out here. 920 return {} 921 for ancestor in mro: 922 for obj in ancestor.values(): 923 inferred = obj 924 if isinstance(obj, nodes.AssignName): 925 inferred = safe_infer(obj) 926 if not inferred: 927 # Might be an abstract function, 928 # but since we don't have enough information 929 # in order to take this decision, we're taking 930 # the *safe* decision instead. 931 if obj.name in visited: 932 del visited[obj.name] 933 continue 934 if not isinstance(inferred, nodes.FunctionDef): 935 if obj.name in visited: 936 del visited[obj.name] 937 if isinstance(inferred, nodes.FunctionDef): 938 # It's critical to use the original name, 939 # since after inferring, an object can be something 940 # else than expected, as in the case of the 941 # following assignment. 942 # 943 # class A: 944 # def keys(self): pass 945 # __iter__ = keys 946 abstract = is_abstract_cb(inferred) 947 if abstract: 948 visited[obj.name] = inferred 949 elif not abstract and obj.name in visited: 950 del visited[obj.name] 951 return visited 952 953 954def find_try_except_wrapper_node( 955 node: nodes.NodeNG, 956) -> Optional[Union[nodes.ExceptHandler, nodes.TryExcept]]: 957 """Return the ExceptHandler or the TryExcept node in which the node is.""" 958 current = node 959 ignores = (nodes.ExceptHandler, nodes.TryExcept) 960 while current and not isinstance(current.parent, ignores): 961 current = current.parent 962 963 if current and isinstance(current.parent, ignores): 964 return current.parent 965 return None 966 967 968def find_except_wrapper_node_in_scope( 969 node: nodes.NodeNG, 970) -> Optional[Union[nodes.ExceptHandler, nodes.TryExcept]]: 971 """Return the ExceptHandler in which the node is, without going out of scope.""" 972 for current in node.node_ancestors(): 973 if isinstance(current, astroid.scoped_nodes.LocalsDictNodeNG): 974 # If we're inside a function/class definition, we don't want to keep checking 975 # higher ancestors for `except` clauses, because if these exist, it means our 976 # function/class was defined in an `except` clause, rather than the current code 977 # actually running in an `except` clause. 978 return None 979 if isinstance(current, nodes.ExceptHandler): 980 return current 981 return None 982 983 984def is_from_fallback_block(node: nodes.NodeNG) -> bool: 985 """Check if the given node is from a fallback import block.""" 986 context = find_try_except_wrapper_node(node) 987 if not context: 988 return False 989 990 if isinstance(context, nodes.ExceptHandler): 991 other_body = context.parent.body 992 handlers = context.parent.handlers 993 else: 994 other_body = itertools.chain.from_iterable( 995 handler.body for handler in context.handlers 996 ) 997 handlers = context.handlers 998 999 has_fallback_imports = any( 1000 isinstance(import_node, (nodes.ImportFrom, nodes.Import)) 1001 for import_node in other_body 1002 ) 1003 ignores_import_error = _except_handlers_ignores_exceptions( 1004 handlers, (ImportError, ModuleNotFoundError) 1005 ) 1006 return ignores_import_error or has_fallback_imports 1007 1008 1009def _except_handlers_ignores_exceptions( 1010 handlers: nodes.ExceptHandler, 1011 exceptions: Tuple[Type[ImportError], Type[ModuleNotFoundError]], 1012) -> bool: 1013 func = partial(error_of_type, error_type=exceptions) 1014 return any(func(handler) for handler in handlers) 1015 1016 1017def get_exception_handlers( 1018 node: nodes.NodeNG, exception=Exception 1019) -> Optional[List[nodes.ExceptHandler]]: 1020 """Return the collections of handlers handling the exception in arguments. 1021 1022 Args: 1023 node (nodes.NodeNG): A node that is potentially wrapped in a try except. 1024 exception (builtin.Exception or str): exception or name of the exception. 1025 1026 Returns: 1027 list: the collection of handlers that are handling the exception or None. 1028 1029 """ 1030 context = find_try_except_wrapper_node(node) 1031 if isinstance(context, nodes.TryExcept): 1032 return [ 1033 handler for handler in context.handlers if error_of_type(handler, exception) 1034 ] 1035 return [] 1036 1037 1038def is_node_inside_try_except(node: nodes.Raise) -> bool: 1039 """Check if the node is directly under a Try/Except statement. 1040 (but not under an ExceptHandler!) 1041 1042 Args: 1043 node (nodes.Raise): the node raising the exception. 1044 1045 Returns: 1046 bool: True if the node is inside a try/except statement, False otherwise. 1047 """ 1048 context = find_try_except_wrapper_node(node) 1049 return isinstance(context, nodes.TryExcept) 1050 1051 1052def node_ignores_exception(node: nodes.NodeNG, exception=Exception) -> bool: 1053 """Check if the node is in a TryExcept which handles the given exception. 1054 1055 If the exception is not given, the function is going to look for bare 1056 excepts. 1057 """ 1058 managing_handlers = get_exception_handlers(node, exception) 1059 if not managing_handlers: 1060 return False 1061 return any(managing_handlers) 1062 1063 1064def class_is_abstract(node: nodes.ClassDef) -> bool: 1065 """return true if the given class node should be considered as an abstract 1066 class 1067 """ 1068 # Only check for explicit metaclass=ABCMeta on this specific class 1069 meta = node.declared_metaclass() 1070 if meta is not None: 1071 if meta.name == "ABCMeta" and meta.root().name in ABC_MODULES: 1072 return True 1073 1074 for ancestor in node.ancestors(): 1075 if ancestor.name == "ABC" and ancestor.root().name in ABC_MODULES: 1076 # abc.ABC inheritance 1077 return True 1078 1079 for method in node.methods(): 1080 if method.parent.frame() is node: 1081 if method.is_abstract(pass_is_abstract=False): 1082 return True 1083 return False 1084 1085 1086def _supports_protocol_method(value: nodes.NodeNG, attr: str) -> bool: 1087 try: 1088 attributes = value.getattr(attr) 1089 except astroid.NotFoundError: 1090 return False 1091 1092 first = attributes[0] 1093 if isinstance(first, nodes.AssignName): 1094 if isinstance(first.parent.value, nodes.Const): 1095 return False 1096 return True 1097 1098 1099def is_comprehension(node: nodes.NodeNG) -> bool: 1100 comprehensions = ( 1101 nodes.ListComp, 1102 nodes.SetComp, 1103 nodes.DictComp, 1104 nodes.GeneratorExp, 1105 ) 1106 return isinstance(node, comprehensions) 1107 1108 1109def _supports_mapping_protocol(value: nodes.NodeNG) -> bool: 1110 return _supports_protocol_method( 1111 value, GETITEM_METHOD 1112 ) and _supports_protocol_method(value, KEYS_METHOD) 1113 1114 1115def _supports_membership_test_protocol(value: nodes.NodeNG) -> bool: 1116 return _supports_protocol_method(value, CONTAINS_METHOD) 1117 1118 1119def _supports_iteration_protocol(value: nodes.NodeNG) -> bool: 1120 return _supports_protocol_method(value, ITER_METHOD) or _supports_protocol_method( 1121 value, GETITEM_METHOD 1122 ) 1123 1124 1125def _supports_async_iteration_protocol(value: nodes.NodeNG) -> bool: 1126 return _supports_protocol_method(value, AITER_METHOD) 1127 1128 1129def _supports_getitem_protocol(value: nodes.NodeNG) -> bool: 1130 return _supports_protocol_method(value, GETITEM_METHOD) 1131 1132 1133def _supports_setitem_protocol(value: nodes.NodeNG) -> bool: 1134 return _supports_protocol_method(value, SETITEM_METHOD) 1135 1136 1137def _supports_delitem_protocol(value: nodes.NodeNG) -> bool: 1138 return _supports_protocol_method(value, DELITEM_METHOD) 1139 1140 1141def _is_abstract_class_name(name: str) -> bool: 1142 lname = name.lower() 1143 is_mixin = lname.endswith("mixin") 1144 is_abstract = lname.startswith("abstract") 1145 is_base = lname.startswith("base") or lname.endswith("base") 1146 return is_mixin or is_abstract or is_base 1147 1148 1149def is_inside_abstract_class(node: nodes.NodeNG) -> bool: 1150 while node is not None: 1151 if isinstance(node, nodes.ClassDef): 1152 if class_is_abstract(node): 1153 return True 1154 name = getattr(node, "name", None) 1155 if name is not None and _is_abstract_class_name(name): 1156 return True 1157 node = node.parent 1158 return False 1159 1160 1161def _supports_protocol( 1162 value: nodes.NodeNG, protocol_callback: nodes.FunctionDef 1163) -> bool: 1164 if isinstance(value, nodes.ClassDef): 1165 if not has_known_bases(value): 1166 return True 1167 # classobj can only be iterable if it has an iterable metaclass 1168 meta = value.metaclass() 1169 if meta is not None: 1170 if protocol_callback(meta): 1171 return True 1172 if isinstance(value, astroid.BaseInstance): 1173 if not has_known_bases(value): 1174 return True 1175 if value.has_dynamic_getattr(): 1176 return True 1177 if protocol_callback(value): 1178 return True 1179 1180 if ( 1181 isinstance(value, astroid.bases.Proxy) 1182 and isinstance(value._proxied, astroid.BaseInstance) 1183 and has_known_bases(value._proxied) 1184 ): 1185 value = value._proxied 1186 return protocol_callback(value) 1187 1188 return False 1189 1190 1191def is_iterable(value: nodes.NodeNG, check_async: bool = False) -> bool: 1192 if check_async: 1193 protocol_check = _supports_async_iteration_protocol 1194 else: 1195 protocol_check = _supports_iteration_protocol 1196 return _supports_protocol(value, protocol_check) 1197 1198 1199def is_mapping(value: nodes.NodeNG) -> bool: 1200 return _supports_protocol(value, _supports_mapping_protocol) 1201 1202 1203def supports_membership_test(value: nodes.NodeNG) -> bool: 1204 supported = _supports_protocol(value, _supports_membership_test_protocol) 1205 return supported or is_iterable(value) 1206 1207 1208def supports_getitem(value: nodes.NodeNG, node: nodes.NodeNG) -> bool: 1209 if isinstance(value, nodes.ClassDef): 1210 if _supports_protocol_method(value, CLASS_GETITEM_METHOD): 1211 return True 1212 if is_class_subscriptable_pep585_with_postponed_evaluation_enabled(value, node): 1213 return True 1214 return _supports_protocol(value, _supports_getitem_protocol) 1215 1216 1217def supports_setitem(value: nodes.NodeNG, _: nodes.NodeNG) -> bool: 1218 return _supports_protocol(value, _supports_setitem_protocol) 1219 1220 1221def supports_delitem(value: nodes.NodeNG, _: nodes.NodeNG) -> bool: 1222 return _supports_protocol(value, _supports_delitem_protocol) 1223 1224 1225def _get_python_type_of_node(node): 1226 pytype = getattr(node, "pytype", None) 1227 if callable(pytype): 1228 return pytype() 1229 return None 1230 1231 1232@lru_cache(maxsize=1024) 1233def safe_infer(node: nodes.NodeNG, context=None) -> Optional[nodes.NodeNG]: 1234 """Return the inferred value for the given node. 1235 1236 Return None if inference failed or if there is some ambiguity (more than 1237 one node has been inferred of different types). 1238 """ 1239 inferred_types = set() 1240 try: 1241 infer_gen = node.infer(context=context) 1242 value = next(infer_gen) 1243 except astroid.InferenceError: 1244 return None 1245 1246 if value is not astroid.Uninferable: 1247 inferred_types.add(_get_python_type_of_node(value)) 1248 1249 try: 1250 for inferred in infer_gen: 1251 inferred_type = _get_python_type_of_node(inferred) 1252 if inferred_type not in inferred_types: 1253 return None # If there is ambiguity on the inferred node. 1254 except astroid.InferenceError: 1255 return None # There is some kind of ambiguity 1256 except StopIteration: 1257 return value 1258 return value if len(inferred_types) <= 1 else None 1259 1260 1261@lru_cache(maxsize=512) 1262def infer_all( 1263 node: nodes.NodeNG, context: InferenceContext = None 1264) -> List[nodes.NodeNG]: 1265 try: 1266 return list(node.infer(context=context)) 1267 except astroid.InferenceError: 1268 return [] 1269 1270 1271def has_known_bases(klass: nodes.ClassDef, context=None) -> bool: 1272 """Return true if all base classes of a class could be inferred.""" 1273 try: 1274 return klass._all_bases_known 1275 except AttributeError: 1276 pass 1277 for base in klass.bases: 1278 result = safe_infer(base, context=context) 1279 if ( 1280 not isinstance(result, nodes.ClassDef) 1281 or result is klass 1282 or not has_known_bases(result, context=context) 1283 ): 1284 klass._all_bases_known = False 1285 return False 1286 klass._all_bases_known = True 1287 return True 1288 1289 1290def is_none(node: nodes.NodeNG) -> bool: 1291 return ( 1292 node is None 1293 or (isinstance(node, nodes.Const) and node.value is None) 1294 or (isinstance(node, nodes.Name) and node.name == "None") 1295 ) 1296 1297 1298def node_type(node: nodes.NodeNG) -> Optional[nodes.NodeNG]: 1299 """Return the inferred type for `node` 1300 1301 If there is more than one possible type, or if inferred type is Uninferable or None, 1302 return None 1303 """ 1304 # check there is only one possible type for the assign node. Else we 1305 # don't handle it for now 1306 types: Set[nodes.NodeNG] = set() 1307 try: 1308 for var_type in node.infer(): 1309 if var_type == astroid.Uninferable or is_none(var_type): 1310 continue 1311 types.add(var_type) 1312 if len(types) > 1: 1313 return None 1314 except astroid.InferenceError: 1315 return None 1316 return types.pop() if types else None 1317 1318 1319def is_registered_in_singledispatch_function(node: nodes.FunctionDef) -> bool: 1320 """Check if the given function node is a singledispatch function.""" 1321 1322 singledispatch_qnames = ( 1323 "functools.singledispatch", 1324 "singledispatch.singledispatch", 1325 ) 1326 1327 if not isinstance(node, nodes.FunctionDef): 1328 return False 1329 1330 decorators = node.decorators.nodes if node.decorators else [] 1331 for decorator in decorators: 1332 # func.register are function calls 1333 if not isinstance(decorator, nodes.Call): 1334 continue 1335 1336 func = decorator.func 1337 if not isinstance(func, nodes.Attribute) or func.attrname != "register": 1338 continue 1339 1340 try: 1341 func_def = next(func.expr.infer()) 1342 except astroid.InferenceError: 1343 continue 1344 1345 if isinstance(func_def, nodes.FunctionDef): 1346 return decorated_with(func_def, singledispatch_qnames) 1347 1348 return False 1349 1350 1351def get_node_last_lineno(node: nodes.NodeNG) -> int: 1352 """ 1353 Get the last lineno of the given node. For a simple statement this will just be node.lineno, 1354 but for a node that has child statements (e.g. a method) this will be the lineno of the last 1355 child statement recursively. 1356 """ 1357 # 'finalbody' is always the last clause in a try statement, if present 1358 if getattr(node, "finalbody", False): 1359 return get_node_last_lineno(node.finalbody[-1]) 1360 # For if, while, and for statements 'orelse' is always the last clause. 1361 # For try statements 'orelse' is the last in the absence of a 'finalbody' 1362 if getattr(node, "orelse", False): 1363 return get_node_last_lineno(node.orelse[-1]) 1364 # try statements have the 'handlers' last if there is no 'orelse' or 'finalbody' 1365 if getattr(node, "handlers", False): 1366 return get_node_last_lineno(node.handlers[-1]) 1367 # All compound statements have a 'body' 1368 if getattr(node, "body", False): 1369 return get_node_last_lineno(node.body[-1]) 1370 # Not a compound statement 1371 return node.lineno 1372 1373 1374def is_postponed_evaluation_enabled(node: nodes.NodeNG) -> bool: 1375 """Check if the postponed evaluation of annotations is enabled""" 1376 module = node.root() 1377 return "annotations" in module.future_imports 1378 1379 1380def is_class_subscriptable_pep585_with_postponed_evaluation_enabled( 1381 value: nodes.ClassDef, node: nodes.NodeNG 1382) -> bool: 1383 """Check if class is subscriptable with PEP 585 and 1384 postponed evaluation enabled. 1385 """ 1386 return ( 1387 is_postponed_evaluation_enabled(node) 1388 and value.qname() in SUBSCRIPTABLE_CLASSES_PEP585 1389 and is_node_in_type_annotation_context(node) 1390 ) 1391 1392 1393def is_node_in_type_annotation_context(node: nodes.NodeNG) -> bool: 1394 """Check if node is in type annotation context. 1395 1396 Check for 'AnnAssign', function 'Arguments', 1397 or part of function return type anntation. 1398 """ 1399 # pylint: disable=too-many-boolean-expressions 1400 current_node, parent_node = node, node.parent 1401 while True: 1402 if ( 1403 isinstance(parent_node, nodes.AnnAssign) 1404 and parent_node.annotation == current_node 1405 or isinstance(parent_node, nodes.Arguments) 1406 and current_node 1407 in ( 1408 *parent_node.annotations, 1409 *parent_node.posonlyargs_annotations, 1410 *parent_node.kwonlyargs_annotations, 1411 parent_node.varargannotation, 1412 parent_node.kwargannotation, 1413 ) 1414 or isinstance(parent_node, nodes.FunctionDef) 1415 and parent_node.returns == current_node 1416 ): 1417 return True 1418 current_node, parent_node = parent_node, parent_node.parent 1419 if isinstance(parent_node, nodes.Module): 1420 return False 1421 1422 1423def is_subclass_of(child: nodes.ClassDef, parent: nodes.ClassDef) -> bool: 1424 """ 1425 Check if first node is a subclass of second node. 1426 :param child: Node to check for subclass. 1427 :param parent: Node to check for superclass. 1428 :returns: True if child is derived from parent. False otherwise. 1429 """ 1430 if not all(isinstance(node, nodes.ClassDef) for node in (child, parent)): 1431 return False 1432 1433 for ancestor in child.ancestors(): 1434 try: 1435 if astroid.helpers.is_subtype(ancestor, parent): 1436 return True 1437 except astroid.exceptions._NonDeducibleTypeHierarchy: 1438 continue 1439 return False 1440 1441 1442@lru_cache(maxsize=1024) 1443def is_overload_stub(node: nodes.NodeNG) -> bool: 1444 """Check if a node if is a function stub decorated with typing.overload. 1445 1446 :param node: Node to check. 1447 :returns: True if node is an overload function stub. False otherwise. 1448 """ 1449 decorators = getattr(node, "decorators", None) 1450 return bool(decorators and decorated_with(node, ["typing.overload", "overload"])) 1451 1452 1453def is_protocol_class(cls: nodes.NodeNG) -> bool: 1454 """Check if the given node represents a protocol class 1455 1456 :param cls: The node to check 1457 :returns: True if the node is a typing protocol class, false otherwise. 1458 """ 1459 if not isinstance(cls, nodes.ClassDef): 1460 return False 1461 1462 # Use .ancestors() since not all protocol classes can have 1463 # their mro deduced. 1464 return any(parent.qname() in TYPING_PROTOCOLS for parent in cls.ancestors()) 1465 1466 1467def is_call_of_name(node: nodes.NodeNG, name: str) -> bool: 1468 """Checks if node is a function call with the given name""" 1469 return ( 1470 isinstance(node, nodes.Call) 1471 and isinstance(node.func, nodes.Name) 1472 and node.func.name == name 1473 ) 1474 1475 1476def is_test_condition( 1477 node: nodes.NodeNG, 1478 parent: Optional[nodes.NodeNG] = None, 1479) -> bool: 1480 """Returns true if the given node is being tested for truthiness""" 1481 parent = parent or node.parent 1482 if isinstance(parent, (nodes.While, nodes.If, nodes.IfExp, nodes.Assert)): 1483 return node is parent.test or parent.test.parent_of(node) 1484 if isinstance(parent, nodes.Comprehension): 1485 return node in parent.ifs 1486 return is_call_of_name(parent, "bool") and parent.parent_of(node) 1487 1488 1489def is_classdef_type(node: nodes.ClassDef) -> bool: 1490 """Test if ClassDef node is Type.""" 1491 if node.name == "type": 1492 return True 1493 return any(isinstance(b, nodes.Name) and b.name == "type" for b in node.bases) 1494 1495 1496def is_attribute_typed_annotation( 1497 node: Union[nodes.ClassDef, astroid.Instance], attr_name: str 1498) -> bool: 1499 """Test if attribute is typed annotation in current node 1500 or any base nodes. 1501 """ 1502 attribute = node.locals.get(attr_name, [None])[0] 1503 if ( 1504 attribute 1505 and isinstance(attribute, nodes.AssignName) 1506 and isinstance(attribute.parent, nodes.AnnAssign) 1507 ): 1508 return True 1509 for base in node.bases: 1510 inferred = safe_infer(base) 1511 if ( 1512 inferred 1513 and isinstance(inferred, nodes.ClassDef) 1514 and is_attribute_typed_annotation(inferred, attr_name) 1515 ): 1516 return True 1517 return False 1518 1519 1520def is_assign_name_annotated_with(node: nodes.AssignName, typing_name: str) -> bool: 1521 """Test if AssignName node has `typing_name` annotation. 1522 1523 Especially useful to check for `typing._SpecialForm` instances 1524 like: `Union`, `Optional`, `Literal`, `ClassVar`, `Final`. 1525 """ 1526 if not isinstance(node.parent, nodes.AnnAssign): 1527 return False 1528 annotation = node.parent.annotation 1529 if isinstance(annotation, nodes.Subscript): 1530 annotation = annotation.value 1531 if ( 1532 isinstance(annotation, nodes.Name) 1533 and annotation.name == typing_name 1534 or isinstance(annotation, nodes.Attribute) 1535 and annotation.attrname == typing_name 1536 ): 1537 return True 1538 return False 1539 1540 1541def get_iterating_dictionary_name( 1542 node: Union[nodes.For, nodes.Comprehension] 1543) -> Optional[str]: 1544 """Get the name of the dictionary which keys are being iterated over on 1545 a ``nodes.For`` or ``nodes.Comprehension`` node. 1546 1547 If the iterating object is not either the keys method of a dictionary 1548 or a dictionary itself, this returns None. 1549 """ 1550 # Is it a proper keys call? 1551 if ( 1552 isinstance(node.iter, nodes.Call) 1553 and isinstance(node.iter.func, nodes.Attribute) 1554 and node.iter.func.attrname == "keys" 1555 ): 1556 inferred = safe_infer(node.iter.func) 1557 if not isinstance(inferred, astroid.BoundMethod): 1558 return None 1559 return node.iter.as_string().rpartition(".keys")[0] 1560 1561 # Is it a dictionary? 1562 if isinstance(node.iter, (nodes.Name, nodes.Attribute)): 1563 inferred = safe_infer(node.iter) 1564 if not isinstance(inferred, nodes.Dict): 1565 return None 1566 return node.iter.as_string() 1567 1568 return None 1569 1570 1571def get_subscript_const_value(node: nodes.Subscript) -> nodes.Const: 1572 """ 1573 Returns the value 'subscript.slice' of a Subscript node. 1574 1575 :param node: Subscript Node to extract value from 1576 :returns: Const Node containing subscript value 1577 :raises InferredTypeError: if the subscript node cannot be inferred as a Const 1578 """ 1579 inferred = safe_infer(node.slice) 1580 if not isinstance(inferred, nodes.Const): 1581 raise InferredTypeError("Subscript.slice cannot be inferred as a nodes.Const") 1582 1583 return inferred 1584 1585 1586def get_import_name( 1587 importnode: Union[nodes.Import, nodes.ImportFrom], modname: str 1588) -> str: 1589 """Get a prepared module name from the given import node 1590 1591 In the case of relative imports, this will return the 1592 absolute qualified module name, which might be useful 1593 for debugging. Otherwise, the initial module name 1594 is returned unchanged. 1595 1596 :param importnode: node representing import statement. 1597 :param modname: module name from import statement. 1598 :returns: absolute qualified module name of the module 1599 used in import. 1600 """ 1601 if isinstance(importnode, nodes.ImportFrom) and importnode.level: 1602 root = importnode.root() 1603 if isinstance(root, nodes.Module): 1604 try: 1605 return root.relative_to_absolute_name(modname, level=importnode.level) 1606 except TooManyLevelsError: 1607 return modname 1608 return modname 1609 1610 1611def is_sys_guard(node: nodes.If) -> bool: 1612 """Return True if IF stmt is a sys.version_info guard. 1613 1614 >>> import sys 1615 >>> if sys.version_info > (3, 8): 1616 >>> from typing import Literal 1617 >>> else: 1618 >>> from typing_extensions import Literal 1619 """ 1620 if isinstance(node.test, nodes.Compare): 1621 value = node.test.left 1622 if isinstance(value, nodes.Subscript): 1623 value = value.value 1624 if ( 1625 isinstance(value, nodes.Attribute) 1626 and value.as_string() == "sys.version_info" 1627 ): 1628 return True 1629 1630 return False 1631 1632 1633def is_typing_guard(node: nodes.If) -> bool: 1634 """Return True if IF stmt is a typing guard. 1635 1636 >>> from typing import TYPE_CHECKING 1637 >>> if TYPE_CHECKING: 1638 >>> from xyz import a 1639 """ 1640 return isinstance( 1641 node.test, (nodes.Name, nodes.Attribute) 1642 ) and node.test.as_string().endswith("TYPE_CHECKING") 1643 1644 1645def is_node_in_guarded_import_block(node: nodes.NodeNG) -> bool: 1646 """Return True if node is part for guarded if block. 1647 I.e. `sys.version_info` or `typing.TYPE_CHECKING` 1648 """ 1649 return isinstance(node.parent, nodes.If) and ( 1650 is_sys_guard(node.parent) or is_typing_guard(node.parent) 1651 ) 1652 1653 1654def is_reassigned_after_current(node: nodes.NodeNG, varname: str) -> bool: 1655 """Check if the given variable name is reassigned in the same scope after the current node""" 1656 return any( 1657 a.name == varname and a.lineno > node.lineno 1658 for a in node.scope().nodes_of_class((nodes.AssignName, nodes.FunctionDef)) 1659 ) 1660 1661 1662def is_function_body_ellipsis(node: nodes.FunctionDef) -> bool: 1663 """Checks whether a function body only consisst of a single Ellipsis""" 1664 return ( 1665 len(node.body) == 1 1666 and isinstance(node.body[0], nodes.Expr) 1667 and isinstance(node.body[0].value, nodes.Const) 1668 and node.body[0].value.value == Ellipsis 1669 ) 1670 1671 1672def is_base_container(node: Optional[nodes.NodeNG]) -> bool: 1673 return isinstance(node, nodes.BaseContainer) and not node.elts 1674 1675 1676def is_empty_dict_literal(node: Optional[nodes.NodeNG]) -> bool: 1677 return isinstance(node, nodes.Dict) and not node.items 1678 1679 1680def is_empty_str_literal(node: Optional[nodes.NodeNG]) -> bool: 1681 return ( 1682 isinstance(node, nodes.Const) and isinstance(node.value, str) and not node.value 1683 ) 1684 1685 1686def returns_bool(node: nodes.NodeNG) -> bool: 1687 """Returns true if a node is a return that returns a constant boolean""" 1688 return ( 1689 isinstance(node, nodes.Return) 1690 and isinstance(node.value, nodes.Const) 1691 and node.value.value in {True, False} 1692 ) 1693