Merge pull request #1973 from PyCQA/py39-plus

py39+
This commit is contained in:
Anthony Sottile 2025-03-29 15:38:33 -04:00 committed by GitHub
commit a7e8f6250c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 64 additions and 78 deletions

View file

@ -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

View file

@ -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/)

View file

@ -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()

View file

@ -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``)

View file

@ -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|:

View file

@ -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",
],

View file

@ -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

View file

@ -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

View file

@ -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 = ["."]

View file

@ -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

View file

@ -2,7 +2,7 @@
from __future__ import annotations
import sys
from typing import Sequence
from collections.abc import Sequence
from flake8.main import application

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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(

View file

@ -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:

View file

@ -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)

View file

@ -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:

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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