summaryrefslogtreecommitdiff
path: root/venv/Lib/site-packages/pylint/checkers/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/Lib/site-packages/pylint/checkers/utils.py')
-rw-r--r--venv/Lib/site-packages/pylint/checkers/utils.py1253
1 files changed, 0 insertions, 1253 deletions
diff --git a/venv/Lib/site-packages/pylint/checkers/utils.py b/venv/Lib/site-packages/pylint/checkers/utils.py
deleted file mode 100644
index 2a6820a..0000000
--- a/venv/Lib/site-packages/pylint/checkers/utils.py
+++ /dev/null
@@ -1,1253 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006-2007, 2009-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com>
-# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
-# Copyright (c) 2012-2014 Google, Inc.
-# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
-# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Ricardo Gemignani <ricardo.gemignani@gmail.com>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
-# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
-# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016, 2018 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2016-2017 Moises Lopez <moylop260@vauxoo.com>
-# Copyright (c) 2016 Brian C. Lane <bcl@redhat.com>
-# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017 ttenhoeve-aa <ttenhoeve@appannie.com>
-# Copyright (c) 2018 Bryce Guinta <bryce.guinta@protonmail.com>
-# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
-# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
-# Copyright (c) 2018 Brian Shaginaw <brian.shaginaw@warbyparker.com>
-# Copyright (c) 2018 Caio Carrara <ccarrara@redhat.com>
-
-# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
-
-"""some functions that may be useful for various checkers
-"""
-import builtins
-import itertools
-import numbers
-import re
-import string
-from functools import lru_cache, partial
-from typing import Callable, Dict, Iterable, List, Match, Optional, Set, Tuple, Union
-
-import astroid
-from astroid import bases as _bases
-from astroid import helpers, scoped_nodes
-from astroid.exceptions import _NonDeducibleTypeHierarchy
-
-import _string # pylint: disable=wrong-import-position, wrong-import-order
-
-BUILTINS_NAME = builtins.__name__
-COMP_NODE_TYPES = (
- astroid.ListComp,
- astroid.SetComp,
- astroid.DictComp,
- astroid.GeneratorExp,
-)
-EXCEPTIONS_MODULE = "builtins"
-ABC_METHODS = {
- "abc.abstractproperty",
- "abc.abstractmethod",
- "abc.abstractclassmethod",
- "abc.abstractstaticmethod",
-}
-TYPING_PROTOCOLS = frozenset({"typing.Protocol", "typing_extensions.Protocol"})
-ITER_METHOD = "__iter__"
-AITER_METHOD = "__aiter__"
-NEXT_METHOD = "__next__"
-GETITEM_METHOD = "__getitem__"
-CLASS_GETITEM_METHOD = "__class_getitem__"
-SETITEM_METHOD = "__setitem__"
-DELITEM_METHOD = "__delitem__"
-CONTAINS_METHOD = "__contains__"
-KEYS_METHOD = "keys"
-
-# Dictionary which maps the number of expected parameters a
-# special method can have to a set of special methods.
-# The following keys are used to denote the parameters restrictions:
-#
-# * None: variable number of parameters
-# * number: exactly that number of parameters
-# * tuple: this are the odd ones. Basically it means that the function
-# can work with any number of arguments from that tuple,
-# although it's best to implement it in order to accept
-# all of them.
-_SPECIAL_METHODS_PARAMS = {
- None: ("__new__", "__init__", "__call__"),
- 0: (
- "__del__",
- "__repr__",
- "__str__",
- "__bytes__",
- "__hash__",
- "__bool__",
- "__dir__",
- "__len__",
- "__length_hint__",
- "__iter__",
- "__reversed__",
- "__neg__",
- "__pos__",
- "__abs__",
- "__invert__",
- "__complex__",
- "__int__",
- "__float__",
- "__neg__",
- "__pos__",
- "__abs__",
- "__complex__",
- "__int__",
- "__float__",
- "__index__",
- "__enter__",
- "__aenter__",
- "__getnewargs_ex__",
- "__getnewargs__",
- "__getstate__",
- "__reduce__",
- "__copy__",
- "__unicode__",
- "__nonzero__",
- "__await__",
- "__aiter__",
- "__anext__",
- "__fspath__",
- ),
- 1: (
- "__format__",
- "__lt__",
- "__le__",
- "__eq__",
- "__ne__",
- "__gt__",
- "__ge__",
- "__getattr__",
- "__getattribute__",
- "__delattr__",
- "__delete__",
- "__instancecheck__",
- "__subclasscheck__",
- "__getitem__",
- "__missing__",
- "__delitem__",
- "__contains__",
- "__add__",
- "__sub__",
- "__mul__",
- "__truediv__",
- "__floordiv__",
- "__rfloordiv__",
- "__mod__",
- "__divmod__",
- "__lshift__",
- "__rshift__",
- "__and__",
- "__xor__",
- "__or__",
- "__radd__",
- "__rsub__",
- "__rmul__",
- "__rtruediv__",
- "__rmod__",
- "__rdivmod__",
- "__rpow__",
- "__rlshift__",
- "__rrshift__",
- "__rand__",
- "__rxor__",
- "__ror__",
- "__iadd__",
- "__isub__",
- "__imul__",
- "__itruediv__",
- "__ifloordiv__",
- "__imod__",
- "__ilshift__",
- "__irshift__",
- "__iand__",
- "__ixor__",
- "__ior__",
- "__ipow__",
- "__setstate__",
- "__reduce_ex__",
- "__deepcopy__",
- "__cmp__",
- "__matmul__",
- "__rmatmul__",
- "__div__",
- ),
- 2: ("__setattr__", "__get__", "__set__", "__setitem__", "__set_name__"),
- 3: ("__exit__", "__aexit__"),
- (0, 1): ("__round__",),
-}
-
-SPECIAL_METHODS_PARAMS = {
- name: params
- for params, methods in _SPECIAL_METHODS_PARAMS.items()
- for name in methods # type: ignore
-}
-PYMETHODS = set(SPECIAL_METHODS_PARAMS)
-
-
-class NoSuchArgumentError(Exception):
- pass
-
-
-def is_inside_except(node):
- """Returns true if node is inside the name of an except handler."""
- current = node
- while current and not isinstance(current.parent, astroid.ExceptHandler):
- current = current.parent
-
- return current and current is current.parent.name
-
-
-def is_inside_lambda(node: astroid.node_classes.NodeNG) -> bool:
- """Return true if given node is inside lambda"""
- parent = node.parent
- while parent is not None:
- if isinstance(parent, astroid.Lambda):
- return True
- parent = parent.parent
- return False
-
-
-def get_all_elements(
- node: astroid.node_classes.NodeNG
-) -> Iterable[astroid.node_classes.NodeNG]:
- """Recursively returns all atoms in nested lists and tuples."""
- if isinstance(node, (astroid.Tuple, astroid.List)):
- for child in node.elts:
- yield from get_all_elements(child)
- else:
- yield node
-
-
-def clobber_in_except(
- node: astroid.node_classes.NodeNG
-) -> Tuple[bool, Optional[Tuple[str, str]]]:
- """Checks if an assignment node in an except handler clobbers an existing
- variable.
-
- Returns (True, args for W0623) if assignment clobbers an existing variable,
- (False, None) otherwise.
- """
- if isinstance(node, astroid.AssignAttr):
- return True, (node.attrname, "object %r" % (node.expr.as_string(),))
- if isinstance(node, astroid.AssignName):
- name = node.name
- if is_builtin(name):
- return True, (name, "builtins")
-
- stmts = node.lookup(name)[1]
- if stmts and not isinstance(
- stmts[0].assign_type(),
- (astroid.Assign, astroid.AugAssign, astroid.ExceptHandler),
- ):
- return True, (name, "outer scope (line %s)" % stmts[0].fromlineno)
- return False, None
-
-
-def is_super(node: astroid.node_classes.NodeNG) -> bool:
- """return True if the node is referencing the "super" builtin function
- """
- if getattr(node, "name", None) == "super" and node.root().name == BUILTINS_NAME:
- return True
- return False
-
-
-def is_error(node: astroid.node_classes.NodeNG) -> bool:
- """return true if the function does nothing but raising an exception"""
- raises = False
- returns = False
- for child_node in node.nodes_of_class((astroid.Raise, astroid.Return)):
- if isinstance(child_node, astroid.Raise):
- raises = True
- if isinstance(child_node, astroid.Return):
- returns = True
- return raises and not returns
-
-
-builtins = builtins.__dict__.copy() # type: ignore
-SPECIAL_BUILTINS = ("__builtins__",) # '__path__', '__file__')
-
-
-def is_builtin_object(node: astroid.node_classes.NodeNG) -> bool:
- """Returns True if the given node is an object from the __builtin__ module."""
- return node and node.root().name == BUILTINS_NAME
-
-
-def is_builtin(name: str) -> bool:
- """return true if <name> could be considered as a builtin defined by python
- """
- return name in builtins or name in SPECIAL_BUILTINS # type: ignore
-
-
-def is_defined_in_scope(
- var_node: astroid.node_classes.NodeNG,
- varname: str,
- scope: astroid.node_classes.NodeNG,
-) -> bool:
- if isinstance(scope, astroid.If):
- for node in scope.body:
- if (
- isinstance(node, astroid.Assign)
- and any(
- isinstance(target, astroid.AssignName) and target.name == varname
- for target in node.targets
- )
- ) or (isinstance(node, astroid.Nonlocal) and varname in node.names):
- return True
- elif isinstance(scope, (COMP_NODE_TYPES, astroid.For)):
- for ass_node in scope.nodes_of_class(astroid.AssignName):
- if ass_node.name == varname:
- return True
- elif isinstance(scope, astroid.With):
- for expr, ids in scope.items:
- if expr.parent_of(var_node):
- break
- if ids and isinstance(ids, astroid.AssignName) and ids.name == varname:
- return True
- elif isinstance(scope, (astroid.Lambda, astroid.FunctionDef)):
- if scope.args.is_argument(varname):
- # If the name is found inside a default value
- # of a function, then let the search continue
- # in the parent's tree.
- if scope.args.parent_of(var_node):
- try:
- scope.args.default_value(varname)
- scope = scope.parent
- is_defined_in_scope(var_node, varname, scope)
- except astroid.NoDefault:
- pass
- return True
- if getattr(scope, "name", None) == varname:
- return True
- elif isinstance(scope, astroid.ExceptHandler):
- if isinstance(scope.name, astroid.AssignName):
- ass_node = scope.name
- if ass_node.name == varname:
- return True
- return False
-
-
-def is_defined_before(var_node: astroid.node_classes.NodeNG) -> bool:
- """return True if the variable node is defined by a parent node (list,
- set, dict, or generator comprehension, lambda) or in a previous sibling
- node on the same line (statement_defining ; statement_using)
- """
- varname = var_node.name
- _node = var_node.parent
- while _node:
- if is_defined_in_scope(var_node, varname, _node):
- return True
- _node = _node.parent
- # possibly multiple statements on the same line using semi colon separator
- stmt = var_node.statement()
- _node = stmt.previous_sibling()
- lineno = stmt.fromlineno
- while _node and _node.fromlineno == lineno:
- for assign_node in _node.nodes_of_class(astroid.AssignName):
- if assign_node.name == varname:
- return True
- for imp_node in _node.nodes_of_class((astroid.ImportFrom, astroid.Import)):
- if varname in [name[1] or name[0] for name in imp_node.names]:
- return True
- _node = _node.previous_sibling()
- return False
-
-
-def is_default_argument(node: astroid.node_classes.NodeNG) -> bool:
- """return true if the given Name node is used in function or lambda
- default argument's value
- """
- parent = node.scope()
- if isinstance(parent, (astroid.FunctionDef, astroid.Lambda)):
- for default_node in parent.args.defaults:
- for default_name_node in default_node.nodes_of_class(astroid.Name):
- if default_name_node is node:
- return True
- return False
-
-
-def is_func_decorator(node: astroid.node_classes.NodeNG) -> bool:
- """return true if the name is used in function decorator"""
- parent = node.parent
- while parent is not None:
- if isinstance(parent, astroid.Decorators):
- return True
- if parent.is_statement or isinstance(
- parent,
- (astroid.Lambda, scoped_nodes.ComprehensionScope, scoped_nodes.ListComp),
- ):
- break
- parent = parent.parent
- return False
-
-
-def is_ancestor_name(
- frame: astroid.node_classes.NodeNG, node: astroid.node_classes.NodeNG
-) -> bool:
- """return True if `frame` is an astroid.Class node with `node` in the
- subtree of its bases attribute
- """
- try:
- bases = frame.bases
- except AttributeError:
- return False
- for base in bases:
- if node in base.nodes_of_class(astroid.Name):
- return True
- return False
-
-
-def assign_parent(node: astroid.node_classes.NodeNG) -> astroid.node_classes.NodeNG:
- """return the higher parent which is not an AssignName, Tuple or List node
- """
- while node and isinstance(node, (astroid.AssignName, astroid.Tuple, astroid.List)):
- node = node.parent
- return node
-
-
-def overrides_a_method(class_node: astroid.node_classes.NodeNG, name: str) -> bool:
- """return True if <name> is a method overridden from an ancestor"""
- for ancestor in class_node.ancestors():
- if name in ancestor and isinstance(ancestor[name], astroid.FunctionDef):
- return True
- return False
-
-
-def check_messages(*messages: str) -> Callable:
- """decorator to store messages that are handled by a checker method"""
-
- def store_messages(func):
- func.checks_msgs = messages
- return func
-
- return store_messages
-
-
-class IncompleteFormatString(Exception):
- """A format string ended in the middle of a format specifier."""
-
-
-class UnsupportedFormatCharacter(Exception):
- """A format character in a format string is not one of the supported
- format characters."""
-
- def __init__(self, index):
- Exception.__init__(self, index)
- self.index = index
-
-
-def parse_format_string(
- format_string: str
-) -> Tuple[Set[str], int, Dict[str, str], List[str]]:
- """Parses a format string, returning a tuple of (keys, num_args), where keys
- is the set of mapping keys in the format string, and num_args is the number
- of arguments required by the format string. Raises
- IncompleteFormatString or UnsupportedFormatCharacter if a
- parse error occurs."""
- keys = set()
- key_types = dict()
- pos_types = []
- num_args = 0
-
- def next_char(i):
- i += 1
- if i == len(format_string):
- raise IncompleteFormatString
- return (i, format_string[i])
-
- i = 0
- while i < len(format_string):
- char = format_string[i]
- if char == "%":
- i, char = next_char(i)
- # Parse the mapping key (optional).
- key = None
- if char == "(":
- depth = 1
- i, char = next_char(i)
- key_start = i
- while depth != 0:
- if char == "(":
- depth += 1
- elif char == ")":
- depth -= 1
- i, char = next_char(i)
- key_end = i - 1
- key = format_string[key_start:key_end]
-
- # Parse the conversion flags (optional).
- while char in "#0- +":
- i, char = next_char(i)
- # Parse the minimum field width (optional).
- if char == "*":
- num_args += 1
- i, char = next_char(i)
- else:
- while char in string.digits:
- i, char = next_char(i)
- # Parse the precision (optional).
- if char == ".":
- i, char = next_char(i)
- if char == "*":
- num_args += 1
- i, char = next_char(i)
- else:
- while char in string.digits:
- i, char = next_char(i)
- # Parse the length modifier (optional).
- if char in "hlL":
- i, char = next_char(i)
- # Parse the conversion type (mandatory).
- flags = "diouxXeEfFgGcrs%a"
- if char not in flags:
- raise UnsupportedFormatCharacter(i)
- if key:
- keys.add(key)
- key_types[key] = char
- elif char != "%":
- num_args += 1
- pos_types.append(char)
- i += 1
- return keys, num_args, key_types, pos_types
-
-
-def split_format_field_names(format_string) -> Tuple[str, Iterable[Tuple[bool, str]]]:
- try:
- return _string.formatter_field_name_split(format_string)
- except ValueError:
- raise IncompleteFormatString()
-
-
-def collect_string_fields(format_string) -> Iterable[Optional[str]]:
- """ Given a format string, return an iterator
- of all the valid format fields. It handles nested fields
- as well.
- """
- formatter = string.Formatter()
- try:
- parseiterator = formatter.parse(format_string)
- for result in parseiterator:
- if all(item is None for item in result[1:]):
- # not a replacement format
- continue
- name = result[1]
- nested = result[2]
- yield name
- if nested:
- for field in collect_string_fields(nested):
- yield field
- except ValueError as exc:
- # Probably the format string is invalid.
- if exc.args[0].startswith("cannot switch from manual"):
- # On Jython, parsing a string with both manual
- # and automatic positions will fail with a ValueError,
- # while on CPython it will simply return the fields,
- # the validation being done in the interpreter (?).
- # We're just returning two mixed fields in order
- # to trigger the format-combined-specification check.
- yield ""
- yield "1"
- return
- raise IncompleteFormatString(format_string)
-
-
-def parse_format_method_string(
- format_string: str
-) -> Tuple[List[Tuple[str, List[Tuple[bool, str]]]], int, int]:
- """
- Parses a PEP 3101 format string, returning a tuple of
- (keyword_arguments, implicit_pos_args_cnt, explicit_pos_args),
- where keyword_arguments is the set of mapping keys in the format string, implicit_pos_args_cnt
- is the number of arguments required by the format string and
- explicit_pos_args is the number of arguments passed with the position.
- """
- keyword_arguments = []
- implicit_pos_args_cnt = 0
- explicit_pos_args = set()
- for name in collect_string_fields(format_string):
- if name and str(name).isdigit():
- explicit_pos_args.add(str(name))
- elif name:
- keyname, fielditerator = split_format_field_names(name)
- if isinstance(keyname, numbers.Number):
- # In Python 2 it will return long which will lead
- # to different output between 2 and 3
- explicit_pos_args.add(str(keyname))
- keyname = int(keyname)
- try:
- keyword_arguments.append((keyname, list(fielditerator)))
- except ValueError:
- raise IncompleteFormatString()
- else:
- implicit_pos_args_cnt += 1
- return keyword_arguments, implicit_pos_args_cnt, len(explicit_pos_args)
-
-
-def is_attr_protected(attrname: str) -> bool:
- """return True if attribute name is protected (start with _ and some other
- details), False otherwise.
- """
- return (
- attrname[0] == "_"
- and attrname != "_"
- and not (attrname.startswith("__") and attrname.endswith("__"))
- )
-
-
-def node_frame_class(node: astroid.node_classes.NodeNG) -> Optional[astroid.ClassDef]:
- """Return the class that is wrapping the given node
-
- The function returns a class for a method node (or a staticmethod or a
- classmethod), otherwise it returns `None`.
- """
- klass = node.frame()
-
- while klass is not None and not isinstance(klass, astroid.ClassDef):
- if klass.parent is None:
- klass = None
- else:
- klass = klass.parent.frame()
-
- return klass
-
-
-def is_attr_private(attrname: str) -> Optional[Match[str]]:
- """Check that attribute name is private (at least two leading underscores,
- at most one trailing underscore)
- """
- regex = re.compile("^_{2,}.*[^_]+_?$")
- return regex.match(attrname)
-
-
-def get_argument_from_call(
- call_node: astroid.Call, position: int = None, keyword: str = None
-) -> astroid.Name:
- """Returns the specified argument from a function call.
-
- :param astroid.Call call_node: Node representing a function call to check.
- :param int position: position of the argument.
- :param str keyword: the keyword of the argument.
-
- :returns: The node representing the argument, None if the argument is not found.
- :rtype: astroid.Name
- :raises ValueError: if both position and keyword are None.
- :raises NoSuchArgumentError: if no argument at the provided position or with
- the provided keyword.
- """
- if position is None and keyword is None:
- raise ValueError("Must specify at least one of: position or keyword.")
- if position is not None:
- try:
- return call_node.args[position]
- except IndexError:
- pass
- if keyword and call_node.keywords:
- for arg in call_node.keywords:
- if arg.arg == keyword:
- return arg.value
-
- raise NoSuchArgumentError
-
-
-def inherit_from_std_ex(node: astroid.node_classes.NodeNG) -> bool:
- """
- Return true if the given class node is subclass of
- exceptions.Exception.
- """
- ancestors = node.ancestors() if hasattr(node, "ancestors") else []
- for ancestor in itertools.chain([node], ancestors):
- if (
- ancestor.name in ("Exception", "BaseException")
- and ancestor.root().name == EXCEPTIONS_MODULE
- ):
- return True
- return False
-
-
-def error_of_type(handler: astroid.ExceptHandler, error_type) -> bool:
- """
- Check if the given exception handler catches
- the given error_type.
-
- The *handler* parameter is a node, representing an ExceptHandler node.
- The *error_type* can be an exception, such as AttributeError,
- the name of an exception, or it can be a tuple of errors.
- The function will return True if the handler catches any of the
- given errors.
- """
-
- def stringify_error(error):
- if not isinstance(error, str):
- return error.__name__
- return error
-
- if not isinstance(error_type, tuple):
- error_type = (error_type,) # type: ignore
- expected_errors = {stringify_error(error) for error in error_type} # type: ignore
- if not handler.type:
- return True
- return handler.catch(expected_errors)
-
-
-def decorated_with_property(node: astroid.FunctionDef) -> bool:
- """Detect if the given function node is decorated with a property. """
- if not node.decorators:
- return False
- for decorator in node.decorators.nodes:
- try:
- if _is_property_decorator(decorator):
- return True
- except astroid.InferenceError:
- pass
- return False
-
-
-def _is_property_kind(node, *kinds):
- if not isinstance(node, (astroid.UnboundMethod, astroid.FunctionDef)):
- return False
- if node.decorators:
- for decorator in node.decorators.nodes:
- if isinstance(decorator, astroid.Attribute) and decorator.attrname in kinds:
- return True
- return False
-
-
-def is_property_setter(node: astroid.FunctionDef) -> bool:
- """Check if the given node is a property setter"""
- return _is_property_kind(node, "setter")
-
-
-def is_property_setter_or_deleter(node: astroid.FunctionDef) -> bool:
- """Check if the given node is either a property setter or a deleter"""
- return _is_property_kind(node, "setter", "deleter")
-
-
-def _is_property_decorator(decorator: astroid.Name) -> bool:
- for inferred in decorator.infer():
- if isinstance(inferred, astroid.ClassDef):
- if inferred.root().name == BUILTINS_NAME and inferred.name == "property":
- return True
- for ancestor in inferred.ancestors():
- if (
- ancestor.name == "property"
- and ancestor.root().name == BUILTINS_NAME
- ):
- return True
- return False
-
-
-def decorated_with(
- func: Union[astroid.FunctionDef, astroid.BoundMethod, astroid.UnboundMethod],
- qnames: Iterable[str],
-) -> bool:
- """Determine if the `func` node has a decorator with the qualified name `qname`."""
- decorators = func.decorators.nodes if func.decorators else []
- for decorator_node in decorators:
- if isinstance(decorator_node, astroid.Call):
- # We only want to infer the function name
- decorator_node = decorator_node.func
- try:
- if any(
- i is not None and i.qname() in qnames or i.name in qnames
- for i in decorator_node.infer()
- ):
- return True
- except astroid.InferenceError:
- continue
- return False
-
-
-@lru_cache(maxsize=1024)
-def unimplemented_abstract_methods(
- node: astroid.node_classes.NodeNG, is_abstract_cb: astroid.FunctionDef = None
-) -> Dict[str, astroid.node_classes.NodeNG]:
- """
- Get the unimplemented abstract methods for the given *node*.
-
- A method can be considered abstract if the callback *is_abstract_cb*
- returns a ``True`` value. The check defaults to verifying that
- a method is decorated with abstract methods.
- The function will work only for new-style classes. For old-style
- classes, it will simply return an empty dictionary.
- For the rest of them, it will return a dictionary of abstract method
- names and their inferred objects.
- """
- if is_abstract_cb is None:
- is_abstract_cb = partial(decorated_with, qnames=ABC_METHODS)
- visited = {} # type: Dict[str, astroid.node_classes.NodeNG]
- try:
- mro = reversed(node.mro())
- except NotImplementedError:
- # Old style class, it will not have a mro.
- return {}
- except astroid.ResolveError:
- # Probably inconsistent hierarchy, don'try
- # to figure this out here.
- return {}
- for ancestor in mro:
- for obj in ancestor.values():
- inferred = obj
- if isinstance(obj, astroid.AssignName):
- inferred = safe_infer(obj)
- if not inferred:
- # Might be an abstract function,
- # but since we don't have enough information
- # in order to take this decision, we're taking
- # the *safe* decision instead.
- if obj.name in visited:
- del visited[obj.name]
- continue
- if not isinstance(inferred, astroid.FunctionDef):
- if obj.name in visited:
- del visited[obj.name]
- if isinstance(inferred, astroid.FunctionDef):
- # It's critical to use the original name,
- # since after inferring, an object can be something
- # else than expected, as in the case of the
- # following assignment.
- #
- # class A:
- # def keys(self): pass
- # __iter__ = keys
- abstract = is_abstract_cb(inferred)
- if abstract:
- visited[obj.name] = inferred
- elif not abstract and obj.name in visited:
- del visited[obj.name]
- return visited
-
-
-def find_try_except_wrapper_node(
- node: astroid.node_classes.NodeNG
-) -> Union[astroid.ExceptHandler, astroid.TryExcept]:
- """Return the ExceptHandler or the TryExcept node in which the node is."""
- current = node
- ignores = (astroid.ExceptHandler, astroid.TryExcept)
- while current and not isinstance(current.parent, ignores):
- current = current.parent
-
- if current and isinstance(current.parent, ignores):
- return current.parent
- return None
-
-
-def is_from_fallback_block(node: astroid.node_classes.NodeNG) -> bool:
- """Check if the given node is from a fallback import block."""
- context = find_try_except_wrapper_node(node)
- if not context:
- return False
-
- if isinstance(context, astroid.ExceptHandler):
- other_body = context.parent.body
- handlers = context.parent.handlers
- else:
- other_body = itertools.chain.from_iterable(
- handler.body for handler in context.handlers
- )
- handlers = context.handlers
-
- has_fallback_imports = any(
- isinstance(import_node, (astroid.ImportFrom, astroid.Import))
- for import_node in other_body
- )
- ignores_import_error = _except_handlers_ignores_exception(handlers, ImportError)
- return ignores_import_error or has_fallback_imports
-
-
-def _except_handlers_ignores_exception(
- handlers: astroid.ExceptHandler, exception
-) -> bool:
- func = partial(error_of_type, error_type=(exception,))
- return any(map(func, handlers))
-
-
-def get_exception_handlers(
- node: astroid.node_classes.NodeNG, exception=Exception
-) -> Optional[List[astroid.ExceptHandler]]:
- """Return the collections of handlers handling the exception in arguments.
-
- Args:
- node (astroid.NodeNG): A node that is potentially wrapped in a try except.
- exception (builtin.Exception or str): exception or name of the exception.
-
- Returns:
- list: the collection of handlers that are handling the exception or None.
-
- """
- context = find_try_except_wrapper_node(node)
- if isinstance(context, astroid.TryExcept):
- return [
- handler for handler in context.handlers if error_of_type(handler, exception)
- ]
- return []
-
-
-def is_node_inside_try_except(node: astroid.Raise) -> bool:
- """Check if the node is directly under a Try/Except statement.
- (but not under an ExceptHandler!)
-
- Args:
- node (astroid.Raise): the node raising the exception.
-
- Returns:
- bool: True if the node is inside a try/except statement, False otherwise.
- """
- context = find_try_except_wrapper_node(node)
- return isinstance(context, astroid.TryExcept)
-
-
-def node_ignores_exception(
- node: astroid.node_classes.NodeNG, exception=Exception
-) -> bool:
- """Check if the node is in a TryExcept which handles the given exception.
-
- If the exception is not given, the function is going to look for bare
- excepts.
- """
- managing_handlers = get_exception_handlers(node, exception)
- if not managing_handlers:
- return False
- return any(managing_handlers)
-
-
-def class_is_abstract(node: astroid.ClassDef) -> bool:
- """return true if the given class node should be considered as an abstract
- class
- """
- for method in node.methods():
- if method.parent.frame() is node:
- if method.is_abstract(pass_is_abstract=False):
- return True
- return False
-
-
-def _supports_protocol_method(value: astroid.node_classes.NodeNG, attr: str) -> bool:
- try:
- attributes = value.getattr(attr)
- except astroid.NotFoundError:
- return False
-
- first = attributes[0]
- if isinstance(first, astroid.AssignName):
- if isinstance(first.parent.value, astroid.Const):
- return False
- return True
-
-
-def is_comprehension(node: astroid.node_classes.NodeNG) -> bool:
- comprehensions = (
- astroid.ListComp,
- astroid.SetComp,
- astroid.DictComp,
- astroid.GeneratorExp,
- )
- return isinstance(node, comprehensions)
-
-
-def _supports_mapping_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(
- value, GETITEM_METHOD
- ) and _supports_protocol_method(value, KEYS_METHOD)
-
-
-def _supports_membership_test_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, CONTAINS_METHOD)
-
-
-def _supports_iteration_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, ITER_METHOD) or _supports_protocol_method(
- value, GETITEM_METHOD
- )
-
-
-def _supports_async_iteration_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, AITER_METHOD)
-
-
-def _supports_getitem_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, GETITEM_METHOD)
-
-
-def _supports_setitem_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, SETITEM_METHOD)
-
-
-def _supports_delitem_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, DELITEM_METHOD)
-
-
-def _is_abstract_class_name(name: str) -> bool:
- lname = name.lower()
- is_mixin = lname.endswith("mixin")
- is_abstract = lname.startswith("abstract")
- is_base = lname.startswith("base") or lname.endswith("base")
- return is_mixin or is_abstract or is_base
-
-
-def is_inside_abstract_class(node: astroid.node_classes.NodeNG) -> bool:
- while node is not None:
- if isinstance(node, astroid.ClassDef):
- if class_is_abstract(node):
- return True
- name = getattr(node, "name", None)
- if name is not None and _is_abstract_class_name(name):
- return True
- node = node.parent
- return False
-
-
-def _supports_protocol(
- value: astroid.node_classes.NodeNG, protocol_callback: astroid.FunctionDef
-) -> bool:
- if isinstance(value, astroid.ClassDef):
- if not has_known_bases(value):
- return True
- # classobj can only be iterable if it has an iterable metaclass
- meta = value.metaclass()
- if meta is not None:
- if protocol_callback(meta):
- return True
- if isinstance(value, astroid.BaseInstance):
- if not has_known_bases(value):
- return True
- if value.has_dynamic_getattr():
- return True
- if protocol_callback(value):
- return True
-
- if (
- isinstance(value, _bases.Proxy)
- and isinstance(value._proxied, astroid.BaseInstance)
- and has_known_bases(value._proxied)
- ):
- value = value._proxied
- return protocol_callback(value)
-
- return False
-
-
-def is_iterable(value: astroid.node_classes.NodeNG, check_async: bool = False) -> bool:
- if check_async:
- protocol_check = _supports_async_iteration_protocol
- else:
- protocol_check = _supports_iteration_protocol
- return _supports_protocol(value, protocol_check)
-
-
-def is_mapping(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol(value, _supports_mapping_protocol)
-
-
-def supports_membership_test(value: astroid.node_classes.NodeNG) -> bool:
- supported = _supports_protocol(value, _supports_membership_test_protocol)
- return supported or is_iterable(value)
-
-
-def supports_getitem(value: astroid.node_classes.NodeNG) -> bool:
- if isinstance(value, astroid.ClassDef):
- if _supports_protocol_method(value, CLASS_GETITEM_METHOD):
- return True
- return _supports_protocol(value, _supports_getitem_protocol)
-
-
-def supports_setitem(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol(value, _supports_setitem_protocol)
-
-
-def supports_delitem(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol(value, _supports_delitem_protocol)
-
-
-@lru_cache(maxsize=1024)
-def safe_infer(
- node: astroid.node_classes.NodeNG, context=None
-) -> Optional[astroid.node_classes.NodeNG]:
- """Return the inferred value for the given node.
-
- Return None if inference failed or if there is some ambiguity (more than
- one node has been inferred).
- """
- try:
- inferit = node.infer(context=context)
- value = next(inferit)
- except astroid.InferenceError:
- return None
- try:
- next(inferit)
- return None # None if there is ambiguity on the inferred node
- except astroid.InferenceError:
- return None # there is some kind of ambiguity
- except StopIteration:
- return value
-
-
-def has_known_bases(klass: astroid.ClassDef, context=None) -> bool:
- """Return true if all base classes of a class could be inferred."""
- try:
- return klass._all_bases_known
- except AttributeError:
- pass
- for base in klass.bases:
- result = safe_infer(base, context=context)
- if (
- not isinstance(result, astroid.ClassDef)
- or result is klass
- or not has_known_bases(result, context=context)
- ):
- klass._all_bases_known = False
- return False
- klass._all_bases_known = True
- return True
-
-
-def is_none(node: astroid.node_classes.NodeNG) -> bool:
- return (
- node is None
- or (isinstance(node, astroid.Const) and node.value is None)
- or (isinstance(node, astroid.Name) and node.name == "None")
- )
-
-
-def node_type(node: astroid.node_classes.NodeNG) -> Optional[type]:
- """Return the inferred type for `node`
-
- If there is more than one possible type, or if inferred type is Uninferable or None,
- return None
- """
- # check there is only one possible type for the assign node. Else we
- # don't handle it for now
- types = set()
- try:
- for var_type in node.infer():
- if var_type == astroid.Uninferable or is_none(var_type):
- continue
- types.add(var_type)
- if len(types) > 1:
- return None
- except astroid.InferenceError:
- return None
- return types.pop() if types else None
-
-
-def is_registered_in_singledispatch_function(node: astroid.FunctionDef) -> bool:
- """Check if the given function node is a singledispatch function."""
-
- singledispatch_qnames = (
- "functools.singledispatch",
- "singledispatch.singledispatch",
- )
-
- if not isinstance(node, astroid.FunctionDef):
- return False
-
- decorators = node.decorators.nodes if node.decorators else []
- for decorator in decorators:
- # func.register are function calls
- if not isinstance(decorator, astroid.Call):
- continue
-
- func = decorator.func
- if not isinstance(func, astroid.Attribute) or func.attrname != "register":
- continue
-
- try:
- func_def = next(func.expr.infer())
- except astroid.InferenceError:
- continue
-
- if isinstance(func_def, astroid.FunctionDef):
- # pylint: disable=redundant-keyword-arg; some flow inference goes wrong here
- return decorated_with(func_def, singledispatch_qnames)
-
- return False
-
-
-def get_node_last_lineno(node: astroid.node_classes.NodeNG) -> int:
- """
- Get the last lineno of the given node. For a simple statement this will just be node.lineno,
- but for a node that has child statements (e.g. a method) this will be the lineno of the last
- child statement recursively.
- """
- # 'finalbody' is always the last clause in a try statement, if present
- if getattr(node, "finalbody", False):
- return get_node_last_lineno(node.finalbody[-1])
- # For if, while, and for statements 'orelse' is always the last clause.
- # For try statements 'orelse' is the last in the absence of a 'finalbody'
- if getattr(node, "orelse", False):
- return get_node_last_lineno(node.orelse[-1])
- # try statements have the 'handlers' last if there is no 'orelse' or 'finalbody'
- if getattr(node, "handlers", False):
- return get_node_last_lineno(node.handlers[-1])
- # All compound statements have a 'body'
- if getattr(node, "body", False):
- return get_node_last_lineno(node.body[-1])
- # Not a compound statement
- return node.lineno
-
-
-def is_postponed_evaluation_enabled(node: astroid.node_classes.NodeNG) -> bool:
- """Check if the postponed evaluation of annotations is enabled"""
- name = "annotations"
- module = node.root()
- stmt = module.locals.get(name)
- return (
- stmt
- and isinstance(stmt[0], astroid.ImportFrom)
- and stmt[0].modname == "__future__"
- )
-
-
-def is_subclass_of(child: astroid.ClassDef, parent: astroid.ClassDef) -> bool:
- """
- Check if first node is a subclass of second node.
- :param child: Node to check for subclass.
- :param parent: Node to check for superclass.
- :returns: True if child is derived from parent. False otherwise.
- """
- if not all(isinstance(node, astroid.ClassDef) for node in (child, parent)):
- return False
-
- for ancestor in child.ancestors():
- try:
- if helpers.is_subtype(ancestor, parent):
- return True
- except _NonDeducibleTypeHierarchy:
- continue
- return False
-
-
-@lru_cache(maxsize=1024)
-def is_overload_stub(node: astroid.node_classes.NodeNG) -> bool:
- """Check if a node if is a function stub decorated with typing.overload.
-
- :param node: Node to check.
- :returns: True if node is an overload function stub. False otherwise.
- """
- decorators = getattr(node, "decorators", None)
- return bool(decorators and decorated_with(node, ["typing.overload", "overload"]))
-
-
-def is_protocol_class(cls: astroid.node_classes.NodeNG) -> bool:
- """Check if the given node represents a protocol class
-
- :param cls: The node to check
- :returns: True if the node is a typing protocol class, false otherwise.
- """
- if not isinstance(cls, astroid.ClassDef):
- return False
-
- # Use .ancestors() since not all protocol classes can have
- # their mro deduced.
- return any(parent.qname() in TYPING_PROTOCOLS for parent in cls.ancestors())