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