From 9d55ccdb729d1255b9cf09438b6073b05b9ce52c Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 29 Mar 2025 15:39:58 -0400 Subject: [PATCH] py39+ --- .github/workflows/main.yml | 10 +++++----- .pre-commit-config.yaml | 10 +++++----- bin/gen-pycodestyle-plugin | 10 +++++----- docs/source/internal/releases.rst | 4 ++-- docs/source/user/invocation.rst | 10 +++++----- example-plugin/setup.py | 2 -- setup.cfg | 2 +- src/flake8/checker.py | 10 ++++------ src/flake8/discover_files.py | 8 ++++---- src/flake8/main/application.py | 2 +- src/flake8/main/cli.py | 2 +- src/flake8/options/aggregator.py | 2 +- src/flake8/options/manager.py | 2 +- src/flake8/options/parse_args.py | 2 +- src/flake8/plugins/finder.py | 12 ++++++------ src/flake8/plugins/pycodestyle.py | 6 +++--- src/flake8/plugins/pyflakes.py | 4 ++-- src/flake8/processor.py | 16 ++++++---------- src/flake8/statistics.py | 4 ++-- src/flake8/style_guide.py | 18 ++++++------------ src/flake8/utils.py | 4 ++-- src/flake8/violation.py | 2 +- 22 files changed, 64 insertions(+), 78 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e210204..08f54ea 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,9 +15,6 @@ jobs: - os: ubuntu-latest python: pypy-3.9 toxenv: py - - os: ubuntu-latest - python: 3.8 - toxenv: py - os: ubuntu-latest python: 3.9 toxenv: py @@ -28,11 +25,14 @@ jobs: python: '3.11' toxenv: py - os: ubuntu-latest - python: '3.12-dev' + python: '3.12' + toxenv: py + - os: ubuntu-latest + python: '3.13' toxenv: py # windows - os: windows-latest - python: 3.8 + python: 3.9 toxenv: py # misc - os: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad60be1..9df4a79 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,19 +12,19 @@ repos: hooks: - id: setup-cfg-fmt - repo: https://github.com/asottile/reorder-python-imports - rev: v3.12.0 + rev: v3.14.0 hooks: - id: reorder-python-imports args: [ --application-directories, '.:src', - --py38-plus, + --py39-plus, --add-import, 'from __future__ import annotations', ] - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.19.1 hooks: - id: pyupgrade - args: [--py38-plus] + args: [--py39-plus] - repo: https://github.com/psf/black rev: 23.12.1 hooks: @@ -35,7 +35,7 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 + rev: v1.15.0 hooks: - id: mypy exclude: ^(docs/|example-plugin/) diff --git a/bin/gen-pycodestyle-plugin b/bin/gen-pycodestyle-plugin index 8bc2efc..c93fbfe 100755 --- a/bin/gen-pycodestyle-plugin +++ b/bin/gen-pycodestyle-plugin @@ -3,9 +3,9 @@ from __future__ import annotations import inspect import os.path +from collections.abc import Generator from typing import Any from typing import Callable -from typing import Generator from typing import NamedTuple import pycodestyle @@ -42,7 +42,7 @@ class Call(NamedTuple): return cls(func.__name__, inspect.isgeneratorfunction(func), params) -def lines() -> Generator[str, None, None]: +def lines() -> Generator[str]: logical = [] physical = [] @@ -58,8 +58,8 @@ def lines() -> Generator[str, None, None]: yield "# fmt: off" yield "from __future__ import annotations" yield "" + yield "from collections.abc import Generator" yield "from typing import Any" - yield "from typing import Generator" yield "" imports = sorted(call.name for call in logical + physical) for name in imports: @@ -71,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]]:" yield ' """Run pycodestyle logical checks."""' for call in sorted(logical): yield call.to_src() @@ -82,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]]:" yield ' """Run pycodestyle physical checks."""' for call in sorted(physical): yield call.to_src() diff --git a/docs/source/internal/releases.rst b/docs/source/internal/releases.rst index 0081509..d71796d 100644 --- a/docs/source/internal/releases.rst +++ b/docs/source/internal/releases.rst @@ -81,9 +81,9 @@ for users. Before releasing, the following tox test environments must pass: -- Python 3.8 (a.k.a., ``tox -e py38``) +- Python 3.9 (a.k.a., ``tox -e py39``) -- Python 3.12 (a.k.a., ``tox -e py312``) +- Python 3.13 (a.k.a., ``tox -e py313``) - PyPy 3 (a.k.a., ``tox -e pypy3``) diff --git a/docs/source/user/invocation.rst b/docs/source/user/invocation.rst index 61cef97..10895dd 100644 --- a/docs/source/user/invocation.rst +++ b/docs/source/user/invocation.rst @@ -14,25 +14,25 @@ like so: Where you simply allow the shell running in your terminal to locate |Flake8|. In some cases, though, you may have installed |Flake8| for multiple versions -of Python (e.g., Python 3.8 and Python 3.9) and you need to call a specific +of Python (e.g., Python 3.13 and Python 3.14) and you need to call a specific version. In that case, you will have much better results using: .. prompt:: bash - python3.8 -m flake8 + python3.13 -m flake8 Or .. prompt:: bash - python3.9 -m flake8 + python3.14 -m flake8 Since that will tell the correct version of Python to run |Flake8|. .. note:: - Installing |Flake8| once will not install it on both Python 3.8 and - Python 3.9. It will only install it for the version of Python that + Installing |Flake8| once will not install it on both Python 3.13 and + Python 3.14. It will only install it for the version of Python that is running pip. It is also possible to specify command-line options directly to |Flake8|: diff --git a/example-plugin/setup.py b/example-plugin/setup.py index c0720bd..9e7c89f 100644 --- a/example-plugin/setup.py +++ b/example-plugin/setup.py @@ -23,8 +23,6 @@ setuptools.setup( "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Quality Assurance", ], diff --git a/setup.cfg b/setup.cfg index 28e6f93..688e349 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,7 +31,7 @@ install_requires = mccabe>=0.7.0,<0.8.0 pycodestyle>=2.12.0,<2.13.0 pyflakes>=3.2.0,<3.3.0 -python_requires = >=3.8.1 +python_requires = >=3.9 package_dir = =src diff --git a/src/flake8/checker.py b/src/flake8/checker.py index ff62679..d1659b7 100644 --- a/src/flake8/checker.py +++ b/src/flake8/checker.py @@ -9,12 +9,10 @@ import multiprocessing.pool import operator import signal import tokenize +from collections.abc import Generator +from collections.abc import Sequence from typing import Any -from typing import Generator -from typing import List from typing import Optional -from typing import Sequence -from typing import Tuple from flake8 import defaults from flake8 import exceptions @@ -27,7 +25,7 @@ from flake8.plugins.finder import Checkers from flake8.plugins.finder import LoadedPlugin from flake8.style_guide import StyleGuideManager -Results = List[Tuple[str, int, int, str, Optional[str]]] +Results = list[tuple[str, int, int, str, Optional[str]]] LOG = logging.getLogger(__name__) @@ -53,7 +51,7 @@ _mp_options: argparse.Namespace @contextlib.contextmanager def _mp_prefork( plugins: Checkers, options: argparse.Namespace -) -> Generator[None, None, None]: +) -> Generator[None]: # we can save significant startup work w/ `fork` multiprocessing global _mp_plugins, _mp_options _mp_plugins, _mp_options = plugins, options diff --git a/src/flake8/discover_files.py b/src/flake8/discover_files.py index 580d5fd..da28ba5 100644 --- a/src/flake8/discover_files.py +++ b/src/flake8/discover_files.py @@ -3,9 +3,9 @@ from __future__ import annotations import logging import os.path +from collections.abc import Generator +from collections.abc import Sequence from typing import Callable -from typing import Generator -from typing import Sequence from flake8 import utils @@ -16,7 +16,7 @@ def _filenames_from( arg: str, *, predicate: Callable[[str], bool], -) -> Generator[str, None, None]: +) -> Generator[str]: """Generate filenames from an argument. :param arg: @@ -55,7 +55,7 @@ def expand_paths( stdin_display_name: str, filename_patterns: Sequence[str], exclude: Sequence[str], -) -> Generator[str, None, None]: +) -> Generator[str]: """Expand out ``paths`` from commandline to the lintable files.""" if not paths: paths = ["."] diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py index b6bfae3..4704cbd 100644 --- a/src/flake8/main/application.py +++ b/src/flake8/main/application.py @@ -5,7 +5,7 @@ import argparse import json import logging import time -from typing import Sequence +from collections.abc import Sequence import flake8 from flake8 import checker diff --git a/src/flake8/main/cli.py b/src/flake8/main/cli.py index 01a67ac..1a52f36 100644 --- a/src/flake8/main/cli.py +++ b/src/flake8/main/cli.py @@ -2,7 +2,7 @@ from __future__ import annotations import sys -from typing import Sequence +from collections.abc import Sequence from flake8.main import application diff --git a/src/flake8/options/aggregator.py b/src/flake8/options/aggregator.py index af8e744..999161a 100644 --- a/src/flake8/options/aggregator.py +++ b/src/flake8/options/aggregator.py @@ -8,7 +8,7 @@ from __future__ import annotations import argparse import configparser import logging -from typing import Sequence +from collections.abc import Sequence from flake8.options import config from flake8.options.manager import OptionManager diff --git a/src/flake8/options/manager.py b/src/flake8/options/manager.py index 4fd26b2..cb195fe 100644 --- a/src/flake8/options/manager.py +++ b/src/flake8/options/manager.py @@ -5,9 +5,9 @@ import argparse import enum import functools import logging +from collections.abc import Sequence from typing import Any from typing import Callable -from typing import Sequence from flake8 import utils from flake8.plugins.finder import Plugins diff --git a/src/flake8/options/parse_args.py b/src/flake8/options/parse_args.py index e3f8795..ff5e08f 100644 --- a/src/flake8/options/parse_args.py +++ b/src/flake8/options/parse_args.py @@ -2,7 +2,7 @@ from __future__ import annotations import argparse -from typing import Sequence +from collections.abc import Sequence import flake8 from flake8.main import options diff --git a/src/flake8/plugins/finder.py b/src/flake8/plugins/finder.py index 380ec3a..88b66a0 100644 --- a/src/flake8/plugins/finder.py +++ b/src/flake8/plugins/finder.py @@ -7,9 +7,9 @@ import inspect import itertools import logging import sys +from collections.abc import Generator +from collections.abc import Iterable from typing import Any -from typing import Generator -from typing import Iterable from typing import NamedTuple from flake8 import utils @@ -68,7 +68,7 @@ class Plugins(NamedTuple): reporters: dict[str, LoadedPlugin] disabled: list[LoadedPlugin] - def all_plugins(self) -> Generator[LoadedPlugin, None, None]: + def all_plugins(self) -> Generator[LoadedPlugin]: """Return an iterator over all :class:`LoadedPlugin`s.""" yield from self.checkers.tree yield from self.checkers.logical_line @@ -151,7 +151,7 @@ def _flake8_plugins( eps: Iterable[importlib.metadata.EntryPoint], name: str, version: str, -) -> Generator[Plugin, None, None]: +) -> Generator[Plugin]: pyflakes_meta = importlib.metadata.distribution("pyflakes").metadata pycodestyle_meta = importlib.metadata.distribution("pycodestyle").metadata @@ -173,7 +173,7 @@ def _flake8_plugins( yield Plugin(name, version, ep) -def _find_importlib_plugins() -> Generator[Plugin, None, None]: +def _find_importlib_plugins() -> Generator[Plugin]: # some misconfigured pythons (RHEL) have things on `sys.path` twice seen = set() for dist in importlib.metadata.distributions(): @@ -212,7 +212,7 @@ def _find_importlib_plugins() -> Generator[Plugin, None, None]: def _find_local_plugins( cfg: configparser.RawConfigParser, -) -> Generator[Plugin, None, None]: +) -> Generator[Plugin]: for plugin_type in ("extension", "report"): group = f"flake8.{plugin_type}" for plugin_s in utils.parse_comma_separated_list( diff --git a/src/flake8/plugins/pycodestyle.py b/src/flake8/plugins/pycodestyle.py index 9e1d2bb..cd760dc 100644 --- a/src/flake8/plugins/pycodestyle.py +++ b/src/flake8/plugins/pycodestyle.py @@ -2,8 +2,8 @@ # fmt: off from __future__ import annotations +from collections.abc import Generator from typing import Any -from typing import Generator from pycodestyle import ambiguous_identifier as _ambiguous_identifier from pycodestyle import bare_except as _bare_except @@ -55,7 +55,7 @@ def pycodestyle_logical( previous_unindented_logical_line: Any, tokens: Any, verbose: Any, -) -> Generator[tuple[int, str], None, None]: +) -> Generator[tuple[int, str]]: """Run pycodestyle logical checks.""" yield from _ambiguous_identifier(logical_line, tokens) yield from _bare_except(logical_line, noqa) @@ -93,7 +93,7 @@ def pycodestyle_physical( noqa: Any, physical_line: Any, total_lines: Any, -) -> Generator[tuple[int, str], None, None]: +) -> Generator[tuple[int, str]]: """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 6c57619..2835e03 100644 --- a/src/flake8/plugins/pyflakes.py +++ b/src/flake8/plugins/pyflakes.py @@ -4,8 +4,8 @@ from __future__ import annotations import argparse import ast import logging +from collections.abc import Generator from typing import Any -from typing import Generator import pyflakes.checker @@ -97,7 +97,7 @@ class FlakesChecker(pyflakes.checker.Checker): cls.builtIns = cls.builtIns.union(options.builtins) cls.with_doctest = options.doctests - def run(self) -> Generator[tuple[int, int, str, type[Any]], None, None]: + def run(self) -> Generator[tuple[int, int, str, type[Any]]]: """Run the plugin.""" for message in self.messages: col = getattr(message, "col", 0) diff --git a/src/flake8/processor.py b/src/flake8/processor.py index e44547b..610964d 100644 --- a/src/flake8/processor.py +++ b/src/flake8/processor.py @@ -6,10 +6,8 @@ import ast import functools import logging import tokenize +from collections.abc import Generator from typing import Any -from typing import Generator -from typing import List -from typing import Tuple from flake8 import defaults from flake8 import utils @@ -24,8 +22,8 @@ SKIP_TOKENS = frozenset( [tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT] ) -_LogicalMapping = List[Tuple[int, Tuple[int, int]]] -_Logical = Tuple[List[str], List[str], _LogicalMapping] +_LogicalMapping = list[tuple[int, tuple[int, int]]] +_Logical = tuple[list[str], list[str], _LogicalMapping] class FileProcessor: @@ -127,9 +125,7 @@ class FileProcessor: """Signal the beginning of an fstring.""" self._fstring_start = lineno - def multiline_string( - self, token: tokenize.TokenInfo - ) -> Generator[str, None, None]: + def multiline_string(self, token: tokenize.TokenInfo) -> Generator[str]: """Iterate through the lines of a multiline string.""" if token.type == FSTRING_END: # pragma: >=3.12 cover start = self._fstring_start @@ -210,7 +206,7 @@ class FileProcessor: brace_offset = text.count("{") + text.count("}") text = "x" * (len(text) + brace_offset) end = (end[0], end[1] + brace_offset) - if previous_row: + if previous_row is not None and previous_column is not None: (start_row, start_column) = start if previous_row != start_row: row_index = previous_row - 1 @@ -263,7 +259,7 @@ class FileProcessor: ) return ret - def generate_tokens(self) -> Generator[tokenize.TokenInfo, None, None]: + def generate_tokens(self) -> Generator[tokenize.TokenInfo]: """Tokenize the file and yield the tokens.""" for token in tokenize.generate_tokens(self.next_line): if token[2][0] > self.total_lines: diff --git a/src/flake8/statistics.py b/src/flake8/statistics.py index a33e6a6..5a22254 100644 --- a/src/flake8/statistics.py +++ b/src/flake8/statistics.py @@ -1,7 +1,7 @@ """Statistic collection logic for Flake8.""" from __future__ import annotations -from typing import Generator +from collections.abc import Generator from typing import NamedTuple from flake8.violation import Violation @@ -36,7 +36,7 @@ class Statistics: def statistics_for( self, prefix: str, filename: str | None = None - ) -> Generator[Statistic, None, None]: + ) -> Generator[Statistic]: """Generate statistics for the prefix and filename. If you have a :class:`Statistics` object that has recorded errors, diff --git a/src/flake8/style_guide.py b/src/flake8/style_guide.py index a409484..f72e6d8 100644 --- a/src/flake8/style_guide.py +++ b/src/flake8/style_guide.py @@ -7,8 +7,8 @@ import copy import enum import functools import logging -from typing import Generator -from typing import Sequence +from collections.abc import Generator +from collections.abc import Sequence from flake8 import defaults from flake8 import statistics @@ -225,13 +225,11 @@ class StyleGuideManager: *self.populate_style_guides_with(options), ] - self.style_guide_for = functools.lru_cache(maxsize=None)( - self._style_guide_for - ) + self.style_guide_for = functools.cache(self._style_guide_for) def populate_style_guides_with( self, options: argparse.Namespace - ) -> Generator[StyleGuide, None, None]: + ) -> Generator[StyleGuide]: """Generate style guides from the per-file-ignores option. :param options: @@ -253,9 +251,7 @@ class StyleGuideManager: ) @contextlib.contextmanager - def processing_file( - self, filename: str - ) -> Generator[StyleGuide, None, None]: + def processing_file(self, filename: str) -> Generator[StyleGuide]: """Record the fact that we're processing the file's results.""" guide = self.style_guide_for(filename) with guide.processing_file(filename): @@ -338,9 +334,7 @@ class StyleGuide: ) @contextlib.contextmanager - def processing_file( - self, filename: str - ) -> Generator[StyleGuide, None, None]: + def processing_file(self, filename: str) -> Generator[StyleGuide]: """Record the fact that we're processing the file's results.""" self.formatter.beginning(filename) yield self diff --git a/src/flake8/utils.py b/src/flake8/utils.py index afc3896..67db33f 100644 --- a/src/flake8/utils.py +++ b/src/flake8/utils.py @@ -11,9 +11,9 @@ import re import sys import textwrap import tokenize +from collections.abc import Sequence +from re import Pattern from typing import NamedTuple -from typing import Pattern -from typing import Sequence from flake8 import exceptions diff --git a/src/flake8/violation.py b/src/flake8/violation.py index 96161d4..ae1631a 100644 --- a/src/flake8/violation.py +++ b/src/flake8/violation.py @@ -4,7 +4,7 @@ from __future__ import annotations import functools import linecache import logging -from typing import Match +from re import Match from typing import NamedTuple from flake8 import defaults