From e94fb1094070ccf4b4fa44468cf5dde08bda68c7 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 31 Jul 2022 19:38:54 -0400 Subject: [PATCH] require python>=3.7 --- .github/workflows/main.yml | 5 +- .pre-commit-config.yaml | 8 +- bin/gen-pycodestyle-plugin | 14 ++-- docs/source/conf.py | 2 + example-plugin/setup.py | 2 + .../src/flake8_example_plugin/__init__.py | 2 + .../flake8_example_plugin/off_by_default.py | 1 + .../flake8_example_plugin/on_by_default.py | 1 + setup.cfg | 3 +- setup.py | 2 + src/flake8/__init__.py | 8 +- src/flake8/__main__.py | 2 + src/flake8/_compat.py | 2 + src/flake8/api/__init__.py | 1 + src/flake8/api/legacy.py | 21 +++-- src/flake8/checker.py | 31 ++++---- src/flake8/defaults.py | 2 + src/flake8/discover_files.py | 2 + src/flake8/exceptions.py | 1 + src/flake8/formatting/__init__.py | 1 + src/flake8/formatting/_windows_color.py | 2 + src/flake8/formatting/base.py | 19 +++-- src/flake8/formatting/default.py | 15 ++-- src/flake8/main/__init__.py | 1 + src/flake8/main/application.py | 33 ++++---- src/flake8/main/cli.py | 5 +- src/flake8/main/debug.py | 5 +- src/flake8/main/options.py | 2 + src/flake8/options/__init__.py | 1 + src/flake8/options/aggregator.py | 5 +- src/flake8/options/config.py | 18 ++--- src/flake8/options/manager.py | 76 +++++++++---------- src/flake8/plugins/__init__.py | 1 + src/flake8/plugins/finder.py | 51 ++++++------- src/flake8/plugins/pycodestyle.py | 7 +- src/flake8/plugins/pyflakes.py | 13 ++-- src/flake8/plugins/reporter.py | 5 +- src/flake8/processor.py | 38 +++++----- src/flake8/statistics.py | 21 +++-- src/flake8/style_guide.py | 62 +++++++-------- src/flake8/utils.py | 32 ++++---- src/flake8/violation.py | 11 ++- tests/__init__.py | 1 + tests/conftest.py | 2 + tests/integration/subdir/aplugin.py | 1 + tests/integration/test_aggregator.py | 2 + tests/integration/test_api_legacy.py | 2 + tests/integration/test_checker.py | 2 + tests/integration/test_main.py | 2 + tests/integration/test_plugins.py | 2 + tests/unit/conftest.py | 2 + tests/unit/plugins/finder_test.py | 2 + tests/unit/plugins/pycodestyle_test.py | 2 + tests/unit/plugins/reporter_test.py | 2 + tests/unit/test_application.py | 2 + tests/unit/test_base_formatter.py | 2 + tests/unit/test_checker_manager.py | 2 + tests/unit/test_debug.py | 2 + tests/unit/test_decision_engine.py | 2 + tests/unit/test_discover_files.py | 2 + tests/unit/test_exceptions.py | 2 + tests/unit/test_file_checker.py | 2 + tests/unit/test_file_processor.py | 2 + tests/unit/test_filenameonly_formatter.py | 2 + tests/unit/test_legacy_api.py | 2 + tests/unit/test_main_options.py | 2 + tests/unit/test_nothing_formatter.py | 2 + tests/unit/test_option.py | 2 + tests/unit/test_option_manager.py | 2 + tests/unit/test_options_config.py | 2 + tests/unit/test_pyflakes_codes.py | 2 + tests/unit/test_statistics.py | 2 + tests/unit/test_style_guide.py | 2 + tests/unit/test_utils.py | 2 + tests/unit/test_violation.py | 2 + tox.ini | 2 +- 76 files changed, 337 insertions(+), 263 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c9b32d7..868b5ea 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,9 +15,6 @@ jobs: - os: ubuntu-latest python: pypy-3.7 toxenv: py - - os: ubuntu-latest - python: 3.6 - toxenv: py - os: ubuntu-latest python: 3.7 toxenv: py @@ -32,7 +29,7 @@ jobs: toxenv: py # windows - os: windows-latest - python: 3.6 + python: 3.7 toxenv: py # misc - os: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 07a863e..faecf55 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,12 +11,16 @@ repos: rev: v3.8.2 hooks: - id: reorder-python-imports - args: [--application-directories, '.:src', --py36-plus] + args: [ + --application-directories, '.:src', + --py37-plus, + --add-import, 'from __future__ import annotations', + ] - repo: https://github.com/asottile/pyupgrade rev: v2.37.3 hooks: - id: pyupgrade - args: [--py36-plus] + args: [--py37-plus] - repo: https://github.com/psf/black rev: 22.6.0 hooks: diff --git a/bin/gen-pycodestyle-plugin b/bin/gen-pycodestyle-plugin index 3540a9a..8bc2efc 100755 --- a/bin/gen-pycodestyle-plugin +++ b/bin/gen-pycodestyle-plugin @@ -1,11 +1,12 @@ #!/usr/bin/env python3 +from __future__ import annotations + import inspect import os.path from typing import Any from typing import Callable from typing import Generator from typing import NamedTuple -from typing import Tuple import pycodestyle @@ -20,7 +21,7 @@ def _too_long(s: str) -> str: class Call(NamedTuple): name: str is_generator: bool - params: Tuple[str, ...] + params: tuple[str, ...] def to_src(self) -> str: params_s = ", ".join(self.params) @@ -35,7 +36,7 @@ class Call(NamedTuple): return "\n".join(lines) @classmethod - def from_func(cls, func: Callable[..., Any]) -> "Call": + def from_func(cls, func: Callable[..., Any]) -> Call: spec = inspect.getfullargspec(func) params = tuple(spec.args) return cls(func.__name__, inspect.isgeneratorfunction(func), params) @@ -55,9 +56,10 @@ def lines() -> Generator[str, None, None]: yield f'"""Generated using ./bin/{os.path.basename(__file__)}."""' yield "# fmt: off" + yield "from __future__ import annotations" + yield "" yield "from typing import Any" yield "from typing import Generator" - yield "from typing import Tuple" yield "" imports = sorted(call.name for call in logical + physical) for name in imports: @@ -69,7 +71,7 @@ def lines() -> Generator[str, None, None]: logical_params = {param for call in logical for param in call.params} for param in sorted(logical_params): yield f" {param}: Any," - yield ") -> Generator[Tuple[int, str], None, None]:" + yield ") -> Generator[tuple[int, str], None, None]:" yield ' """Run pycodestyle logical checks."""' for call in sorted(logical): yield call.to_src() @@ -80,7 +82,7 @@ def lines() -> Generator[str, None, None]: physical_params = {param for call in physical for param in call.params} for param in sorted(physical_params): yield f" {param}: Any," - yield ") -> Generator[Tuple[int, str], None, None]:" + yield ") -> Generator[tuple[int, str], None, None]:" yield ' """Run pycodestyle physical checks."""' for call in sorted(physical): yield call.to_src() diff --git a/docs/source/conf.py b/docs/source/conf.py index e0406c2..a2b4af3 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -14,6 +14,8 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) +from __future__ import annotations + import flake8 # -- General configuration ------------------------------------------------ diff --git a/example-plugin/setup.py b/example-plugin/setup.py index 70d56fa..c0720bd 100644 --- a/example-plugin/setup.py +++ b/example-plugin/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import setuptools setuptools.setup( diff --git a/example-plugin/src/flake8_example_plugin/__init__.py b/example-plugin/src/flake8_example_plugin/__init__.py index 3f6f163..47851da 100644 --- a/example-plugin/src/flake8_example_plugin/__init__.py +++ b/example-plugin/src/flake8_example_plugin/__init__.py @@ -1,4 +1,6 @@ """Module for an example Flake8 plugin.""" +from __future__ import annotations + from .off_by_default import ExampleTwo from .on_by_default import ExampleOne diff --git a/example-plugin/src/flake8_example_plugin/off_by_default.py b/example-plugin/src/flake8_example_plugin/off_by_default.py index 54737cb..d140ca1 100644 --- a/example-plugin/src/flake8_example_plugin/off_by_default.py +++ b/example-plugin/src/flake8_example_plugin/off_by_default.py @@ -1,4 +1,5 @@ """Our first example plugin.""" +from __future__ import annotations class ExampleTwo: diff --git a/example-plugin/src/flake8_example_plugin/on_by_default.py b/example-plugin/src/flake8_example_plugin/on_by_default.py index a3e5332..d2da126 100644 --- a/example-plugin/src/flake8_example_plugin/on_by_default.py +++ b/example-plugin/src/flake8_example_plugin/on_by_default.py @@ -1,4 +1,5 @@ """Our first example plugin.""" +from __future__ import annotations class ExampleOne: diff --git a/setup.cfg b/setup.cfg index 5c7b2d3..5e20adf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,7 +20,6 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 @@ -43,7 +42,7 @@ install_requires = pycodestyle>=2.9.0,<2.10.0 pyflakes>=2.5.0,<2.6.0 importlib-metadata>=1.1.0,<4.3;python_version<"3.8" -python_requires = >=3.6.1 +python_requires = >=3.7 [options.packages.find] where = src diff --git a/setup.py b/setup.py index 3822d9e..253a22e 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,6 @@ """Packaging logic for Flake8.""" +from __future__ import annotations + import os import sys diff --git a/src/flake8/__init__.py b/src/flake8/__init__.py index ea571c9..43f3bec 100644 --- a/src/flake8/__init__.py +++ b/src/flake8/__init__.py @@ -9,10 +9,10 @@ This module .. autofunction:: flake8.configure_logging """ +from __future__ import annotations + import logging import sys -from typing import Optional -from typing import Type LOG = logging.getLogger(__name__) LOG.addHandler(logging.NullHandler()) @@ -35,7 +35,7 @@ LOG_FORMAT = ( def configure_logging( verbosity: int, - filename: Optional[str] = None, + filename: str | None = None, logformat: str = LOG_FORMAT, ) -> None: """Configure logging for flake8. @@ -56,7 +56,7 @@ def configure_logging( if not filename or filename in ("stderr", "stdout"): fileobj = getattr(sys, filename or "stderr") - handler_cls: Type[logging.Handler] = logging.StreamHandler + handler_cls: type[logging.Handler] = logging.StreamHandler else: fileobj = filename handler_cls = logging.FileHandler diff --git a/src/flake8/__main__.py b/src/flake8/__main__.py index de240dc..8f7e7c9 100644 --- a/src/flake8/__main__.py +++ b/src/flake8/__main__.py @@ -1,4 +1,6 @@ """Module allowing for ``python -m flake8 ...``.""" +from __future__ import annotations + from flake8.main.cli import main if __name__ == "__main__": diff --git a/src/flake8/_compat.py b/src/flake8/_compat.py index 81da7be..91770bc 100644 --- a/src/flake8/_compat.py +++ b/src/flake8/_compat.py @@ -1,4 +1,6 @@ """Expose backports in a single place.""" +from __future__ import annotations + import sys if sys.version_info >= (3, 8): # pragma: no cover (PY38+) diff --git a/src/flake8/api/__init__.py b/src/flake8/api/__init__.py index c2eefbe..c5f9711 100644 --- a/src/flake8/api/__init__.py +++ b/src/flake8/api/__init__.py @@ -3,3 +3,4 @@ This is the only submodule in Flake8 with a guaranteed stable API. All other submodules are considered internal only and are subject to change. """ +from __future__ import annotations diff --git a/src/flake8/api/legacy.py b/src/flake8/api/legacy.py index 5881aa9..9635756 100644 --- a/src/flake8/api/legacy.py +++ b/src/flake8/api/legacy.py @@ -3,13 +3,12 @@ Previously, users would import :func:`get_style_guide` from ``flake8.engine``. In 3.0 we no longer have an "engine" module but we maintain the API from it. """ +from __future__ import annotations + import argparse import logging import os.path from typing import Any -from typing import List -from typing import Optional -from typing import Type import flake8 from flake8.discover_files import expand_paths @@ -53,7 +52,7 @@ class Report: """Return the total number of errors.""" return self._application.result_count - def get_statistics(self, violation: str) -> List[str]: + def get_statistics(self, violation: str) -> list[str]: """Get the list of occurrences of a violation. :returns: @@ -97,12 +96,12 @@ class StyleGuide: return self._application.options @property - def paths(self) -> List[str]: + def paths(self) -> list[str]: """Return the extra arguments passed as paths.""" assert self._application.options is not None return self._application.options.filenames - def check_files(self, paths: Optional[List[str]] = None) -> Report: + def check_files(self, paths: list[str] | None = None) -> Report: """Run collected checks on the files provided. This will check the files passed in and return a :class:`Report` @@ -119,7 +118,7 @@ class StyleGuide: self._application.report_errors() return Report(self._application) - def excluded(self, filename: str, parent: Optional[str] = None) -> bool: + def excluded(self, filename: str, parent: str | None = None) -> bool: """Determine if a file is excluded. :param filename: @@ -148,7 +147,7 @@ class StyleGuide: def init_report( self, - reporter: Optional[Type[formatter.BaseFormatter]] = None, + reporter: type[formatter.BaseFormatter] | None = None, ) -> None: """Set up a formatter for this run of Flake8.""" if reporter is None: @@ -170,9 +169,9 @@ class StyleGuide: def input_file( self, filename: str, - lines: Optional[Any] = None, - expected: Optional[Any] = None, - line_offset: Optional[Any] = 0, + lines: Any | None = None, + expected: Any | None = None, + line_offset: Any | None = 0, ) -> Report: """Run collected checks on a single file. diff --git a/src/flake8/checker.py b/src/flake8/checker.py index 00cff39..d73349f 100644 --- a/src/flake8/checker.py +++ b/src/flake8/checker.py @@ -1,4 +1,6 @@ """Checker Manager and Checker classes.""" +from __future__ import annotations + import argparse import collections import errno @@ -8,7 +10,6 @@ import multiprocessing.pool import signal import tokenize from typing import Any -from typing import Dict from typing import List from typing import Optional from typing import Tuple @@ -71,8 +72,8 @@ class Manager: self.options = style_guide.options self.plugins = plugins self.jobs = self._job_count() - self._all_checkers: List[FileChecker] = [] - self.checkers: List[FileChecker] = [] + self._all_checkers: list[FileChecker] = [] + self.checkers: list[FileChecker] = [] self.statistics = { "files": 0, "logical lines": 0, @@ -152,7 +153,7 @@ class Manager: ) return reported_results_count - def make_checkers(self, paths: Optional[List[str]] = None) -> None: + def make_checkers(self, paths: list[str] | None = None) -> None: """Create checkers for each file.""" if paths is None: paths = self.options.filenames @@ -174,7 +175,7 @@ class Manager: self.checkers = [c for c in self._all_checkers if c.should_process] LOG.info("Checking %d files", len(self.checkers)) - def report(self) -> Tuple[int, int]: + def report(self) -> tuple[int, int]: """Report all of the errors found in the managed file checkers. This iterates over each of the checkers and reports the errors sorted @@ -195,8 +196,8 @@ class Manager: def run_parallel(self) -> None: """Run the checkers in parallel.""" # fmt: off - final_results: Dict[str, List[Tuple[str, int, int, str, Optional[str]]]] = collections.defaultdict(list) # noqa: E501 - final_statistics: Dict[str, Dict[str, int]] = collections.defaultdict(dict) # noqa: E501 + final_results: dict[str, list[tuple[str, int, int, str, str | None]]] = collections.defaultdict(list) # noqa: E501 + final_statistics: dict[str, dict[str, int]] = collections.defaultdict(dict) # noqa: E501 # fmt: on pool = _try_initialize_processpool(self.jobs) @@ -254,7 +255,7 @@ class Manager: LOG.warning("Flake8 was interrupted by the user") raise exceptions.EarlyQuit("Early quit while running checks") - def start(self, paths: Optional[List[str]] = None) -> None: + def start(self, paths: list[str] | None = None) -> None: """Start checking files. :param paths: @@ -301,7 +302,7 @@ class FileChecker: """Provide helpful debugging representation.""" return f"FileChecker for {self.filename}" - def _make_processor(self) -> Optional[processor.FileProcessor]: + def _make_processor(self) -> processor.FileProcessor | None: try: return processor.FileProcessor(self.filename, self.options) except OSError as e: @@ -316,7 +317,7 @@ class FileChecker: def report( self, - error_code: Optional[str], + error_code: str | None, line_number: int, column: int, text: str, @@ -361,7 +362,7 @@ class FileChecker: ) @staticmethod - def _extract_syntax_information(exception: Exception) -> Tuple[int, int]: + def _extract_syntax_information(exception: Exception) -> tuple[int, int]: if ( len(exception.args) > 1 and exception.args[1] @@ -524,7 +525,7 @@ class FileChecker: self.run_physical_checks(file_processor.lines[-1]) self.run_logical_checks() - def run_checks(self) -> Tuple[str, Results, Dict[str, int]]: + def run_checks(self) -> tuple[str, Results, dict[str, int]]: """Run checks against the file.""" assert self.processor is not None try: @@ -592,7 +593,7 @@ def _pool_init() -> None: def _try_initialize_processpool( job_count: int, -) -> Optional[multiprocessing.pool.Pool]: +) -> multiprocessing.pool.Pool | None: """Return a new process pool instance if we are able to create one.""" try: return multiprocessing.Pool(job_count, _pool_init) @@ -617,13 +618,13 @@ def calculate_pool_chunksize(num_checkers: int, num_jobs: int) -> int: return max(num_checkers // (num_jobs * 2), 1) -def _run_checks(checker: FileChecker) -> Tuple[str, Results, Dict[str, int]]: +def _run_checks(checker: FileChecker) -> tuple[str, Results, dict[str, int]]: return checker.run_checks() def find_offset( offset: int, mapping: processor._LogicalMapping -) -> Tuple[int, int]: +) -> tuple[int, int]: """Find the offset tuple for a single offset.""" if isinstance(offset, tuple): return offset diff --git a/src/flake8/defaults.py b/src/flake8/defaults.py index a1c04fc..4ba0048 100644 --- a/src/flake8/defaults.py +++ b/src/flake8/defaults.py @@ -1,4 +1,6 @@ """Constants that define defaults.""" +from __future__ import annotations + import re EXCLUDE = ( diff --git a/src/flake8/discover_files.py b/src/flake8/discover_files.py index 8c21064..b8592c8 100644 --- a/src/flake8/discover_files.py +++ b/src/flake8/discover_files.py @@ -1,4 +1,6 @@ """Functions related to discovering paths.""" +from __future__ import annotations + import logging import os.path from typing import Callable diff --git a/src/flake8/exceptions.py b/src/flake8/exceptions.py index 8e13cd8..18646e7 100644 --- a/src/flake8/exceptions.py +++ b/src/flake8/exceptions.py @@ -1,4 +1,5 @@ """Exception classes for all of Flake8.""" +from __future__ import annotations class Flake8Exception(Exception): diff --git a/src/flake8/formatting/__init__.py b/src/flake8/formatting/__init__.py index bf44801..732d0b6 100644 --- a/src/flake8/formatting/__init__.py +++ b/src/flake8/formatting/__init__.py @@ -1 +1,2 @@ """Submodule containing the default formatters for Flake8.""" +from __future__ import annotations diff --git a/src/flake8/formatting/_windows_color.py b/src/flake8/formatting/_windows_color.py index 1d2c73f..a06fdb9 100644 --- a/src/flake8/formatting/_windows_color.py +++ b/src/flake8/formatting/_windows_color.py @@ -2,6 +2,8 @@ See: https://github.com/pre-commit/pre-commit/blob/cb40e96/pre_commit/color.py """ +from __future__ import annotations + import sys if sys.platform == "win32": # pragma: no cover (windows) diff --git a/src/flake8/formatting/base.py b/src/flake8/formatting/base.py index 78d10e9..d986d65 100644 --- a/src/flake8/formatting/base.py +++ b/src/flake8/formatting/base.py @@ -1,11 +1,10 @@ """The base class and interface for all formatting plugins.""" +from __future__ import annotations + import argparse import os import sys from typing import IO -from typing import List -from typing import Optional -from typing import Tuple from flake8.formatting import _windows_color from flake8.statistics import Statistics @@ -46,7 +45,7 @@ class BaseFormatter: """ self.options = options self.filename = options.output_file - self.output_fd: Optional[IO[str]] = None + self.output_fd: IO[str] | None = None self.newline = "\n" self.color = options.color == "always" or ( options.color == "auto" @@ -84,7 +83,7 @@ class BaseFormatter: os.makedirs(dirname, exist_ok=True) self.output_fd = open(self.filename, "a") - def handle(self, error: "Violation") -> None: + def handle(self, error: Violation) -> None: """Handle an error reported by Flake8. This defaults to calling :meth:`format`, :meth:`show_source`, and @@ -99,7 +98,7 @@ class BaseFormatter: source = self.show_source(error) self.write(line, source) - def format(self, error: "Violation") -> Optional[str]: + def format(self, error: Violation) -> str | None: """Format an error reported by Flake8. This method **must** be implemented by subclasses. @@ -114,7 +113,7 @@ class BaseFormatter: "Subclass of BaseFormatter did not implement" " format." ) - def show_statistics(self, statistics: "Statistics") -> None: + def show_statistics(self, statistics: Statistics) -> None: """Format and print the statistics.""" for error_code in statistics.error_codes(): stats_for_error_code = statistics.statistics_for(error_code) @@ -123,7 +122,7 @@ class BaseFormatter: count += sum(stat.count for stat in stats_for_error_code) self._write(f"{count:<5} {error_code} {statistic.message}") - def show_benchmarks(self, benchmarks: List[Tuple[str, float]]) -> None: + def show_benchmarks(self, benchmarks: list[tuple[str, float]]) -> None: """Format and print the benchmarks.""" # NOTE(sigmavirus24): The format strings are a little confusing, even # to me, so here's a quick explanation: @@ -144,7 +143,7 @@ class BaseFormatter: benchmark = float_format(statistic=statistic, value=value) self._write(benchmark) - def show_source(self, error: "Violation") -> Optional[str]: + def show_source(self, error: Violation) -> str | None: """Show the physical line generating the error. This also adds an indicator for the particular part of the line that @@ -178,7 +177,7 @@ class BaseFormatter: if self.output_fd is None or self.options.tee: sys.stdout.buffer.write(output.encode() + self.newline.encode()) - def write(self, line: Optional[str], source: Optional[str]) -> None: + def write(self, line: str | None, source: str | None) -> None: """Write the line either to the output file or stdout. This handles deciding whether to write to a file or print to standard diff --git a/src/flake8/formatting/default.py b/src/flake8/formatting/default.py index f43dc42..b5d08ff 100644 --- a/src/flake8/formatting/default.py +++ b/src/flake8/formatting/default.py @@ -1,6 +1,5 @@ """Default formatting class for Flake8.""" -from typing import Optional -from typing import Set +from __future__ import annotations from flake8.formatting import base from flake8.violation import Violation @@ -38,7 +37,7 @@ class SimpleFormatter(base.BaseFormatter): error_format: str - def format(self, error: "Violation") -> Optional[str]: + def format(self, error: Violation) -> str | None: """Format and write error out. If an output filename is specified, write formatted errors to that @@ -86,12 +85,12 @@ class FilenameOnly(SimpleFormatter): def after_init(self) -> None: """Initialize our set of filenames.""" - self.filenames_already_printed: Set[str] = set() + self.filenames_already_printed: set[str] = set() - def show_source(self, error: "Violation") -> Optional[str]: + def show_source(self, error: Violation) -> str | None: """Do not include the source code.""" - def format(self, error: "Violation") -> Optional[str]: + def format(self, error: Violation) -> str | None: """Ensure we only print each error once.""" if error.filename not in self.filenames_already_printed: self.filenames_already_printed.add(error.filename) @@ -103,8 +102,8 @@ class FilenameOnly(SimpleFormatter): class Nothing(base.BaseFormatter): """Print absolutely nothing.""" - def format(self, error: "Violation") -> Optional[str]: + def format(self, error: Violation) -> str | None: """Do nothing.""" - def show_source(self, error: "Violation") -> Optional[str]: + def show_source(self, error: Violation) -> str | None: """Do not print the source.""" diff --git a/src/flake8/main/__init__.py b/src/flake8/main/__init__.py index d3aa1de..85bcff4 100644 --- a/src/flake8/main/__init__.py +++ b/src/flake8/main/__init__.py @@ -1 +1,2 @@ """Module containing the logic for the Flake8 entry-points.""" +from __future__ import annotations diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py index 15c2477..13ece4e 100644 --- a/src/flake8/main/application.py +++ b/src/flake8/main/application.py @@ -1,15 +1,12 @@ """Module containing the application logic for Flake8.""" +from __future__ import annotations + import argparse import configparser import json import logging import time -from typing import Dict -from typing import List -from typing import Optional from typing import Sequence -from typing import Set -from typing import Tuple import flake8 from flake8 import checker @@ -38,27 +35,27 @@ class Application: #: The timestamp when the Application instance was instantiated. self.start_time = time.time() #: The timestamp when the Application finished reported errors. - self.end_time: Optional[float] = None + self.end_time: float | None = None #: The prelimary argument parser for handling options required for #: obtaining and parsing the configuration file. self.prelim_arg_parser = options.stage1_arg_parser() #: The instance of :class:`flake8.options.manager.OptionManager` used #: to parse and handle the options and arguments passed by the user - self.option_manager: Optional[manager.OptionManager] = None + self.option_manager: manager.OptionManager | None = None - self.plugins: Optional[finder.Plugins] = None + self.plugins: finder.Plugins | None = None #: The user-selected formatter from :attr:`formatting_plugins` - self.formatter: Optional[BaseFormatter] = None + self.formatter: BaseFormatter | None = None #: The :class:`flake8.style_guide.StyleGuideManager` built from the #: user's options - self.guide: Optional[style_guide.StyleGuideManager] = None + self.guide: style_guide.StyleGuideManager | None = None #: The :class:`flake8.checker.Manager` that will handle running all of #: the checks selected by the user. - self.file_checker_manager: Optional[checker.Manager] = None + self.file_checker_manager: checker.Manager | None = None #: The user-supplied options parsed into an instance of #: :class:`argparse.Namespace` - self.options: Optional[argparse.Namespace] = None + self.options: argparse.Namespace | None = None #: The number of errors, warnings, and other messages after running #: flake8 and taking into account ignored errors and lines. self.result_count = 0 @@ -70,11 +67,11 @@ class Application: self.catastrophic_failure = False #: The parsed diff information - self.parsed_diff: Dict[str, Set[int]] = {} + self.parsed_diff: dict[str, set[int]] = {} def parse_preliminary_options( self, argv: Sequence[str] - ) -> Tuple[argparse.Namespace, List[str]]: + ) -> tuple[argparse.Namespace, list[str]]: """Get preliminary options from the CLI, pre-plugin-loading. We need to know the values of a few standard options so that we can @@ -111,8 +108,8 @@ class Application: cfg: configparser.RawConfigParser, cfg_dir: str, *, - enable_extensions: Optional[str], - require_plugins: Optional[str], + enable_extensions: str | None, + require_plugins: str | None, ) -> None: """Find and load the plugins for this application. @@ -143,7 +140,7 @@ class Application: self, cfg: configparser.RawConfigParser, cfg_dir: str, - argv: List[str], + argv: list[str], ) -> None: """Parse configuration files and the CLI options.""" assert self.option_manager is not None @@ -218,7 +215,7 @@ class Application: assert self.options is not None assert self.file_checker_manager is not None if self.options.diff: - files: Optional[List[str]] = sorted(self.parsed_diff) + files: list[str] | None = sorted(self.parsed_diff) if not files: return else: diff --git a/src/flake8/main/cli.py b/src/flake8/main/cli.py index b4bb202..01a67ac 100644 --- a/src/flake8/main/cli.py +++ b/src/flake8/main/cli.py @@ -1,12 +1,13 @@ """Command-line implementation of flake8.""" +from __future__ import annotations + import sys -from typing import Optional from typing import Sequence from flake8.main import application -def main(argv: Optional[Sequence[str]] = None) -> int: +def main(argv: Sequence[str] | None = None) -> int: """Execute the main bit of the application. This handles the creation of an instance of :class:`Application`, runs it, diff --git a/src/flake8/main/debug.py b/src/flake8/main/debug.py index 03671bc..c3a8b0b 100644 --- a/src/flake8/main/debug.py +++ b/src/flake8/main/debug.py @@ -1,12 +1,13 @@ """Module containing the logic for our debugging logic.""" +from __future__ import annotations + import platform from typing import Any -from typing import Dict from flake8.plugins.finder import Plugins -def information(version: str, plugins: Plugins) -> Dict[str, Any]: +def information(version: str, plugins: Plugins) -> dict[str, Any]: """Generate the information to be printed for the bug report.""" versions = sorted( { diff --git a/src/flake8/main/options.py b/src/flake8/main/options.py index 4c9dfb8..d603232 100644 --- a/src/flake8/main/options.py +++ b/src/flake8/main/options.py @@ -1,4 +1,6 @@ """Contains the logic for all of the default options for Flake8.""" +from __future__ import annotations + import argparse from flake8 import defaults diff --git a/src/flake8/options/__init__.py b/src/flake8/options/__init__.py index cc20daa..3578223 100644 --- a/src/flake8/options/__init__.py +++ b/src/flake8/options/__init__.py @@ -10,3 +10,4 @@ to aggregate configuration into one object used by plugins and Flake8. """ +from __future__ import annotations diff --git a/src/flake8/options/aggregator.py b/src/flake8/options/aggregator.py index 580def6..af8e744 100644 --- a/src/flake8/options/aggregator.py +++ b/src/flake8/options/aggregator.py @@ -3,10 +3,11 @@ This holds the logic that uses the collected and merged config files and applies the user-specified command-line configuration on top of it. """ +from __future__ import annotations + import argparse import configparser import logging -from typing import Optional from typing import Sequence from flake8.options import config @@ -19,7 +20,7 @@ def aggregate_options( manager: OptionManager, cfg: configparser.RawConfigParser, cfg_dir: str, - argv: Optional[Sequence[str]], + argv: Sequence[str] | None, ) -> argparse.Namespace: """Aggregate and merge CLI and config file options.""" # Get defaults from the option parser diff --git a/src/flake8/options/config.py b/src/flake8/options/config.py index daf8529..e158737 100644 --- a/src/flake8/options/config.py +++ b/src/flake8/options/config.py @@ -1,12 +1,10 @@ """Config handling logic for Flake8.""" +from __future__ import annotations + import configparser import logging import os.path from typing import Any -from typing import Dict -from typing import List -from typing import Optional -from typing import Tuple from flake8 import exceptions from flake8.options.manager import OptionManager @@ -14,13 +12,13 @@ from flake8.options.manager import OptionManager LOG = logging.getLogger(__name__) -def _stat_key(s: str) -> Tuple[int, int]: +def _stat_key(s: str) -> tuple[int, int]: # same as what's used by samefile / samestat st = os.stat(s) return st.st_ino, st.st_dev -def _find_config_file(path: str) -> Optional[str]: +def _find_config_file(path: str) -> str | None: # on windows if the homedir isn't detected this returns back `~` home = os.path.expanduser("~") try: @@ -55,11 +53,11 @@ def _find_config_file(path: str) -> Optional[str]: def load_config( - config: Optional[str], - extra: List[str], + config: str | None, + extra: list[str], *, isolated: bool = False, -) -> Tuple[configparser.RawConfigParser, str]: +) -> tuple[configparser.RawConfigParser, str]: """Load the configuration given the user options. - in ``isolated`` mode, return an empty configuration @@ -97,7 +95,7 @@ def parse_config( option_manager: OptionManager, cfg: configparser.RawConfigParser, cfg_dir: str, -) -> Dict[str, Any]: +) -> dict[str, Any]: """Parse and normalize the typed configuration options.""" if "flake8" not in cfg: return {} diff --git a/src/flake8/options/manager.py b/src/flake8/options/manager.py index ff5a229..e333c9e 100644 --- a/src/flake8/options/manager.py +++ b/src/flake8/options/manager.py @@ -1,18 +1,14 @@ """Option handling and Option management logic.""" +from __future__ import annotations + import argparse import enum import functools import logging from typing import Any from typing import Callable -from typing import Dict -from typing import List from typing import Mapping -from typing import Optional from typing import Sequence -from typing import Tuple -from typing import Type -from typing import Union from flake8 import utils from flake8.plugins.finder import Plugins @@ -24,7 +20,7 @@ LOG = logging.getLogger(__name__) _ARG = enum.Enum("_ARG", "NO") -_optparse_callable_map: Dict[str, Union[Type[Any], _ARG]] = { +_optparse_callable_map: dict[str, type[Any] | _ARG] = { "int": int, "long": int, "string": str, @@ -44,7 +40,7 @@ class _CallbackAction(argparse.Action): *args: Any, callback: Callable[..., Any], callback_args: Sequence[Any] = (), - callback_kwargs: Optional[Dict[str, Any]] = None, + callback_kwargs: dict[str, Any] | None = None, **kwargs: Any, ) -> None: self._callback = callback @@ -56,8 +52,8 @@ class _CallbackAction(argparse.Action): self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, - values: Optional[Union[Sequence[str], str]], - option_string: Optional[str] = None, + values: Sequence[str] | str | None, + option_string: str | None = None, ) -> None: if not values: values = None @@ -78,8 +74,8 @@ def _flake8_normalize( *args: str, comma_separated_list: bool = False, normalize_paths: bool = False, -) -> Union[str, List[str]]: - ret: Union[str, List[str]] = value +) -> str | list[str]: + ret: str | list[str] = value if comma_separated_list and isinstance(ret, str): ret = utils.parse_comma_separated_list(value) @@ -97,24 +93,24 @@ class Option: def __init__( self, - short_option_name: Union[str, _ARG] = _ARG.NO, - long_option_name: Union[str, _ARG] = _ARG.NO, + short_option_name: str | _ARG = _ARG.NO, + long_option_name: str | _ARG = _ARG.NO, # Options below here are taken from the optparse.Option class - action: Union[str, Type[argparse.Action], _ARG] = _ARG.NO, - default: Union[Any, _ARG] = _ARG.NO, - type: Union[str, Callable[..., Any], _ARG] = _ARG.NO, - dest: Union[str, _ARG] = _ARG.NO, - nargs: Union[int, str, _ARG] = _ARG.NO, - const: Union[Any, _ARG] = _ARG.NO, - choices: Union[Sequence[Any], _ARG] = _ARG.NO, - help: Union[str, _ARG] = _ARG.NO, - metavar: Union[str, _ARG] = _ARG.NO, + action: str | type[argparse.Action] | _ARG = _ARG.NO, + default: Any | _ARG = _ARG.NO, + type: str | Callable[..., Any] | _ARG = _ARG.NO, + dest: str | _ARG = _ARG.NO, + nargs: int | str | _ARG = _ARG.NO, + const: Any | _ARG = _ARG.NO, + choices: Sequence[Any] | _ARG = _ARG.NO, + help: str | _ARG = _ARG.NO, + metavar: str | _ARG = _ARG.NO, # deprecated optparse-only options - callback: Union[Callable[..., Any], _ARG] = _ARG.NO, - callback_args: Union[Sequence[Any], _ARG] = _ARG.NO, - callback_kwargs: Union[Mapping[str, Any], _ARG] = _ARG.NO, + callback: Callable[..., Any] | _ARG = _ARG.NO, + callback_args: Sequence[Any] | _ARG = _ARG.NO, + callback_kwargs: Mapping[str, Any] | _ARG = _ARG.NO, # Options below are taken from argparse.ArgumentParser.add_argument - required: Union[bool, _ARG] = _ARG.NO, + required: bool | _ARG = _ARG.NO, # Options below here are specific to Flake8 parse_from_config: bool = False, comma_separated_list: bool = False, @@ -247,7 +243,7 @@ class Option: self.help = help self.metavar = metavar self.required = required - self.option_kwargs: Dict[str, Union[Any, _ARG]] = { + self.option_kwargs: dict[str, Any | _ARG] = { "action": self.action, "default": self.default, "type": self.type, @@ -268,7 +264,7 @@ class Option: self.comma_separated_list = comma_separated_list self.normalize_paths = normalize_paths - self.config_name: Optional[str] = None + self.config_name: str | None = None if parse_from_config: if long_option_name is _ARG.NO: raise ValueError( @@ -280,7 +276,7 @@ class Option: self._opt = None @property - def filtered_option_kwargs(self) -> Dict[str, Any]: + def filtered_option_kwargs(self) -> dict[str, Any]: """Return any actually-specified arguments.""" return { k: v for k, v in self.option_kwargs.items() if v is not _ARG.NO @@ -307,7 +303,7 @@ class Option: return value - def to_argparse(self) -> Tuple[List[str], Dict[str, Any]]: + def to_argparse(self) -> tuple[list[str], dict[str, Any]]: """Convert a Flake8 Option to argparse ``add_argument`` arguments.""" return self.option_args, self.filtered_option_kwargs @@ -320,7 +316,7 @@ class OptionManager: *, version: str, plugin_versions: str, - parents: List[argparse.ArgumentParser], + parents: list[argparse.ArgumentParser], ) -> None: """Initialize an instance of an OptionManager. @@ -350,17 +346,17 @@ class OptionManager: ) self.parser.add_argument("filenames", nargs="*", metavar="filename") - self.config_options_dict: Dict[str, Option] = {} - self.options: List[Option] = [] - self.extended_default_ignore: List[str] = [] - self.extended_default_select: List[str] = [] + self.config_options_dict: dict[str, Option] = {} + self.options: list[Option] = [] + self.extended_default_ignore: list[str] = [] + self.extended_default_select: list[str] = [] - self._current_group: Optional[argparse._ArgumentGroup] = None + self._current_group: argparse._ArgumentGroup | None = None # TODO: maybe make this a free function to reduce api surface area def register_plugins(self, plugins: Plugins) -> None: """Register the plugin options (if needed).""" - groups: Dict[str, argparse._ArgumentGroup] = {} + groups: dict[str, argparse._ArgumentGroup] = {} def _set_group(name: str) -> None: try: @@ -428,8 +424,8 @@ class OptionManager: def parse_args( self, - args: Optional[Sequence[str]] = None, - values: Optional[argparse.Namespace] = None, + args: Sequence[str] | None = None, + values: argparse.Namespace | None = None, ) -> argparse.Namespace: """Proxy to calling the OptionParser's parse_args method.""" if values: diff --git a/src/flake8/plugins/__init__.py b/src/flake8/plugins/__init__.py index fda6a44..b540313 100644 --- a/src/flake8/plugins/__init__.py +++ b/src/flake8/plugins/__init__.py @@ -1 +1,2 @@ """Submodule of built-in plugins and plugin managers.""" +from __future__ import annotations diff --git a/src/flake8/plugins/finder.py b/src/flake8/plugins/finder.py index 9e9e3af..c051488 100644 --- a/src/flake8/plugins/finder.py +++ b/src/flake8/plugins/finder.py @@ -1,4 +1,6 @@ """Functions related to finding and loading plugins.""" +from __future__ import annotations + import configparser import inspect import itertools @@ -6,14 +8,9 @@ import logging import re import sys from typing import Any -from typing import Dict -from typing import FrozenSet from typing import Generator from typing import Iterable -from typing import List from typing import NamedTuple -from typing import Optional -from typing import Tuple from flake8 import utils from flake8._compat import importlib_metadata @@ -45,7 +42,7 @@ class LoadedPlugin(NamedTuple): plugin: Plugin obj: Any - parameters: Dict[str, bool] + parameters: dict[str, bool] @property def entry_name(self) -> str: @@ -61,17 +58,17 @@ class LoadedPlugin(NamedTuple): class Checkers(NamedTuple): """Classified plugins needed for checking.""" - tree: List[LoadedPlugin] - logical_line: List[LoadedPlugin] - physical_line: List[LoadedPlugin] + tree: list[LoadedPlugin] + logical_line: list[LoadedPlugin] + physical_line: list[LoadedPlugin] class Plugins(NamedTuple): """Classified plugins.""" checkers: Checkers - reporters: Dict[str, LoadedPlugin] - disabled: List[LoadedPlugin] + reporters: dict[str, LoadedPlugin] + disabled: list[LoadedPlugin] def all_plugins(self) -> Generator[LoadedPlugin, None, None]: """Return an iterator over all :class:`LoadedPlugin`s.""" @@ -96,12 +93,12 @@ class Plugins(NamedTuple): class PluginOptions(NamedTuple): """Options related to plugin loading.""" - local_plugin_paths: Tuple[str, ...] - enable_extensions: FrozenSet[str] - require_plugins: FrozenSet[str] + local_plugin_paths: tuple[str, ...] + enable_extensions: frozenset[str] + require_plugins: frozenset[str] @classmethod - def blank(cls) -> "PluginOptions": + def blank(cls) -> PluginOptions: """Make a blank PluginOptions, mostly used for tests.""" return cls( local_plugin_paths=(), @@ -113,8 +110,8 @@ class PluginOptions(NamedTuple): def _parse_option( cfg: configparser.RawConfigParser, cfg_opt_name: str, - opt: Optional[str], -) -> List[str]: + opt: str | None, +) -> list[str]: # specified on commandline: use that if opt is not None: return utils.parse_comma_separated_list(opt) @@ -133,8 +130,8 @@ def parse_plugin_options( cfg: configparser.RawConfigParser, cfg_dir: str, *, - enable_extensions: Optional[str], - require_plugins: Optional[str], + enable_extensions: str | None, + require_plugins: str | None, ) -> PluginOptions: """Parse plugin loading related options.""" paths_s = cfg.get("flake8:local-plugins", "paths", fallback="").strip() @@ -231,8 +228,8 @@ def _find_local_plugins( def _check_required_plugins( - plugins: List[Plugin], - expected: FrozenSet[str], + plugins: list[Plugin], + expected: frozenset[str], ) -> None: plugin_names = { utils.normalize_pypi_name(plugin.package) for plugin in plugins @@ -252,7 +249,7 @@ def _check_required_plugins( def find_plugins( cfg: configparser.RawConfigParser, opts: PluginOptions, -) -> List[Plugin]: +) -> list[Plugin]: """Discovers all plugins (but does not load them).""" ret = [*_find_importlib_plugins(), *_find_local_plugins(cfg)] @@ -264,7 +261,7 @@ def find_plugins( return ret -def _parameters_for(func: Any) -> Dict[str, bool]: +def _parameters_for(func: Any) -> dict[str, bool]: """Return the parameters for the plugin. This will inspect the plugin and return either the function parameters @@ -305,15 +302,15 @@ def _load_plugin(plugin: Plugin) -> LoadedPlugin: def _import_plugins( - plugins: List[Plugin], + plugins: list[Plugin], opts: PluginOptions, -) -> List[LoadedPlugin]: +) -> list[LoadedPlugin]: sys.path.extend(opts.local_plugin_paths) return [_load_plugin(p) for p in plugins] def _classify_plugins( - plugins: List[LoadedPlugin], + plugins: list[LoadedPlugin], opts: PluginOptions, ) -> Plugins: tree = [] @@ -358,7 +355,7 @@ def _classify_plugins( def load_plugins( - plugins: List[Plugin], + plugins: list[Plugin], opts: PluginOptions, ) -> Plugins: """Load and classify all flake8 plugins. diff --git a/src/flake8/plugins/pycodestyle.py b/src/flake8/plugins/pycodestyle.py index 753af23..4b0d67f 100644 --- a/src/flake8/plugins/pycodestyle.py +++ b/src/flake8/plugins/pycodestyle.py @@ -1,8 +1,9 @@ """Generated using ./bin/gen-pycodestyle-plugin.""" # fmt: off +from __future__ import annotations + from typing import Any from typing import Generator -from typing import Tuple from pycodestyle import ambiguous_identifier as _ambiguous_identifier from pycodestyle import bare_except as _bare_except @@ -60,7 +61,7 @@ def pycodestyle_logical( previous_unindented_logical_line: Any, tokens: Any, verbose: Any, -) -> Generator[Tuple[int, str], None, None]: +) -> Generator[tuple[int, str], None, None]: """Run pycodestyle logical checks.""" yield from _ambiguous_identifier(logical_line, tokens) yield from _bare_except(logical_line, noqa) @@ -104,7 +105,7 @@ def pycodestyle_physical( noqa: Any, physical_line: Any, total_lines: Any, -) -> Generator[Tuple[int, str], None, None]: +) -> Generator[tuple[int, str], None, None]: """Run pycodestyle physical checks.""" ret = _maximum_line_length(physical_line, max_line_length, multiline, line_number, noqa) # noqa: E501 if ret is not None: diff --git a/src/flake8/plugins/pyflakes.py b/src/flake8/plugins/pyflakes.py index 54eaeca..7b99fd4 100644 --- a/src/flake8/plugins/pyflakes.py +++ b/src/flake8/plugins/pyflakes.py @@ -1,13 +1,12 @@ """Plugin built-in to Flake8 to treat pyflakes as a plugin.""" +from __future__ import annotations + import argparse import ast import os import tokenize from typing import Any from typing import Generator -from typing import List -from typing import Tuple -from typing import Type import pyflakes.checker @@ -68,13 +67,13 @@ class FlakesChecker(pyflakes.checker.Checker): """Subclass the Pyflakes checker to conform with the flake8 API.""" with_doctest = False - include_in_doctest: List[str] = [] - exclude_from_doctest: List[str] = [] + include_in_doctest: list[str] = [] + exclude_from_doctest: list[str] = [] def __init__( self, tree: ast.AST, - file_tokens: List[tokenize.TokenInfo], + file_tokens: list[tokenize.TokenInfo], filename: str, ) -> None: """Initialize the PyFlakes plugin with an AST tree and filename.""" @@ -180,7 +179,7 @@ class FlakesChecker(pyflakes.checker.Checker): f"both for doctesting." ) - def run(self) -> Generator[Tuple[int, int, str, Type[Any]], None, None]: + def run(self) -> Generator[tuple[int, int, str, type[Any]], None, None]: """Run the plugin.""" for message in self.messages: col = getattr(message, "col", 0) diff --git a/src/flake8/plugins/reporter.py b/src/flake8/plugins/reporter.py index 5bbbd81..f63b20c 100644 --- a/src/flake8/plugins/reporter.py +++ b/src/flake8/plugins/reporter.py @@ -1,7 +1,8 @@ """Functions for construcing the requested report plugin.""" +from __future__ import annotations + import argparse import logging -from typing import Dict from flake8.formatting.base import BaseFormatter from flake8.plugins.finder import LoadedPlugin @@ -10,7 +11,7 @@ LOG = logging.getLogger(__name__) def make( - reporters: Dict[str, LoadedPlugin], + reporters: dict[str, LoadedPlugin], options: argparse.Namespace, ) -> BaseFormatter: """Make the formatter from the requested user options. diff --git a/src/flake8/processor.py b/src/flake8/processor.py index fa9bd2f..644192d 100644 --- a/src/flake8/processor.py +++ b/src/flake8/processor.py @@ -1,14 +1,14 @@ """Module containing our file processor that tokenizes a file for checks.""" +from __future__ import annotations + import argparse import ast import contextlib import logging import tokenize from typing import Any -from typing import Dict from typing import Generator from typing import List -from typing import Optional from typing import Tuple from flake8 import defaults @@ -61,7 +61,7 @@ class FileProcessor: self, filename: str, options: argparse.Namespace, - lines: Optional[List[str]] = None, + lines: list[str] | None = None, ) -> None: """Initialice our file processor. @@ -78,13 +78,13 @@ class FileProcessor: #: Number of blank lines self.blank_lines = 0 #: Checker states for each plugin? - self._checker_states: Dict[str, Dict[Any, Any]] = {} + self._checker_states: dict[str, dict[Any, Any]] = {} #: Current checker state - self.checker_state: Dict[Any, Any] = {} + self.checker_state: dict[Any, Any] = {} #: User provided option for hang closing self.hang_closing = options.hang_closing #: Character used for indentation - self.indent_char: Optional[str] = None + self.indent_char: str | None = None #: Current level of indentation self.indent_level = 0 #: Number of spaces used for indentation @@ -106,19 +106,19 @@ class FileProcessor: #: Previous unindented (i.e. top-level) logical line self.previous_unindented_logical_line = "" #: Current set of tokens - self.tokens: List[tokenize.TokenInfo] = [] + self.tokens: list[tokenize.TokenInfo] = [] #: Total number of lines in the file self.total_lines = len(self.lines) #: Verbosity level of Flake8 self.verbose = options.verbose #: Statistics dictionary self.statistics = {"logical lines": 0} - self._file_tokens: Optional[List[tokenize.TokenInfo]] = None + self._file_tokens: list[tokenize.TokenInfo] | None = None # map from line number to the line we'll search for `noqa` in - self._noqa_line_mapping: Optional[Dict[int, str]] = None + self._noqa_line_mapping: dict[int, str] | None = None @property - def file_tokens(self) -> List[tokenize.TokenInfo]: + def file_tokens(self) -> list[tokenize.TokenInfo]: """Return the complete set of tokens for a file.""" if self._file_tokens is None: line_iter = iter(self.lines) @@ -217,7 +217,7 @@ class FileProcessor: """Build an abstract syntax tree from the list of lines.""" return ast.parse("".join(self.lines)) - def build_logical_line(self) -> Tuple[str, str, _LogicalMapping]: + def build_logical_line(self) -> tuple[str, str, _LogicalMapping]: """Build a logical line from the current tokens list.""" comments, logical, mapping_list = self.build_logical_line_tokens() joined_comments = "".join(comments) @@ -240,9 +240,9 @@ class FileProcessor: def keyword_arguments_for( self, - parameters: Dict[str, bool], - arguments: Dict[str, Any], - ) -> Dict[str, Any]: + parameters: dict[str, bool], + arguments: dict[str, Any], + ) -> dict[str, Any]: """Generate the keyword arguments for a list of parameters.""" ret = {} for param, required in parameters.items(): @@ -269,12 +269,12 @@ class FileProcessor: self.tokens.append(token) yield token - def _noqa_line_range(self, min_line: int, max_line: int) -> Dict[int, str]: + def _noqa_line_range(self, min_line: int, max_line: int) -> dict[int, str]: line_range = range(min_line, max_line + 1) joined = "".join(self.lines[min_line - 1 : max_line]) return dict.fromkeys(line_range, joined) - def noqa_line_for(self, line_number: int) -> Optional[str]: + def noqa_line_for(self, line_number: int) -> str | None: """Retrieve the line which will be used to determine noqa.""" if self._noqa_line_mapping is None: try: @@ -324,7 +324,7 @@ class FileProcessor: self.indent_char = line[0] return line - def read_lines(self) -> List[str]: + def read_lines(self) -> list[str]: """Read the lines for this file checker.""" if self.filename is None or self.filename == "-": self.filename = self.options.stdin_display_name or "stdin" @@ -333,7 +333,7 @@ class FileProcessor: lines = self.read_lines_from_filename() return lines - def read_lines_from_filename(self) -> List[str]: + def read_lines_from_filename(self) -> list[str]: """Read the lines for a file.""" try: with tokenize.open(self.filename) as fd: @@ -344,7 +344,7 @@ class FileProcessor: with open(self.filename, encoding="latin-1") as fd: return fd.readlines() - def read_lines_from_stdin(self) -> List[str]: + def read_lines_from_stdin(self) -> list[str]: """Read the lines from standard in.""" return utils.stdin_get_lines() diff --git a/src/flake8/statistics.py b/src/flake8/statistics.py index ae89be1..a33e6a6 100644 --- a/src/flake8/statistics.py +++ b/src/flake8/statistics.py @@ -1,9 +1,8 @@ """Statistic collection logic for Flake8.""" -from typing import Dict +from __future__ import annotations + from typing import Generator -from typing import List from typing import NamedTuple -from typing import Optional from flake8.violation import Violation @@ -13,9 +12,9 @@ class Statistics: def __init__(self) -> None: """Initialize the underlying dictionary for our statistics.""" - self._store: Dict[Key, "Statistic"] = {} + self._store: dict[Key, Statistic] = {} - def error_codes(self) -> List[str]: + def error_codes(self) -> list[str]: """Return all unique error codes stored. :returns: @@ -23,7 +22,7 @@ class Statistics: """ return sorted({key.code for key in self._store}) - def record(self, error: "Violation") -> None: + def record(self, error: Violation) -> None: """Add the fact that the error was seen in the file. :param error: @@ -36,8 +35,8 @@ class Statistics: self._store[key].increment() def statistics_for( - self, prefix: str, filename: Optional[str] = None - ) -> Generator["Statistic", None, None]: + self, prefix: str, filename: str | None = None + ) -> Generator[Statistic, None, None]: """Generate statistics for the prefix and filename. If you have a :class:`Statistics` object that has recorded errors, @@ -79,11 +78,11 @@ class Key(NamedTuple): code: str @classmethod - def create_from(cls, error: "Violation") -> "Key": + def create_from(cls, error: Violation) -> Key: """Create a Key from :class:`flake8.violation.Violation`.""" return cls(filename=error.filename, code=error.code) - def matches(self, prefix: str, filename: Optional[str]) -> bool: + def matches(self, prefix: str, filename: str | None) -> bool: """Determine if this key matches some constraints. :param prefix: @@ -118,7 +117,7 @@ class Statistic: self.count = count @classmethod - def create_from(cls, error: "Violation") -> "Statistic": + def create_from(cls, error: Violation) -> Statistic: """Create a Statistic from a :class:`flake8.violation.Violation`.""" return cls( error_code=error.code, diff --git a/src/flake8/style_guide.py b/src/flake8/style_guide.py index b7115a0..2fee0f3 100644 --- a/src/flake8/style_guide.py +++ b/src/flake8/style_guide.py @@ -1,4 +1,6 @@ """Implementation of the StyleGuide used by Flake8.""" +from __future__ import annotations + import argparse import contextlib import copy @@ -6,14 +8,8 @@ import enum import functools import itertools import logging -from typing import Dict from typing import Generator -from typing import List -from typing import Optional from typing import Sequence -from typing import Set -from typing import Tuple -from typing import Union from flake8 import defaults from flake8 import statistics @@ -49,20 +45,20 @@ class Decision(enum.Enum): def _explicitly_chosen( *, - option: Optional[List[str]], - extend: Optional[List[str]], -) -> Tuple[str, ...]: + option: list[str] | None, + extend: list[str] | None, +) -> tuple[str, ...]: ret = [*(option or []), *(extend or [])] return tuple(sorted(ret, reverse=True)) def _select_ignore( *, - option: Optional[List[str]], - default: Tuple[str, ...], - extended_default: List[str], - extend: Optional[List[str]], -) -> Tuple[str, ...]: + option: list[str] | None, + default: tuple[str, ...], + extended_default: list[str], + extend: list[str] | None, +) -> tuple[str, ...]: # option was explicitly set, ignore the default and extended default if option is not None: ret = [*option, *(extend or [])] @@ -80,7 +76,7 @@ class DecisionEngine: def __init__(self, options: argparse.Namespace) -> None: """Initialize the engine.""" - self.cache: Dict[str, Decision] = {} + self.cache: dict[str, Decision] = {} self.selected_explicitly = _explicitly_chosen( option=options.select, @@ -104,7 +100,7 @@ class DecisionEngine: extend=options.extend_ignore, ) - def was_selected(self, code: str) -> Union[Selected, Ignored]: + def was_selected(self, code: str) -> Selected | Ignored: """Determine if the code has been selected by the user. :param code: The code for the check that has been run. @@ -122,7 +118,7 @@ class DecisionEngine: else: return Ignored.Implicitly - def was_ignored(self, code: str) -> Union[Selected, Ignored]: + def was_ignored(self, code: str) -> Selected | Ignored: """Determine if the code has been ignored by the user. :param code: @@ -211,7 +207,7 @@ class StyleGuideManager: self, options: argparse.Namespace, formatter: base_formatter.BaseFormatter, - decider: Optional[DecisionEngine] = None, + decider: DecisionEngine | None = None, ) -> None: """Initialize our StyleGuide. @@ -221,7 +217,7 @@ class StyleGuideManager: self.formatter = formatter self.stats = statistics.Statistics() self.decider = decider or DecisionEngine(options) - self.style_guides: List[StyleGuide] = [] + self.style_guides: list[StyleGuide] = [] self.default_style_guide = StyleGuide( options, formatter, self.stats, decider=decider ) @@ -238,7 +234,7 @@ class StyleGuideManager: def populate_style_guides_with( self, options: argparse.Namespace - ) -> Generator["StyleGuide", None, None]: + ) -> Generator[StyleGuide, None, None]: """Generate style guides from the per-file-ignores option. :param options: @@ -252,7 +248,7 @@ class StyleGuideManager: filename=filename, extend_ignore_with=violations ) - def _style_guide_for(self, filename: str) -> "StyleGuide": + def _style_guide_for(self, filename: str) -> StyleGuide: """Find the StyleGuide for the filename in particular.""" return max( (g for g in self.style_guides if g.applies_to(filename)), @@ -262,7 +258,7 @@ class StyleGuideManager: @contextlib.contextmanager def processing_file( self, filename: str - ) -> Generator["StyleGuide", None, None]: + ) -> Generator[StyleGuide, None, None]: """Record the fact that we're processing the file's results.""" guide = self.style_guide_for(filename) with guide.processing_file(filename): @@ -275,7 +271,7 @@ class StyleGuideManager: line_number: int, column_number: int, text: str, - physical_line: Optional[str] = None, + physical_line: str | None = None, ) -> int: """Handle an error reported by a check. @@ -302,7 +298,7 @@ class StyleGuideManager: code, filename, line_number, column_number, text, physical_line ) - def add_diff_ranges(self, diffinfo: Dict[str, Set[int]]) -> None: + def add_diff_ranges(self, diffinfo: dict[str, set[int]]) -> None: """Update the StyleGuides to filter out information not in the diff. This provides information to the underlying StyleGuides so that only @@ -323,8 +319,8 @@ class StyleGuide: options: argparse.Namespace, formatter: base_formatter.BaseFormatter, stats: statistics.Statistics, - filename: Optional[str] = None, - decider: Optional[DecisionEngine] = None, + filename: str | None = None, + decider: DecisionEngine | None = None, ): """Initialize our StyleGuide. @@ -337,7 +333,7 @@ class StyleGuide: self.filename = filename if self.filename: self.filename = utils.normalize_path(self.filename) - self._parsed_diff: Dict[str, Set[int]] = {} + self._parsed_diff: dict[str, set[int]] = {} def __repr__(self) -> str: """Make it easier to debug which StyleGuide we're using.""" @@ -345,9 +341,9 @@ class StyleGuide: def copy( self, - filename: Optional[str] = None, - extend_ignore_with: Optional[Sequence[str]] = None, - ) -> "StyleGuide": + filename: str | None = None, + extend_ignore_with: Sequence[str] | None = None, + ) -> StyleGuide: """Create a copy of this style guide with different values.""" filename = filename or self.filename options = copy.deepcopy(self.options) @@ -360,7 +356,7 @@ class StyleGuide: @contextlib.contextmanager def processing_file( self, filename: str - ) -> Generator["StyleGuide", None, None]: + ) -> Generator[StyleGuide, None, None]: """Record the fact that we're processing the file's results.""" self.formatter.beginning(filename) yield self @@ -405,7 +401,7 @@ class StyleGuide: line_number: int, column_number: int, text: str, - physical_line: Optional[str] = None, + physical_line: str | None = None, ) -> int: """Handle an error reported by a check. @@ -451,7 +447,7 @@ class StyleGuide: return 1 return 0 - def add_diff_ranges(self, diffinfo: Dict[str, Set[int]]) -> None: + def add_diff_ranges(self, diffinfo: dict[str, set[int]]) -> None: """Update the StyleGuide to filter out information not in the diff. This provides information to the StyleGuide so that only the errors diff --git a/src/flake8/utils.py b/src/flake8/utils.py index cc47ffc..60555a9 100644 --- a/src/flake8/utils.py +++ b/src/flake8/utils.py @@ -1,4 +1,6 @@ """Utility methods for flake8.""" +from __future__ import annotations + import collections import fnmatch as _fnmatch import functools @@ -10,15 +12,9 @@ import re import sys import textwrap import tokenize -from typing import Dict -from typing import List from typing import NamedTuple -from typing import Optional from typing import Pattern from typing import Sequence -from typing import Set -from typing import Tuple -from typing import Union from flake8 import exceptions @@ -30,7 +26,7 @@ NORMALIZE_PACKAGE_NAME_RE = re.compile(r"[-_.]+") def parse_comma_separated_list( value: str, regexp: Pattern[str] = COMMA_SEPARATED_LIST_RE -) -> List[str]: +) -> list[str]: """Parse a comma-separated list. :param value: @@ -64,7 +60,7 @@ _FILE_LIST_TOKEN_TYPES = [ ] -def _tokenize_files_to_codes_mapping(value: str) -> List[_Token]: +def _tokenize_files_to_codes_mapping(value: str) -> list[_Token]: tokens = [] i = 0 while i < len(value): @@ -82,8 +78,8 @@ def _tokenize_files_to_codes_mapping(value: str) -> List[_Token]: def parse_files_to_codes_mapping( # noqa: C901 - value_: Union[Sequence[str], str] -) -> List[Tuple[str, List[str]]]: + value_: Sequence[str] | str, +) -> list[tuple[str, list[str]]]: """Parse a files-to-codes mapping. A files-to-codes mapping a sequence of values specified as @@ -97,15 +93,15 @@ def parse_files_to_codes_mapping( # noqa: C901 else: value = value_ - ret: List[Tuple[str, List[str]]] = [] + ret: list[tuple[str, list[str]]] = [] if not value.strip(): return ret class State: seen_sep = True seen_colon = False - filenames: List[str] = [] - codes: List[str] = [] + filenames: list[str] = [] + codes: list[str] = [] def _reset() -> None: if State.codes: @@ -157,7 +153,7 @@ def parse_files_to_codes_mapping( # noqa: C901 def normalize_paths( paths: Sequence[str], parent: str = os.curdir -) -> List[str]: +) -> list[str]: """Normalize a list of paths relative to a parent directory. :returns: @@ -201,12 +197,12 @@ def stdin_get_value() -> str: return stdin_value.decode("utf-8") -def stdin_get_lines() -> List[str]: +def stdin_get_lines() -> list[str]: """Return lines of stdin split according to file splitting.""" return list(io.StringIO(stdin_get_value())) -def parse_unified_diff(diff: Optional[str] = None) -> Dict[str, Set[int]]: +def parse_unified_diff(diff: str | None = None) -> dict[str, set[int]]: """Parse the unified diff passed on stdin. :returns: @@ -218,7 +214,7 @@ def parse_unified_diff(diff: Optional[str] = None) -> Dict[str, Set[int]]: number_of_rows = None current_path = None - parsed_paths: Dict[str, Set[int]] = collections.defaultdict(set) + parsed_paths: dict[str, set[int]] = collections.defaultdict(set) for line in diff.splitlines(): if number_of_rows: if not line or line[0] != "-": @@ -271,7 +267,7 @@ def parse_unified_diff(diff: Optional[str] = None) -> Dict[str, Set[int]]: return parsed_paths -def is_using_stdin(paths: List[str]) -> bool: +def is_using_stdin(paths: list[str]) -> bool: """Determine if we're going to read from stdin. :param paths: diff --git a/src/flake8/violation.py b/src/flake8/violation.py index d2d2578..45834b2 100644 --- a/src/flake8/violation.py +++ b/src/flake8/violation.py @@ -1,12 +1,11 @@ """Contains the Violation error class used internally.""" +from __future__ import annotations + import functools import linecache import logging -from typing import Dict from typing import Match from typing import NamedTuple -from typing import Optional -from typing import Set from flake8 import defaults from flake8 import utils @@ -16,7 +15,7 @@ LOG = logging.getLogger(__name__) @functools.lru_cache(maxsize=512) -def _find_noqa(physical_line: str) -> Optional[Match[str]]: +def _find_noqa(physical_line: str) -> Match[str] | None: return defaults.NOQA_INLINE_REGEXP.search(physical_line) @@ -28,7 +27,7 @@ class Violation(NamedTuple): line_number: int column_number: int text: str - physical_line: Optional[str] + physical_line: str | None def is_inline_ignored(self, disable_noqa: bool) -> bool: """Determine if a comment has been added to ignore this line. @@ -69,7 +68,7 @@ class Violation(NamedTuple): ) return False - def is_in(self, diff: Dict[str, Set[int]]) -> bool: + def is_in(self, diff: dict[str, set[int]]) -> bool: """Determine if the violation is included in a diff's line ranges. This function relies on the parsed data added via diff --git a/tests/__init__.py b/tests/__init__.py index f7ac891..ee1f2a0 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1,2 @@ """This is here because mypy doesn't understand PEP 420.""" +from __future__ import annotations diff --git a/tests/conftest.py b/tests/conftest.py index 0f48309..ac413fb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,6 @@ """Test configuration for py.test.""" +from __future__ import annotations + import sys import flake8 diff --git a/tests/integration/subdir/aplugin.py b/tests/integration/subdir/aplugin.py index 801f2c0..97b06a9 100644 --- a/tests/integration/subdir/aplugin.py +++ b/tests/integration/subdir/aplugin.py @@ -1,4 +1,5 @@ """Module that is off sys.path by default, for testing local-plugin-paths.""" +from __future__ import annotations class ExtensionTestPlugin2: diff --git a/tests/integration/test_aggregator.py b/tests/integration/test_aggregator.py index d35266f..a5b39d7 100644 --- a/tests/integration/test_aggregator.py +++ b/tests/integration/test_aggregator.py @@ -1,4 +1,6 @@ """Test aggregation of config files and command-line options.""" +from __future__ import annotations + import os import pytest diff --git a/tests/integration/test_api_legacy.py b/tests/integration/test_api_legacy.py index efb0fc9..b386bd5 100644 --- a/tests/integration/test_api_legacy.py +++ b/tests/integration/test_api_legacy.py @@ -1,4 +1,6 @@ """Integration tests for the legacy api.""" +from __future__ import annotations + from flake8.api import legacy diff --git a/tests/integration/test_checker.py b/tests/integration/test_checker.py index 9223ec4..ab9eb27 100644 --- a/tests/integration/test_checker.py +++ b/tests/integration/test_checker.py @@ -1,4 +1,6 @@ """Integration tests for the checker submodule.""" +from __future__ import annotations + import sys from unittest import mock diff --git a/tests/integration/test_main.py b/tests/integration/test_main.py index fe254b7..e711fb3 100644 --- a/tests/integration/test_main.py +++ b/tests/integration/test_main.py @@ -1,4 +1,6 @@ """Integration tests for the main entrypoint of flake8.""" +from __future__ import annotations + import json import os import sys diff --git a/tests/integration/test_plugins.py b/tests/integration/test_plugins.py index 4323d05..0b4424a 100644 --- a/tests/integration/test_plugins.py +++ b/tests/integration/test_plugins.py @@ -1,4 +1,6 @@ """Integration tests for plugin loading.""" +from __future__ import annotations + import pytest from flake8.main.cli import main diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 904366e..0f8386a 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -1,4 +1,6 @@ """Shared fixtures between unit tests.""" +from __future__ import annotations + import argparse import pytest diff --git a/tests/unit/plugins/finder_test.py b/tests/unit/plugins/finder_test.py index 63f8156..d526fd1 100644 --- a/tests/unit/plugins/finder_test.py +++ b/tests/unit/plugins/finder_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import configparser import sys from unittest import mock diff --git a/tests/unit/plugins/pycodestyle_test.py b/tests/unit/plugins/pycodestyle_test.py index 703970f..1b00d9d 100644 --- a/tests/unit/plugins/pycodestyle_test.py +++ b/tests/unit/plugins/pycodestyle_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import importlib.machinery import importlib.util import os.path diff --git a/tests/unit/plugins/reporter_test.py b/tests/unit/plugins/reporter_test.py index 4b46cc4..07b0dfa 100644 --- a/tests/unit/plugins/reporter_test.py +++ b/tests/unit/plugins/reporter_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import argparse import pytest diff --git a/tests/unit/test_application.py b/tests/unit/test_application.py index 508f83b..04147ec 100644 --- a/tests/unit/test_application.py +++ b/tests/unit/test_application.py @@ -1,4 +1,6 @@ """Tests for the Application class.""" +from __future__ import annotations + import argparse import pytest diff --git a/tests/unit/test_base_formatter.py b/tests/unit/test_base_formatter.py index 7830eb4..5b57335 100644 --- a/tests/unit/test_base_formatter.py +++ b/tests/unit/test_base_formatter.py @@ -1,4 +1,6 @@ """Tests for the BaseFormatter object.""" +from __future__ import annotations + import argparse import sys from unittest import mock diff --git a/tests/unit/test_checker_manager.py b/tests/unit/test_checker_manager.py index c6114f6..32bc9a4 100644 --- a/tests/unit/test_checker_manager.py +++ b/tests/unit/test_checker_manager.py @@ -1,4 +1,6 @@ """Tests for the Manager object for FileCheckers.""" +from __future__ import annotations + import errno import multiprocessing from unittest import mock diff --git a/tests/unit/test_debug.py b/tests/unit/test_debug.py index 1fc93ef..6d8806c 100644 --- a/tests/unit/test_debug.py +++ b/tests/unit/test_debug.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from unittest import mock from flake8._compat import importlib_metadata diff --git a/tests/unit/test_decision_engine.py b/tests/unit/test_decision_engine.py index 59c372a..27ced3f 100644 --- a/tests/unit/test_decision_engine.py +++ b/tests/unit/test_decision_engine.py @@ -1,4 +1,6 @@ """Tests for the flake8.style_guide.DecisionEngine class.""" +from __future__ import annotations + import argparse import pytest diff --git a/tests/unit/test_discover_files.py b/tests/unit/test_discover_files.py index f2dfa13..edf047d 100644 --- a/tests/unit/test_discover_files.py +++ b/tests/unit/test_discover_files.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os.path import pytest diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py index 96d0244..99b298b 100644 --- a/tests/unit/test_exceptions.py +++ b/tests/unit/test_exceptions.py @@ -1,4 +1,6 @@ """Tests for the flake8.exceptions module.""" +from __future__ import annotations + import pickle import pytest diff --git a/tests/unit/test_file_checker.py b/tests/unit/test_file_checker.py index 3fe2e51..368b5f6 100644 --- a/tests/unit/test_file_checker.py +++ b/tests/unit/test_file_checker.py @@ -1,4 +1,6 @@ """Unit tests for the FileChecker class.""" +from __future__ import annotations + import argparse from unittest import mock diff --git a/tests/unit/test_file_processor.py b/tests/unit/test_file_processor.py index e8ebbc1..bd693b3 100644 --- a/tests/unit/test_file_processor.py +++ b/tests/unit/test_file_processor.py @@ -1,4 +1,6 @@ """Tests for the FileProcessor class.""" +from __future__ import annotations + import ast import tokenize from unittest import mock diff --git a/tests/unit/test_filenameonly_formatter.py b/tests/unit/test_filenameonly_formatter.py index e92d4bb..77f75b9 100644 --- a/tests/unit/test_filenameonly_formatter.py +++ b/tests/unit/test_filenameonly_formatter.py @@ -1,4 +1,6 @@ """Tests for the FilenameOnly formatter object.""" +from __future__ import annotations + import argparse from flake8.formatting import default diff --git a/tests/unit/test_legacy_api.py b/tests/unit/test_legacy_api.py index 0e5b535..844bd5a 100644 --- a/tests/unit/test_legacy_api.py +++ b/tests/unit/test_legacy_api.py @@ -1,4 +1,6 @@ """Tests for Flake8's legacy API.""" +from __future__ import annotations + import argparse import configparser import os.path diff --git a/tests/unit/test_main_options.py b/tests/unit/test_main_options.py index aea2071..7c1feba 100644 --- a/tests/unit/test_main_options.py +++ b/tests/unit/test_main_options.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from flake8.main import options diff --git a/tests/unit/test_nothing_formatter.py b/tests/unit/test_nothing_formatter.py index eb4b862..76929fd 100644 --- a/tests/unit/test_nothing_formatter.py +++ b/tests/unit/test_nothing_formatter.py @@ -1,4 +1,6 @@ """Tests for the Nothing formatter obbject.""" +from __future__ import annotations + import argparse from flake8.formatting import default diff --git a/tests/unit/test_option.py b/tests/unit/test_option.py index d576c49..4b3070d 100644 --- a/tests/unit/test_option.py +++ b/tests/unit/test_option.py @@ -1,4 +1,6 @@ """Unit tests for flake8.options.manager.Option.""" +from __future__ import annotations + import functools from unittest import mock diff --git a/tests/unit/test_option_manager.py b/tests/unit/test_option_manager.py index 0a56076..d5b88c3 100644 --- a/tests/unit/test_option_manager.py +++ b/tests/unit/test_option_manager.py @@ -1,4 +1,6 @@ """Unit tests for flake.options.manager.OptionManager.""" +from __future__ import annotations + import argparse import os from unittest import mock diff --git a/tests/unit/test_options_config.py b/tests/unit/test_options_config.py index 4ad6acd..0890ea9 100644 --- a/tests/unit/test_options_config.py +++ b/tests/unit/test_options_config.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import configparser import os.path from unittest import mock diff --git a/tests/unit/test_pyflakes_codes.py b/tests/unit/test_pyflakes_codes.py index c251721..4626e3d 100644 --- a/tests/unit/test_pyflakes_codes.py +++ b/tests/unit/test_pyflakes_codes.py @@ -1,4 +1,6 @@ """Tests of pyflakes monkey patches.""" +from __future__ import annotations + import ast import pyflakes diff --git a/tests/unit/test_statistics.py b/tests/unit/test_statistics.py index 03f3189..261f360 100644 --- a/tests/unit/test_statistics.py +++ b/tests/unit/test_statistics.py @@ -1,4 +1,6 @@ """Tests for the statistics module in Flake8.""" +from __future__ import annotations + import pytest from flake8 import statistics as stats diff --git a/tests/unit/test_style_guide.py b/tests/unit/test_style_guide.py index e4aaff2..94fcb26 100644 --- a/tests/unit/test_style_guide.py +++ b/tests/unit/test_style_guide.py @@ -1,4 +1,6 @@ """Tests for the flake8.style_guide.StyleGuide class.""" +from __future__ import annotations + import argparse from unittest import mock diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index fefe662..98a5a5d 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1,4 +1,6 @@ """Tests for flake8's utils module.""" +from __future__ import annotations + import io import logging import os diff --git a/tests/unit/test_violation.py b/tests/unit/test_violation.py index 6b47691..a4a43da 100644 --- a/tests/unit/test_violation.py +++ b/tests/unit/test_violation.py @@ -1,4 +1,6 @@ """Tests for the flake8.violation.Violation class.""" +from __future__ import annotations + from unittest import mock import pytest diff --git a/tox.ini b/tox.ini index c0763c1..df5a75c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion=2.3.1 -envlist = py36,py37,py38,flake8,linters,docs +envlist = py37,py38,flake8,linters,docs [testenv] deps =