From baaa81a2994cdd517fbde8693b0a4b0a67f5a4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Fri, 3 Sep 2021 13:47:23 +0200 Subject: [PATCH] Refactor various typing related issues (#4940) * Add type annotations to ``visit`` & ``leave`` calls This adds typing to most calls that visit nodes. All other changes are due to mypy errors resulting from introduction of typing. * Fix outstanding mypy issues This removes some of the `type: ignore` comments in favour of solving the mypy issues these comments were surpressing. * Fix remaining references to node_classes Except for two references to node_classes in the changelog this should be the last of them Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Co-authored-by: Pierre Sassoulas --- doc/how_tos/custom_checkers.rst | 16 +-- pylint/checkers/async.py | 4 +- pylint/checkers/base.py | 105 +++++++++--------- pylint/checkers/classes.py | 14 +-- pylint/checkers/deprecated.py | 9 +- pylint/checkers/design_analysis.py | 22 ++-- pylint/checkers/exceptions.py | 56 +++++----- pylint/checkers/format.py | 2 +- pylint/checkers/imports.py | 25 +++-- pylint/checkers/logging.py | 26 ++--- pylint/checkers/newstyle.py | 2 +- pylint/checkers/refactoring/len_checker.py | 4 +- pylint/checkers/refactoring/not_checker.py | 2 +- .../refactoring/refactoring_checker.py | 24 ++-- pylint/checkers/similar.py | 4 +- pylint/checkers/spelling.py | 8 +- pylint/checkers/stdlib.py | 18 +-- pylint/checkers/strings.py | 28 +++-- pylint/checkers/typecheck.py | 62 ++++++----- pylint/checkers/utils.py | 27 ++--- pylint/checkers/variables.py | 52 ++++----- pylint/extensions/bad_builtin.py | 2 +- pylint/extensions/broad_try_clause.py | 8 +- pylint/extensions/check_elif.py | 8 +- pylint/extensions/code_style.py | 6 +- pylint/extensions/comparetozero.py | 7 +- pylint/extensions/confusing_elif.py | 2 +- pylint/extensions/docparams.py | 13 ++- pylint/extensions/docstyle.py | 8 +- pylint/extensions/emptystring.py | 6 +- pylint/extensions/mccabe.py | 3 +- pylint/extensions/overlapping_exceptions.py | 7 +- pylint/extensions/redefined_variable_type.py | 12 +- pylint/extensions/typing.py | 4 +- pylint/extensions/while_used.py | 4 +- pylint/graph.py | 4 +- pylint/message/message_id_store.py | 7 +- pylint/pyreverse/diadefslib.py | 17 ++- pylint/pyreverse/inspector.py | 73 ++++++------ pylint/pyreverse/main.py | 2 +- pylint/testutils/reporter_for_tests.py | 4 +- setup.cfg | 1 + tests/extensions/test_check_docs.py | 2 +- tests/functional/g/generated_members.py | 4 +- tests/functional/g/generated_members.rc | 2 +- tests/functional/u/unused/unused_variable.py | 4 +- tests/pyreverse/test_utils.py | 4 +- 47 files changed, 375 insertions(+), 349 deletions(-) diff --git a/doc/how_tos/custom_checkers.rst b/doc/how_tos/custom_checkers.rst index add8e1a698..c5deddd94b 100644 --- a/doc/how_tos/custom_checkers.rst +++ b/doc/how_tos/custom_checkers.rst @@ -36,9 +36,11 @@ Firstly we will need to fill in some required boilerplate: .. code-block:: python import astroid + from astroid import nodes from pylint.checkers import BaseChecker from pylint.interfaces import IAstroidChecker + from pylint.lint import PyLinter class UniqueReturnChecker(BaseChecker): __implements__ = IAstroidChecker @@ -117,14 +119,14 @@ Next we'll track when we enter and leave a function. .. code-block:: python - def __init__(self, linter=None): + def __init__(self, linter: PyLinter =None) -> None: super(UniqueReturnChecker, self).__init__(linter) self._function_stack = [] - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: self._function_stack.append([]) - def leave_functiondef(self, node): + def leave_functiondef(self, node: nodes.FunctionDef) -> None: self._function_stack.pop() In the constructor we initialise a stack to keep a list of return nodes @@ -138,13 +140,13 @@ and to remove the list of return nodes when we leave the function. Finally we'll implement the check. We will define a ``visit_return`` function, -which is called with a :class:`.astroid.node_classes.Return` node. +which is called with a :class:`.astroid.nodes.Return` node. .. _astroid_extract_node: .. TODO We can shorten/remove this bit once astroid has API docs. We'll need to be able to figure out what attributes a -:class:`.astroid.node_classes.Return` node has available. +:class:`.astroid.nodes.Return` node has available. We can use :func:`astroid.extract_node` for this:: >>> node = astroid.extract_node("return 5") @@ -178,8 +180,8 @@ Now we know how to use the astroid node, we can implement our check. .. code-block:: python - def visit_return(self, node): - if not isinstance(node.value, astroid.node_classes.Const): + def visit_return(self, node: nodes.Return) -> None: + if not isinstance(node.value, nodes.Const): return for other_return in self._function_stack[-1]: diff --git a/pylint/checkers/async.py b/pylint/checkers/async.py index a6831b3948..939678d3e3 100644 --- a/pylint/checkers/async.py +++ b/pylint/checkers/async.py @@ -46,7 +46,7 @@ def open(self): self._async_generators = ["contextlib.asynccontextmanager"] @checker_utils.check_messages("yield-inside-async-function") - def visit_asyncfunctiondef(self, node): + def visit_asyncfunctiondef(self, node: nodes.AsyncFunctionDef) -> None: for child in node.nodes_of_class(nodes.Yield): if child.scope() is node and ( sys.version_info[:2] == (3, 5) or isinstance(child, nodes.YieldFrom) @@ -54,7 +54,7 @@ def visit_asyncfunctiondef(self, node): self.add_message("yield-inside-async-function", node=child) @checker_utils.check_messages("not-async-context-manager") - def visit_asyncwith(self, node): + def visit_asyncwith(self, node: nodes.AsyncWith) -> None: for ctx_mgr, _ in node.items: inferred = checker_utils.safe_infer(ctx_mgr) if inferred is None or inferred is astroid.Uninferable: diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index f37752ac96..2ca79453a7 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -66,7 +66,7 @@ import itertools import re import sys -from typing import Optional, Pattern +from typing import Any, Iterator, Optional, Pattern import astroid from astroid import nodes @@ -565,7 +565,7 @@ class BasicErrorChecker(_BasicChecker): } @utils.check_messages("function-redefined") - def visit_classdef(self, node): + def visit_classdef(self, node: nodes.ClassDef) -> None: self._check_redefinition("class", node) def _too_many_starred_for_tuple(self, assign_tuple): @@ -578,7 +578,7 @@ def _too_many_starred_for_tuple(self, assign_tuple): return starred_count > 1 @utils.check_messages("too-many-star-expressions", "invalid-star-assignment-target") - def visit_assign(self, node): + def visit_assign(self, node: nodes.Assign) -> None: # Check *a, *b = ... assign_target = node.targets[0] # Check *a = b @@ -591,7 +591,7 @@ def visit_assign(self, node): self.add_message("too-many-star-expressions", node=node) @utils.check_messages("star-needs-assignment-target") - def visit_starred(self, node): + def visit_starred(self, node: nodes.Starred) -> None: """Check that a Starred expression is used in an assignment target.""" if isinstance(node.parent, nodes.Call): # f(*args) is converted to Call(args=[Starred]), so ignore @@ -617,7 +617,7 @@ def visit_starred(self, node): "nonlocal-and-global", "used-prior-global-declaration", ) - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: self._check_nonlocal_and_global(node) self._check_name_used_prior_global(node) if not redefined_by_decorator( @@ -638,7 +638,7 @@ def visit_functiondef(self, node): self.add_message("return-in-init", node=node) # Check for duplicate names by clustering args with same name for detailed report arg_clusters = collections.defaultdict(list) - arguments = filter(None, [node.args.args, node.args.kwonlyargs]) + arguments: Iterator[Any] = filter(None, [node.args.args, node.args.kwonlyargs]) for arg in itertools.chain.from_iterable(arguments): arg_clusters[arg.name].append(arg) @@ -712,36 +712,36 @@ def same_scope(current): self.add_message("nonlocal-and-global", args=(name,), node=node) @utils.check_messages("return-outside-function") - def visit_return(self, node): + def visit_return(self, node: nodes.Return) -> None: if not isinstance(node.frame(), nodes.FunctionDef): self.add_message("return-outside-function", node=node) @utils.check_messages("yield-outside-function") - def visit_yield(self, node): + def visit_yield(self, node: nodes.Yield) -> None: self._check_yield_outside_func(node) @utils.check_messages("yield-outside-function") - def visit_yieldfrom(self, node): + def visit_yieldfrom(self, node: nodes.YieldFrom) -> None: self._check_yield_outside_func(node) @utils.check_messages("not-in-loop", "continue-in-finally") - def visit_continue(self, node): + def visit_continue(self, node: nodes.Continue) -> None: self._check_in_loop(node, "continue") @utils.check_messages("not-in-loop") - def visit_break(self, node): + def visit_break(self, node: nodes.Break) -> None: self._check_in_loop(node, "break") @utils.check_messages("useless-else-on-loop") - def visit_for(self, node): + def visit_for(self, node: nodes.For) -> None: self._check_else_on_loop(node) @utils.check_messages("useless-else-on-loop") - def visit_while(self, node): + def visit_while(self, node: nodes.While) -> None: self._check_else_on_loop(node) @utils.check_messages("nonexistent-operator") - def visit_unaryop(self, node): + def visit_unaryop(self, node: nodes.UnaryOp) -> None: """check use of the non-existent ++ and -- operator operator""" if ( (node.op in "+-") @@ -771,12 +771,12 @@ def _check_nonlocal_without_binding(self, node, name): self.add_message("nonlocal-without-binding", args=(name,), node=node) @utils.check_messages("nonlocal-without-binding") - def visit_nonlocal(self, node): + def visit_nonlocal(self, node: nodes.Nonlocal) -> None: for name in node.names: self._check_nonlocal_without_binding(node, name) @utils.check_messages("abstract-class-instantiated") - def visit_call(self, node): + def visit_call(self, node: nodes.Call) -> None: """Check instantiating abstract class with abc.ABCMeta as metaclass. """ @@ -1091,15 +1091,15 @@ def open(self): self.stats = self.linter.add_stats(module=0, function=0, method=0, class_=0) @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test") - def visit_if(self, node): + def visit_if(self, node: nodes.If) -> None: self._check_using_constant_test(node, node.test) @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test") - def visit_ifexp(self, node): + def visit_ifexp(self, node: nodes.IfExp) -> None: self._check_using_constant_test(node, node.test) @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test") - def visit_comprehension(self, node): + def visit_comprehension(self, node: nodes.Comprehension) -> None: if node.ifs: for if_test in node.ifs: self._check_using_constant_test(node, if_test) @@ -1157,11 +1157,11 @@ def _check_using_constant_test(self, node, test): pass self.add_message("using-constant-test", node=node) - def visit_module(self, _): + def visit_module(self, _: nodes.Module) -> None: """check module name, docstring and required arguments""" self.stats["module"] += 1 - def visit_classdef(self, node): # pylint: disable=unused-argument + def visit_classdef(self, _: nodes.ClassDef) -> None: """check module name, docstring and redefinition increment branch counter """ @@ -1170,7 +1170,7 @@ def visit_classdef(self, node): # pylint: disable=unused-argument @utils.check_messages( "pointless-statement", "pointless-string-statement", "expression-not-assigned" ) - def visit_expr(self, node): + def visit_expr(self, node: nodes.Expr) -> None: """Check for various kind of statements without effect""" expr = node.value if isinstance(expr, nodes.Const) and isinstance(expr.value, str): @@ -1240,7 +1240,7 @@ def _has_variadic_argument(args, variadic_name): return False @utils.check_messages("unnecessary-lambda") - def visit_lambda(self, node): + def visit_lambda(self, node: nodes.Lambda) -> None: """check whether or not the lambda is suspicious""" # if the body of the lambda is a call expression with the same # argument list as the lambda itself, then the lambda is @@ -1300,7 +1300,7 @@ def visit_lambda(self, node): self.add_message("unnecessary-lambda", line=node.fromlineno, node=node) @utils.check_messages("dangerous-default-value") - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: """check function name, docstring, arguments, redefinition, variable names, max locals """ @@ -1350,7 +1350,7 @@ def is_iterable(internal_node): self.add_message("dangerous-default-value", node=node, args=(msg,)) @utils.check_messages("unreachable", "lost-exception") - def visit_return(self, node): + def visit_return(self, node: nodes.Return) -> None: """1 - check is the node has a right sibling (if so, that's some unreachable code) 2 - check is the node is inside the finally clause of a try...finally @@ -1361,14 +1361,14 @@ def visit_return(self, node): self._check_not_in_finally(node, "return", (nodes.FunctionDef,)) @utils.check_messages("unreachable") - def visit_continue(self, node): + def visit_continue(self, node: nodes.Continue) -> None: """check is the node has a right sibling (if so, that's some unreachable code) """ self._check_unreachable(node) @utils.check_messages("unreachable", "lost-exception") - def visit_break(self, node): + def visit_break(self, node: nodes.Break) -> None: """1 - check is the node has a right sibling (if so, that's some unreachable code) 2 - check is the node is inside the finally clause of a try...finally @@ -1380,7 +1380,7 @@ def visit_break(self, node): self._check_not_in_finally(node, "break", (nodes.For, nodes.While)) @utils.check_messages("unreachable") - def visit_raise(self, node): + def visit_raise(self, node: nodes.Raise) -> None: """check if the node has a right sibling (if so, that's some unreachable code) """ @@ -1410,7 +1410,7 @@ def _check_misplaced_format_function(self, call_node): @utils.check_messages( "eval-used", "exec-used", "bad-reversed-sequence", "misplaced-format-function" ) - def visit_call(self, node): + def visit_call(self, node: nodes.Call) -> None: """visit a Call node -> check if this is not a disallowed builtin call and check for * or ** use """ @@ -1428,7 +1428,7 @@ def visit_call(self, node): self.add_message("eval-used", node=node) @utils.check_messages("assert-on-tuple", "assert-on-string-literal") - def visit_assert(self, node): + def visit_assert(self, node: nodes.Assert) -> None: """check whether assert is used on a tuple or string literal.""" if ( node.fail is None @@ -1445,7 +1445,7 @@ def visit_assert(self, node): self.add_message("assert-on-string-literal", node=node, args=(when,)) @utils.check_messages("duplicate-key") - def visit_dict(self, node): + def visit_dict(self, node: nodes.Dict) -> None: """check duplicate key in dictionary""" keys = set() for k, _ in node.items: @@ -1455,11 +1455,11 @@ def visit_dict(self, node): self.add_message("duplicate-key", node=node, args=key) keys.add(key) - def visit_tryfinally(self, node): + def visit_tryfinally(self, node: nodes.TryFinally) -> None: """update try...finally flag""" self._tryfinallys.append(node) - def leave_tryfinally(self, node): # pylint: disable=unused-argument + def leave_tryfinally(self, _: nodes.TryFinally) -> None: """update try...finally flag""" self._tryfinallys.pop() @@ -1554,7 +1554,7 @@ def _check_reversed(self, node): self.add_message("bad-reversed-sequence", node=node) @utils.check_messages("confusing-with-statement") - def visit_with(self, node): + def visit_with(self, node: nodes.With) -> None: # a "with" statement with multiple managers corresponds # to one AST "With" node with multiple items pairs = node.items @@ -1636,12 +1636,12 @@ def _check_redeclared_assign_name(self, targets): ) @utils.check_messages("self-assigning-variable", "redeclared-assigned-name") - def visit_assign(self, node): + def visit_assign(self, node: nodes.Assign) -> None: self._check_self_assigning_variable(node) self._check_redeclared_assign_name(node.targets) @utils.check_messages("redeclared-assigned-name") - def visit_for(self, node): + def visit_for(self, node: nodes.For) -> None: self._check_redeclared_assign_name([node.target]) @@ -1892,11 +1892,11 @@ def _create_naming_rules(self): return regexps, hints @utils.check_messages("disallowed-name", "invalid-name", "non-ascii-name") - def visit_module(self, node): + def visit_module(self, node: nodes.Module) -> None: self._check_name("module", node.name.split(".")[-1], node) self._bad_names = {} - def leave_module(self, node): # pylint: disable=unused-argument + def leave_module(self, _: nodes.Module) -> None: for all_groups in self._bad_names.values(): if len(all_groups) < 2: continue @@ -1915,12 +1915,12 @@ def leave_module(self, node): # pylint: disable=unused-argument else: warnings = groups[min_warnings][0] for args in warnings: - self._raise_name_warning(*args, prevalent_group=prevalent_group) + self._raise_name_warning(prevalent_group, *args) @utils.check_messages( "disallowed-name", "invalid-name", "assign-to-new-keyword", "non-ascii-name" ) - def visit_classdef(self, node): + def visit_classdef(self, node: nodes.ClassDef) -> None: self._check_assign_to_new_keyword_violation(node.name, node) self._check_name("class", node.name, node) for attr, anodes in node.instance_attrs.items(): @@ -1930,7 +1930,7 @@ def visit_classdef(self, node): @utils.check_messages( "disallowed-name", "invalid-name", "assign-to-new-keyword", "non-ascii-name" ) - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: # Do not emit any warnings if the method is just an implementation # of a base class method. self._check_assign_to_new_keyword_violation(node.name, node) @@ -1958,14 +1958,14 @@ def visit_functiondef(self, node): visit_asyncfunctiondef = visit_functiondef @utils.check_messages("disallowed-name", "invalid-name", "non-ascii-name") - def visit_global(self, node): + def visit_global(self, node: nodes.Global) -> None: for name in node.names: self._check_name("const", name, node) @utils.check_messages( "disallowed-name", "invalid-name", "assign-to-new-keyword", "non-ascii-name" ) - def visit_assignname(self, node): + def visit_assignname(self, node: nodes.AssignName) -> None: """check module level assigned names""" self._check_assign_to_new_keyword_violation(node.name, node) frame = node.frame() @@ -2017,12 +2017,12 @@ def _find_name_group(self, node_type): def _raise_name_warning( self, + prevalent_group: Optional[str], node: nodes.NodeNG, node_type: str, name: str, confidence, warning: str = "invalid-name", - prevalent_group: Optional[str] = None, ) -> None: type_label = HUMAN_READABLE_TYPES[node_type] hint = self._name_hints[node_type] @@ -2057,7 +2057,7 @@ def _check_name(self, node_type, name, node, confidence=interfaces.HIGH): non_ascii_match = self._non_ascii_rgx_compiled.match(name) if non_ascii_match is not None: self._raise_name_warning( - node, node_type, name, confidence, warning="non-ascii-name" + None, node, node_type, name, confidence, warning="non-ascii-name" ) def _should_exempt_from_invalid_name(node): @@ -2083,7 +2083,7 @@ def _should_exempt_from_invalid_name(node): warnings.append((node, node_type, name, confidence)) if match is None and not _should_exempt_from_invalid_name(node): - self._raise_name_warning(node, node_type, name, confidence) + self._raise_name_warning(None, node, node_type, name, confidence) def _check_assign_to_new_keyword_violation(self, name, node): keyword_first_version = self._name_became_keyword_in_version( @@ -2172,16 +2172,16 @@ def open(self): ) @utils.check_messages("missing-docstring", "empty-docstring") - def visit_module(self, node): + def visit_module(self, node: nodes.Module) -> None: self._check_docstring("module", node) @utils.check_messages("missing-docstring", "empty-docstring") - def visit_classdef(self, node): + def visit_classdef(self, node: nodes.ClassDef) -> None: if self.config.no_docstring_rgx.match(node.name) is None: self._check_docstring("class", node) @utils.check_messages("missing-docstring", "empty-docstring") - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: if self.config.no_docstring_rgx.match(node.name) is None: ftype = "method" if node.is_method() else "function" if ( @@ -2276,7 +2276,7 @@ class PassChecker(_BasicChecker): } @utils.check_messages("unnecessary-pass") - def visit_pass(self, node): + def visit_pass(self, node: nodes.Pass) -> None: if len(node.parent.child_sequence(node)) > 1 or ( isinstance(node.parent, (nodes.ClassDef, nodes.FunctionDef)) and (node.parent.doc is not None) @@ -2486,10 +2486,9 @@ def _check_misplaced_constant(self, node, left, right, operator): suggestion = f"{right.as_string()} {operator} {left.value!r}" self.add_message("misplaced-comparison-constant", node=node, args=(suggestion,)) - def _check_logical_tautology(self, node): + def _check_logical_tautology(self, node: nodes.Compare): """Check if identifier is compared against itself. :param node: Compare node - :type node: astroid.node_classes.Compare :Example: val = 786 if val == val: # [comparison-with-itself] @@ -2540,7 +2539,7 @@ def _check_callable_comparison(self, node): "comparison-with-itself", "comparison-with-callable", ) - def visit_compare(self, node): + def visit_compare(self, node: nodes.Compare) -> None: self._check_callable_comparison(node) self._check_logical_tautology(node) self._check_unidiomatic_typecheck(node) diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 0b37a9f532..59e4742204 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -845,7 +845,7 @@ def _ignore_mixin(self): "inconsistent-mro", "duplicate-bases", ) - def visit_classdef(self, node): + def visit_classdef(self, node: nodes.ClassDef) -> None: """init visit variable _accessed""" self._check_bases_classes(node) # if not an exception or a metaclass @@ -1092,7 +1092,7 @@ def _check_attribute_defined_outside_init(self, cnode: nodes.ClassDef) -> None: "attribute-defined-outside-init", args=attr, node=node ) - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: """check method arguments, overriding""" # ignore actual functions if not node.is_method(): @@ -1400,7 +1400,7 @@ def _check_slots_elt(self, elt, node): "class-variable-slots-conflict", args=(inferred.value,), node=elt ) - def leave_functiondef(self, node): + def leave_functiondef(self, node: nodes.FunctionDef) -> None: """on method node, check if this method couldn't be a function ignore class, static and abstract methods, initializer, @@ -1427,7 +1427,7 @@ def leave_functiondef(self, node): ): self.add_message("no-self-use", node=node) - def visit_attribute(self, node): + def visit_attribute(self, node: nodes.Attribute) -> None: """check if the getattr is an access to a class member if so, register it. Also check for access to protected class member from outside its class (but ignore __special__ @@ -1514,7 +1514,7 @@ def _check_in_slots(self, node): @check_messages( "protected-access", "no-classmethod-decorator", "no-staticmethod-decorator" ) - def visit_assign(self, assign_node): + def visit_assign(self, assign_node: nodes.Assign) -> None: self._check_classmethod_declaration(assign_node) node = assign_node.targets[0] if not isinstance(node, nodes.AssignAttr): @@ -1701,7 +1701,7 @@ def _is_class_attribute(name, klass): except astroid.NotFoundError: return False - def visit_name(self, node): + def visit_name(self, node: nodes.Name) -> None: """check if the name handle an access to a class member if so, register it """ @@ -2154,7 +2154,7 @@ def __init__(self, linter=None): "invalid-getnewargs-returned", "invalid-getnewargs-ex-returned", ) - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: if not node.is_method(): return diff --git a/pylint/checkers/deprecated.py b/pylint/checkers/deprecated.py index 381d5dac67..ac1cd6425d 100644 --- a/pylint/checkers/deprecated.py +++ b/pylint/checkers/deprecated.py @@ -9,6 +9,7 @@ from astroid import nodes from pylint.checkers import utils +from pylint.checkers.base_checker import BaseChecker from pylint.checkers.utils import get_import_name, infer_all, safe_infer ACCEPTABLE_NODES = ( @@ -19,7 +20,7 @@ ) -class DeprecatedMixin: +class DeprecatedMixin(BaseChecker): """A mixin implementing logic for checking deprecated symbols. A class implementing mixin must define "deprecated-method" Message. """ @@ -68,7 +69,7 @@ def visit_call(self, node: nodes.Call) -> None: "deprecated-module", "deprecated-class", ) - def visit_import(self, node): + def visit_import(self, node: nodes.Import) -> None: """triggered when an import statement is seen""" for name in (name for name, _ in node.names): self.check_deprecated_module(node, name) @@ -87,7 +88,7 @@ def deprecated_decorators(self) -> Iterable: return () @utils.check_messages("deprecated-decorator") - def visit_decorators(self, node): + def visit_decorators(self, node: nodes.Decorators) -> None: """Triggered when a decorator statement is seen""" children = list(node.get_children()) if not children: @@ -104,7 +105,7 @@ def visit_decorators(self, node): "deprecated-module", "deprecated-class", ) - def visit_importfrom(self, node): + def visit_importfrom(self, node: nodes.ImportFrom) -> None: """triggered when a from statement is seen""" basename = node.modname basename = get_import_name(node, basename) diff --git a/pylint/checkers/design_analysis.py b/pylint/checkers/design_analysis.py index dbc132be4f..9777b568e6 100644 --- a/pylint/checkers/design_analysis.py +++ b/pylint/checkers/design_analysis.py @@ -264,7 +264,7 @@ def _get_parents( if parent.qname() in ignored_parents: continue parents.add(parent) - to_explore.extend(parent.ancestors(recurs=False)) # type: ignore + to_explore.extend(parent.ancestors(recurs=False)) return parents @@ -417,7 +417,7 @@ def _ignored_argument_names(self): "too-few-public-methods", "too-many-public-methods", ) - def visit_classdef(self, node: nodes.ClassDef): + def visit_classdef(self, node: nodes.ClassDef) -> None: """check size of inheritance hierarchy and number of instance attributes""" parents = _get_parents( node, STDLIB_CLASSES_IGNORE_ANCESTOR.union(self.config.ignored_parents) @@ -438,7 +438,7 @@ def visit_classdef(self, node: nodes.ClassDef): ) @check_messages("too-few-public-methods", "too-many-public-methods") - def leave_classdef(self, node): + def leave_classdef(self, node: nodes.ClassDef) -> None: """check number of public methods""" my_methods = sum( 1 for method in node.mymethods() if not method.name.startswith("_") @@ -482,7 +482,7 @@ def leave_classdef(self, node): "too-many-statements", "keyword-arg-before-vararg", ) - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: """check function name, docstring, arguments, redefinition, variable names, max locals """ @@ -525,7 +525,7 @@ def visit_functiondef(self, node): "too-many-locals", "too-many-statements", ) - def leave_functiondef(self, node): + def leave_functiondef(self, node: nodes.FunctionDef) -> None: """most of the work is done here on close: checks for max returns, branch, return in __init__ """ @@ -554,20 +554,20 @@ def leave_functiondef(self, node): leave_asyncfunctiondef = leave_functiondef - def visit_return(self, _): + def visit_return(self, _: nodes.Return) -> None: """count number of returns""" if not self._returns: return # return outside function, reported by the base checker self._returns[-1] += 1 - def visit_default(self, node): + def visit_default(self, node: nodes.NodeNG) -> None: """default visit method -> increments the statements counter if necessary """ if node.is_statement: self._inc_all_stmts(1) - def visit_tryexcept(self, node): + def visit_tryexcept(self, node: nodes.TryExcept) -> None: """increments the branches counter""" branches = len(node.handlers) if node.orelse: @@ -575,13 +575,13 @@ def visit_tryexcept(self, node): self._inc_branch(node, branches) self._inc_all_stmts(branches) - def visit_tryfinally(self, node): + def visit_tryfinally(self, node: nodes.TryFinally) -> None: """increments the branches counter""" self._inc_branch(node, 2) self._inc_all_stmts(2) @check_messages("too-many-boolean-expressions") - def visit_if(self, node): + def visit_if(self, node: nodes.If) -> None: """increments the branches counter and checks boolean expressions""" self._check_boolean_expressions(node) branches = 1 @@ -609,7 +609,7 @@ def _check_boolean_expressions(self, node): args=(nb_bool_expr, self.config.max_bool_expr), ) - def visit_while(self, node): + def visit_while(self, node: nodes.While) -> None: """increments the branches counter""" branches = 1 if node.orelse: diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index e3443c85f2..7c09be3f77 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -34,10 +34,10 @@ """Checks for various exception related errors.""" import builtins import inspect -import typing +from typing import Any, List, Optional import astroid -from astroid import nodes +from astroid import nodes, objects from pylint import checkers, interfaces from pylint.checkers import utils @@ -70,7 +70,7 @@ def _annotated_unpack_infer(stmt, context=None): yield stmt, inferred -def _is_raising(body: typing.List) -> bool: +def _is_raising(body: List) -> bool: """Return true if the given statement node raise an exception""" for node in body: if isinstance(node, nodes.Raise): @@ -201,26 +201,26 @@ def visit(self, node): else: self.visit_default(node) - def visit_default(self, node): # pylint: disable=unused-argument + def visit_default(self, _: nodes.NodeNG) -> None: """Default implementation for all the nodes.""" class ExceptionRaiseRefVisitor(BaseVisitor): """Visit references (anything that is not an AST leaf).""" - def visit_name(self, name): - if name.name == "NotImplemented": + def visit_name(self, node: nodes.Name) -> None: + if node.name == "NotImplemented": self._checker.add_message("notimplemented-raised", node=self._node) - def visit_call(self, call): - if isinstance(call.func, nodes.Name): - self.visit_name(call.func) + def visit_call(self, node: nodes.Call) -> None: + if isinstance(node.func, nodes.Name): + self.visit_name(node.func) if ( - len(call.args) > 1 - and isinstance(call.args[0], nodes.Const) - and isinstance(call.args[0].value, str) + len(node.args) > 1 + and isinstance(node.args[0], nodes.Const) + and isinstance(node.args[0].value, str) ): - msg = call.args[0].value + msg = node.args[0].value if "%" in msg or ("{" in msg and "}" in msg): self._checker.add_message("raising-format-tuple", node=self._node) @@ -228,12 +228,12 @@ def visit_call(self, call): class ExceptionRaiseLeafVisitor(BaseVisitor): """Visitor for handling leaf kinds of a raise value.""" - def visit_const(self, const): + def visit_const(self, node: nodes.Const) -> None: self._checker.add_message( - "raising-bad-type", node=self._node, args=const.value.__class__.__name__ + "raising-bad-type", node=self._node, args=node.value.__class__.__name__ ) - def visit_instance(self, instance): + def visit_instance(self, instance: objects.ExceptionInstance) -> None: # pylint: disable=protected-access cls = instance._proxied self.visit_classdef(cls) @@ -241,15 +241,15 @@ def visit_instance(self, instance): # Exception instances have a particular class type visit_exceptioninstance = visit_instance - def visit_classdef(self, cls): - if not utils.inherit_from_std_ex(cls) and utils.has_known_bases(cls): - if cls.newstyle: + def visit_classdef(self, node: nodes.ClassDef) -> None: + if not utils.inherit_from_std_ex(node) and utils.has_known_bases(node): + if node.newstyle: self._checker.add_message("raising-non-exception", node=self._node) - def visit_tuple(self, _): + def visit_tuple(self, _: nodes.Tuple) -> None: self._checker.add_message("raising-bad-type", node=self._node, args="tuple") - def visit_default(self, node): + def visit_default(self, node: nodes.NodeNG) -> None: name = getattr(node, "name", node.__class__.__name__) self._checker.add_message("raising-bad-type", node=self._node, args=name) @@ -289,7 +289,7 @@ def open(self): "raising-format-tuple", "raise-missing-from", ) - def visit_raise(self, node): + def visit_raise(self, node: nodes.Raise) -> None: if node.exc is None: self._check_misplaced_bare_raise(node) return @@ -428,8 +428,8 @@ def _check_catching_non_exception(self, handler, exc, part): def _check_try_except_raise(self, node): def gather_exceptions_from_handler( handler, - ) -> typing.Optional[typing.List[nodes.NodeNG]]: - exceptions: typing.List[nodes.NodeNG] = [] + ) -> Optional[List[nodes.NodeNG]]: + exceptions: List[nodes.NodeNG] = [] if handler.type: exceptions_in_handler = utils.safe_infer(handler.type) if isinstance(exceptions_in_handler, nodes.Tuple): @@ -483,14 +483,14 @@ def gather_exceptions_from_handler( self.add_message("try-except-raise", node=handler_having_bare_raise) @utils.check_messages("wrong-exception-operation") - def visit_binop(self, node): + def visit_binop(self, node: nodes.BinOp) -> None: if isinstance(node.parent, nodes.ExceptHandler): # except (V | A) suggestion = f"Did you mean '({node.left.as_string()}, {node.right.as_string()})' instead?" self.add_message("wrong-exception-operation", node=node, args=(suggestion,)) @utils.check_messages("wrong-exception-operation") - def visit_compare(self, node): + def visit_compare(self, node: nodes.Compare) -> None: if isinstance(node.parent, nodes.ExceptHandler): # except (V < A) suggestion = f"Did you mean '({node.left.as_string()}, {', '.join(operand.as_string() for _, operand in node.ops)})' instead?" @@ -505,10 +505,10 @@ def visit_compare(self, node): "catching-non-exception", "duplicate-except", ) - def visit_tryexcept(self, node): + def visit_tryexcept(self, node: nodes.TryExcept) -> None: """check for empty except""" self._check_try_except_raise(node) - exceptions_classes = [] + exceptions_classes: List[Any] = [] nb_handlers = len(node.handlers) for index, handler in enumerate(node.handlers): if handler.type is None: diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index 1306070ba7..ffd1b21698 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -582,7 +582,7 @@ def _check_line_ending(self, line_ending, line_num): ) @check_messages("multiple-statements") - def visit_default(self, node): + def visit_default(self, node: nodes.NodeNG) -> None: """check the node line number and check it if not yet done""" if not node.is_statement: return diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index d4d813812b..7e0cda26dc 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -50,7 +50,7 @@ import os import sys from distutils import sysconfig -from typing import Dict, List, Set, Union +from typing import Any, Dict, List, Set, Tuple, Union import astroid from astroid import nodes @@ -66,6 +66,7 @@ from pylint.exceptions import EmptyReportError from pylint.graph import DotBackend, get_cycles from pylint.interfaces import IAstroidChecker +from pylint.lint import PyLinter from pylint.reporters.ureports.nodes import Paragraph, VerbatimText, VNode from pylint.utils import IsortDriver, get_global_option @@ -418,14 +419,18 @@ class ImportsChecker(DeprecatedMixin, BaseChecker): ), ) - def __init__(self, linter=None): + def __init__( + self, linter: PyLinter = None + ): # pylint: disable=super-init-not-called # See https://github.com/PyCQA/pylint/issues/4941 BaseChecker.__init__(self, linter) - self.stats = None - self.import_graph = None - self._imports_stack = [] + self.stats: Dict[Any, Any] = {} + self.import_graph: collections.defaultdict = collections.defaultdict(set) + self._imports_stack: List[Tuple[Any, Any]] = [] self._first_non_import_node = None - self._module_pkg = {} # mapping of modules to the pkg they belong in - self._allow_any_import_level = set() + self._module_pkg: Dict[ + Any, Any + ] = {} # mapping of modules to the pkg they belong in + self._allow_any_import_level: Set[Any] = set() self.reports = ( ("RP0401", "External dependencies", self._report_external_dependencies), ("RP0402", "Modules dependencies graph", self._report_dependencies_graph), @@ -494,7 +499,7 @@ def deprecated_modules(self): return self.config.deprecated_modules @check_messages(*MSGS) - def visit_import(self, node): + def visit_import(self, node: nodes.Import) -> None: """triggered when an import statement is seen""" self._check_reimport(node) self._check_import_as_rename(node) @@ -520,7 +525,7 @@ def visit_import(self, node): self._add_imported_module(node, imported_module.name) @check_messages(*MSGS) - def visit_importfrom(self, node): + def visit_importfrom(self, node: nodes.ImportFrom) -> None: """triggered when a from statement is seen""" basename = node.modname imported_module = self._get_imported_module(node, basename) @@ -618,7 +623,7 @@ def compute_first_non_import_node(self, node): visit_ifexp ) = visit_comprehension = visit_expr = visit_if = compute_first_non_import_node - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: if not self.linter.is_message_enabled("wrong-import-position", node.fromlineno): return # If it is the first non import instruction of the module, record it. diff --git a/pylint/checkers/logging.py b/pylint/checkers/logging.py index c54ef03406..3cbf209562 100644 --- a/pylint/checkers/logging.py +++ b/pylint/checkers/logging.py @@ -24,6 +24,7 @@ """checker for use of Python logging """ import string +from typing import Set import astroid from astroid import nodes @@ -160,12 +161,12 @@ class LoggingChecker(checkers.BaseChecker): ), ) - def visit_module(self, node): # pylint: disable=unused-argument + def visit_module(self, _: nodes.Module) -> None: """Clears any state left in this checker from last module checked.""" # The code being checked can just as easily "import logging as foo", # so it is necessary to process the imports and store in this field # what name the logging module is actually given. - self._logging_names = set() + self._logging_names: Set[str] = set() logging_mods = self.config.logging_modules self._format_style = self.config.logging_format_style @@ -177,7 +178,7 @@ def visit_module(self, node): # pylint: disable=unused-argument if len(parts) > 1: self._from_imports[parts[0]] = parts[1] - def visit_importfrom(self, node): + def visit_importfrom(self, node: nodes.ImportFrom) -> None: """Checks to see if a module uses a non-Python logging module.""" try: logging_name = self._from_imports[node.modname] @@ -187,14 +188,14 @@ def visit_importfrom(self, node): except KeyError: pass - def visit_import(self, node): + def visit_import(self, node: nodes.Import) -> None: """Checks to see if this module uses Python's built-in logging.""" for module, as_name in node.names: if module in self._logging_modules: self._logging_names.add(as_name or module) @check_messages(*MSGS) - def visit_call(self, node): + def visit_call(self, node: nodes.Call) -> None: """Checks calls to logging methods.""" def is_logging_name(): @@ -294,18 +295,15 @@ def _is_operand_literal_str(operand): """ return isinstance(operand, nodes.Const) and operand.name == "str" - def _check_call_func(self, node): - """Checks that function call is not format_string.format(). - - Args: - node (astroid.node_classes.Call): - Call AST node to be checked. - """ + def _check_call_func(self, node: nodes.Call): + """Checks that function call is not format_string.format().""" func = utils.safe_infer(node.func) types = ("str", "unicode") methods = ("format",) - if is_method_call(func, types, methods) and not is_complex_format_str( - func.bound + if ( + isinstance(func, astroid.BoundMethod) + and is_method_call(func, types, methods) + and not is_complex_format_str(func.bound) ): self.add_message( "logging-format-interpolation", diff --git a/pylint/checkers/newstyle.py b/pylint/checkers/newstyle.py index 8c5ae5e831..0c63db232e 100644 --- a/pylint/checkers/newstyle.py +++ b/pylint/checkers/newstyle.py @@ -56,7 +56,7 @@ class NewStyleConflictChecker(BaseChecker): options = () @check_messages("bad-super-call") - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: """check use of super""" # ignore actual functions or method within a new style class if not node.is_method(): diff --git a/pylint/checkers/refactoring/len_checker.py b/pylint/checkers/refactoring/len_checker.py index 6b36681a25..4e4eb2b468 100644 --- a/pylint/checkers/refactoring/len_checker.py +++ b/pylint/checkers/refactoring/len_checker.py @@ -52,7 +52,7 @@ class LenChecker(checkers.BaseChecker): options = () @utils.check_messages("len-as-condition") - def visit_call(self, node): + def visit_call(self, node: nodes.Call) -> None: # a len(S) call is used inside a test condition # could be if, while, assert or if expression statement # e.g. `if len(S):` @@ -102,7 +102,7 @@ def instance_has_bool(class_def: nodes.ClassDef) -> bool: return False @utils.check_messages("len-as-condition") - def visit_unaryop(self, node): + def visit_unaryop(self, node: nodes.UnaryOp) -> None: """`not len(S)` must become `not S` regardless if the parent block is a test condition or something else (boolean expression) e.g. `if not len(S):`""" diff --git a/pylint/checkers/refactoring/not_checker.py b/pylint/checkers/refactoring/not_checker.py index ebe2c0ebe8..f8c670e96f 100644 --- a/pylint/checkers/refactoring/not_checker.py +++ b/pylint/checkers/refactoring/not_checker.py @@ -42,7 +42,7 @@ class NotChecker(checkers.BaseChecker): skipped_classnames = [f"builtins.{qname}" for qname in ("set", "frozenset")] @utils.check_messages("unneeded-not") - def visit_unaryop(self, node): + def visit_unaryop(self, node: nodes.UnaryOp) -> None: if node.op != "not": return operand = node.operand diff --git a/pylint/checkers/refactoring/refactoring_checker.py b/pylint/checkers/refactoring/refactoring_checker.py index 9ce42e3957..7b977d39bb 100644 --- a/pylint/checkers/refactoring/refactoring_checker.py +++ b/pylint/checkers/refactoring/refactoring_checker.py @@ -595,7 +595,7 @@ def process_tokens(self, tokens): self.add_message("trailing-comma-tuple", line=token.start[0]) @utils.check_messages("consider-using-with") - def leave_module(self, _): + def leave_module(self, _: nodes.Module) -> None: # check for context managers that have been created but not used self._emit_consider_using_with_if_needed( self._consider_using_with_stack.module_scope @@ -603,7 +603,7 @@ def leave_module(self, _): self._init() @utils.check_messages("too-many-nested-blocks") - def visit_tryexcept(self, node): + def visit_tryexcept(self, node: nodes.TryExcept) -> None: self._check_nested_blocks(node) visit_tryfinally = visit_tryexcept @@ -635,7 +635,7 @@ def _check_redefined_argument_from_local(self, name_node): "too-many-nested-blocks", "unnecessary-dict-index-lookup", ) - def visit_for(self, node): + def visit_for(self, node: nodes.For) -> None: self._check_nested_blocks(node) self._check_unnecessary_dict_index_lookup(node) @@ -643,12 +643,12 @@ def visit_for(self, node): self._check_redefined_argument_from_local(name) @utils.check_messages("redefined-argument-from-local") - def visit_excepthandler(self, node): + def visit_excepthandler(self, node: nodes.ExceptHandler) -> None: if node.name and isinstance(node.name, nodes.AssignName): self._check_redefined_argument_from_local(node.name) @utils.check_messages("redefined-argument-from-local") - def visit_with(self, node): + def visit_with(self, node: nodes.With) -> None: for var, names in node.items: if isinstance(var, nodes.Name): for stack in self._consider_using_with_stack: @@ -762,7 +762,7 @@ def _check_consider_get(self, node): "no-else-continue", "consider-using-get", ) - def visit_if(self, node): + def visit_if(self, node: nodes.If) -> None: self._check_simplifiable_if(node) self._check_nested_blocks(node) self._check_superfluous_else_return(node) @@ -852,7 +852,7 @@ def _check_consider_using_min_max_builtin(self, node: nodes.If): ) @utils.check_messages("simplifiable-if-expression") - def visit_ifexp(self, node): + def visit_ifexp(self, node: nodes.IfExp) -> None: self._check_simplifiable_ifexp(node) def _check_simplifiable_ifexp(self, node): @@ -911,7 +911,7 @@ def leave_classdef(self, _: nodes.ClassDef) -> None: self._consider_using_with_stack.class_scope.clear() @utils.check_messages("stop-iteration-return") - def visit_raise(self, node): + def visit_raise(self, node: nodes.Raise) -> None: self._check_stop_iteration_inside_generator(node) def _check_stop_iteration_inside_generator(self, node): @@ -990,7 +990,7 @@ def _check_consider_using_generator(self, node): "use-list-literal", "use-dict-literal", ) - def visit_call(self, node): + def visit_call(self, node: nodes.Call) -> None: self._check_raising_stopiteration_in_generator_next_call(node) self._check_consider_using_comprehension_constructor(node) self._check_quit_exit_call(node) @@ -1338,7 +1338,7 @@ def _check_simplifiable_condition(self, node): "simplifiable-condition", "condition-evals-to-constant", ) - def visit_boolop(self, node): + def visit_boolop(self, node: nodes.BoolOp) -> None: self._check_consider_merging_isinstance(node) self._check_consider_using_in(node) self._check_chained_comparison(node) @@ -1527,7 +1527,7 @@ def _check_consider_using_join(self, aug_assign): self.add_message("consider-using-join", node=aug_assign) @utils.check_messages("consider-using-join") - def visit_augassign(self, node): + def visit_augassign(self, node: nodes.AugAssign) -> None: self._check_consider_using_join(node) @utils.check_messages("unnecessary-comprehension", "unnecessary-dict-index-lookup") @@ -1638,7 +1638,7 @@ def _and_or_ternary_arguments(node): condition, true_value = node.values[0].values return condition, true_value, false_value - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: self._return_nodes[node.name] = list( node.nodes_of_class(nodes.Return, skip_klass=nodes.FunctionDef) ) diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py index c3c4ed0804..5a294e9958 100644 --- a/pylint/checkers/similar.py +++ b/pylint/checkers/similar.py @@ -736,7 +736,7 @@ class SimilarChecker(BaseChecker, Similar, MapReduceMixin): # for available dict keys/values see the optik parser 'add_option' method options = ( ( - "min-similarity-lines", # type: ignore + "min-similarity-lines", { "default": DEFAULT_MIN_SIMILARITY_LINE, "type": "int", @@ -782,7 +782,7 @@ class SimilarChecker(BaseChecker, Similar, MapReduceMixin): ), ) # reports - reports = (("RP0801", "Duplication", report_similarities),) # type: ignore + reports = (("RP0801", "Duplication", report_similarities),) def __init__(self, linter=None) -> None: BaseChecker.__init__(self, linter) diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py index d5ab5c1ca4..3b2973ffa9 100644 --- a/pylint/checkers/spelling.py +++ b/pylint/checkers/spelling.py @@ -33,6 +33,8 @@ import tokenize from typing import Pattern +from astroid import nodes + from pylint.checkers import BaseTokenChecker from pylint.checkers.utils import check_messages from pylint.interfaces import IAstroidChecker, ITokenChecker @@ -437,19 +439,19 @@ def process_tokens(self, tokens): self._check_spelling("wrong-spelling-in-comment", token, start_row) @check_messages("wrong-spelling-in-docstring") - def visit_module(self, node): + def visit_module(self, node: nodes.Module) -> None: if not self.initialized: return self._check_docstring(node) @check_messages("wrong-spelling-in-docstring") - def visit_classdef(self, node): + def visit_classdef(self, node: nodes.ClassDef) -> None: if not self.initialized: return self._check_docstring(node) @check_messages("wrong-spelling-in-docstring") - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: if not self.initialized: return self._check_docstring(node) diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index f11cbac988..ee5311b438 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -39,12 +39,14 @@ import sys from collections.abc import Iterable +from typing import Any, Dict, Set import astroid from astroid import nodes from pylint.checkers import BaseChecker, DeprecatedMixin, utils from pylint.interfaces import IAstroidChecker +from pylint.lint import PyLinter OPEN_FILES_MODE = ("open", "file") OPEN_FILES_ENCODING = ("open", "read_text", "write_text") @@ -117,7 +119,7 @@ } -DEPRECATED_METHODS = { +DEPRECATED_METHODS: Dict = { 0: { "cgi.parse_qs", "cgi.parse_qsl", @@ -444,9 +446,11 @@ class StdlibChecker(DeprecatedMixin, BaseChecker): ), } - def __init__(self, linter=None): + def __init__( + self, linter: PyLinter = None + ): # pylint: disable=super-init-not-called # See https://github.com/PyCQA/pylint/issues/4941 BaseChecker.__init__(self, linter) - self._deprecated_methods = set() + self._deprecated_methods: Set[Any] = set() self._deprecated_methods.update(DEPRECATED_METHODS[0]) for since_vers, func_list in DEPRECATED_METHODS[sys.version_info[0]].items(): if since_vers <= sys.version_info: @@ -548,20 +552,20 @@ def visit_call(self, node: nodes.Call) -> None: self.check_deprecated_method(node, inferred) @utils.check_messages("boolean-datetime") - def visit_unaryop(self, node): + def visit_unaryop(self, node: nodes.UnaryOp) -> None: if node.op == "not": self._check_datetime(node.operand) @utils.check_messages("boolean-datetime") - def visit_if(self, node): + def visit_if(self, node: nodes.If) -> None: self._check_datetime(node.test) @utils.check_messages("boolean-datetime") - def visit_ifexp(self, node): + def visit_ifexp(self, node: nodes.IfExp) -> None: self._check_datetime(node.test) @utils.check_messages("boolean-datetime") - def visit_boolop(self, node): + def visit_boolop(self, node: nodes.BoolOp) -> None: for value in node.values: self._check_datetime(value) diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py index b45b7bfdc3..efdfdd9fef 100644 --- a/pylint/checkers/strings.py +++ b/pylint/checkers/strings.py @@ -278,7 +278,7 @@ class StringFormatChecker(BaseChecker): "bad-string-format-type", "format-string-without-interpolation", ) - def visit_binop(self, node): + def visit_binop(self, node: nodes.BinOp) -> None: if node.op != "%": return left = node.left @@ -353,7 +353,8 @@ def visit_binop(self, node): arg_type = utils.safe_infer(arg) if ( format_type is not None - and arg_type not in (None, astroid.Uninferable) + and arg_type + and arg_type != astroid.Uninferable and not arg_matches_format_type(arg_type, format_type) ): self.add_message( @@ -373,11 +374,11 @@ def visit_binop(self, node): # Check that the number of arguments passed to the RHS of # the % operator matches the number required by the format # string. - args_elts = () + args_elts = [] if isinstance(args, nodes.Tuple): rhs_tuple = utils.safe_infer(args) num_args = None - if hasattr(rhs_tuple, "elts"): + if isinstance(rhs_tuple, nodes.BaseContainer): args_elts = rhs_tuple.elts num_args = len(args_elts) elif isinstance(args, (OTHER_NODES, (nodes.Dict, nodes.DictComp))): @@ -399,10 +400,7 @@ def visit_binop(self, node): arg_type = utils.safe_infer(arg) if ( arg_type - not in ( - None, - astroid.Uninferable, - ) + and arg_type != astroid.Uninferable and not arg_matches_format_type(arg_type, format_type) ): self.add_message( @@ -412,7 +410,7 @@ def visit_binop(self, node): ) @check_messages("f-string-without-interpolation") - def visit_joinedstr(self, node): + def visit_joinedstr(self, node: nodes.JoinedStr) -> None: if isinstance(node.parent, nodes.FormattedValue): return for value in node.values: @@ -421,7 +419,7 @@ def visit_joinedstr(self, node): self.add_message("f-string-without-interpolation", node=node) @check_messages(*MSGS) - def visit_call(self, node): + def visit_call(self, node: nodes.Call) -> None: func = utils.safe_infer(node.func) if ( isinstance(func, astroid.BoundMethod) @@ -750,18 +748,18 @@ def process_tokens(self, tokens): self.check_for_consistent_string_delimiters(tokens) @check_messages("implicit-str-concat") - def visit_list(self, node): + def visit_list(self, node: nodes.List) -> None: self.check_for_concatenated_strings(node.elts, "list") @check_messages("implicit-str-concat") - def visit_set(self, node): + def visit_set(self, node: nodes.Set) -> None: self.check_for_concatenated_strings(node.elts, "set") @check_messages("implicit-str-concat") - def visit_tuple(self, node): + def visit_tuple(self, node: nodes.Tuple) -> None: self.check_for_concatenated_strings(node.elts, "tuple") - def visit_assign(self, node): + def visit_assign(self, node: nodes.Assign) -> None: if isinstance(node.value, nodes.Const) and isinstance(node.value.value, str): self.check_for_concatenated_strings([node.value], "assignment") @@ -917,7 +915,7 @@ def process_non_raw_string_token( @check_messages("redundant-u-string-prefix") @check_messages("consider-using-f-string") - def visit_const(self, node: nodes.Const): + def visit_const(self, node: nodes.Const) -> None: if node.pytype() == "builtins.str" and not isinstance( node.parent, nodes.JoinedStr ): diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index a79d2fa6b0..aeab4645ea 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -67,7 +67,7 @@ from collections import deque from collections.abc import Sequence from functools import singledispatch -from typing import Pattern, Tuple +from typing import Any, Callable, Iterator, List, Optional, Pattern, Tuple import astroid from astroid import bases, nodes @@ -915,7 +915,7 @@ def _compiled_generated_members(self) -> Tuple[Pattern, ...]: return tuple(re.compile(exp) for exp in generated_members) @check_messages("keyword-arg-before-vararg") - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: # check for keyword arg before varargs if node.args.vararg and node.args.defaults: self.add_message("keyword-arg-before-vararg", node=node, args=(node.name)) @@ -923,7 +923,7 @@ def visit_functiondef(self, node): visit_asyncfunctiondef = visit_functiondef @check_messages("invalid-metaclass") - def visit_classdef(self, node): + def visit_classdef(self, node: nodes.ClassDef) -> None: def _metaclass_name(metaclass): # pylint: disable=unidiomatic-typecheck if isinstance(metaclass, (nodes.ClassDef, nodes.FunctionDef)): @@ -955,15 +955,15 @@ def _metaclass_name(metaclass): "invalid-metaclass", node=node, args=(_metaclass_name(metaclass),) ) - def visit_assignattr(self, node): + def visit_assignattr(self, node: nodes.AssignAttr) -> None: if isinstance(node.assign_type(), nodes.AugAssign): self.visit_attribute(node) - def visit_delattr(self, node): + def visit_delattr(self, node: nodes.DelAttr) -> None: self.visit_attribute(node) @check_messages("no-member", "c-extension-no-member") - def visit_attribute(self, node): + def visit_attribute(self, node: nodes.Attribute) -> None: """check that the accessed attribute exists to avoid too much false positives for now, we'll consider the code as @@ -1090,7 +1090,7 @@ def _get_nomember_msgid_hint(self, node, owner): "assignment-from-none", "non-str-assignment-to-dunder-name", ) - def visit_assign(self, node): + def visit_assign(self, node: nodes.Assign) -> None: """ Process assignments in the AST. """ @@ -1270,7 +1270,7 @@ def _check_isinstance_args(self, node): # pylint: disable=too-many-branches,too-many-locals @check_messages(*(list(MSGS.keys()))) - def visit_call(self, node): + def visit_call(self, node: nodes.Call) -> None: """check that called functions/methods are inferred to callable objects, and that the arguments passed to the function match the parameters in the inferred function's definition @@ -1359,7 +1359,7 @@ def visit_call(self, node): # Analyze the list of formal parameters. args = list(itertools.chain(called.args.posonlyargs or (), called.args.args)) num_mandatory_parameters = len(args) - len(called.args.defaults) - parameters = [] + parameters: List[List[Any]] = [] parameter_name_to_index = {} for i, arg in enumerate(args): if isinstance(arg, nodes.Tuple): @@ -1553,7 +1553,7 @@ def _check_invalid_sequence_index(self, subscript: nodes.Subscript): return None @check_messages("invalid-sequence-index") - def visit_extslice(self, node): + def visit_extslice(self, node: nodes.ExtSlice) -> None: if not node.parent or not hasattr(node.parent, "value"): return None # Check extended slice objects as if they were used as a sequence @@ -1615,7 +1615,7 @@ def _check_invalid_slice_index(self, node): self.add_message("invalid-slice-index", node=node) @check_messages("not-context-manager") - def visit_with(self, node): + def visit_with(self, node: nodes.With) -> None: for ctx_mgr, _ in node.items: context = astroid.context.InferenceContext() inferred = safe_infer(ctx_mgr, context=context) @@ -1641,7 +1641,9 @@ def visit_with(self, node): # of self explaining tests. # Retrieve node from all previusly visited nodes in the the inference history - context_path_names = filter(None, _unflatten(context.path)) + context_path_names: Iterator[Any] = filter( + None, _unflatten(context.path) + ) inferred_paths = _flatten_container( safe_infer(path) for path in context_path_names ) @@ -1677,7 +1679,7 @@ def visit_with(self, node): ) @check_messages("invalid-unary-operand-type") - def visit_unaryop(self, node): + def visit_unaryop(self, node: nodes.UnaryOp) -> None: """Detect TypeErrors for unary operands.""" for error in node.type_errors(): @@ -1685,7 +1687,7 @@ def visit_unaryop(self, node): self.add_message("invalid-unary-operand-type", args=str(error), node=node) @check_messages("unsupported-binary-operation") - def visit_binop(self, node: nodes.BinOp): + def visit_binop(self, node: nodes.BinOp) -> None: if node.op == "|": self._detect_unsupported_alternative_union_syntax(node) @@ -1740,12 +1742,12 @@ def _check_unsupported_alternative_union_syntax(self, node: nodes.BinOp) -> None break @check_messages("unsupported-binary-operation") - def _visit_binop(self, node): + def _visit_binop(self, node: nodes.BinOp) -> None: """Detect TypeErrors for binary arithmetic operands.""" self._check_binop_errors(node) @check_messages("unsupported-binary-operation") - def _visit_augassign(self, node): + def _visit_augassign(self, node: nodes.AugAssign) -> None: """Detect TypeErrors for augmented binary arithmetic operands.""" self._check_binop_errors(node) @@ -1773,7 +1775,7 @@ def _check_membership_test(self, node): ) @check_messages("unsupported-membership-test") - def visit_compare(self, node): + def visit_compare(self, node: nodes.Compare) -> None: if len(node.ops) != 1: return @@ -1789,17 +1791,17 @@ def visit_compare(self, node): "invalid-sequence-index", "invalid-slice-index", ) - def visit_subscript(self, node): + def visit_subscript(self, node: nodes.Subscript) -> None: self._check_invalid_sequence_index(node) - supported_protocol = None + supported_protocol: Optional[Callable[[Any, Any], bool]] = None if isinstance(node.value, (nodes.ListComp, nodes.DictComp)): return if isinstance(node.value, nodes.Dict): # Assert dict key is hashable inferred = safe_infer(node.slice) - if inferred not in (None, astroid.Uninferable): + if inferred and inferred != astroid.Uninferable: try: hash_fn = next(inferred.igetattr("__hash__")) except astroid.InferenceError: @@ -1838,11 +1840,11 @@ def visit_subscript(self, node): return # It would be better to handle function # decorators, but let's start slow. - if not supported_protocol(inferred, node): + if supported_protocol and not supported_protocol(inferred, node): self.add_message(msg, args=node.value.as_string(), node=node.value) @check_messages("dict-items-missing-iter") - def visit_for(self, node): + def visit_for(self, node: nodes.For) -> None: if not isinstance(node.target, nodes.Tuple): # target is not a tuple return @@ -1939,43 +1941,43 @@ def _check_mapping(self, node): self.add_message("not-a-mapping", args=node.as_string(), node=node) @check_messages("not-an-iterable") - def visit_for(self, node): + def visit_for(self, node: nodes.For) -> None: self._check_iterable(node.iter) @check_messages("not-an-iterable") - def visit_asyncfor(self, node): + def visit_asyncfor(self, node: nodes.AsyncFor) -> None: self._check_iterable(node.iter, check_async=True) @check_messages("not-an-iterable") - def visit_yieldfrom(self, node): + def visit_yieldfrom(self, node: nodes.YieldFrom) -> None: if self._is_asyncio_coroutine(node.value): return self._check_iterable(node.value) @check_messages("not-an-iterable", "not-a-mapping") - def visit_call(self, node): + def visit_call(self, node: nodes.Call) -> None: for stararg in node.starargs: self._check_iterable(stararg.value) for kwarg in node.kwargs: self._check_mapping(kwarg.value) @check_messages("not-an-iterable") - def visit_listcomp(self, node): + def visit_listcomp(self, node: nodes.ListComp) -> None: for gen in node.generators: self._check_iterable(gen.iter, check_async=gen.is_async) @check_messages("not-an-iterable") - def visit_dictcomp(self, node): + def visit_dictcomp(self, node: nodes.DictComp) -> None: for gen in node.generators: self._check_iterable(gen.iter, check_async=gen.is_async) @check_messages("not-an-iterable") - def visit_setcomp(self, node): + def visit_setcomp(self, node: nodes.SetComp) -> None: for gen in node.generators: self._check_iterable(gen.iter, check_async=gen.is_async) @check_messages("not-an-iterable") - def visit_generatorexp(self, node): + def visit_generatorexp(self, node: nodes.GeneratorExp) -> None: for gen in node.generators: self._check_iterable(gen.iter, check_async=gen.is_async) diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 05f69401c9..6c08a48b45 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -60,18 +60,7 @@ import re import string from functools import lru_cache, partial -from typing import ( - Any, - Callable, - Dict, - Iterable, - List, - Match, - Optional, - Set, - Tuple, - Union, -) +from typing import Callable, Dict, Iterable, List, Match, Optional, Set, Tuple, Union import _string import astroid @@ -228,7 +217,7 @@ SPECIAL_METHODS_PARAMS = { name: params for params, methods in _SPECIAL_METHODS_PARAMS.items() - for name in methods # type: ignore + for name in methods } PYMETHODS = set(SPECIAL_METHODS_PARAMS) @@ -748,8 +737,8 @@ def stringify_error(error): 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 + error_type = (error_type,) + expected_errors = {stringify_error(error) for error in error_type} if not handler.type: return False return handler.catch(expected_errors) @@ -1164,11 +1153,11 @@ def supports_getitem(value: nodes.NodeNG, node: nodes.NodeNG) -> bool: return _supports_protocol(value, _supports_getitem_protocol) -def supports_setitem(value: nodes.NodeNG, *_: Any) -> bool: +def supports_setitem(value: nodes.NodeNG, _: nodes.NodeNG) -> bool: return _supports_protocol(value, _supports_setitem_protocol) -def supports_delitem(value: nodes.NodeNG, *_: Any) -> bool: +def supports_delitem(value: nodes.NodeNG, _: nodes.NodeNG) -> bool: return _supports_protocol(value, _supports_delitem_protocol) @@ -1245,7 +1234,7 @@ def is_none(node: nodes.NodeNG) -> bool: ) -def node_type(node: nodes.NodeNG) -> Optional[type]: +def node_type(node: nodes.NodeNG) -> Optional[nodes.NodeNG]: """Return the inferred type for `node` If there is more than one possible type, or if inferred type is Uninferable or None, @@ -1253,7 +1242,7 @@ def node_type(node: nodes.NodeNG) -> Optional[type]: """ # check there is only one possible type for the assign node. Else we # don't handle it for now - types = set() + types: Set[nodes.NodeNG] = set() try: for var_type in node.infer(): if var_type == astroid.Uninferable or is_none(var_type): diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 922cc03704..aa5ac14a1d 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -715,7 +715,7 @@ def __init__(self, linter=None): self._postponed_evaluation_enabled = False @utils.check_messages("redefined-outer-name") - def visit_for(self, node): + def visit_for(self, node: nodes.For) -> None: assigned_to = [a.name for a in node.target.nodes_of_class(nodes.AssignName)] # Only check variables that are used @@ -737,11 +737,11 @@ def visit_for(self, node): self._loop_variables.append((node, assigned_to)) @utils.check_messages("redefined-outer-name") - def leave_for(self, node): + def leave_for(self, node: nodes.For) -> None: self._loop_variables.pop() self._store_type_annotation_names(node) - def visit_module(self, node): + def visit_module(self, node: nodes.Module) -> None: """visit module : update consumption analysis variable checks globals doesn't overrides builtins """ @@ -763,7 +763,7 @@ def visit_module(self, node): "invalid-all-format", "unused-variable", ) - def leave_module(self, node): + def leave_module(self, node: nodes.Module) -> None: """leave module: check globals""" assert len(self._to_consume) == 1 @@ -782,52 +782,52 @@ def leave_module(self, node): self._check_imports(not_consumed) - def visit_classdef(self, node): + def visit_classdef(self, node: nodes.ClassDef) -> None: """visit class: update consumption analysis variable""" self._to_consume.append(NamesConsumer(node, "class")) - def leave_classdef(self, _): + def leave_classdef(self, _: nodes.ClassDef) -> None: """leave class: update consumption analysis variable""" # do not check for not used locals here (no sense) self._to_consume.pop() - def visit_lambda(self, node): + def visit_lambda(self, node: nodes.Lambda) -> None: """visit lambda: update consumption analysis variable""" self._to_consume.append(NamesConsumer(node, "lambda")) - def leave_lambda(self, _): + def leave_lambda(self, _: nodes.Lambda) -> None: """leave lambda: update consumption analysis variable""" # do not check for not used locals here self._to_consume.pop() - def visit_generatorexp(self, node): + def visit_generatorexp(self, node: nodes.GeneratorExp) -> None: """visit genexpr: update consumption analysis variable""" self._to_consume.append(NamesConsumer(node, "comprehension")) - def leave_generatorexp(self, _): + def leave_generatorexp(self, _: nodes.GeneratorExp) -> None: """leave genexpr: update consumption analysis variable""" # do not check for not used locals here self._to_consume.pop() - def visit_dictcomp(self, node): + def visit_dictcomp(self, node: nodes.DictComp) -> None: """visit dictcomp: update consumption analysis variable""" self._to_consume.append(NamesConsumer(node, "comprehension")) - def leave_dictcomp(self, _): + def leave_dictcomp(self, _: nodes.DictComp) -> None: """leave dictcomp: update consumption analysis variable""" # do not check for not used locals here self._to_consume.pop() - def visit_setcomp(self, node): + def visit_setcomp(self, node: nodes.SetComp) -> None: """visit setcomp: update consumption analysis variable""" self._to_consume.append(NamesConsumer(node, "comprehension")) - def leave_setcomp(self, _): + def leave_setcomp(self, _: nodes.SetComp) -> None: """leave setcomp: update consumption analysis variable""" # do not check for not used locals here self._to_consume.pop() - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: """visit function: update consumption analysis variable and check locals""" self._to_consume.append(NamesConsumer(node, "function")) if not ( @@ -869,7 +869,7 @@ def visit_functiondef(self, node): # do not print Redefining builtin for additional builtins self.add_message("redefined-builtin", args=name, node=stmt) - def leave_functiondef(self, node): + def leave_functiondef(self, node: nodes.FunctionDef) -> None: """leave function: check function's locals are consumed""" self._check_metaclasses(node) @@ -911,7 +911,7 @@ def leave_functiondef(self, node): "global-at-module-level", "redefined-builtin", ) - def visit_global(self, node): + def visit_global(self, node: nodes.Global) -> None: """check names imported exists in the global scope""" frame = node.frame() if isinstance(frame, nodes.Module): @@ -955,15 +955,15 @@ def visit_global(self, node): if default_message: self.add_message("global-statement", node=node) - def visit_assignname(self, node): + def visit_assignname(self, node: nodes.AssignName) -> None: if isinstance(node.assign_type(), nodes.AugAssign): self.visit_name(node) - def visit_delname(self, node): + def visit_delname(self, node: nodes.DelName) -> None: self.visit_name(node) # pylint: disable=too-many-branches - def visit_name(self, node): + def visit_name(self, node: nodes.Name) -> None: """Check that a name is defined in the current scope""" stmt = node.statement() if stmt.fromlineno is None: @@ -1249,7 +1249,7 @@ def visit_importfrom(self, node: nodes.ImportFrom) -> None: @utils.check_messages( "unbalanced-tuple-unpacking", "unpacking-non-sequence", "self-cls-assignment" ) - def visit_assign(self, node): + def visit_assign(self, node: nodes.Assign) -> None: """Check unbalanced tuple unpacking for assignments and unpacking non-sequences as well as in case self/cls get assigned. @@ -1267,22 +1267,22 @@ def visit_assign(self, node): return # listcomp have now also their scope - def visit_listcomp(self, node): + def visit_listcomp(self, node: nodes.ListComp) -> None: """visit dictcomp: update consumption analysis variable""" self._to_consume.append(NamesConsumer(node, "comprehension")) - def leave_listcomp(self, _): + def leave_listcomp(self, _: nodes.ListComp) -> None: """leave dictcomp: update consumption analysis variable""" # do not check for not used locals here self._to_consume.pop() - def leave_assign(self, node): + def leave_assign(self, node: nodes.Assign) -> None: self._store_type_annotation_names(node) - def leave_with(self, node): + def leave_with(self, node: nodes.With) -> None: self._store_type_annotation_names(node) - def visit_arguments(self, node): + def visit_arguments(self, node: nodes.Arguments) -> None: for annotation in node.type_comment_args: self._store_type_annotation_node(annotation) diff --git a/pylint/extensions/bad_builtin.py b/pylint/extensions/bad_builtin.py index ad5d3120ac..55908e2934 100644 --- a/pylint/extensions/bad_builtin.py +++ b/pylint/extensions/bad_builtin.py @@ -51,7 +51,7 @@ class BadBuiltinChecker(BaseChecker): ) @check_messages("bad-builtin") - def visit_call(self, node): + def visit_call(self, node: nodes.Call) -> None: if isinstance(node.func, nodes.Name): name = node.func.name # ignore the name if it's not a builtin (i.e. not defined in the diff --git a/pylint/extensions/broad_try_clause.py b/pylint/extensions/broad_try_clause.py index b7d2fc974c..5cc894af4b 100644 --- a/pylint/extensions/broad_try_clause.py +++ b/pylint/extensions/broad_try_clause.py @@ -10,7 +10,7 @@ """Looks for try/except statements with too much code in the try clause.""" -from astroid.node_classes import For, If, While, With +from astroid import nodes from pylint import checkers, interfaces @@ -53,12 +53,12 @@ def _count_statements(self, try_node): statement_count = len(try_node.body) for body_node in try_node.body: - if isinstance(body_node, (For, If, While, With)): + if isinstance(body_node, (nodes.For, nodes.If, nodes.While, nodes.With)): statement_count += self._count_statements(body_node) return statement_count - def visit_tryexcept(self, node): + def visit_tryexcept(self, node: nodes.TryExcept) -> None: try_clause_statements = self._count_statements(node) if try_clause_statements > self.config.max_try_statements: msg = f"try clause contains {try_clause_statements} statements, expected at most {self.config.max_try_statements}" @@ -66,7 +66,7 @@ def visit_tryexcept(self, node): "too-many-try-statements", node.lineno, node=node, args=msg ) - def visit_tryfinally(self, node): + def visit_tryfinally(self, node: nodes.TryFinally) -> None: self.visit_tryexcept(node) diff --git a/pylint/extensions/check_elif.py b/pylint/extensions/check_elif.py index d0f487dd15..bed840fb6a 100644 --- a/pylint/extensions/check_elif.py +++ b/pylint/extensions/check_elif.py @@ -48,19 +48,19 @@ def process_tokens(self, tokens): elif token == "if": self._elifs.append(False) - def leave_module(self, _): + def leave_module(self, _: nodes.Module) -> None: self._init() - def visit_ifexp(self, node): + def visit_ifexp(self, node: nodes.IfExp) -> None: if isinstance(node.parent, nodes.FormattedValue): return self._if_counter += 1 - def visit_comprehension(self, node): + def visit_comprehension(self, node: nodes.Comprehension) -> None: self._if_counter += len(node.ifs) @check_messages("else-if-used") - def visit_if(self, node): + def visit_if(self, node: nodes.If) -> None: if isinstance(node.parent, nodes.If): orelse = node.parent.orelse # current if node must directly follow an "else" diff --git a/pylint/extensions/code_style.py b/pylint/extensions/code_style.py index 0d8bfd1648..8ec8d4a6a2 100644 --- a/pylint/extensions/code_style.py +++ b/pylint/extensions/code_style.py @@ -77,9 +77,9 @@ def __init__(self, linter: PyLinter) -> None: super().__init__(linter=linter) def open(self) -> None: - py_version: Tuple[int, int] = get_global_option(self, "py-version") # type: ignore + py_version: Tuple[int, int] = get_global_option(self, "py-version") self._py38_plus = py_version >= (3, 8) - self._max_length: int = ( # type: ignore + self._max_length: int = ( self.config.max_line_length_suggestions or get_global_option(self, "max-line-length") ) @@ -160,7 +160,7 @@ def _check_dict_consider_namedtuple_dataclass(self, node: nodes.Dict) -> None: for _, dict_value in node.items ): # Make sure all sublists have the same length > 0 - list_length = len(node.items[0][1].elts) # type: ignore + list_length = len(node.items[0][1].elts) if list_length == 0: return for _, dict_value in node.items[1:]: diff --git a/pylint/extensions/comparetozero.py b/pylint/extensions/comparetozero.py index 04a3f29427..05020bf67e 100644 --- a/pylint/extensions/comparetozero.py +++ b/pylint/extensions/comparetozero.py @@ -12,8 +12,10 @@ """Looks for comparisons to zero.""" import itertools +from typing import Any, Iterable import astroid +from astroid import nodes from pylint import checkers, interfaces from pylint.checkers import utils @@ -46,7 +48,7 @@ class CompareToZeroChecker(checkers.BaseChecker): options = () @utils.check_messages("compare-to-zero") - def visit_compare(self, node): + def visit_compare(self, node: nodes.Compare) -> None: _operators = ["!=", "==", "is not", "is"] # note: astroid.Compare has the left most operand in node.left # while the rest are a list of tuples in node.ops @@ -54,7 +56,8 @@ def visit_compare(self, node): # here we squash everything into `ops` to make it easier for processing later ops = [("", node.left)] ops.extend(node.ops) - ops = list(itertools.chain(*ops)) + iter_ops: Iterable[Any] = iter(ops) + ops = list(itertools.chain(*iter_ops)) for ops_idx in range(len(ops) - 2): op_1 = ops[ops_idx] diff --git a/pylint/extensions/confusing_elif.py b/pylint/extensions/confusing_elif.py index ba1e6ff016..2b62bc02cb 100644 --- a/pylint/extensions/confusing_elif.py +++ b/pylint/extensions/confusing_elif.py @@ -32,7 +32,7 @@ class ConfusingConsecutiveElifChecker(BaseChecker): } @check_messages("confusing-consecutive-elif") - def visit_if(self, node: nodes.If): + def visit_if(self, node: nodes.If) -> None: body_ends_with_if = isinstance( node.body[-1], nodes.If ) and self._has_no_else_clause(node.body[-1]) diff --git a/pylint/extensions/docparams.py b/pylint/extensions/docparams.py index 4344444fff..bb5ac68057 100644 --- a/pylint/extensions/docparams.py +++ b/pylint/extensions/docparams.py @@ -27,6 +27,7 @@ from typing import Optional import astroid +from astroid import nodes from pylint.checkers import BaseChecker from pylint.checkers import utils as checker_utils @@ -205,7 +206,7 @@ class DocstringParameterChecker(BaseChecker): constructor_names = {"__init__", "__new__"} not_needed_param_in_docstring = {"self", "cls"} - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: """Called for function and method definitions (def). :param node: Node for a function or method definition in the AST @@ -278,7 +279,7 @@ def check_functiondef_yields(self, node, node_doc): ) and not node.is_generator(): self.add_message("redundant-yields-doc", node=node) - def visit_raise(self, node): + def visit_raise(self, node: nodes.Raise) -> None: func_node = node.frame() if not isinstance(func_node, astroid.FunctionDef): return @@ -308,7 +309,7 @@ def visit_raise(self, node): missing_excs = expected_excs - found_excs_class_names self._add_raise_message(missing_excs, func_node) - def visit_return(self, node): + def visit_return(self, node: nodes.Return) -> None: if not utils.returns_something(node): return @@ -331,7 +332,7 @@ def visit_return(self, node): if not (doc.has_rtype() or (doc.has_property_type() and is_property)): self.add_message("missing-return-type-doc", node=func_node) - def visit_yield(self, node): + def visit_yield(self, node: nodes.Yield) -> None: func_node = node.frame() if not isinstance(func_node, astroid.FunctionDef): return @@ -353,7 +354,7 @@ def visit_yield(self, node): if not (doc_has_yields_type or func_node.returns): self.add_message("missing-yield-type-doc", node=func_node) - def visit_yieldfrom(self, node): + def visit_yieldfrom(self, node: nodes.YieldFrom) -> None: self.visit_yield(node) def _compare_missing_args( @@ -612,7 +613,7 @@ def _add_raise_message(self, missing_excs, node): :type missing_excs: set(str) :param node: The node show the message on. - :type node: astroid.node_classes.NodeNG + :type node: nodes.NodeNG """ if node.is_abstract(): try: diff --git a/pylint/extensions/docstyle.py b/pylint/extensions/docstyle.py index e56c707bf4..816bda2e21 100644 --- a/pylint/extensions/docstyle.py +++ b/pylint/extensions/docstyle.py @@ -11,6 +11,8 @@ import linecache +from astroid import nodes + from pylint import checkers from pylint.checkers.utils import check_messages from pylint.interfaces import HIGH, IAstroidChecker @@ -36,13 +38,13 @@ class DocStringStyleChecker(checkers.BaseChecker): } @check_messages("docstring-first-line-empty", "bad-docstring-quotes") - def visit_module(self, node): + def visit_module(self, node: nodes.Module) -> None: self._check_docstring("module", node) - def visit_classdef(self, node): + def visit_classdef(self, node: nodes.ClassDef) -> None: self._check_docstring("class", node) - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: ftype = "method" if node.is_method() else "function" self._check_docstring(ftype, node) diff --git a/pylint/extensions/emptystring.py b/pylint/extensions/emptystring.py index 6f07a8e81f..f5a4a2740b 100644 --- a/pylint/extensions/emptystring.py +++ b/pylint/extensions/emptystring.py @@ -11,6 +11,7 @@ """Looks for comparisons to empty string.""" import itertools +from typing import Any, Iterable from astroid import nodes @@ -45,7 +46,7 @@ class CompareToEmptyStringChecker(checkers.BaseChecker): options = () @utils.check_messages("compare-to-empty-string") - def visit_compare(self, node): + def visit_compare(self, node: nodes.Compare) -> None: _operators = ["!=", "==", "is not", "is"] # note: astroid.Compare has the left most operand in node.left # while the rest are a list of tuples in node.ops @@ -53,7 +54,8 @@ def visit_compare(self, node): # here we squash everything into `ops` to make it easier for processing later ops = [("", node.left)] ops.extend(node.ops) - ops = list(itertools.chain(*ops)) + iter_ops: Iterable[Any] = iter(ops) + ops = list(itertools.chain(*iter_ops)) for ops_idx in range(len(ops) - 2): op_1 = ops[ops_idx] diff --git a/pylint/extensions/mccabe.py b/pylint/extensions/mccabe.py index c85588ca2d..86b8eb9431 100644 --- a/pylint/extensions/mccabe.py +++ b/pylint/extensions/mccabe.py @@ -11,6 +11,7 @@ """Module to add McCabe checker class for pylint. """ +from astroid import nodes from mccabe import PathGraph as Mccabe_PathGraph from mccabe import PathGraphingAstVisitor as Mccabe_PathGraphingAstVisitor @@ -171,7 +172,7 @@ class McCabeMethodChecker(checkers.BaseChecker): ) @check_messages("too-complex") - def visit_module(self, node): + def visit_module(self, node: nodes.Module) -> None: """visit an astroid.Module node to check too complex rating and add message if is greather than max_complexity stored from options""" visitor = PathGraphingAstVisitor() diff --git a/pylint/extensions/overlapping_exceptions.py b/pylint/extensions/overlapping_exceptions.py index 891dde59fc..2ed9038e32 100644 --- a/pylint/extensions/overlapping_exceptions.py +++ b/pylint/extensions/overlapping_exceptions.py @@ -3,7 +3,10 @@ """Looks for overlapping exceptions.""" +from typing import Any, List, Tuple + import astroid +from astroid import nodes from pylint import checkers, interfaces from pylint.checkers import utils @@ -29,7 +32,7 @@ class OverlappingExceptionsChecker(checkers.BaseChecker): options = () @utils.check_messages("overlapping-except") - def visit_tryexcept(self, node): + def visit_tryexcept(self, node: nodes.TryExcept) -> None: """check for empty except""" for handler in node.handlers: if handler.type is None: @@ -41,7 +44,7 @@ def visit_tryexcept(self, node): except astroid.InferenceError: continue - handled_in_clause = [] + handled_in_clause: List[Tuple[Any, Any]] = [] for part, exc in excs: if exc is astroid.Uninferable: continue diff --git a/pylint/extensions/redefined_variable_type.py b/pylint/extensions/redefined_variable_type.py index b634bb6c90..26dc3ecc23 100644 --- a/pylint/extensions/redefined_variable_type.py +++ b/pylint/extensions/redefined_variable_type.py @@ -10,6 +10,8 @@ # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +from typing import List + from astroid import nodes from pylint.checkers import BaseChecker @@ -43,18 +45,18 @@ class MultipleTypesChecker(BaseChecker): ) } - def visit_classdef(self, _): + def visit_classdef(self, _: nodes.ClassDef) -> None: self._assigns.append({}) @check_messages("redefined-variable-type") - def leave_classdef(self, _): + def leave_classdef(self, _: nodes.ClassDef) -> None: self._check_and_add_messages() visit_functiondef = visit_classdef leave_functiondef = leave_module = leave_classdef - def visit_module(self, _): - self._assigns = [{}] + def visit_module(self, _: nodes.Module) -> None: + self._assigns: List[dict] = [{}] def _check_and_add_messages(self): assigns = self._assigns.pop() @@ -93,7 +95,7 @@ def _check_and_add_messages(self): ) break - def visit_assign(self, node): + def visit_assign(self, node: nodes.Assign) -> None: # we don't handle multiple assignment nor slice assignment target = node.targets[0] if isinstance(target, (nodes.Tuple, nodes.Subscript)): diff --git a/pylint/extensions/typing.py b/pylint/extensions/typing.py index cf0281f330..66e46bcfdb 100644 --- a/pylint/extensions/typing.py +++ b/pylint/extensions/typing.py @@ -139,7 +139,7 @@ def __init__(self, linter: PyLinter) -> None: self._consider_using_alias_msgs: List[DeprecatedTypingAliasMsg] = [] def open(self) -> None: - py_version: Tuple[int, int] = get_global_option(self, "py-version") # type: ignore + py_version: Tuple[int, int] = get_global_option(self, "py-version") self._py37_plus = py_version >= (3, 7) self._py39_plus = py_version >= (3, 9) self._py310_plus = py_version >= (3, 10) @@ -173,7 +173,7 @@ def visit_name(self, node: nodes.Name) -> None: "consider-using-alias", "consider-alternative-union-syntax", ) - def visit_attribute(self, node: nodes.Attribute): + def visit_attribute(self, node: nodes.Attribute) -> None: if self._should_check_typing_alias and node.attrname in ALIAS_NAMES: self._check_for_typing_alias(node) if self._should_check_alternative_union_syntax and node.attrname in UNION_NAMES: diff --git a/pylint/extensions/while_used.py b/pylint/extensions/while_used.py index 9476478c38..8d05ace900 100644 --- a/pylint/extensions/while_used.py +++ b/pylint/extensions/while_used.py @@ -1,4 +1,6 @@ """Check for use of while loops.""" +from astroid import nodes + from pylint.checkers import BaseChecker from pylint.checkers.utils import check_messages from pylint.interfaces import IAstroidChecker @@ -17,7 +19,7 @@ class WhileChecker(BaseChecker): } @check_messages("while-used") - def visit_while(self, node): + def visit_while(self, node: nodes.While) -> None: self.add_message("while-used", node=node) diff --git a/pylint/graph.py b/pylint/graph.py index 927fac47ee..a5e3362262 100644 --- a/pylint/graph.py +++ b/pylint/graph.py @@ -109,8 +109,8 @@ def generate(self, outputfile: str = None, mapfile: str = None) -> str: os.close(pdot) else: dot_sourcepath = outputfile - with codecs.open(dot_sourcepath, "w", encoding="utf8") as pdot: # type: ignore - pdot.write(self.source) # type: ignore + with codecs.open(dot_sourcepath, "w", encoding="utf8") as file: + file.write(self.source) if target not in graphviz_extensions: if shutil.which(self.renderer) is None: raise RuntimeError( diff --git a/pylint/message/message_id_store.py b/pylint/message/message_id_store.py index 2f061e4b6c..84c3747fe8 100644 --- a/pylint/message/message_id_store.py +++ b/pylint/message/message_id_store.py @@ -76,9 +76,9 @@ def check_msgid_and_symbol(self, msgid: str, symbol: str) -> None: if existing_msgid is not None: if existing_msgid != msgid: self._raise_duplicate_msgid(symbol, msgid, existing_msgid) - if existing_symbol != symbol: + if existing_symbol and existing_symbol != symbol: # See https://github.com/python/mypy/issues/10559 - self._raise_duplicate_symbol(msgid, symbol, existing_symbol) # type: ignore + self._raise_duplicate_symbol(msgid, symbol, existing_symbol) @staticmethod def _raise_duplicate_symbol(msgid: str, symbol: str, other_symbol: str): @@ -105,11 +105,12 @@ def get_active_msgids(self, msgid_or_symbol: str) -> List[str]: """Return msgids but the input can be a symbol.""" # Only msgid can have a digit as second letter is_msgid: bool = msgid_or_symbol[1:].isdigit() + msgid = None if is_msgid: msgid = msgid_or_symbol.upper() symbol = self.__msgid_to_symbol.get(msgid) else: - msgid = self.__symbol_to_msgid.get(msgid_or_symbol) # type: ignore + msgid = self.__symbol_to_msgid.get(msgid_or_symbol) symbol = msgid_or_symbol if msgid is None or symbol is None or not msgid or not symbol: error_msg = f"No such message id or symbol '{msgid_or_symbol}'." diff --git a/pylint/pyreverse/diadefslib.py b/pylint/pyreverse/diadefslib.py index df9a7f6ad5..776b832187 100644 --- a/pylint/pyreverse/diadefslib.py +++ b/pylint/pyreverse/diadefslib.py @@ -20,10 +20,13 @@ """handle diagram generation options for class diagram or default diagrams """ +from typing import Any, Optional + import astroid from astroid import nodes from pylint.pyreverse.diagrams import ClassDiagram, PackageDiagram +from pylint.pyreverse.inspector import Project from pylint.pyreverse.utils import LocalsVisitor # diagram generators ########################################################## @@ -132,19 +135,21 @@ def __init__(self, linker, handler): DiaDefGenerator.__init__(self, linker, handler) LocalsVisitor.__init__(self) - def visit_project(self, node): + def visit_project(self, node: Project) -> None: """visit a pyreverse.utils.Project node create a diagram definition for packages """ mode = self.config.mode if len(node.modules) > 1: - self.pkgdiagram = PackageDiagram(f"packages {node.name}", mode) + self.pkgdiagram: Optional[PackageDiagram] = PackageDiagram( + f"packages {node.name}", mode + ) else: self.pkgdiagram = None self.classdiagram = ClassDiagram(f"classes {node.name}", mode) - def leave_project(self, node): # pylint: disable=unused-argument + def leave_project(self, _: Project) -> Any: """leave the pyreverse.utils.Project node return the generated diagram definition @@ -153,7 +158,7 @@ def leave_project(self, node): # pylint: disable=unused-argument return self.pkgdiagram, self.classdiagram return (self.classdiagram,) - def visit_module(self, node): + def visit_module(self, node: nodes.Module) -> None: """visit an astroid.Module node add this class to the package diagram definition @@ -162,7 +167,7 @@ def visit_module(self, node): self.linker.visit(node) self.pkgdiagram.add_object(node.name, node) - def visit_classdef(self, node): + def visit_classdef(self, node: nodes.ClassDef) -> None: """visit an astroid.Class node add this class to the class diagram definition @@ -170,7 +175,7 @@ def visit_classdef(self, node): anc_level, association_level = self._get_levels() self.extract_classes(node, anc_level, association_level) - def visit_importfrom(self, node): + def visit_importfrom(self, node: nodes.ImportFrom) -> None: """visit astroid.ImportFrom and catch modules for package diagram""" if self.pkgdiagram: self.pkgdiagram.add_from_depend(node, node.modname) diff --git a/pylint/pyreverse/inspector.py b/pylint/pyreverse/inspector.py index 6471b2b61c..0b4773153a 100644 --- a/pylint/pyreverse/inspector.py +++ b/pylint/pyreverse/inspector.py @@ -53,7 +53,7 @@ def interfaces(node, herited=True, handler_func=_iface_hdlr): return found = set() missing = False - for iface in astroid.node_classes.unpack_infer(implements): + for iface in nodes.unpack_infer(implements): if iface is astroid.Uninferable: missing = True continue @@ -80,6 +80,35 @@ def generate_id(self): return self.id_count +class Project: + """a project handle a set of modules / packages""" + + def __init__(self, name=""): + self.name = name + self.uid = None + self.path = None + self.modules = [] + self.locals = {} + self.__getitem__ = self.locals.__getitem__ + self.__iter__ = self.locals.__iter__ + self.values = self.locals.values + self.keys = self.locals.keys + self.items = self.locals.items + + def add_module(self, node): + self.locals[node.name] = node + self.modules.append(node) + + def get_module(self, name): + return self.locals[name] + + def get_children(self): + return self.modules + + def __repr__(self): + return f"" + + class Linker(IdGeneratorMixIn, utils.LocalsVisitor): """Walk on the project tree and resolve relationships. @@ -113,7 +142,7 @@ def __init__(self, project, inherited_interfaces=0, tag=False): # visited project self.project = project - def visit_project(self, node): + def visit_project(self, node: Project) -> None: """visit a pyreverse.utils.Project node * optionally tag the node with a unique id @@ -123,7 +152,7 @@ def visit_project(self, node): for module in node.modules: self.visit(module) - def visit_module(self, node): + def visit_module(self, node: nodes.Module) -> None: """visit an astroid.Module node * set the locals_type mapping @@ -137,7 +166,7 @@ def visit_module(self, node): if self.tag: node.uid = self.generate_id() - def visit_classdef(self, node): + def visit_classdef(self, node: nodes.ClassDef) -> None: """visit an astroid.Class node * set the locals_type and instance_attrs_type mappings @@ -166,7 +195,7 @@ def visit_classdef(self, node): except astroid.InferenceError: node.implements = [] - def visit_functiondef(self, node): + def visit_functiondef(self, node: nodes.FunctionDef) -> None: """visit an astroid.Function node * set the locals_type mapping @@ -183,7 +212,7 @@ def visit_functiondef(self, node): link_class = visit_classdef link_function = visit_functiondef - def visit_assignname(self, node): + def visit_assignname(self, node: nodes.AssignName) -> None: """visit an astroid.AssignName node handle locals_type @@ -224,7 +253,7 @@ def handle_assignattr_type(node, parent): current | utils.infer_node(node) ) - def visit_import(self, node): + def visit_import(self, node: nodes.Import) -> None: """visit an astroid.Import node resolve module dependencies @@ -234,7 +263,7 @@ def visit_import(self, node): relative = astroid.modutils.is_relative(name[0], context_file) self._imported_module(node, name[0], relative) - def visit_importfrom(self, node): + def visit_importfrom(self, node: nodes.ImportFrom) -> None: """visit an astroid.ImportFrom node resolve module dependencies @@ -282,34 +311,6 @@ def _imported_module(self, node, mod_path, relative): mod_paths.append(mod_path) -class Project: - """a project handle a set of modules / packages""" - - def __init__(self, name=""): - self.name = name - self.path = None - self.modules = [] - self.locals = {} - self.__getitem__ = self.locals.__getitem__ - self.__iter__ = self.locals.__iter__ - self.values = self.locals.values - self.keys = self.locals.keys - self.items = self.locals.items - - def add_module(self, node): - self.locals[node.name] = node - self.modules.append(node) - - def get_module(self, name): - return self.locals[name] - - def get_children(self): - return self.modules - - def __repr__(self): - return f"" - - def project_from_files( files, func_wrapper=_astroid_wrapper, project_name="no name", black_list=("CVS",) ): diff --git a/pylint/pyreverse/main.py b/pylint/pyreverse/main.py index 601e672528..6301467900 100644 --- a/pylint/pyreverse/main.py +++ b/pylint/pyreverse/main.py @@ -196,7 +196,7 @@ class Run(ConfigurationMixIn): """base class providing common behaviour for pyreverse commands""" - options = OPTIONS # type: ignore + options = OPTIONS def __init__(self, args: Iterable[str]): ConfigurationMixIn.__init__(self, usage=__doc__) diff --git a/pylint/testutils/reporter_for_tests.py b/pylint/testutils/reporter_for_tests.py index b1b7af7e4f..b9e01b5a44 100644 --- a/pylint/testutils/reporter_for_tests.py +++ b/pylint/testutils/reporter_for_tests.py @@ -15,7 +15,9 @@ class GenericTestReporter(BaseReporter): __implements__ = interfaces.IReporter - def __init__(self): # pylint: disable=super-init-not-called + def __init__( + self, + ): # pylint: disable=super-init-not-called # See https://github.com/PyCQA/pylint/issues/4941 self.reset() def reset(self): diff --git a/setup.cfg b/setup.cfg index c04b0a820e..8d5ff90444 100644 --- a/setup.cfg +++ b/setup.cfg @@ -84,6 +84,7 @@ src_paths = pylint [mypy] scripts_are_modules = True +warn_unused_ignores = True [mypy-astroid.*] ignore_missing_imports = True diff --git a/tests/extensions/test_check_docs.py b/tests/extensions/test_check_docs.py index aa8a700f0d..f462ea3d17 100644 --- a/tests/extensions/test_check_docs.py +++ b/tests/extensions/test_check_docs.py @@ -341,7 +341,7 @@ def function_foo(x, y): with self.assertNoMessages(): self.checker.visit_functiondef(node) - def _visit_methods_of_class(self, node): + def _visit_methods_of_class(self, node: nodes.ClassDef) -> None: """Visit all methods of a class node :param node: class node diff --git a/tests/functional/g/generated_members.py b/tests/functional/g/generated_members.py index b79c008dda..2b3a2b07db 100644 --- a/tests/functional/g/generated_members.py +++ b/tests/functional/g/generated_members.py @@ -1,7 +1,7 @@ """Test the generated-members config option.""" # pylint: disable=pointless-statement, invalid-name, useless-object-inheritance from __future__ import print_function -from astroid import node_classes +from astroid import nodes from pylint import checkers class Klass(object): @@ -11,7 +11,7 @@ class Klass(object): print(Klass().aBC_set1) print(Klass().ham.does.not_.exist) print(Klass().spam.does.not_.exist) # [no-member] -node_classes.Tuple.does.not_.exist +nodes.Tuple.does.not_.exist checkers.base.doesnotexist() session = Klass() diff --git a/tests/functional/g/generated_members.rc b/tests/functional/g/generated_members.rc index 31b87ffd0b..5f8f9cd0f5 100644 --- a/tests/functional/g/generated_members.rc +++ b/tests/functional/g/generated_members.rc @@ -6,6 +6,6 @@ generated-members= \Afunctional\.g\.generated_members\.Klass\.ham\Z, DoesNotExist, "[a-zA-Z]+_set{1,2}", - node_classes.Tuple.*, + nodes.Tuple.*, checkers.*?base, (session|SESSION).rollback diff --git a/tests/functional/u/unused/unused_variable.py b/tests/functional/u/unused/unused_variable.py index 8e164acd1b..84ca8c50f3 100644 --- a/tests/functional/u/unused/unused_variable.py +++ b/tests/functional/u/unused/unused_variable.py @@ -79,8 +79,8 @@ def function(matches): index += 1 print(match) - -def visit_if(self, node): +from astroid import nodes +def visit_if(self, node: nodes.If) -> None: """increments the branches counter""" branches = 1 # don't double count If nodes coming from some 'elif' diff --git a/tests/pyreverse/test_utils.py b/tests/pyreverse/test_utils.py index e110020350..1d231eafa2 100644 --- a/tests/pyreverse/test_utils.py +++ b/tests/pyreverse/test_utils.py @@ -80,7 +80,7 @@ class A: @patch("pylint.pyreverse.utils.get_annotation") -@patch("astroid.node_classes.NodeNG.infer", side_effect=astroid.InferenceError) +@patch("astroid.nodes.NodeNG.infer", side_effect=astroid.InferenceError) def test_infer_node_1(mock_infer, mock_get_annotation): """Return set() when astroid.InferenceError is raised and an annotation has not been returned @@ -93,7 +93,7 @@ def test_infer_node_1(mock_infer, mock_get_annotation): @patch("pylint.pyreverse.utils.get_annotation") -@patch("astroid.node_classes.NodeNG.infer") +@patch("astroid.nodes.NodeNG.infer") def test_infer_node_2(mock_infer, mock_get_annotation): """Return set(node.infer()) when InferenceError is not raised and an annotation has not been returned