Coverage for C:\Repos\ekr-pylint\pylint\checkers\utils.py: 21%
765 statements
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
1# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
2# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
3# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
5"""Some functions that may be useful for various checkers."""
7from __future__ import annotations
9import builtins
10import itertools
11import numbers
12import re
13import string
14import warnings
15from collections.abc import Iterable
16from functools import lru_cache, partial
17from re import Match
18from typing import TYPE_CHECKING, Callable, TypeVar
20import _string
21import astroid.objects
22from astroid import TooManyLevelsError, nodes
23from astroid.context import InferenceContext
25from pylint.constants import TYPING_TYPE_CHECKS_GUARDS
27if TYPE_CHECKING:
28 from pylint.checkers import BaseChecker
30_NodeT = TypeVar("_NodeT", bound=nodes.NodeNG)
31_CheckerT = TypeVar("_CheckerT", bound="BaseChecker")
32AstCallbackMethod = Callable[[_CheckerT, _NodeT], None]
34COMP_NODE_TYPES = (
35 nodes.ListComp,
36 nodes.SetComp,
37 nodes.DictComp,
38 nodes.GeneratorExp,
39)
40EXCEPTIONS_MODULE = "builtins"
41ABC_MODULES = {"abc", "_py_abc"}
42ABC_METHODS = {
43 "abc.abstractproperty",
44 "abc.abstractmethod",
45 "abc.abstractclassmethod",
46 "abc.abstractstaticmethod",
47}
48TYPING_PROTOCOLS = frozenset(
49 {"typing.Protocol", "typing_extensions.Protocol", ".Protocol"}
50)
51ITER_METHOD = "__iter__"
52AITER_METHOD = "__aiter__"
53NEXT_METHOD = "__next__"
54GETITEM_METHOD = "__getitem__"
55CLASS_GETITEM_METHOD = "__class_getitem__"
56SETITEM_METHOD = "__setitem__"
57DELITEM_METHOD = "__delitem__"
58CONTAINS_METHOD = "__contains__"
59KEYS_METHOD = "keys"
61# Dictionary which maps the number of expected parameters a
62# special method can have to a set of special methods.
63# The following keys are used to denote the parameters restrictions:
64#
65# * None: variable number of parameters
66# * number: exactly that number of parameters
67# * tuple: these are the odd ones. Basically it means that the function
68# can work with any number of arguments from that tuple,
69# although it's best to implement it in order to accept
70# all of them.
71_SPECIAL_METHODS_PARAMS = {
72 None: ("__new__", "__init__", "__call__"),
73 0: (
74 "__del__",
75 "__repr__",
76 "__str__",
77 "__bytes__",
78 "__hash__",
79 "__bool__",
80 "__dir__",
81 "__len__",
82 "__length_hint__",
83 "__iter__",
84 "__reversed__",
85 "__neg__",
86 "__pos__",
87 "__abs__",
88 "__invert__",
89 "__complex__",
90 "__int__",
91 "__float__",
92 "__index__",
93 "__trunc__",
94 "__floor__",
95 "__ceil__",
96 "__enter__",
97 "__aenter__",
98 "__getnewargs_ex__",
99 "__getnewargs__",
100 "__getstate__",
101 "__reduce__",
102 "__copy__",
103 "__unicode__",
104 "__nonzero__",
105 "__await__",
106 "__aiter__",
107 "__anext__",
108 "__fspath__",
109 "__subclasses__",
110 "__init_subclass__",
111 ),
112 1: (
113 "__format__",
114 "__lt__",
115 "__le__",
116 "__eq__",
117 "__ne__",
118 "__gt__",
119 "__ge__",
120 "__getattr__",
121 "__getattribute__",
122 "__delattr__",
123 "__delete__",
124 "__instancecheck__",
125 "__subclasscheck__",
126 "__getitem__",
127 "__missing__",
128 "__delitem__",
129 "__contains__",
130 "__add__",
131 "__sub__",
132 "__mul__",
133 "__truediv__",
134 "__floordiv__",
135 "__rfloordiv__",
136 "__mod__",
137 "__divmod__",
138 "__lshift__",
139 "__rshift__",
140 "__and__",
141 "__xor__",
142 "__or__",
143 "__radd__",
144 "__rsub__",
145 "__rmul__",
146 "__rtruediv__",
147 "__rmod__",
148 "__rdivmod__",
149 "__rpow__",
150 "__rlshift__",
151 "__rrshift__",
152 "__rand__",
153 "__rxor__",
154 "__ror__",
155 "__iadd__",
156 "__isub__",
157 "__imul__",
158 "__itruediv__",
159 "__ifloordiv__",
160 "__imod__",
161 "__ilshift__",
162 "__irshift__",
163 "__iand__",
164 "__ixor__",
165 "__ior__",
166 "__ipow__",
167 "__setstate__",
168 "__reduce_ex__",
169 "__deepcopy__",
170 "__cmp__",
171 "__matmul__",
172 "__rmatmul__",
173 "__imatmul__",
174 "__div__",
175 ),
176 2: ("__setattr__", "__get__", "__set__", "__setitem__", "__set_name__"),
177 3: ("__exit__", "__aexit__"),
178 (0, 1): ("__round__",),
179 (1, 2): ("__pow__",),
180}
182SPECIAL_METHODS_PARAMS = {
183 name: params
184 for params, methods in _SPECIAL_METHODS_PARAMS.items()
185 for name in methods
186}
187PYMETHODS = set(SPECIAL_METHODS_PARAMS)
189SUBSCRIPTABLE_CLASSES_PEP585 = frozenset(
190 (
191 "builtins.tuple",
192 "builtins.list",
193 "builtins.dict",
194 "builtins.set",
195 "builtins.frozenset",
196 "builtins.type",
197 "collections.deque",
198 "collections.defaultdict",
199 "collections.OrderedDict",
200 "collections.Counter",
201 "collections.ChainMap",
202 "_collections_abc.Awaitable",
203 "_collections_abc.Coroutine",
204 "_collections_abc.AsyncIterable",
205 "_collections_abc.AsyncIterator",
206 "_collections_abc.AsyncGenerator",
207 "_collections_abc.Iterable",
208 "_collections_abc.Iterator",
209 "_collections_abc.Generator",
210 "_collections_abc.Reversible",
211 "_collections_abc.Container",
212 "_collections_abc.Collection",
213 "_collections_abc.Callable",
214 "_collections_abc.Set",
215 "_collections_abc.MutableSet",
216 "_collections_abc.Mapping",
217 "_collections_abc.MutableMapping",
218 "_collections_abc.Sequence",
219 "_collections_abc.MutableSequence",
220 "_collections_abc.ByteString",
221 "_collections_abc.MappingView",
222 "_collections_abc.KeysView",
223 "_collections_abc.ItemsView",
224 "_collections_abc.ValuesView",
225 "contextlib.AbstractContextManager",
226 "contextlib.AbstractAsyncContextManager",
227 "re.Pattern",
228 "re.Match",
229 )
230)
233class NoSuchArgumentError(Exception):
234 pass
237class InferredTypeError(Exception):
238 pass
241def is_inside_lambda(node: nodes.NodeNG) -> bool:
242 """Return whether the given node is inside a lambda."""
243 warnings.warn(
244 "utils.is_inside_lambda will be removed in favour of calling "
245 "utils.get_node_first_ancestor_of_type(x, nodes.Lambda) in pylint 3.0",
246 DeprecationWarning,
247 )
248 return any(isinstance(parent, nodes.Lambda) for parent in node.node_ancestors())
251def get_all_elements(
252 node: nodes.NodeNG,
253) -> Iterable[nodes.NodeNG]:
254 """Recursively returns all atoms in nested lists and tuples."""
255 if isinstance(node, (nodes.Tuple, nodes.List)):
256 for child in node.elts:
257 yield from get_all_elements(child)
258 else:
259 yield node
262def is_super(node: nodes.NodeNG) -> bool:
263 """Return True if the node is referencing the "super" builtin function."""
264 if getattr(node, "name", None) == "super" and node.root().name == "builtins":
265 return True
266 return False
269def is_error(node: nodes.FunctionDef) -> bool:
270 """Return true if the given function node only raises an exception."""
271 return len(node.body) == 1 and isinstance(node.body[0], nodes.Raise)
274builtins = builtins.__dict__.copy() # type: ignore[assignment]
275SPECIAL_BUILTINS = ("__builtins__",) # '__path__', '__file__')
278def is_builtin_object(node: nodes.NodeNG) -> bool:
279 """Returns True if the given node is an object from the __builtin__ module."""
280 return node and node.root().name == "builtins"
283def is_builtin(name: str) -> bool:
284 """Return true if <name> could be considered as a builtin defined by python."""
285 return name in builtins or name in SPECIAL_BUILTINS # type: ignore[attr-defined]
288def is_defined_in_scope(
289 var_node: nodes.NodeNG,
290 varname: str,
291 scope: nodes.NodeNG,
292) -> bool:
293 if isinstance(scope, nodes.If):
294 for node in scope.body:
295 if (
296 isinstance(node, nodes.Assign)
297 and any(
298 isinstance(target, nodes.AssignName) and target.name == varname
299 for target in node.targets
300 )
301 ) or (isinstance(node, nodes.Nonlocal) and varname in node.names):
302 return True
303 elif isinstance(scope, (COMP_NODE_TYPES, nodes.For)):
304 for ass_node in scope.nodes_of_class(nodes.AssignName):
305 if ass_node.name == varname:
306 return True
307 elif isinstance(scope, nodes.With):
308 for expr, ids in scope.items:
309 if expr.parent_of(var_node):
310 break
311 if ids and isinstance(ids, nodes.AssignName) and ids.name == varname:
312 return True
313 elif isinstance(scope, (nodes.Lambda, nodes.FunctionDef)):
314 if scope.args.is_argument(varname):
315 # If the name is found inside a default value
316 # of a function, then let the search continue
317 # in the parent's tree.
318 if scope.args.parent_of(var_node):
319 try:
320 scope.args.default_value(varname)
321 scope = scope.parent
322 is_defined_in_scope(var_node, varname, scope)
323 except astroid.NoDefault:
324 pass
325 return True
326 if getattr(scope, "name", None) == varname:
327 return True
328 elif isinstance(scope, nodes.ExceptHandler):
329 if isinstance(scope.name, nodes.AssignName):
330 ass_node = scope.name
331 if ass_node.name == varname:
332 return True
333 return False
336def is_defined_before(var_node: nodes.Name) -> bool:
337 """Check if the given variable node is defined before.
339 Verify that the variable node is defined by a parent node
340 (list, set, dict, or generator comprehension, lambda)
341 or in a previous sibling node on the same line
342 (statement_defining ; statement_using).
343 """
344 varname = var_node.name
345 for parent in var_node.node_ancestors():
346 if is_defined_in_scope(var_node, varname, parent):
347 return True
348 # possibly multiple statements on the same line using semicolon separator
349 stmt = var_node.statement(future=True)
350 _node = stmt.previous_sibling()
351 lineno = stmt.fromlineno
352 while _node and _node.fromlineno == lineno:
353 for assign_node in _node.nodes_of_class(nodes.AssignName):
354 if assign_node.name == varname:
355 return True
356 for imp_node in _node.nodes_of_class((nodes.ImportFrom, nodes.Import)):
357 if varname in [name[1] or name[0] for name in imp_node.names]:
358 return True
359 _node = _node.previous_sibling()
360 return False
363def is_default_argument(node: nodes.NodeNG, scope: nodes.NodeNG | None = None) -> bool:
364 """Return true if the given Name node is used in function or lambda
365 default argument's value.
366 """
367 if not scope:
368 scope = node.scope()
369 if isinstance(scope, (nodes.FunctionDef, nodes.Lambda)):
370 all_defaults = itertools.chain(
371 scope.args.defaults, (d for d in scope.args.kw_defaults if d is not None)
372 )
373 return any(
374 default_name_node is node
375 for default_node in all_defaults
376 for default_name_node in default_node.nodes_of_class(nodes.Name)
377 )
379 return False
382def is_func_decorator(node: nodes.NodeNG) -> bool:
383 """Return true if the name is used in function decorator."""
384 for parent in node.node_ancestors():
385 if isinstance(parent, nodes.Decorators):
386 return True
387 if parent.is_statement or isinstance(
388 parent,
389 (
390 nodes.Lambda,
391 nodes.ComprehensionScope,
392 nodes.ListComp,
393 ),
394 ):
395 break
396 return False
399def is_ancestor_name(frame: nodes.ClassDef, node: nodes.NodeNG) -> bool:
400 """Return whether `frame` is an astroid.Class node with `node` in the
401 subtree of its bases attribute.
402 """
403 if not isinstance(frame, nodes.ClassDef):
404 return False
405 return any(node in base.nodes_of_class(nodes.Name) for base in frame.bases)
408def is_being_called(node: nodes.NodeNG) -> bool:
409 """Return True if node is the function being called in a Call node."""
410 return isinstance(node.parent, nodes.Call) and node.parent.func is node
413def assign_parent(node: nodes.NodeNG) -> nodes.NodeNG:
414 """Return the higher parent which is not an AssignName, Tuple or List node."""
415 while node and isinstance(node, (nodes.AssignName, nodes.Tuple, nodes.List)):
416 node = node.parent
417 return node
420def overrides_a_method(class_node: nodes.ClassDef, name: str) -> bool:
421 """Return True if <name> is a method overridden from an ancestor
422 which is not the base object class.
423 """
424 for ancestor in class_node.ancestors():
425 if ancestor.name == "object":
426 continue
427 if name in ancestor and isinstance(ancestor[name], nodes.FunctionDef):
428 return True
429 return False
432def only_required_for_messages(
433 *messages: str,
434) -> Callable[
435 [AstCallbackMethod[_CheckerT, _NodeT]], AstCallbackMethod[_CheckerT, _NodeT]
436]:
437 """Decorator to store messages that are handled by a checker method as an
438 attribute of the function object.
440 This information is used by ``ASTWalker`` to decide whether to call the decorated
441 method or not. If none of the messages is enabled, the method will be skipped.
442 Therefore, the list of messages must be well maintained at all times!
443 This decorator only has an effect on ``visit_*`` and ``leave_*`` methods
444 of a class inheriting from ``BaseChecker``.
445 """
447 def store_messages(
448 func: AstCallbackMethod[_CheckerT, _NodeT]
449 ) -> AstCallbackMethod[_CheckerT, _NodeT]:
450 setattr(func, "checks_msgs", messages)
451 return func
453 return store_messages
456def check_messages(
457 *messages: str,
458) -> Callable[
459 [AstCallbackMethod[_CheckerT, _NodeT]], AstCallbackMethod[_CheckerT, _NodeT]
460]:
461 """Kept for backwards compatibility, deprecated.
463 Use only_required_for_messages instead, which conveys the intent of the decorator much clearer.
464 """
465 warnings.warn(
466 "utils.check_messages will be removed in favour of calling "
467 "utils.only_required_for_messages in pylint 3.0",
468 DeprecationWarning,
469 )
471 return only_required_for_messages(*messages)
474class IncompleteFormatString(Exception):
475 """A format string ended in the middle of a format specifier."""
478class UnsupportedFormatCharacter(Exception):
479 """A format character in a format string is not one of the supported
480 format characters.
481 """
483 def __init__(self, index):
484 super().__init__(index)
485 self.index = index
488def parse_format_string(
489 format_string: str,
490) -> tuple[set[str], int, dict[str, str], list[str]]:
491 """Parses a format string, returning a tuple (keys, num_args).
493 Where 'keys' is the set of mapping keys in the format string, and 'num_args' is the number
494 of arguments required by the format string. Raises IncompleteFormatString or
495 UnsupportedFormatCharacter if a parse error occurs.
496 """
497 keys = set()
498 key_types = {}
499 pos_types = []
500 num_args = 0
502 def next_char(i):
503 i += 1
504 if i == len(format_string):
505 raise IncompleteFormatString
506 return (i, format_string[i])
508 i = 0
509 while i < len(format_string):
510 char = format_string[i]
511 if char == "%":
512 i, char = next_char(i)
513 # Parse the mapping key (optional).
514 key = None
515 if char == "(":
516 depth = 1
517 i, char = next_char(i)
518 key_start = i
519 while depth != 0:
520 if char == "(":
521 depth += 1
522 elif char == ")":
523 depth -= 1
524 i, char = next_char(i)
525 key_end = i - 1
526 key = format_string[key_start:key_end]
528 # Parse the conversion flags (optional).
529 while char in "#0- +":
530 i, char = next_char(i)
531 # Parse the minimum field width (optional).
532 if char == "*":
533 num_args += 1
534 i, char = next_char(i)
535 else:
536 while char in string.digits:
537 i, char = next_char(i)
538 # Parse the precision (optional).
539 if char == ".":
540 i, char = next_char(i)
541 if char == "*":
542 num_args += 1
543 i, char = next_char(i)
544 else:
545 while char in string.digits:
546 i, char = next_char(i)
547 # Parse the length modifier (optional).
548 if char in "hlL":
549 i, char = next_char(i)
550 # Parse the conversion type (mandatory).
551 flags = "diouxXeEfFgGcrs%a"
552 if char not in flags:
553 raise UnsupportedFormatCharacter(i)
554 if key:
555 keys.add(key)
556 key_types[key] = char
557 elif char != "%":
558 num_args += 1
559 pos_types.append(char)
560 i += 1
561 return keys, num_args, key_types, pos_types
564def split_format_field_names(format_string) -> tuple[str, Iterable[tuple[bool, str]]]:
565 try:
566 return _string.formatter_field_name_split(format_string)
567 except ValueError as e:
568 raise IncompleteFormatString() from e
571def collect_string_fields(format_string) -> Iterable[str | None]:
572 """Given a format string, return an iterator
573 of all the valid format fields.
575 It handles nested fields as well.
576 """
577 formatter = string.Formatter()
578 try:
579 parseiterator = formatter.parse(format_string)
580 for result in parseiterator:
581 if all(item is None for item in result[1:]):
582 # not a replacement format
583 continue
584 name = result[1]
585 nested = result[2]
586 yield name
587 if nested:
588 yield from collect_string_fields(nested)
589 except ValueError as exc:
590 # Probably the format string is invalid.
591 if exc.args[0].startswith("cannot switch from manual"):
592 # On Jython, parsing a string with both manual
593 # and automatic positions will fail with a ValueError,
594 # while on CPython it will simply return the fields,
595 # the validation being done in the interpreter (?).
596 # We're just returning two mixed fields in order
597 # to trigger the format-combined-specification check.
598 yield ""
599 yield "1"
600 return
601 raise IncompleteFormatString(format_string) from exc
604def parse_format_method_string(
605 format_string: str,
606) -> tuple[list[tuple[str, list[tuple[bool, str]]]], int, int]:
607 """Parses a PEP 3101 format string, returning a tuple of
608 (keyword_arguments, implicit_pos_args_cnt, explicit_pos_args).
610 keyword_arguments is the set of mapping keys in the format string, implicit_pos_args_cnt
611 is the number of arguments required by the format string and
612 explicit_pos_args is the number of arguments passed with the position.
613 """
614 keyword_arguments = []
615 implicit_pos_args_cnt = 0
616 explicit_pos_args = set()
617 for name in collect_string_fields(format_string):
618 if name and str(name).isdigit():
619 explicit_pos_args.add(str(name))
620 elif name:
621 keyname, fielditerator = split_format_field_names(name)
622 if isinstance(keyname, numbers.Number):
623 explicit_pos_args.add(str(keyname))
624 try:
625 keyword_arguments.append((keyname, list(fielditerator)))
626 except ValueError as e:
627 raise IncompleteFormatString() from e
628 else:
629 implicit_pos_args_cnt += 1
630 return keyword_arguments, implicit_pos_args_cnt, len(explicit_pos_args)
633def is_attr_protected(attrname: str) -> bool:
634 """Return True if attribute name is protected (start with _ and some other
635 details), False otherwise.
636 """
637 return (
638 attrname[0] == "_"
639 and attrname != "_"
640 and not (attrname.startswith("__") and attrname.endswith("__"))
641 )
644def node_frame_class(node: nodes.NodeNG) -> nodes.ClassDef | None:
645 """Return the class that is wrapping the given node.
647 The function returns a class for a method node (or a staticmethod or a
648 classmethod), otherwise it returns `None`.
649 """
650 klass = node.frame(future=True)
651 nodes_to_check = (
652 nodes.NodeNG,
653 astroid.UnboundMethod,
654 astroid.BaseInstance,
655 )
656 while (
657 klass
658 and isinstance(klass, nodes_to_check)
659 and not isinstance(klass, nodes.ClassDef)
660 ):
661 if klass.parent is None:
662 return None
664 klass = klass.parent.frame(future=True)
666 return klass
669def get_outer_class(class_node: astroid.ClassDef) -> astroid.ClassDef | None:
670 """Return the class that is the outer class of given (nested) class_node."""
671 parent_klass = class_node.parent.frame(future=True)
673 return parent_klass if isinstance(parent_klass, astroid.ClassDef) else None
676def is_attr_private(attrname: str) -> Match[str] | None:
677 """Check that attribute name is private (at least two leading underscores,
678 at most one trailing underscore).
679 """
680 regex = re.compile("^_{2,}.*[^_]+_?$")
681 return regex.match(attrname)
684def get_argument_from_call(
685 call_node: nodes.Call, position: int | None = None, keyword: str | None = None
686) -> nodes.Name:
687 """Returns the specified argument from a function call.
689 :param nodes.Call call_node: Node representing a function call to check.
690 :param int position: position of the argument.
691 :param str keyword: the keyword of the argument.
693 :returns: The node representing the argument, None if the argument is not found.
694 :rtype: nodes.Name
695 :raises ValueError: if both position and keyword are None.
696 :raises NoSuchArgumentError: if no argument at the provided position or with
697 the provided keyword.
698 """
699 if position is None and keyword is None:
700 raise ValueError("Must specify at least one of: position or keyword.")
701 if position is not None:
702 try:
703 return call_node.args[position]
704 except IndexError:
705 pass
706 if keyword and call_node.keywords:
707 for arg in call_node.keywords:
708 if arg.arg == keyword:
709 return arg.value
711 raise NoSuchArgumentError
714def inherit_from_std_ex(node: nodes.NodeNG | astroid.Instance) -> bool:
715 """Return whether the given class node is subclass of
716 exceptions.Exception.
717 """
718 ancestors = node.ancestors() if hasattr(node, "ancestors") else []
719 return any(
720 ancestor.name in {"Exception", "BaseException"}
721 and ancestor.root().name == EXCEPTIONS_MODULE
722 for ancestor in itertools.chain([node], ancestors)
723 )
726def error_of_type(handler: nodes.ExceptHandler, error_type) -> bool:
727 """Check if the given exception handler catches
728 the given error_type.
730 The *handler* parameter is a node, representing an ExceptHandler node.
731 The *error_type* can be an exception, such as AttributeError,
732 the name of an exception, or it can be a tuple of errors.
733 The function will return True if the handler catches any of the
734 given errors.
735 """
737 def stringify_error(error):
738 if not isinstance(error, str):
739 return error.__name__
740 return error
742 if not isinstance(error_type, tuple):
743 error_type = (error_type,)
744 expected_errors = {stringify_error(error) for error in error_type}
745 if not handler.type:
746 return False
747 return handler.catch(expected_errors)
750def decorated_with_property(node: nodes.FunctionDef) -> bool:
751 """Detect if the given function node is decorated with a property."""
752 if not node.decorators:
753 return False
754 for decorator in node.decorators.nodes:
755 try:
756 if _is_property_decorator(decorator):
757 return True
758 except astroid.InferenceError:
759 pass
760 return False
763def _is_property_kind(node, *kinds):
764 if not isinstance(node, (astroid.UnboundMethod, nodes.FunctionDef)):
765 return False
766 if node.decorators:
767 for decorator in node.decorators.nodes:
768 if isinstance(decorator, nodes.Attribute) and decorator.attrname in kinds:
769 return True
770 return False
773def is_property_setter(node: nodes.FunctionDef) -> bool:
774 """Check if the given node is a property setter."""
775 return _is_property_kind(node, "setter")
778def is_property_deleter(node: nodes.FunctionDef) -> bool:
779 """Check if the given node is a property deleter."""
780 return _is_property_kind(node, "deleter")
783def is_property_setter_or_deleter(node: nodes.FunctionDef) -> bool:
784 """Check if the given node is either a property setter or a deleter."""
785 return _is_property_kind(node, "setter", "deleter")
788def _is_property_decorator(decorator: nodes.Name) -> bool:
789 for inferred in decorator.infer():
790 if isinstance(inferred, nodes.ClassDef):
791 if inferred.qname() in {"builtins.property", "functools.cached_property"}:
792 return True
793 for ancestor in inferred.ancestors():
794 if ancestor.name == "property" and ancestor.root().name == "builtins":
795 return True
796 elif isinstance(inferred, nodes.FunctionDef):
797 # If decorator is function, check if it has exactly one return
798 # and the return is itself a function decorated with property
799 returns: list[nodes.Return] = list(
800 inferred._get_return_nodes_skip_functions()
801 )
802 if len(returns) == 1 and isinstance(
803 returns[0].value, (nodes.Name, nodes.Attribute)
804 ):
805 inferred = safe_infer(returns[0].value)
806 if (
807 inferred
808 and isinstance(inferred, astroid.objects.Property)
809 and isinstance(inferred.function, nodes.FunctionDef)
810 ):
811 return decorated_with_property(inferred.function)
812 return False
815def decorated_with(
816 func: (
817 nodes.ClassDef | nodes.FunctionDef | astroid.BoundMethod | astroid.UnboundMethod
818 ),
819 qnames: Iterable[str],
820) -> bool:
821 """Determine if the `func` node has a decorator with the qualified name `qname`."""
822 decorators = func.decorators.nodes if func.decorators else []
823 for decorator_node in decorators:
824 if isinstance(decorator_node, nodes.Call):
825 # We only want to infer the function name
826 decorator_node = decorator_node.func
827 try:
828 if any(
829 i.name in qnames or i.qname() in qnames
830 for i in decorator_node.infer()
831 if i is not None and i != astroid.Uninferable
832 ):
833 return True
834 except astroid.InferenceError:
835 continue
836 return False
839def uninferable_final_decorators(
840 node: nodes.Decorators,
841) -> list[nodes.Attribute | nodes.Name | None]:
842 """Return a list of uninferable `typing.final` decorators in `node`.
844 This function is used to determine if the `typing.final` decorator is used
845 with an unsupported Python version; the decorator cannot be inferred when
846 using a Python version lower than 3.8.
847 """
848 decorators = []
849 for decorator in getattr(node, "nodes", []):
850 import_nodes: tuple[nodes.Import | nodes.ImportFrom] | None = None
852 # Get the `Import` node. The decorator is of the form: @module.name
853 if isinstance(decorator, nodes.Attribute):
854 inferred = safe_infer(decorator.expr)
855 if isinstance(inferred, nodes.Module) and inferred.qname() == "typing":
856 _, import_nodes = decorator.expr.lookup(decorator.expr.name)
858 # Get the `ImportFrom` node. The decorator is of the form: @name
859 elif isinstance(decorator, nodes.Name):
860 _, import_nodes = decorator.lookup(decorator.name)
862 # The `final` decorator is expected to be found in the
863 # import_nodes. Continue if we don't find any `Import` or `ImportFrom`
864 # nodes for this decorator.
865 if not import_nodes:
866 continue
867 import_node = import_nodes[0]
869 if not isinstance(import_node, (astroid.Import, astroid.ImportFrom)):
870 continue
872 import_names = dict(import_node.names)
874 # Check if the import is of the form: `from typing import final`
875 is_from_import = ("final" in import_names) and import_node.modname == "typing"
877 # Check if the import is of the form: `import typing`
878 is_import = ("typing" in import_names) and getattr(
879 decorator, "attrname", None
880 ) == "final"
882 if (is_from_import or is_import) and safe_infer(decorator) in [
883 astroid.Uninferable,
884 None,
885 ]:
886 decorators.append(decorator)
887 return decorators
890@lru_cache(maxsize=1024)
891def unimplemented_abstract_methods(
892 node: nodes.ClassDef, is_abstract_cb: nodes.FunctionDef = None
893) -> dict[str, nodes.NodeNG]:
894 """Get the unimplemented abstract methods for the given *node*.
896 A method can be considered abstract if the callback *is_abstract_cb*
897 returns a ``True`` value. The check defaults to verifying that
898 a method is decorated with abstract methods.
899 The function will work only for new-style classes. For old-style
900 classes, it will simply return an empty dictionary.
901 For the rest of them, it will return a dictionary of abstract method
902 names and their inferred objects.
903 """
904 if is_abstract_cb is None:
905 is_abstract_cb = partial(decorated_with, qnames=ABC_METHODS)
906 visited: dict[str, nodes.NodeNG] = {}
907 try:
908 mro = reversed(node.mro())
909 except NotImplementedError:
910 # Old style class, it will not have a mro.
911 return {}
912 except astroid.ResolveError:
913 # Probably inconsistent hierarchy, don't try to figure this out here.
914 return {}
915 for ancestor in mro:
916 for obj in ancestor.values():
917 inferred = obj
918 if isinstance(obj, nodes.AssignName):
919 inferred = safe_infer(obj)
920 if not inferred:
921 # Might be an abstract function,
922 # but since we don't have enough information
923 # in order to take this decision, we're taking
924 # the *safe* decision instead.
925 if obj.name in visited:
926 del visited[obj.name]
927 continue
928 if not isinstance(inferred, nodes.FunctionDef):
929 if obj.name in visited:
930 del visited[obj.name]
931 if isinstance(inferred, nodes.FunctionDef):
932 # It's critical to use the original name,
933 # since after inferring, an object can be something
934 # else than expected, as in the case of the
935 # following assignment.
936 #
937 # class A:
938 # def keys(self): pass
939 # __iter__ = keys
940 abstract = is_abstract_cb(inferred)
941 if abstract:
942 visited[obj.name] = inferred
943 elif not abstract and obj.name in visited:
944 del visited[obj.name]
945 return visited
948def find_try_except_wrapper_node(
949 node: nodes.NodeNG,
950) -> nodes.ExceptHandler | nodes.TryExcept | None:
951 """Return the ExceptHandler or the TryExcept node in which the node is."""
952 current = node
953 ignores = (nodes.ExceptHandler, nodes.TryExcept)
954 while current and not isinstance(current.parent, ignores):
955 current = current.parent
957 if current and isinstance(current.parent, ignores):
958 return current.parent
959 return None
962def find_except_wrapper_node_in_scope(
963 node: nodes.NodeNG,
964) -> nodes.ExceptHandler | nodes.TryExcept | None:
965 """Return the ExceptHandler in which the node is, without going out of scope."""
966 for current in node.node_ancestors():
967 if isinstance(current, astroid.scoped_nodes.LocalsDictNodeNG):
968 # If we're inside a function/class definition, we don't want to keep checking
969 # higher ancestors for `except` clauses, because if these exist, it means our
970 # function/class was defined in an `except` clause, rather than the current code
971 # actually running in an `except` clause.
972 return None
973 if isinstance(current, nodes.ExceptHandler):
974 return current
975 return None
978def is_from_fallback_block(node: nodes.NodeNG) -> bool:
979 """Check if the given node is from a fallback import block."""
980 context = find_try_except_wrapper_node(node)
981 if not context:
982 return False
984 if isinstance(context, nodes.ExceptHandler):
985 other_body = context.parent.body
986 handlers = context.parent.handlers
987 else:
988 other_body = itertools.chain.from_iterable(
989 handler.body for handler in context.handlers
990 )
991 handlers = context.handlers
993 has_fallback_imports = any(
994 isinstance(import_node, (nodes.ImportFrom, nodes.Import))
995 for import_node in other_body
996 )
997 ignores_import_error = _except_handlers_ignores_exceptions(
998 handlers, (ImportError, ModuleNotFoundError)
999 )
1000 return ignores_import_error or has_fallback_imports
1003def _except_handlers_ignores_exceptions(
1004 handlers: nodes.ExceptHandler,
1005 exceptions: tuple[type[ImportError], type[ModuleNotFoundError]],
1006) -> bool:
1007 func = partial(error_of_type, error_type=exceptions)
1008 return any(func(handler) for handler in handlers)
1011def get_exception_handlers(
1012 node: nodes.NodeNG, exception=Exception
1013) -> list[nodes.ExceptHandler] | None:
1014 """Return the collections of handlers handling the exception in arguments.
1016 Args:
1017 node (nodes.NodeNG): A node that is potentially wrapped in a try except.
1018 exception (builtin.Exception or str): exception or name of the exception.
1020 Returns:
1021 list: the collection of handlers that are handling the exception or None.
1023 """
1024 context = find_try_except_wrapper_node(node)
1025 if isinstance(context, nodes.TryExcept):
1026 return [
1027 handler for handler in context.handlers if error_of_type(handler, exception)
1028 ]
1029 return []
1032def is_node_inside_try_except(node: nodes.Raise) -> bool:
1033 """Check if the node is directly under a Try/Except statement
1034 (but not under an ExceptHandler!).
1036 Args:
1037 node (nodes.Raise): the node raising the exception.
1039 Returns:
1040 bool: True if the node is inside a try/except statement, False otherwise.
1041 """
1042 context = find_try_except_wrapper_node(node)
1043 return isinstance(context, nodes.TryExcept)
1046def node_ignores_exception(node: nodes.NodeNG, exception=Exception) -> bool:
1047 """Check if the node is in a TryExcept which handles the given exception.
1049 If the exception is not given, the function is going to look for bare
1050 excepts.
1051 """
1052 managing_handlers = get_exception_handlers(node, exception)
1053 if not managing_handlers:
1054 return False
1055 return any(managing_handlers)
1058def class_is_abstract(node: nodes.ClassDef) -> bool:
1059 """Return true if the given class node should be considered as an abstract
1060 class.
1061 """
1062 # Only check for explicit metaclass=ABCMeta on this specific class
1063 meta = node.declared_metaclass()
1064 if meta is not None:
1065 if meta.name == "ABCMeta" and meta.root().name in ABC_MODULES:
1066 return True
1068 for ancestor in node.ancestors():
1069 if ancestor.name == "ABC" and ancestor.root().name in ABC_MODULES:
1070 # abc.ABC inheritance
1071 return True
1073 for method in node.methods():
1074 if method.parent.frame(future=True) is node:
1075 if method.is_abstract(pass_is_abstract=False):
1076 return True
1077 return False
1080def _supports_protocol_method(value: nodes.NodeNG, attr: str) -> bool:
1081 try:
1082 attributes = value.getattr(attr)
1083 except astroid.NotFoundError:
1084 return False
1086 first = attributes[0]
1088 # Return False if a constant is assigned
1089 if isinstance(first, nodes.AssignName):
1090 this_assign_parent = get_node_first_ancestor_of_type(
1091 first, (nodes.Assign, nodes.NamedExpr)
1092 )
1093 if this_assign_parent is None: # pragma: no cover
1094 # Cannot imagine this being None, but return True to avoid false positives
1095 return True
1096 if isinstance(this_assign_parent.value, nodes.BaseContainer):
1097 if all(isinstance(n, nodes.Const) for n in this_assign_parent.value.elts):
1098 return False
1099 if isinstance(this_assign_parent.value, nodes.Const):
1100 return False
1101 return True
1104def is_comprehension(node: nodes.NodeNG) -> bool:
1105 comprehensions = (
1106 nodes.ListComp,
1107 nodes.SetComp,
1108 nodes.DictComp,
1109 nodes.GeneratorExp,
1110 )
1111 return isinstance(node, comprehensions)
1114def _supports_mapping_protocol(value: nodes.NodeNG) -> bool:
1115 return _supports_protocol_method(
1116 value, GETITEM_METHOD
1117 ) and _supports_protocol_method(value, KEYS_METHOD)
1120def _supports_membership_test_protocol(value: nodes.NodeNG) -> bool:
1121 return _supports_protocol_method(value, CONTAINS_METHOD)
1124def _supports_iteration_protocol(value: nodes.NodeNG) -> bool:
1125 return _supports_protocol_method(value, ITER_METHOD) or _supports_protocol_method(
1126 value, GETITEM_METHOD
1127 )
1130def _supports_async_iteration_protocol(value: nodes.NodeNG) -> bool:
1131 return _supports_protocol_method(value, AITER_METHOD)
1134def _supports_getitem_protocol(value: nodes.NodeNG) -> bool:
1135 return _supports_protocol_method(value, GETITEM_METHOD)
1138def _supports_setitem_protocol(value: nodes.NodeNG) -> bool:
1139 return _supports_protocol_method(value, SETITEM_METHOD)
1142def _supports_delitem_protocol(value: nodes.NodeNG) -> bool:
1143 return _supports_protocol_method(value, DELITEM_METHOD)
1146def _is_abstract_class_name(name: str) -> bool:
1147 lname = name.lower()
1148 is_mixin = lname.endswith("mixin")
1149 is_abstract = lname.startswith("abstract")
1150 is_base = lname.startswith("base") or lname.endswith("base")
1151 return is_mixin or is_abstract or is_base
1154def is_inside_abstract_class(node: nodes.NodeNG) -> bool:
1155 while node is not None:
1156 if isinstance(node, nodes.ClassDef):
1157 if class_is_abstract(node):
1158 return True
1159 name = getattr(node, "name", None)
1160 if name is not None and _is_abstract_class_name(name):
1161 return True
1162 node = node.parent
1163 return False
1166def _supports_protocol(
1167 value: nodes.NodeNG, protocol_callback: nodes.FunctionDef
1168) -> bool:
1169 if isinstance(value, nodes.ClassDef):
1170 if not has_known_bases(value):
1171 return True
1172 # classobj can only be iterable if it has an iterable metaclass
1173 meta = value.metaclass()
1174 if meta is not None:
1175 if protocol_callback(meta):
1176 return True
1177 if isinstance(value, astroid.BaseInstance):
1178 if not has_known_bases(value):
1179 return True
1180 if value.has_dynamic_getattr():
1181 return True
1182 if protocol_callback(value):
1183 return True
1185 if isinstance(value, nodes.ComprehensionScope):
1186 return True
1188 if (
1189 isinstance(value, astroid.bases.Proxy)
1190 and isinstance(value._proxied, astroid.BaseInstance)
1191 and has_known_bases(value._proxied)
1192 ):
1193 value = value._proxied
1194 return protocol_callback(value)
1196 return False
1199def is_iterable(value: nodes.NodeNG, check_async: bool = False) -> bool:
1200 if check_async:
1201 protocol_check = _supports_async_iteration_protocol
1202 else:
1203 protocol_check = _supports_iteration_protocol
1204 return _supports_protocol(value, protocol_check)
1207def is_mapping(value: nodes.NodeNG) -> bool:
1208 return _supports_protocol(value, _supports_mapping_protocol)
1211def supports_membership_test(value: nodes.NodeNG) -> bool:
1212 supported = _supports_protocol(value, _supports_membership_test_protocol)
1213 return supported or is_iterable(value)
1216def supports_getitem(value: nodes.NodeNG, node: nodes.NodeNG) -> bool:
1217 if isinstance(value, nodes.ClassDef):
1218 if _supports_protocol_method(value, CLASS_GETITEM_METHOD):
1219 return True
1220 if subscriptable_with_postponed_evaluation_enabled(node):
1221 return True
1222 return _supports_protocol(value, _supports_getitem_protocol)
1225def supports_setitem(value: nodes.NodeNG, _: nodes.NodeNG) -> bool:
1226 return _supports_protocol(value, _supports_setitem_protocol)
1229def supports_delitem(value: nodes.NodeNG, _: nodes.NodeNG) -> bool:
1230 return _supports_protocol(value, _supports_delitem_protocol)
1233def _get_python_type_of_node(node: nodes.NodeNG) -> str | None:
1234 pytype = getattr(node, "pytype", None)
1235 if callable(pytype):
1236 return pytype()
1237 return None
1240@lru_cache(maxsize=1024)
1241def safe_infer(
1242 node: nodes.NodeNG, context: InferenceContext | None = None
1243) -> nodes.NodeNG | type[astroid.Uninferable] | None:
1244 """Return the inferred value for the given node.
1246 Return None if inference failed or if there is some ambiguity (more than
1247 one node has been inferred of different types).
1248 """
1249 inferred_types: set[str | None] = set()
1250 try:
1251 infer_gen = node.infer(context=context)
1252 value = next(infer_gen)
1253 except astroid.InferenceError:
1254 return None
1256 if value is not astroid.Uninferable:
1257 inferred_types.add(_get_python_type_of_node(value))
1259 try:
1260 for inferred in infer_gen:
1261 inferred_type = _get_python_type_of_node(inferred)
1262 if inferred_type not in inferred_types:
1263 return None # If there is ambiguity on the inferred node.
1264 if (
1265 isinstance(inferred, nodes.FunctionDef)
1266 and inferred.args.args is not None
1267 and isinstance(value, nodes.FunctionDef)
1268 and value.args.args is not None
1269 and len(inferred.args.args) != len(value.args.args)
1270 ):
1271 return None # Different number of arguments indicates ambiguity
1272 except astroid.InferenceError:
1273 return None # There is some kind of ambiguity
1274 except StopIteration:
1275 return value
1276 return value if len(inferred_types) <= 1 else None
1279@lru_cache(maxsize=512)
1280def infer_all(
1281 node: nodes.NodeNG, context: InferenceContext = None
1282) -> list[nodes.NodeNG]:
1283 try:
1284 return list(node.infer(context=context))
1285 except astroid.InferenceError:
1286 return []
1289def has_known_bases(klass: nodes.ClassDef, context=None) -> bool:
1290 """Return true if all base classes of a class could be inferred."""
1291 try:
1292 return klass._all_bases_known
1293 except AttributeError:
1294 pass
1295 for base in klass.bases:
1296 result = safe_infer(base, context=context)
1297 if (
1298 not isinstance(result, nodes.ClassDef)
1299 or result is klass
1300 or not has_known_bases(result, context=context)
1301 ):
1302 klass._all_bases_known = False
1303 return False
1304 klass._all_bases_known = True
1305 return True
1308def is_none(node: nodes.NodeNG) -> bool:
1309 return (
1310 node is None
1311 or (isinstance(node, nodes.Const) and node.value is None)
1312 or (isinstance(node, nodes.Name) and node.name == "None")
1313 )
1316def node_type(node: nodes.NodeNG) -> nodes.NodeNG | None:
1317 """Return the inferred type for `node`.
1319 If there is more than one possible type, or if inferred type is Uninferable or None,
1320 return None
1321 """
1322 # check there is only one possible type for the assign node. Else we
1323 # don't handle it for now
1324 types: set[nodes.NodeNG] = set()
1325 try:
1326 for var_type in node.infer():
1327 if var_type == astroid.Uninferable or is_none(var_type):
1328 continue
1329 types.add(var_type)
1330 if len(types) > 1:
1331 return None
1332 except astroid.InferenceError:
1333 return None
1334 return types.pop() if types else None
1337def is_registered_in_singledispatch_function(node: nodes.FunctionDef) -> bool:
1338 """Check if the given function node is a singledispatch function."""
1340 singledispatch_qnames = (
1341 "functools.singledispatch",
1342 "singledispatch.singledispatch",
1343 )
1345 if not isinstance(node, nodes.FunctionDef):
1346 return False
1348 decorators = node.decorators.nodes if node.decorators else []
1349 for decorator in decorators:
1350 # func.register are function calls
1351 if not isinstance(decorator, nodes.Call):
1352 continue
1354 func = decorator.func
1355 if not isinstance(func, nodes.Attribute) or func.attrname != "register":
1356 continue
1358 try:
1359 func_def = next(func.expr.infer())
1360 except astroid.InferenceError:
1361 continue
1363 if isinstance(func_def, nodes.FunctionDef):
1364 return decorated_with(func_def, singledispatch_qnames)
1366 return False
1369def get_node_last_lineno(node: nodes.NodeNG) -> int:
1370 """Get the last lineno of the given node.
1372 For a simple statement this will just be node.lineno,
1373 but for a node that has child statements (e.g. a method) this will be the lineno of the last
1374 child statement recursively.
1375 """
1376 # 'finalbody' is always the last clause in a try statement, if present
1377 if getattr(node, "finalbody", False):
1378 return get_node_last_lineno(node.finalbody[-1])
1379 # For if, while, and for statements 'orelse' is always the last clause.
1380 # For try statements 'orelse' is the last in the absence of a 'finalbody'
1381 if getattr(node, "orelse", False):
1382 return get_node_last_lineno(node.orelse[-1])
1383 # try statements have the 'handlers' last if there is no 'orelse' or 'finalbody'
1384 if getattr(node, "handlers", False):
1385 return get_node_last_lineno(node.handlers[-1])
1386 # All compound statements have a 'body'
1387 if getattr(node, "body", False):
1388 return get_node_last_lineno(node.body[-1])
1389 # Not a compound statement
1390 return node.lineno
1393def is_postponed_evaluation_enabled(node: nodes.NodeNG) -> bool:
1394 """Check if the postponed evaluation of annotations is enabled."""
1395 module = node.root()
1396 return "annotations" in module.future_imports
1399def is_class_subscriptable_pep585_with_postponed_evaluation_enabled(
1400 value: nodes.ClassDef, node: nodes.NodeNG
1401) -> bool:
1402 """Check if class is subscriptable with PEP 585 and
1403 postponed evaluation enabled.
1404 """
1405 warnings.warn(
1406 "'is_class_subscriptable_pep585_with_postponed_evaluation_enabled' has been "
1407 "deprecated and will be removed in pylint 3.0. "
1408 "Use 'subscriptable_with_postponed_evaluation_enabled' instead.",
1409 DeprecationWarning,
1410 )
1411 return (
1412 is_postponed_evaluation_enabled(node)
1413 and value.qname() in SUBSCRIPTABLE_CLASSES_PEP585
1414 and is_node_in_type_annotation_context(node)
1415 )
1418def subscriptable_with_postponed_evaluation_enabled(node: nodes.NodeNG) -> bool:
1419 """Check if class can be subscriptable in type annotation context."""
1420 return is_postponed_evaluation_enabled(node) and is_node_in_type_annotation_context(
1421 node
1422 )
1425def is_node_in_type_annotation_context(node: nodes.NodeNG) -> bool:
1426 """Check if node is in type annotation context.
1428 Check for 'AnnAssign', function 'Arguments',
1429 or part of function return type annotation.
1430 """
1431 # pylint: disable=too-many-boolean-expressions
1432 current_node, parent_node = node, node.parent
1433 while True:
1434 if (
1435 isinstance(parent_node, nodes.AnnAssign)
1436 and parent_node.annotation == current_node
1437 or isinstance(parent_node, nodes.Arguments)
1438 and current_node
1439 in (
1440 *parent_node.annotations,
1441 *parent_node.posonlyargs_annotations,
1442 *parent_node.kwonlyargs_annotations,
1443 parent_node.varargannotation,
1444 parent_node.kwargannotation,
1445 )
1446 or isinstance(parent_node, nodes.FunctionDef)
1447 and parent_node.returns == current_node
1448 ):
1449 return True
1450 current_node, parent_node = parent_node, parent_node.parent
1451 if isinstance(parent_node, nodes.Module):
1452 return False
1455def is_subclass_of(child: nodes.ClassDef, parent: nodes.ClassDef) -> bool:
1456 """Check if first node is a subclass of second node.
1458 :param child: Node to check for subclass.
1459 :param parent: Node to check for superclass.
1460 :returns: True if child is derived from parent. False otherwise.
1461 """
1462 if not all(isinstance(node, nodes.ClassDef) for node in (child, parent)):
1463 return False
1465 for ancestor in child.ancestors():
1466 try:
1467 if astroid.helpers.is_subtype(ancestor, parent):
1468 return True
1469 except astroid.exceptions._NonDeducibleTypeHierarchy:
1470 continue
1471 return False
1474@lru_cache(maxsize=1024)
1475def is_overload_stub(node: nodes.NodeNG) -> bool:
1476 """Check if a node is a function stub decorated with typing.overload.
1478 :param node: Node to check.
1479 :returns: True if node is an overload function stub. False otherwise.
1480 """
1481 decorators = getattr(node, "decorators", None)
1482 return bool(decorators and decorated_with(node, ["typing.overload", "overload"]))
1485def is_protocol_class(cls: nodes.NodeNG) -> bool:
1486 """Check if the given node represents a protocol class.
1488 :param cls: The node to check
1489 :returns: True if the node is a typing protocol class, false otherwise.
1490 """
1491 if not isinstance(cls, nodes.ClassDef):
1492 return False
1494 # Use .ancestors() since not all protocol classes can have
1495 # their mro deduced.
1496 return any(parent.qname() in TYPING_PROTOCOLS for parent in cls.ancestors())
1499def is_call_of_name(node: nodes.NodeNG, name: str) -> bool:
1500 """Checks if node is a function call with the given name."""
1501 return (
1502 isinstance(node, nodes.Call)
1503 and isinstance(node.func, nodes.Name)
1504 and node.func.name == name
1505 )
1508def is_test_condition(
1509 node: nodes.NodeNG,
1510 parent: nodes.NodeNG | None = None,
1511) -> bool:
1512 """Returns true if the given node is being tested for truthiness."""
1513 parent = parent or node.parent
1514 if isinstance(parent, (nodes.While, nodes.If, nodes.IfExp, nodes.Assert)):
1515 return node is parent.test or parent.test.parent_of(node)
1516 if isinstance(parent, nodes.Comprehension):
1517 return node in parent.ifs
1518 return is_call_of_name(parent, "bool") and parent.parent_of(node)
1521def is_classdef_type(node: nodes.ClassDef) -> bool:
1522 """Test if ClassDef node is Type."""
1523 if node.name == "type":
1524 return True
1525 return any(isinstance(b, nodes.Name) and b.name == "type" for b in node.bases)
1528def is_attribute_typed_annotation(
1529 node: nodes.ClassDef | astroid.Instance, attr_name: str
1530) -> bool:
1531 """Test if attribute is typed annotation in current node
1532 or any base nodes.
1533 """
1534 attribute = node.locals.get(attr_name, [None])[0]
1535 if (
1536 attribute
1537 and isinstance(attribute, nodes.AssignName)
1538 and isinstance(attribute.parent, nodes.AnnAssign)
1539 ):
1540 return True
1541 for base in node.bases:
1542 inferred = safe_infer(base)
1543 if (
1544 inferred
1545 and isinstance(inferred, nodes.ClassDef)
1546 and is_attribute_typed_annotation(inferred, attr_name)
1547 ):
1548 return True
1549 return False
1552def is_assign_name_annotated_with(node: nodes.AssignName, typing_name: str) -> bool:
1553 """Test if AssignName node has `typing_name` annotation.
1555 Especially useful to check for `typing._SpecialForm` instances
1556 like: `Union`, `Optional`, `Literal`, `ClassVar`, `Final`.
1557 """
1558 if not isinstance(node.parent, nodes.AnnAssign):
1559 return False
1560 annotation = node.parent.annotation
1561 if isinstance(annotation, nodes.Subscript):
1562 annotation = annotation.value
1563 if (
1564 isinstance(annotation, nodes.Name)
1565 and annotation.name == typing_name
1566 or isinstance(annotation, nodes.Attribute)
1567 and annotation.attrname == typing_name
1568 ):
1569 return True
1570 return False
1573def get_iterating_dictionary_name(node: nodes.For | nodes.Comprehension) -> str | None:
1574 """Get the name of the dictionary which keys are being iterated over on
1575 a ``nodes.For`` or ``nodes.Comprehension`` node.
1577 If the iterating object is not either the keys method of a dictionary
1578 or a dictionary itself, this returns None.
1579 """
1580 # Is it a proper keys call?
1581 if (
1582 isinstance(node.iter, nodes.Call)
1583 and isinstance(node.iter.func, nodes.Attribute)
1584 and node.iter.func.attrname == "keys"
1585 ):
1586 inferred = safe_infer(node.iter.func)
1587 if not isinstance(inferred, astroid.BoundMethod):
1588 return None
1589 return node.iter.as_string().rpartition(".keys")[0]
1591 # Is it a dictionary?
1592 if isinstance(node.iter, (nodes.Name, nodes.Attribute)):
1593 inferred = safe_infer(node.iter)
1594 if not isinstance(inferred, nodes.Dict):
1595 return None
1596 return node.iter.as_string()
1598 return None
1601def get_subscript_const_value(node: nodes.Subscript) -> nodes.Const:
1602 """Returns the value 'subscript.slice' of a Subscript node.
1604 :param node: Subscript Node to extract value from
1605 :returns: Const Node containing subscript value
1606 :raises InferredTypeError: if the subscript node cannot be inferred as a Const
1607 """
1608 inferred = safe_infer(node.slice)
1609 if not isinstance(inferred, nodes.Const):
1610 raise InferredTypeError("Subscript.slice cannot be inferred as a nodes.Const")
1612 return inferred
1615def get_import_name(importnode: nodes.Import | nodes.ImportFrom, modname: str) -> str:
1616 """Get a prepared module name from the given import node.
1618 In the case of relative imports, this will return the
1619 absolute qualified module name, which might be useful
1620 for debugging. Otherwise, the initial module name
1621 is returned unchanged.
1623 :param importnode: node representing import statement.
1624 :param modname: module name from import statement.
1625 :returns: absolute qualified module name of the module
1626 used in import.
1627 """
1628 if isinstance(importnode, nodes.ImportFrom) and importnode.level:
1629 root = importnode.root()
1630 if isinstance(root, nodes.Module):
1631 try:
1632 return root.relative_to_absolute_name(modname, level=importnode.level)
1633 except TooManyLevelsError:
1634 return modname
1635 return modname
1638def is_sys_guard(node: nodes.If) -> bool:
1639 """Return True if IF stmt is a sys.version_info guard.
1641 >>> import sys
1642 >>> if sys.version_info > (3, 8):
1643 >>> from typing import Literal
1644 >>> else:
1645 >>> from typing_extensions import Literal
1646 """
1647 if isinstance(node.test, nodes.Compare):
1648 value = node.test.left
1649 if isinstance(value, nodes.Subscript):
1650 value = value.value
1651 if (
1652 isinstance(value, nodes.Attribute)
1653 and value.as_string() == "sys.version_info"
1654 ):
1655 return True
1657 return False
1660def is_typing_guard(node: nodes.If) -> bool:
1661 """Return True if IF stmt is a typing guard.
1663 >>> from typing import TYPE_CHECKING
1664 >>> if TYPE_CHECKING:
1665 >>> from xyz import a
1666 """
1667 return isinstance(
1668 node.test, (nodes.Name, nodes.Attribute)
1669 ) and node.test.as_string().endswith("TYPE_CHECKING")
1672def is_node_in_typing_guarded_import_block(node: nodes.NodeNG) -> bool:
1673 """Return True if node is part for guarded `typing.TYPE_CHECKING` if block."""
1674 return isinstance(node.parent, nodes.If) and is_typing_guard(node.parent)
1677def is_node_in_guarded_import_block(node: nodes.NodeNG) -> bool:
1678 """Return True if node is part for guarded if block.
1680 I.e. `sys.version_info` or `typing.TYPE_CHECKING`
1681 """
1682 return isinstance(node.parent, nodes.If) and (
1683 is_sys_guard(node.parent) or is_typing_guard(node.parent)
1684 )
1687def is_reassigned_after_current(node: nodes.NodeNG, varname: str) -> bool:
1688 """Check if the given variable name is reassigned in the same scope after the current node."""
1689 return any(
1690 a.name == varname and a.lineno > node.lineno
1691 for a in node.scope().nodes_of_class(
1692 (nodes.AssignName, nodes.ClassDef, nodes.FunctionDef)
1693 )
1694 )
1697def is_deleted_after_current(node: nodes.NodeNG, varname: str) -> bool:
1698 """Check if the given variable name is deleted in the same scope after the current node."""
1699 return any(
1700 getattr(target, "name", None) == varname and target.lineno > node.lineno
1701 for del_node in node.scope().nodes_of_class(nodes.Delete)
1702 for target in del_node.targets
1703 )
1706def is_function_body_ellipsis(node: nodes.FunctionDef) -> bool:
1707 """Checks whether a function body only consists of a single Ellipsis."""
1708 return (
1709 len(node.body) == 1
1710 and isinstance(node.body[0], nodes.Expr)
1711 and isinstance(node.body[0].value, nodes.Const)
1712 and node.body[0].value.value == Ellipsis
1713 )
1716def is_base_container(node: nodes.NodeNG | None) -> bool:
1717 return isinstance(node, nodes.BaseContainer) and not node.elts
1720def is_empty_dict_literal(node: nodes.NodeNG | None) -> bool:
1721 return isinstance(node, nodes.Dict) and not node.items
1724def is_empty_str_literal(node: nodes.NodeNG | None) -> bool:
1725 return (
1726 isinstance(node, nodes.Const) and isinstance(node.value, str) and not node.value
1727 )
1730def returns_bool(node: nodes.NodeNG) -> bool:
1731 """Returns true if a node is a return that returns a constant boolean."""
1732 return (
1733 isinstance(node, nodes.Return)
1734 and isinstance(node.value, nodes.Const)
1735 and node.value.value in {True, False}
1736 )
1739def get_node_first_ancestor_of_type(
1740 node: nodes.NodeNG, ancestor_type: type[_NodeT] | tuple[type[_NodeT], ...]
1741) -> _NodeT | None:
1742 """Return the first parent node that is any of the provided types (or None)."""
1743 for ancestor in node.node_ancestors():
1744 if isinstance(ancestor, ancestor_type):
1745 return ancestor
1746 return None
1749def get_node_first_ancestor_of_type_and_its_child(
1750 node: nodes.NodeNG, ancestor_type: type[_NodeT] | tuple[type[_NodeT], ...]
1751) -> tuple[None, None] | tuple[_NodeT, nodes.NodeNG]:
1752 """Modified version of get_node_first_ancestor_of_type to also return the
1753 descendant visited directly before reaching the sought ancestor.
1755 Useful for extracting whether a statement is guarded by a try, except, or finally
1756 when searching for a TryFinally ancestor.
1757 """
1758 child = node
1759 for ancestor in node.node_ancestors():
1760 if isinstance(ancestor, ancestor_type):
1761 return (ancestor, child)
1762 child = ancestor
1763 return None, None
1766def in_type_checking_block(node: nodes.NodeNG) -> bool:
1767 """Check if a node is guarded by a TYPE_CHECKS guard."""
1768 return any(
1769 isinstance(ancestor, nodes.If)
1770 and ancestor.test.as_string() in TYPING_TYPE_CHECKS_GUARDS
1771 for ancestor in node.node_ancestors()
1772 )
1775@lru_cache()
1776def in_for_else_branch(parent: nodes.NodeNG, stmt: nodes.Statement) -> bool:
1777 """Returns True if stmt is inside the else branch for a parent For stmt."""
1778 return isinstance(parent, nodes.For) and any(
1779 else_stmt.parent_of(stmt) or else_stmt == stmt for else_stmt in parent.orelse
1780 )