diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py index fa14f5eea5..f0e679374c 100644 --- a/pylint/checkers/__init__.py +++ b/pylint/checkers/__init__.py @@ -41,8 +41,9 @@ """ +from __future__ import annotations + import sys -from typing import List, Optional, Tuple, Union from pylint.checkers.base_checker import BaseChecker, BaseTokenChecker from pylint.checkers.deprecated import DeprecatedMixin @@ -57,17 +58,17 @@ def table_lines_from_stats( stats: LinterStats, - old_stats: Optional[LinterStats], + old_stats: LinterStats | None, stat_type: Literal["duplicated_lines", "message_types"], -) -> List[str]: +) -> list[str]: """Get values listed in from and , and return a formatted list of values. The return value is designed to be given to a ureport.Table object """ - lines: List[str] = [] + lines: list[str] = [] if stat_type == "duplicated_lines": - new: List[Tuple[str, Union[int, float]]] = [ + new: list[tuple[str, int | float]] = [ ("nb_duplicated_lines", stats.duplicated_lines["nb_duplicated_lines"]), ( "percent_duplicated_lines", @@ -75,7 +76,7 @@ def table_lines_from_stats( ), ] if old_stats: - old: List[Tuple[str, Union[str, int, float]]] = [ + old: list[tuple[str, str | int | float]] = [ ( "nb_duplicated_lines", old_stats.duplicated_lines["nb_duplicated_lines"], diff --git a/pylint/checkers/async.py b/pylint/checkers/async.py index 1615dc82e5..0726a22e2a 100644 --- a/pylint/checkers/async.py +++ b/pylint/checkers/async.py @@ -4,6 +4,8 @@ """Checker for anything related to the async protocol (PEP 492).""" +from __future__ import annotations + import sys from typing import TYPE_CHECKING @@ -91,5 +93,5 @@ def visit_asyncwith(self, node: nodes.AsyncWith) -> None: ) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(AsyncChecker(linter)) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index bf166a3dcf..ca0207b33f 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -2,10 +2,12 @@ # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt +from __future__ import annotations + import functools import warnings from inspect import cleandoc -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from astroid import nodes @@ -37,7 +39,7 @@ class BaseChecker(_ArgumentsProvider): # mark this checker as enabled or not. enabled: bool = True - def __init__(self, linter: "PyLinter") -> None: + def __init__(self, linter: PyLinter) -> None: """Checker instances should have the linter as argument.""" if self.name is not None: self.name = self.name.lower() @@ -114,13 +116,13 @@ def get_full_documentation(self, msgs, options, reports, doc=None, module=None): def add_message( self, msgid: str, - line: Optional[int] = None, - node: Optional[nodes.NodeNG] = None, + line: int | None = None, + node: nodes.NodeNG | None = None, args: Any = None, - confidence: Optional[Confidence] = None, - col_offset: Optional[int] = None, - end_lineno: Optional[int] = None, - end_col_offset: Optional[int] = None, + confidence: Confidence | None = None, + col_offset: int | None = None, + end_lineno: int | None = None, + end_col_offset: int | None = None, ) -> None: self.linter.add_message( msgid, line, node, args, confidence, col_offset, end_lineno, end_col_offset diff --git a/pylint/checkers/deprecated.py b/pylint/checkers/deprecated.py index 299922f2b3..a0e0c3a429 100644 --- a/pylint/checkers/deprecated.py +++ b/pylint/checkers/deprecated.py @@ -3,8 +3,11 @@ # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt """Checker mixin for deprecated functionality.""" + +from __future__ import annotations + from itertools import chain -from typing import Any, Container, Iterable, Tuple, Union +from typing import Any, Container, Iterable import astroid from astroid import nodes @@ -124,9 +127,7 @@ def deprecated_methods(self) -> Container[str]: # pylint: disable=no-self-use return () - def deprecated_arguments( - self, method: str - ) -> Iterable[Tuple[Union[int, None], str]]: + def deprecated_arguments(self, method: str) -> Iterable[tuple[int | None, str]]: """Callback returning the deprecated arguments of method/function. Args: diff --git a/pylint/checkers/design_analysis.py b/pylint/checkers/design_analysis.py index eb1477ccbc..f5eca5afc8 100644 --- a/pylint/checkers/design_analysis.py +++ b/pylint/checkers/design_analysis.py @@ -4,10 +4,12 @@ """Check for signs of poor design.""" +from __future__ import annotations + import re import sys from collections import defaultdict -from typing import TYPE_CHECKING, FrozenSet, Iterator, List, Set, cast +from typing import TYPE_CHECKING, Iterator, List, cast import astroid from astroid import nodes @@ -229,7 +231,7 @@ def _count_methods_in_class(node): def _get_parents_iter( - node: nodes.ClassDef, ignored_parents: FrozenSet[str] + node: nodes.ClassDef, ignored_parents: frozenset[str] ) -> Iterator[nodes.ClassDef]: r"""Get parents of ``node``, excluding ancestors of ``ignored_parents``. @@ -246,7 +248,7 @@ def _get_parents_iter( And ``ignored_parents`` is ``{"E"}``, then this function will return ``{A, B, C, D}`` -- both ``E`` and its ancestors are excluded. """ - parents: Set[nodes.ClassDef] = set() + parents: set[nodes.ClassDef] = set() to_explore = cast(List[nodes.ClassDef], list(node.ancestors(recurs=False))) while to_explore: parent = to_explore.pop() @@ -265,8 +267,8 @@ def _get_parents_iter( def _get_parents( - node: nodes.ClassDef, ignored_parents: FrozenSet[str] -) -> Set[nodes.ClassDef]: + node: nodes.ClassDef, ignored_parents: frozenset[str] +) -> set[nodes.ClassDef]: return set(_get_parents_iter(node, ignored_parents)) @@ -649,5 +651,5 @@ def _inc_branch(self, node, branchesnum=1): self._branches[node.scope()] += branchesnum -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(MisdesignChecker(linter)) diff --git a/pylint/checkers/dunder_methods.py b/pylint/checkers/dunder_methods.py index 0e7beb16a5..12ec176d81 100644 --- a/pylint/checkers/dunder_methods.py +++ b/pylint/checkers/dunder_methods.py @@ -2,6 +2,8 @@ # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt +from __future__ import annotations + from typing import TYPE_CHECKING from astroid import nodes @@ -155,5 +157,5 @@ def visit_call(self, node: nodes.Call) -> None: ) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(DunderCallChecker(linter)) diff --git a/pylint/checkers/ellipsis_checker.py b/pylint/checkers/ellipsis_checker.py index 7a1298afe2..4fe476eb3b 100644 --- a/pylint/checkers/ellipsis_checker.py +++ b/pylint/checkers/ellipsis_checker.py @@ -3,6 +3,9 @@ # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt """Ellipsis checker for Python code.""" + +from __future__ import annotations + from typing import TYPE_CHECKING from astroid import nodes @@ -53,5 +56,5 @@ def visit_const(self, node: nodes.Const) -> None: self.add_message("unnecessary-ellipsis", node=node) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(EllipsisChecker(linter)) diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index e9ed418305..e9cbccdb48 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -3,9 +3,12 @@ # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt """Checks for various exception related errors.""" + +from __future__ import annotations + import builtins import inspect -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Any import astroid from astroid import nodes, objects @@ -44,7 +47,7 @@ def _annotated_unpack_infer(stmt, context=None): yield stmt, inferred -def _is_raising(body: List) -> bool: +def _is_raising(body: list) -> bool: """Return whether the given statement node raises an exception.""" return any(isinstance(node, nodes.Raise) for node in body) @@ -394,8 +397,8 @@ def _check_catching_non_exception(self, handler, exc, part): def _check_try_except_raise(self, node): def gather_exceptions_from_handler( handler, - ) -> Optional[List[nodes.NodeNG]]: - exceptions: List[nodes.NodeNG] = [] + ) -> list[nodes.NodeNG] | None: + exceptions: list[nodes.NodeNG] = [] if handler.type: exceptions_in_handler = utils.safe_infer(handler.type) if isinstance(exceptions_in_handler, nodes.Tuple): @@ -474,7 +477,7 @@ def visit_compare(self, node: nodes.Compare) -> None: def visit_tryexcept(self, node: nodes.TryExcept) -> None: """Check for empty except.""" self._check_try_except_raise(node) - exceptions_classes: List[Any] = [] + exceptions_classes: list[Any] = [] nb_handlers = len(node.handlers) for index, handler in enumerate(node.handlers): if handler.type is None: @@ -539,5 +542,5 @@ def visit_tryexcept(self, node: nodes.TryExcept) -> None: exceptions_classes += [exc for _, exc in exceptions] -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(ExceptionsChecker(linter)) diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index da203d35ca..df832a31af 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -11,9 +11,11 @@ Some parts of the process_token method is based from The Tab Nanny std module. """ +from __future__ import annotations + import tokenize from functools import reduce -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING from astroid import nodes @@ -322,7 +324,7 @@ def process_module(self, _node: nodes.Module) -> None: # pylint: disable-next=too-many-return-statements def _check_keyword_parentheses( - self, tokens: List[tokenize.TokenInfo], start: int + self, tokens: list[tokenize.TokenInfo], start: int ) -> None: """Check that there are not unnecessary parentheses after a keyword. @@ -670,7 +672,7 @@ def is_line_length_check_activated(pylint_pattern_match_object) -> bool: return True @staticmethod - def specific_splitlines(lines: str) -> List[str]: + def specific_splitlines(lines: str) -> list[str]: """Split lines according to universal newlines except those in a specific sets.""" unsplit_ends = { "\x0b", # synonym of \v @@ -768,5 +770,5 @@ def check_indent_level(self, string, expected, line_num): ) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(FormatChecker(linter)) diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index f1d5d6dd59..61f088ccfd 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -4,10 +4,12 @@ """Imports checkers for Python code.""" +from __future__ import annotations + import collections import copy import os -from typing import TYPE_CHECKING, Any, Dict, List, Set, Tuple, Union +from typing import TYPE_CHECKING, Any import astroid from astroid import nodes @@ -127,7 +129,7 @@ def _repr_tree_defs(data, indent_str=None): return "\n".join(lines) -def _dependencies_graph(filename: str, dep_info: Dict[str, Set[str]]) -> str: +def _dependencies_graph(filename: str, dep_info: dict[str, set[str]]) -> str: """Write dependencies as a dot (graphviz) file.""" done = {} printer = DotBackend(os.path.splitext(os.path.basename(filename))[0], rankdir="LR") @@ -147,7 +149,7 @@ def _dependencies_graph(filename: str, dep_info: Dict[str, Set[str]]) -> str: def _make_graph( - filename: str, dep_info: Dict[str, Set[str]], sect: Section, gtype: str + filename: str, dep_info: dict[str, set[str]], sect: Section, gtype: str ): """Generate a dependencies graph and add some information about it in the report's section @@ -370,15 +372,15 @@ class ImportsChecker(DeprecatedMixin, BaseChecker): ), ) - def __init__(self, linter: "PyLinter") -> None: + def __init__(self, linter: PyLinter) -> None: BaseChecker.__init__(self, linter) self.import_graph: collections.defaultdict = collections.defaultdict(set) - self._imports_stack: List[Tuple[Any, Any]] = [] + self._imports_stack: list[tuple[Any, Any]] = [] self._first_non_import_node = None - self._module_pkg: Dict[ + self._module_pkg: dict[ Any, Any ] = {} # mapping of modules to the pkg they belong in - self._allow_any_import_level: Set[Any] = set() + 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), @@ -478,8 +480,8 @@ def leave_module(self, node: nodes.Module) -> None: std_imports, ext_imports, loc_imports = self._check_imports_order(node) # Check that imports are grouped by package within a given category - met_import: Set[str] = set() # set for 'import x' style - met_from: Set[str] = set() # set for 'from x import y' style + met_import: set[str] = set() # set for 'import x' style + met_from: set[str] = set() # set for 'from x import y' style current_package = None for import_node, import_name in std_imports + ext_imports + loc_imports: met = met_from if isinstance(import_node, nodes.ImportFrom) else met_import @@ -746,7 +748,7 @@ def _get_imported_module(self, importnode, modname): return None def _add_imported_module( - self, node: Union[nodes.Import, nodes.ImportFrom], importedmodname: str + self, node: nodes.Import | nodes.ImportFrom, importedmodname: str ) -> None: """Notify an imported module, used to analyze dependencies.""" module_file = node.root().file @@ -775,7 +777,7 @@ def _add_imported_module( self._module_pkg[context_name] = context_name.rsplit(".", 1)[0] # handle dependencies - dependencies_stat: Dict[str, Set[str]] = self.linter.stats.dependencies + dependencies_stat: dict[str, set[str]] = self.linter.stats.dependencies importedmodnames = dependencies_stat.setdefault(importedmodname, set()) if context_name not in importedmodnames: importedmodnames.add(context_name) @@ -797,9 +799,7 @@ def _check_preferred_module(self, node, mod_path): args=(self.preferred_modules[mod_path], mod_path), ) - def _check_import_as_rename( - self, node: Union[nodes.Import, nodes.ImportFrom] - ) -> None: + def _check_import_as_rename(self, node: nodes.Import | nodes.ImportFrom) -> None: names = node.names for name in names: if not all(name): @@ -935,5 +935,5 @@ def _check_toplevel(self, node): ) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(ImportsChecker(linter)) diff --git a/pylint/checkers/logging.py b/pylint/checkers/logging.py index 83b838ad43..555a3331ac 100644 --- a/pylint/checkers/logging.py +++ b/pylint/checkers/logging.py @@ -3,8 +3,11 @@ # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt """Checker for use of Python logging.""" + +from __future__ import annotations + import string -from typing import TYPE_CHECKING, Set +from typing import TYPE_CHECKING import astroid from astroid import nodes @@ -149,7 +152,7 @@ def visit_module(self, _: nodes.Module) -> None: # 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[str] = set() + self._logging_names: set[str] = set() logging_mods = self.linter.namespace.logging_modules self._format_style = self.linter.namespace.logging_format_style @@ -380,5 +383,5 @@ def _count_supplied_tokens(args): return sum(1 for arg in args if not isinstance(arg, nodes.Keyword)) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(LoggingChecker(linter)) diff --git a/pylint/checkers/misc.py b/pylint/checkers/misc.py index 755ee2742b..63f7f1f61d 100644 --- a/pylint/checkers/misc.py +++ b/pylint/checkers/misc.py @@ -4,9 +4,11 @@ """Check source code is ascii only or has an encoding declaration (PEP 263).""" +from __future__ import annotations + import re import tokenize -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING from astroid import nodes @@ -37,7 +39,7 @@ class ByIdManagedMessagesChecker(BaseChecker): def _clear_by_id_managed_msgs(self) -> None: self.linter._by_id_managed_msgs.clear() - def _get_by_id_managed_msgs(self) -> List[ManagedMessage]: + def _get_by_id_managed_msgs(self) -> list[ManagedMessage]: return self.linter._by_id_managed_msgs def process_module(self, node: nodes.Module) -> None: @@ -111,7 +113,7 @@ def open(self): def _check_encoding( self, lineno: int, line: bytes, file_encoding: str - ) -> Optional[str]: + ) -> str | None: try: return line.decode(file_encoding) except UnicodeDecodeError: @@ -180,6 +182,6 @@ def process_tokens(self, tokens): ) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(EncodingChecker(linter)) linter.register_checker(ByIdManagedMessagesChecker(linter)) diff --git a/pylint/checkers/modified_iterating_checker.py b/pylint/checkers/modified_iterating_checker.py index ce401493ec..0b975cf659 100644 --- a/pylint/checkers/modified_iterating_checker.py +++ b/pylint/checkers/modified_iterating_checker.py @@ -2,7 +2,9 @@ # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt -from typing import TYPE_CHECKING, Union +from __future__ import annotations + +from typing import TYPE_CHECKING from astroid import nodes @@ -99,7 +101,7 @@ def _is_node_expr_that_calls_attribute_name(node: nodes.NodeNG) -> bool: def _common_cond_list_set( node: nodes.Expr, iter_obj: nodes.NodeNG, - infer_val: Union[nodes.List, nodes.Set], + infer_val: nodes.List | nodes.Set, ) -> bool: return (infer_val == utils.safe_infer(iter_obj)) and ( node.value.func.expr.name == iter_obj.name @@ -151,5 +153,5 @@ def _modified_iterating_set_cond( ) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(ModifiedIterationChecker(linter)) diff --git a/pylint/checkers/newstyle.py b/pylint/checkers/newstyle.py index 0b5f4963cc..a36ca851c8 100644 --- a/pylint/checkers/newstyle.py +++ b/pylint/checkers/newstyle.py @@ -3,6 +3,9 @@ # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt """Check for new / old style related problems.""" + +from __future__ import annotations + from typing import TYPE_CHECKING import astroid @@ -118,5 +121,5 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None: visit_asyncfunctiondef = visit_functiondef -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(NewStyleConflictChecker(linter)) diff --git a/pylint/checkers/non_ascii_names.py b/pylint/checkers/non_ascii_names.py index d91af4a3e0..d7892dbbc9 100644 --- a/pylint/checkers/non_ascii_names.py +++ b/pylint/checkers/non_ascii_names.py @@ -10,7 +10,7 @@ The following checkers are intended to make users are aware of these issues. """ -from typing import Optional, Union +from __future__ import annotations from astroid import nodes @@ -73,9 +73,7 @@ class NonAsciiNameChecker(base_checker.BaseChecker): name = "NonASCII-Checker" - def _check_name( - self, node_type: str, name: Optional[str], node: nodes.NodeNG - ) -> None: + def _check_name(self, node_type: str, name: str | None, node: nodes.NodeNG) -> None: """Check whether a name is using non-ASCII characters.""" if name is None: @@ -102,7 +100,7 @@ def visit_module(self, node: nodes.Module) -> None: @utils.check_messages("non-ascii-name") def visit_functiondef( - self, node: Union[nodes.FunctionDef, nodes.AsyncFunctionDef] + self, node: nodes.FunctionDef | nodes.AsyncFunctionDef ) -> None: self._check_name("function", node.name, node) @@ -163,7 +161,7 @@ def visit_classdef(self, node: nodes.ClassDef) -> None: if not any(node.instance_attr_ancestors(attr)): self._check_name("attr", attr, anodes[0]) - def _check_module_import(self, node: Union[nodes.ImportFrom, nodes.Import]) -> None: + def _check_module_import(self, node: nodes.ImportFrom | nodes.Import) -> None: for module_name, alias in node.names: name = alias or module_name self._check_name("module", name, node) diff --git a/pylint/checkers/raw_metrics.py b/pylint/checkers/raw_metrics.py index 64d6cfb803..9e0fe08f10 100644 --- a/pylint/checkers/raw_metrics.py +++ b/pylint/checkers/raw_metrics.py @@ -2,9 +2,11 @@ # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt +from __future__ import annotations + import sys import tokenize -from typing import TYPE_CHECKING, Any, Optional, cast +from typing import TYPE_CHECKING, Any, cast from pylint.checkers import BaseTokenChecker from pylint.interfaces import ITokenChecker @@ -23,7 +25,7 @@ def report_raw_stats( sect, stats: LinterStats, - old_stats: Optional[LinterStats], + old_stats: LinterStats | None, ) -> None: """Calculate percentage of code / doc / comment / empty.""" total_lines = stats.code_type_count["total"] @@ -110,5 +112,5 @@ def get_type(tokens, start_index): return i, pos[0] - start[0] + 1, line_type -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(RawMetricsChecker(linter)) diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py index b5075e8ee8..f60d9f1e50 100644 --- a/pylint/checkers/similar.py +++ b/pylint/checkers/similar.py @@ -16,6 +16,9 @@ in fact five lines which are common. Once postprocessed the values of association table are the result looked for, i.e start and end lines numbers of common lines in both files. """ + +from __future__ import annotations + import copy import functools import itertools @@ -31,14 +34,11 @@ TYPE_CHECKING, Any, Dict, - FrozenSet, Generator, Iterable, List, NamedTuple, NewType, - Optional, - Set, TextIO, Tuple, Union, @@ -93,8 +93,8 @@ class CplSuccessiveLinesLimits: def __init__( self, - first_file: "SuccessiveLinesLimits", - second_file: "SuccessiveLinesLimits", + first_file: SuccessiveLinesLimits, + second_file: SuccessiveLinesLimits, effective_cmn_lines_nb: int, ) -> None: self.first_file = first_file @@ -192,7 +192,7 @@ def __eq__(self, other) -> bool: def __hash__(self) -> int: return hash(self.fst_lineset_index) + hash(self.snd_lineset_index) - def increment(self, value: Index) -> "LineSetStartCouple": + def increment(self, value: Index) -> LineSetStartCouple: return LineSetStartCouple( Index(self.fst_lineset_index + value), Index(self.snd_lineset_index + value), @@ -203,8 +203,8 @@ def increment(self, value: Index) -> "LineSetStartCouple": def hash_lineset( - lineset: "LineSet", min_common_lines: int = DEFAULT_MIN_SIMILARITY_LINE -) -> Tuple[HashToIndex_T, IndexToLines_T]: + lineset: LineSet, min_common_lines: int = DEFAULT_MIN_SIMILARITY_LINE +) -> tuple[HashToIndex_T, IndexToLines_T]: """Return two dicts. The first associates the hash of successive stripped lines of a lineset @@ -289,9 +289,9 @@ def remove_successives(all_couples: CplIndexToCplLines_T) -> None: def filter_noncode_lines( - ls_1: "LineSet", + ls_1: LineSet, stindex_1: Index, - ls_2: "LineSet", + ls_2: LineSet, stindex_2: Index, common_lines_nb: int, ) -> int: @@ -324,10 +324,10 @@ def filter_noncode_lines( class Commonality(NamedTuple): cmn_lines_nb: int - fst_lset: "LineSet" + fst_lset: LineSet fst_file_start: LineNumber fst_file_end: LineNumber - snd_lset: "LineSet" + snd_lset: LineSet snd_file_start: LineNumber snd_file_end: LineNumber @@ -348,10 +348,10 @@ def __init__( self.ignore_docstrings = ignore_docstrings self.ignore_imports = ignore_imports self.ignore_signatures = ignore_signatures - self.linesets: List["LineSet"] = [] + self.linesets: list[LineSet] = [] def append_stream( - self, streamid: str, stream: STREAM_TYPES, encoding: Optional[str] = None + self, streamid: str, stream: STREAM_TYPES, encoding: str | None = None ) -> None: """Append a file to search for similarities.""" if isinstance(stream, BufferedIOBase): @@ -361,7 +361,7 @@ def append_stream( else: readlines = stream.readlines # type: ignore[assignment] # hint parameter is incorrectly typed as non-optional try: - active_lines: List[str] = [] + active_lines: list[str] = [] if hasattr(self, "linter"): # Remove those lines that should be ignored because of disables for index, line in enumerate(readlines()): @@ -389,9 +389,9 @@ def run(self) -> None: return self._display_sims(self._compute_sims()) - def _compute_sims(self) -> List[Tuple[int, Set[LinesChunkLimits_T]]]: + def _compute_sims(self) -> list[tuple[int, set[LinesChunkLimits_T]]]: """Compute similarities in appended files.""" - no_duplicates: Dict[int, List[Set[LinesChunkLimits_T]]] = defaultdict(list) + no_duplicates: dict[int, list[set[LinesChunkLimits_T]]] = defaultdict(list) for commonality in self._iter_sims(): num = commonality.cmn_lines_nb @@ -403,7 +403,7 @@ def _compute_sims(self) -> List[Tuple[int, Set[LinesChunkLimits_T]]]: end_line_2 = commonality.snd_file_end duplicate = no_duplicates[num] - couples: Set[LinesChunkLimits_T] + couples: set[LinesChunkLimits_T] for couples in duplicate: if (lineset1, start_line_1, end_line_1) in couples or ( lineset2, @@ -418,10 +418,10 @@ def _compute_sims(self) -> List[Tuple[int, Set[LinesChunkLimits_T]]]: (lineset2, start_line_2, end_line_2), } ) - sims: List[Tuple[int, Set[LinesChunkLimits_T]]] = [] - ensembles: List[Set[LinesChunkLimits_T]] + sims: list[tuple[int, set[LinesChunkLimits_T]]] = [] + ensembles: list[set[LinesChunkLimits_T]] for num, ensembles in no_duplicates.items(): - cpls: Set[LinesChunkLimits_T] + cpls: set[LinesChunkLimits_T] for cpls in ensembles: sims.append((num, cpls)) sims.sort() @@ -429,14 +429,14 @@ def _compute_sims(self) -> List[Tuple[int, Set[LinesChunkLimits_T]]]: return sims def _display_sims( - self, similarities: List[Tuple[int, Set[LinesChunkLimits_T]]] + self, similarities: list[tuple[int, set[LinesChunkLimits_T]]] ) -> None: """Display computed similarities on stdout.""" report = self._get_similarity_report(similarities) print(report) def _get_similarity_report( - self, similarities: List[Tuple[int, Set[LinesChunkLimits_T]]] + self, similarities: list[tuple[int, set[LinesChunkLimits_T]]] ) -> str: """Create a report from similarities.""" report: str = "" @@ -456,7 +456,7 @@ def _get_similarity_report( return report def _find_common( - self, lineset1: "LineSet", lineset2: "LineSet" + self, lineset1: LineSet, lineset2: LineSet ) -> Generator[Commonality, None, None]: """Find similarities in the two given linesets. @@ -474,8 +474,8 @@ def _find_common( hash_to_index_1, index_to_lines_1 = hash_lineset(lineset1, self.min_lines) hash_to_index_2, index_to_lines_2 = hash_lineset(lineset2, self.min_lines) - hash_1: FrozenSet[LinesChunk] = frozenset(hash_to_index_1.keys()) - hash_2: FrozenSet[LinesChunk] = frozenset(hash_to_index_2.keys()) + hash_1: frozenset[LinesChunk] = frozenset(hash_to_index_1.keys()) + hash_2: frozenset[LinesChunk] = frozenset(hash_to_index_2.keys()) common_hashes: Iterable[LinesChunk] = sorted( hash_1 & hash_2, key=lambda m: hash_to_index_1[m][0] @@ -553,7 +553,7 @@ def stripped_lines( ignore_docstrings: bool, ignore_imports: bool, ignore_signatures: bool, -) -> List[LineSpecifs]: +) -> list[LineSpecifs]: """Return tuples of line/line number/line type with leading/trailing whitespace and any ignored code features removed. :param lines: a collection of lines @@ -580,8 +580,8 @@ def stripped_lines( if ignore_signatures: def _get_functions( - functions: List[nodes.NodeNG], tree: nodes.NodeNG - ) -> List[nodes.NodeNG]: + functions: list[nodes.NodeNG], tree: nodes.NodeNG + ) -> list[nodes.NodeNG]: """Recursively get all functions including nested in the classes from the tree.""" for node in tree.body: @@ -653,7 +653,7 @@ class LineSet: def __init__( self, name: str, - lines: List[str], + lines: list[str], ignore_comments: bool = False, ignore_docstrings: bool = False, ignore_imports: bool = False, @@ -708,7 +708,7 @@ def real_lines(self): def report_similarities( sect, stats: LinterStats, - old_stats: Optional[LinterStats], + old_stats: LinterStats | None, ) -> None: """Make a layout with some stats about duplication.""" lines = ["", "now", "previous", "difference"] @@ -882,7 +882,7 @@ def reduce_map_data(self, linter, data): Similar.combine_mapreduce_data(self, linesets_collection=data) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(SimilarChecker(linter)) diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py index 4cd2e3f1aa..cf221dfadb 100644 --- a/pylint/checkers/spelling.py +++ b/pylint/checkers/spelling.py @@ -3,6 +3,9 @@ # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt """Checker for spelling errors in comments and docstrings.""" + +from __future__ import annotations + import os import re import tokenize @@ -445,5 +448,5 @@ def _check_docstring(self, node): self._check_spelling("wrong-spelling-in-docstring", line, start_line + idx) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(SpellingChecker(linter)) diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index 3cbb78f647..c2cb009436 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -4,9 +4,11 @@ """Checkers for various standard library functions.""" +from __future__ import annotations + import sys from collections.abc import Iterable -from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple +from typing import TYPE_CHECKING import astroid from astroid import nodes @@ -98,7 +100,7 @@ } -DEPRECATED_METHODS: Dict = { +DEPRECATED_METHODS: dict = { 0: { "cgi.parse_qs", "cgi.parse_qsl", @@ -440,15 +442,13 @@ class StdlibChecker(DeprecatedMixin, BaseChecker): ), } - def __init__(self, linter: "PyLinter") -> None: + def __init__(self, linter: PyLinter) -> None: BaseChecker.__init__(self, linter) - self._deprecated_methods: Set[str] = set() - self._deprecated_arguments: Dict[ - str, Tuple[Tuple[Optional[int], str], ...] - ] = {} - self._deprecated_classes: Dict[str, Set[str]] = {} - self._deprecated_modules: Set[str] = set() - self._deprecated_decorators: Set[str] = set() + self._deprecated_methods: set[str] = set() + self._deprecated_arguments: dict[str, tuple[tuple[int | None, str], ...]] = {} + self._deprecated_classes: dict[str, set[str]] = {} + self._deprecated_modules: set[str] = set() + self._deprecated_decorators: set[str] = set() for since_vers, func_list in DEPRECATED_METHODS[sys.version_info[0]].items(): if since_vers <= sys.version_info: @@ -570,7 +570,7 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None: def _check_lru_cache_decorators(self, decorators: nodes.Decorators) -> None: """Check if instance methods are decorated with functools.lru_cache.""" - lru_cache_nodes: List[nodes.NodeNG] = [] + lru_cache_nodes: list[nodes.NodeNG] = [] for d_node in decorators.nodes: try: for infered_node in d_node.infer(): @@ -766,5 +766,5 @@ def deprecated_decorators(self) -> Iterable: return self._deprecated_decorators -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(StdlibChecker(linter)) diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py index 1d447032c8..ed125fee4a 100644 --- a/pylint/checkers/strings.py +++ b/pylint/checkers/strings.py @@ -4,6 +4,8 @@ """Checker for string formatting operations.""" +from __future__ import annotations + import collections import numbers import re @@ -905,7 +907,7 @@ def _detect_u_string_prefix(self, node: nodes.Const): ) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(StringFormatChecker(linter)) linter.register_checker(StringConstantChecker(linter)) diff --git a/pylint/checkers/threading_checker.py b/pylint/checkers/threading_checker.py index 6a720d334f..cb565d79bc 100644 --- a/pylint/checkers/threading_checker.py +++ b/pylint/checkers/threading_checker.py @@ -2,6 +2,8 @@ # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt +from __future__ import annotations + from typing import TYPE_CHECKING from astroid import nodes @@ -56,5 +58,5 @@ def visit_with(self, node: nodes.With) -> None: self.add_message("useless-with-lock", node=node, args=qname) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(ThreadingChecker(linter)) diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 87a82fad80..48995faf1c 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -4,6 +4,8 @@ """Try to find more bugs in the code using astroid inference capabilities.""" +from __future__ import annotations + import fnmatch import heapq import itertools @@ -15,19 +17,8 @@ from collections import deque from collections.abc import Sequence from functools import singledispatch -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Iterator, - List, - Optional, - Pattern, - Tuple, - Union, -) +from typing import TYPE_CHECKING, Any, Callable, Iterator, Pattern, Union -import astroid import astroid.exceptions from astroid import bases, nodes @@ -533,7 +524,7 @@ def _emit_no_member( def _determine_callable( callable_obj: nodes.NodeNG, -) -> Tuple[CallableObjects, int, str]: +) -> tuple[CallableObjects, int, str]: # pylint: disable=fixme # TODO: The typing of the second return variable is actually Literal[0,1] # We need typing on astroid.NodeNG.implicit_parameters for this @@ -893,7 +884,7 @@ def _suggestion_mode(self): return get_global_option(self, "suggestion-mode", default=True) @cached_property - def _compiled_generated_members(self) -> Tuple[Pattern, ...]: + def _compiled_generated_members(self) -> tuple[Pattern, ...]: # do this lazily since config not fully initialized in __init__ # generated_members may contain regular expressions # (surrounded by quote `"` and followed by a comma `,`) @@ -1158,7 +1149,7 @@ def _check_assignment_from_function_call(self, node: nodes.Assign) -> None: @staticmethod def _is_ignored_function( - function_node: Union[nodes.FunctionDef, bases.UnboundMethod] + function_node: nodes.FunctionDef | bases.UnboundMethod, ) -> bool: return ( isinstance(function_node, nodes.AsyncFunctionDef) @@ -1372,7 +1363,7 @@ def visit_call(self, node: nodes.Call) -> None: # 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: List[List[Any]] = [] + parameters: list[list[Any]] = [] parameter_name_to_index = {} for i, arg in enumerate(args): if isinstance(arg, nodes.Tuple): @@ -1610,7 +1601,7 @@ def _check_invalid_sequence_index(self, subscript: nodes.Subscript): return None def _check_not_callable( - self, node: nodes.Call, inferred_call: Optional[nodes.NodeNG] + self, node: nodes.Call, inferred_call: nodes.NodeNG | None ) -> None: """Checks to see if the not-callable message should be emitted. @@ -1650,7 +1641,7 @@ def visit_extslice(self, node: nodes.ExtSlice) -> None: def _check_invalid_slice_index(self, node: nodes.Slice) -> None: # Check the type of each part of the slice - invalid_slices_nodes: List[nodes.NodeNG] = [] + invalid_slices_nodes: list[nodes.NodeNG] = [] for index in (node.lower, node.upper, node.step): if index is None: continue @@ -1888,7 +1879,7 @@ def visit_compare(self, node: nodes.Compare) -> None: def visit_subscript(self, node: nodes.Subscript) -> None: self._check_invalid_sequence_index(node) - supported_protocol: Optional[Callable[[Any, Any], bool]] = None + supported_protocol: Callable[[Any, Any], bool] | None = None if isinstance(node.value, (nodes.ListComp, nodes.DictComp)): return @@ -2094,6 +2085,6 @@ def visit_generatorexp(self, node: nodes.GeneratorExp) -> None: self._check_iterable(gen.iter, check_async=gen.is_async) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(TypeChecker(linter)) linter.register_checker(IterableChecker(linter)) diff --git a/pylint/checkers/unicode.py b/pylint/checkers/unicode.py index ca9c635ce0..1f248eb780 100644 --- a/pylint/checkers/unicode.py +++ b/pylint/checkers/unicode.py @@ -10,6 +10,9 @@ The following checkers are intended to make users are aware of these issues. """ + +from __future__ import annotations + import codecs import contextlib import io @@ -17,7 +20,7 @@ from collections import OrderedDict from functools import lru_cache from tokenize import detect_encoding -from typing import Dict, Iterable, NamedTuple, Tuple, TypeVar +from typing import Iterable, NamedTuple, TypeVar from astroid import nodes @@ -151,10 +154,10 @@ def _line_length(line: _StrLike, codec: str) -> int: def _map_positions_to_result( line: _StrLike, - search_dict: Dict[_StrLike, _BadChar], + search_dict: dict[_StrLike, _BadChar], new_line: _StrLike, byte_str_length: int = 1, -) -> Dict[int, _BadChar]: +) -> dict[int, _BadChar]: """Get all occurrences of search dict keys within line. Ignores Windows end of line and can handle bytes as well as string. @@ -162,7 +165,7 @@ def _map_positions_to_result( default to 8 Bit. """ - result: Dict[int, _BadChar] = {} + result: dict[int, _BadChar] = {} for search_for, char in search_dict.items(): if search_for not in line: @@ -379,7 +382,7 @@ def _is_unicode(codec: str) -> bool: return codec.startswith("utf") @classmethod - def _find_line_matches(cls, line: bytes, codec: str) -> Dict[int, _BadChar]: + def _find_line_matches(cls, line: bytes, codec: str) -> dict[int, _BadChar]: """Find all matches of BAD_CHARS within line. Args: @@ -399,7 +402,7 @@ def _find_line_matches(cls, line: bytes, codec: str) -> Dict[int, _BadChar]: # If we can't decode properly, we simply use bytes, even so the column offsets # might be wrong a bit, but it is still better then nothing line_search_byte = line - search_dict_byte: Dict[bytes, _BadChar] = {} + search_dict_byte: dict[bytes, _BadChar] = {} for char in BAD_CHARS: # Some characters might not exist in all encodings with contextlib.suppress(UnicodeDecodeError): @@ -415,7 +418,7 @@ def _find_line_matches(cls, line: bytes, codec: str) -> Dict[int, _BadChar]: ) @staticmethod - def _determine_codec(stream: io.BytesIO) -> Tuple[str, int]: + def _determine_codec(stream: io.BytesIO) -> tuple[str, int]: """Determine the codec from the given stream. first tries https://www.python.org/dev/peps/pep-0263/ diff --git a/pylint/checkers/unsupported_version.py b/pylint/checkers/unsupported_version.py index d63d4fb20b..8b7796bfed 100644 --- a/pylint/checkers/unsupported_version.py +++ b/pylint/checkers/unsupported_version.py @@ -6,6 +6,7 @@ indicated by the py-version setting. """ +from __future__ import annotations from typing import TYPE_CHECKING @@ -82,5 +83,5 @@ def _check_typing_final(self, node: nodes.Decorators) -> None: ) -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(UnsupportedVersionChecker(linter)) diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 0267dfd582..a150394a2b 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -3,6 +3,9 @@ # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt """Some functions that may be useful for various checkers.""" + +from __future__ import annotations + import builtins import itertools import numbers @@ -10,22 +13,9 @@ import string import warnings from functools import lru_cache, partial -from typing import ( - Callable, - Dict, - Iterable, - List, - Match, - Optional, - Set, - Tuple, - Type, - TypeVar, - Union, -) +from typing import Callable, Iterable, Match, TypeVar import _string -import astroid import astroid.objects from astroid import TooManyLevelsError, nodes from astroid.context import InferenceContext @@ -363,9 +353,7 @@ def is_defined_before(var_node: nodes.Name) -> bool: return False -def is_default_argument( - node: nodes.NodeNG, scope: Optional[nodes.NodeNG] = None -) -> bool: +def is_default_argument(node: nodes.NodeNG, scope: nodes.NodeNG | None = None) -> bool: """Return true if the given Name node is used in function or lambda default argument's value """ @@ -460,7 +448,7 @@ def __init__(self, index): def parse_format_string( format_string: str, -) -> Tuple[Set[str], int, Dict[str, str], List[str]]: +) -> tuple[set[str], int, dict[str, str], list[str]]: """Parses a format string, returning a tuple (keys, num_args). Where 'keys' is the set of mapping keys in the format string, and 'num_args' is the number @@ -534,14 +522,14 @@ def next_char(i): return keys, num_args, key_types, pos_types -def split_format_field_names(format_string) -> Tuple[str, Iterable[Tuple[bool, str]]]: +def split_format_field_names(format_string) -> tuple[str, Iterable[tuple[bool, str]]]: try: return _string.formatter_field_name_split(format_string) except ValueError as e: raise IncompleteFormatString() from e -def collect_string_fields(format_string) -> Iterable[Optional[str]]: +def collect_string_fields(format_string) -> Iterable[str | None]: """Given a format string, return an iterator of all the valid format fields. @@ -576,7 +564,7 @@ def collect_string_fields(format_string) -> Iterable[Optional[str]]: def parse_format_method_string( format_string: str, -) -> Tuple[List[Tuple[str, List[Tuple[bool, str]]]], int, int]: +) -> tuple[list[tuple[str, list[tuple[bool, str]]]], int, int]: """Parses a PEP 3101 format string, returning a tuple of (keyword_arguments, implicit_pos_args_cnt, explicit_pos_args). @@ -614,7 +602,7 @@ def is_attr_protected(attrname: str) -> bool: ) -def node_frame_class(node: nodes.NodeNG) -> Optional[nodes.ClassDef]: +def node_frame_class(node: nodes.NodeNG) -> nodes.ClassDef | None: """Return the class that is wrapping the given node. The function returns a class for a method node (or a staticmethod or a @@ -639,14 +627,14 @@ def node_frame_class(node: nodes.NodeNG) -> Optional[nodes.ClassDef]: return klass -def get_outer_class(class_node: astroid.ClassDef) -> Optional[astroid.ClassDef]: +def get_outer_class(class_node: astroid.ClassDef) -> astroid.ClassDef | None: """Return the class that is the outer class of given (nested) class_node.""" parent_klass = class_node.parent.frame(future=True) return parent_klass if isinstance(parent_klass, astroid.ClassDef) else None -def is_attr_private(attrname: str) -> Optional[Match[str]]: +def is_attr_private(attrname: str) -> Match[str] | None: """Check that attribute name is private (at least two leading underscores, at most one trailing underscore) """ @@ -655,7 +643,7 @@ def is_attr_private(attrname: str) -> Optional[Match[str]]: def get_argument_from_call( - call_node: nodes.Call, position: Optional[int] = None, keyword: Optional[str] = None + call_node: nodes.Call, position: int | None = None, keyword: str | None = None ) -> nodes.Name: """Returns the specified argument from a function call. @@ -769,7 +757,7 @@ def _is_property_decorator(decorator: nodes.Name) -> bool: elif isinstance(inferred, nodes.FunctionDef): # If decorator is function, check if it has exactly one return # and the return is itself a function decorated with property - returns: List[nodes.Return] = list( + returns: list[nodes.Return] = list( inferred._get_return_nodes_skip_functions() ) if len(returns) == 1 and isinstance( @@ -786,9 +774,9 @@ def _is_property_decorator(decorator: nodes.Name) -> bool: def decorated_with( - func: Union[ - nodes.ClassDef, nodes.FunctionDef, astroid.BoundMethod, astroid.UnboundMethod - ], + func: ( + nodes.ClassDef | nodes.FunctionDef | astroid.BoundMethod | astroid.UnboundMethod + ), qnames: Iterable[str], ) -> bool: """Determine if the `func` node has a decorator with the qualified name `qname`.""" @@ -811,7 +799,7 @@ def decorated_with( def uninferable_final_decorators( node: nodes.Decorators, -) -> List[Optional[Union[nodes.Attribute, nodes.Name]]]: +) -> list[nodes.Attribute | nodes.Name | None]: """Return a list of uninferable `typing.final` decorators in `node`. This function is used to determine if the `typing.final` decorator is used @@ -857,7 +845,7 @@ def uninferable_final_decorators( @lru_cache(maxsize=1024) def unimplemented_abstract_methods( node: nodes.ClassDef, is_abstract_cb: nodes.FunctionDef = None -) -> Dict[str, nodes.NodeNG]: +) -> dict[str, nodes.NodeNG]: """Get the unimplemented abstract methods for the given *node*. A method can be considered abstract if the callback *is_abstract_cb* @@ -870,7 +858,7 @@ def unimplemented_abstract_methods( """ if is_abstract_cb is None: is_abstract_cb = partial(decorated_with, qnames=ABC_METHODS) - visited: Dict[str, nodes.NodeNG] = {} + visited: dict[str, nodes.NodeNG] = {} try: mro = reversed(node.mro()) except NotImplementedError: @@ -914,7 +902,7 @@ def unimplemented_abstract_methods( def find_try_except_wrapper_node( node: nodes.NodeNG, -) -> Optional[Union[nodes.ExceptHandler, nodes.TryExcept]]: +) -> nodes.ExceptHandler | nodes.TryExcept | None: """Return the ExceptHandler or the TryExcept node in which the node is.""" current = node ignores = (nodes.ExceptHandler, nodes.TryExcept) @@ -928,7 +916,7 @@ def find_try_except_wrapper_node( def find_except_wrapper_node_in_scope( node: nodes.NodeNG, -) -> Optional[Union[nodes.ExceptHandler, nodes.TryExcept]]: +) -> nodes.ExceptHandler | nodes.TryExcept | None: """Return the ExceptHandler in which the node is, without going out of scope.""" for current in node.node_ancestors(): if isinstance(current, astroid.scoped_nodes.LocalsDictNodeNG): @@ -969,7 +957,7 @@ def is_from_fallback_block(node: nodes.NodeNG) -> bool: def _except_handlers_ignores_exceptions( handlers: nodes.ExceptHandler, - exceptions: Tuple[Type[ImportError], Type[ModuleNotFoundError]], + exceptions: tuple[type[ImportError], type[ModuleNotFoundError]], ) -> bool: func = partial(error_of_type, error_type=exceptions) return any(func(handler) for handler in handlers) @@ -977,7 +965,7 @@ def _except_handlers_ignores_exceptions( def get_exception_handlers( node: nodes.NodeNG, exception=Exception -) -> Optional[List[nodes.ExceptHandler]]: +) -> list[nodes.ExceptHandler] | None: """Return the collections of handlers handling the exception in arguments. Args: @@ -1188,7 +1176,7 @@ def supports_delitem(value: nodes.NodeNG, _: nodes.NodeNG) -> bool: return _supports_protocol(value, _supports_delitem_protocol) -def _get_python_type_of_node(node: nodes.NodeNG) -> Optional[str]: +def _get_python_type_of_node(node: nodes.NodeNG) -> str | None: pytype = getattr(node, "pytype", None) if callable(pytype): return pytype() @@ -1197,14 +1185,14 @@ def _get_python_type_of_node(node: nodes.NodeNG) -> Optional[str]: @lru_cache(maxsize=1024) def safe_infer( - node: nodes.NodeNG, context: Optional[InferenceContext] = None -) -> Union[nodes.NodeNG, Type[astroid.Uninferable], None]: + node: nodes.NodeNG, context: InferenceContext | None = None +) -> nodes.NodeNG | type[astroid.Uninferable] | None: """Return the inferred value for the given node. Return None if inference failed or if there is some ambiguity (more than one node has been inferred of different types). """ - inferred_types: Set[Optional[str]] = set() + inferred_types: set[str | None] = set() try: infer_gen = node.infer(context=context) value = next(infer_gen) @@ -1237,7 +1225,7 @@ def safe_infer( @lru_cache(maxsize=512) def infer_all( node: nodes.NodeNG, context: InferenceContext = None -) -> List[nodes.NodeNG]: +) -> list[nodes.NodeNG]: try: return list(node.infer(context=context)) except astroid.InferenceError: @@ -1271,7 +1259,7 @@ def is_none(node: nodes.NodeNG) -> bool: ) -def node_type(node: nodes.NodeNG) -> Optional[nodes.NodeNG]: +def node_type(node: nodes.NodeNG) -> nodes.NodeNG | None: """Return the inferred type for `node`. If there is more than one possible type, or if inferred type is Uninferable or None, @@ -1279,7 +1267,7 @@ def node_type(node: nodes.NodeNG) -> Optional[nodes.NodeNG]: """ # check there is only one possible type for the assign node. Else we # don't handle it for now - types: Set[nodes.NodeNG] = set() + types: set[nodes.NodeNG] = set() try: for var_type in node.infer(): if var_type == astroid.Uninferable or is_none(var_type): @@ -1452,7 +1440,7 @@ def is_call_of_name(node: nodes.NodeNG, name: str) -> bool: def is_test_condition( node: nodes.NodeNG, - parent: Optional[nodes.NodeNG] = None, + parent: nodes.NodeNG | None = None, ) -> bool: """Returns true if the given node is being tested for truthiness.""" parent = parent or node.parent @@ -1471,7 +1459,7 @@ def is_classdef_type(node: nodes.ClassDef) -> bool: def is_attribute_typed_annotation( - node: Union[nodes.ClassDef, astroid.Instance], attr_name: str + node: nodes.ClassDef | astroid.Instance, attr_name: str ) -> bool: """Test if attribute is typed annotation in current node or any base nodes. @@ -1515,9 +1503,7 @@ def is_assign_name_annotated_with(node: nodes.AssignName, typing_name: str) -> b return False -def get_iterating_dictionary_name( - node: Union[nodes.For, nodes.Comprehension] -) -> Optional[str]: +def get_iterating_dictionary_name(node: nodes.For | nodes.Comprehension) -> str | None: """Get the name of the dictionary which keys are being iterated over on a ``nodes.For`` or ``nodes.Comprehension`` node. @@ -1559,9 +1545,7 @@ def get_subscript_const_value(node: nodes.Subscript) -> nodes.Const: return inferred -def get_import_name( - importnode: Union[nodes.Import, nodes.ImportFrom], modname: str -) -> str: +def get_import_name(importnode: nodes.Import | nodes.ImportFrom, modname: str) -> str: """Get a prepared module name from the given import node. In the case of relative imports, this will return the @@ -1662,15 +1646,15 @@ def is_function_body_ellipsis(node: nodes.FunctionDef) -> bool: ) -def is_base_container(node: Optional[nodes.NodeNG]) -> bool: +def is_base_container(node: nodes.NodeNG | None) -> bool: return isinstance(node, nodes.BaseContainer) and not node.elts -def is_empty_dict_literal(node: Optional[nodes.NodeNG]) -> bool: +def is_empty_dict_literal(node: nodes.NodeNG | None) -> bool: return isinstance(node, nodes.Dict) and not node.items -def is_empty_str_literal(node: Optional[nodes.NodeNG]) -> bool: +def is_empty_str_literal(node: nodes.NodeNG | None) -> bool: return ( isinstance(node, nodes.Const) and isinstance(node.value, str) and not node.value ) @@ -1686,8 +1670,8 @@ def returns_bool(node: nodes.NodeNG) -> bool: def get_node_first_ancestor_of_type( - node: nodes.NodeNG, ancestor_type: Union[Type[T_Node], Tuple[Type[T_Node], ...]] -) -> Optional[T_Node]: + node: nodes.NodeNG, ancestor_type: type[T_Node] | tuple[type[T_Node], ...] +) -> T_Node | None: """Return the first parent node that is any of the provided types (or None).""" for ancestor in node.node_ancestors(): if isinstance(ancestor, ancestor_type): @@ -1696,8 +1680,8 @@ def get_node_first_ancestor_of_type( def get_node_first_ancestor_of_type_and_its_child( - node: nodes.NodeNG, ancestor_type: Union[Type[T_Node], Tuple[Type[T_Node], ...]] -) -> Union[Tuple[None, None], Tuple[T_Node, nodes.NodeNG]]: + node: nodes.NodeNG, ancestor_type: type[T_Node] | tuple[type[T_Node], ...] +) -> tuple[None, None] | tuple[T_Node, nodes.NodeNG]: """Modified version of get_node_first_ancestor_of_type to also return the descendant visited directly before reaching the sought ancestor diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index e5dead864a..06809caf14 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -3,6 +3,9 @@ # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt """Variables checkers for Python code.""" + +from __future__ import annotations + import collections import copy import itertools @@ -11,20 +14,7 @@ import sys from enum import Enum from functools import lru_cache -from typing import ( - TYPE_CHECKING, - Any, - DefaultDict, - Dict, - Iterable, - Iterator, - List, - NamedTuple, - Optional, - Set, - Tuple, - Union, -) +from typing import TYPE_CHECKING, Any, DefaultDict, Iterable, Iterator, NamedTuple import astroid from astroid import nodes @@ -348,8 +338,8 @@ def _import_name_is_global(stmt, global_names): def _flattened_scope_names( - iterator: Iterator[Union[nodes.Global, nodes.Nonlocal]] -) -> Set[str]: + iterator: Iterator[nodes.Global | nodes.Nonlocal], +) -> set[str]: values = (set(stmt.names) for stmt in iterator) return set(itertools.chain.from_iterable(values)) @@ -523,9 +513,9 @@ def _has_locals_call_after_node(stmt, scope): class ScopeConsumer(NamedTuple): """Store nodes and their consumption states.""" - to_consume: Dict[str, List[nodes.NodeNG]] - consumed: Dict[str, List[nodes.NodeNG]] - consumed_uncertain: DefaultDict[str, List[nodes.NodeNG]] + to_consume: dict[str, list[nodes.NodeNG]] + consumed: dict[str, list[nodes.NodeNG]] + consumed_uncertain: DefaultDict[str, list[nodes.NodeNG]] scope_type: str @@ -566,7 +556,7 @@ def consumed(self): return self._atomic.consumed @property - def consumed_uncertain(self) -> DefaultDict[str, List[nodes.NodeNG]]: + def consumed_uncertain(self) -> DefaultDict[str, list[nodes.NodeNG]]: """Retrieves nodes filtered out by get_next_to_consume() that may not have executed, such as statements in except blocks, or statements @@ -594,7 +584,7 @@ def mark_as_consumed(self, name, consumed_nodes): else: del self.to_consume[name] - def get_next_to_consume(self, node: nodes.Name) -> Optional[List[nodes.NodeNG]]: + def get_next_to_consume(self, node: nodes.Name) -> list[nodes.NodeNG] | None: """Return a list of the nodes that define `node` from this scope. If it is uncertain whether a node will be consumed, such as for statements in @@ -680,10 +670,10 @@ def get_next_to_consume(self, node: nodes.Name) -> Optional[List[nodes.NodeNG]]: @staticmethod def _uncertain_nodes_in_except_blocks( - found_nodes: List[nodes.NodeNG], + found_nodes: list[nodes.NodeNG], node: nodes.NodeNG, node_statement: nodes.Statement, - ) -> List[nodes.NodeNG]: + ) -> list[nodes.NodeNG]: """Return any nodes in ``found_nodes`` that should be treated as uncertain because they are in an except block. """ @@ -799,9 +789,9 @@ def _check_loop_finishes_via_except( """ if not other_node_try_except.orelse: return False - closest_loop: Optional[ - Union[nodes.For, nodes.While] - ] = utils.get_node_first_ancestor_of_type(node, (nodes.For, nodes.While)) + closest_loop: None | ( + nodes.For | nodes.While + ) = utils.get_node_first_ancestor_of_type(node, (nodes.For, nodes.While)) if closest_loop is None: return False if not any( @@ -819,7 +809,7 @@ def _check_loop_finishes_via_except( return False def _try_in_loop_body( - other_node_try_except: nodes.TryExcept, loop: Union[nodes.For, nodes.While] + other_node_try_except: nodes.TryExcept, loop: nodes.For | nodes.While ) -> bool: """Return True if `other_node_try_except` is a descendant of `loop`.""" return any( @@ -869,12 +859,12 @@ def _recursive_search_for_continue_before_break( @staticmethod def _uncertain_nodes_in_try_blocks_when_evaluating_except_blocks( - found_nodes: List[nodes.NodeNG], node_statement: nodes.Statement - ) -> List[nodes.NodeNG]: + found_nodes: list[nodes.NodeNG], node_statement: nodes.Statement + ) -> list[nodes.NodeNG]: """Return any nodes in ``found_nodes`` that should be treated as uncertain because they are in a try block and the ``node_statement`` being evaluated is in one of its except handlers. """ - uncertain_nodes: List[nodes.NodeNG] = [] + uncertain_nodes: list[nodes.NodeNG] = [] closest_except_handler = utils.get_node_first_ancestor_of_type( node_statement, nodes.ExceptHandler ) @@ -915,9 +905,9 @@ def _uncertain_nodes_in_try_blocks_when_evaluating_except_blocks( @staticmethod def _uncertain_nodes_in_try_blocks_when_evaluating_finally_blocks( - found_nodes: List[nodes.NodeNG], node_statement: nodes.Statement - ) -> List[nodes.NodeNG]: - uncertain_nodes: List[nodes.NodeNG] = [] + found_nodes: list[nodes.NodeNG], node_statement: nodes.Statement + ) -> list[nodes.NodeNG]: + uncertain_nodes: list[nodes.NodeNG] = [] ( closest_try_finally_ancestor, child_of_closest_try_finally_ancestor, @@ -1075,12 +1065,12 @@ class VariablesChecker(BaseChecker): def __init__(self, linter=None): super().__init__(linter) - self._to_consume: List[NamesConsumer] = [] + self._to_consume: list[NamesConsumer] = [] self._checking_mod_attr = None self._loop_variables = [] self._type_annotation_names = [] - self._except_handler_names_queue: List[ - Tuple[nodes.ExceptHandler, nodes.AssignName] + self._except_handler_names_queue: list[ + tuple[nodes.ExceptHandler, nodes.AssignName] ] = [] """This is a queue, last in first out.""" self._postponed_evaluation_enabled = False @@ -1278,7 +1268,7 @@ def leave_functiondef(self, node: nodes.FunctionDef) -> None: global_names = _flattened_scope_names(node.nodes_of_class(nodes.Global)) nonlocal_names = _flattened_scope_names(node.nodes_of_class(nodes.Nonlocal)) - comprehension_target_names: List[str] = [] + comprehension_target_names: list[str] = [] for comprehension_scope in node.nodes_of_class(nodes.ComprehensionScope): for generator in comprehension_scope.generators: @@ -1495,8 +1485,8 @@ def _should_node_be_skipped( def _find_assigned_names_recursive( self, - target: Union[nodes.AssignName, nodes.BaseContainer], - target_names: List[str], + target: nodes.AssignName | nodes.BaseContainer, + target_names: list[str], ) -> None: """Update `target_names` in place with the names of assignment targets, recursively (to account for nested assignments). @@ -1515,7 +1505,7 @@ def _check_consumer( frame: nodes.LocalsDictNodeNG, current_consumer: NamesConsumer, base_scope_type: Any, - ) -> Tuple[VariableVisitConsumerAction, Optional[List[nodes.NodeNG]]]: + ) -> tuple[VariableVisitConsumerAction, list[nodes.NodeNG] | None]: """Checks a consumer for conditions that should trigger messages.""" # If the name has already been consumed, only check it's not a loop # variable used outside the loop. @@ -1868,7 +1858,7 @@ def _is_variable_violation( defframe, base_scope_type, is_recursive_klass, - ) -> Tuple[bool, bool, bool]: + ) -> tuple[bool, bool, bool]: # pylint: disable=too-many-nested-blocks maybe_before_assign = True annotation_return = False @@ -2117,8 +2107,8 @@ def _is_only_type_assignment(node: nodes.Name, defstmt: nodes.Statement) -> bool @staticmethod def _is_first_level_self_reference( - node: nodes.Name, defstmt: nodes.ClassDef, found_nodes: List[nodes.NodeNG] - ) -> Tuple[VariableVisitConsumerAction, Optional[List[nodes.NodeNG]]]: + node: nodes.Name, defstmt: nodes.ClassDef, found_nodes: list[nodes.NodeNG] + ) -> tuple[VariableVisitConsumerAction, list[nodes.NodeNG] | None]: """Check if a first level method's annotation or default values refers to its own class, and return a consumer action """ @@ -2290,7 +2280,7 @@ def _check_is_unused( stmt, global_names, nonlocal_names: Iterable[str], - comprehension_target_names: List[str], + comprehension_target_names: list[str], ) -> None: # Ignore some special names specified by user configuration. if self._is_name_ignored(stmt, name): @@ -2533,7 +2523,7 @@ def _store_type_annotation_names(self, node): def _check_self_cls_assign(self, node: nodes.Assign) -> None: """Check that self/cls don't get assigned.""" - assign_names: Set[Optional[str]] = set() + assign_names: set[str | None] = set() for target in node.targets: if isinstance(target, nodes.AssignName): assign_names.add(target.name) @@ -2604,7 +2594,7 @@ def _check_unpacking(self, inferred, node, targets): ) @staticmethod - def _nodes_to_unpack(node: nodes.NodeNG) -> Optional[List[nodes.NodeNG]]: + def _nodes_to_unpack(node: nodes.NodeNG) -> list[nodes.NodeNG] | None: """Return the list of values of the `Assign` node.""" if isinstance(node, (nodes.Tuple, nodes.List)): return node.itered() @@ -2708,7 +2698,7 @@ def _check_imports(self, not_consumed): local_names = _fix_dot_imports(not_consumed) checked = set() unused_wildcard_imports: DefaultDict[ - Tuple[str, nodes.ImportFrom], List[str] + tuple[str, nodes.ImportFrom], list[str] ] = collections.defaultdict(list) for name, stmt in local_names: for imports in stmt.names: @@ -2853,7 +2843,7 @@ def visit_subscript(self, node: nodes.Subscript) -> None: self._check_potential_index_error(node, inferred_slice) def _check_potential_index_error( - self, node: nodes.Subscript, inferred_slice: Optional[nodes.NodeNG] + self, node: nodes.Subscript, inferred_slice: nodes.NodeNG | None ) -> None: """Check for the potential-index-error message.""" # Currently we only check simple slices of a single integer @@ -2872,5 +2862,5 @@ def _check_potential_index_error( return -def register(linter: "PyLinter") -> None: +def register(linter: PyLinter) -> None: linter.register_checker(VariablesChecker(linter)) diff --git a/pylint/message/message.py b/pylint/message/message.py index afe10f7f3e..67f7875722 100644 --- a/pylint/message/message.py +++ b/pylint/message/message.py @@ -63,7 +63,7 @@ def __new__( cls, msg_id: str, symbol: str, - location: (tuple[str, str, str, str, int, int] | MessageLocationTuple), + location: tuple[str, str, str, str, int, int] | MessageLocationTuple, msg: str, confidence: Confidence | None, ) -> Message: