Compare commits

..

No commits in common. "main" and "7.2.0" have entirely different histories.
main ... 7.2.0

42 changed files with 195 additions and 233 deletions

View file

@ -13,7 +13,10 @@ jobs:
include: include:
# linux # linux
- os: ubuntu-latest - os: ubuntu-latest
python: pypy-3.11 python: pypy-3.9
toxenv: py
- os: ubuntu-latest
python: 3.9
toxenv: py toxenv: py
- os: ubuntu-latest - os: ubuntu-latest
python: '3.10' python: '3.10'
@ -27,12 +30,9 @@ jobs:
- os: ubuntu-latest - os: ubuntu-latest
python: '3.13' python: '3.13'
toxenv: py toxenv: py
- os: ubuntu-latest
python: '3.14'
toxenv: py
# windows # windows
- os: windows-latest - os: windows-latest
python: '3.10' python: 3.9
toxenv: py toxenv: py
# misc # misc
- os: ubuntu-latest - os: ubuntu-latest
@ -46,8 +46,8 @@ jobs:
toxenv: dogfood toxenv: dogfood
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- uses: actions/setup-python@v5 - uses: actions/setup-python@v2
with: with:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
- run: python -mpip install --upgrade setuptools pip tox virtualenv - run: python -mpip install --upgrade setuptools pip tox virtualenv

View file

@ -1,10 +1,6 @@
repos: repos:
- repo: https://github.com/asottile/add-trailing-comma
rev: v4.0.0
hooks:
- id: add-trailing-comma
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0 rev: v4.5.0
hooks: hooks:
- id: check-yaml - id: check-yaml
- id: debug-statements - id: debug-statements
@ -12,33 +8,34 @@ repos:
- id: trailing-whitespace - id: trailing-whitespace
exclude: ^tests/fixtures/ exclude: ^tests/fixtures/
- repo: https://github.com/asottile/setup-cfg-fmt - repo: https://github.com/asottile/setup-cfg-fmt
rev: v3.2.0 rev: v2.5.0
hooks: hooks:
- id: setup-cfg-fmt - id: setup-cfg-fmt
- repo: https://github.com/asottile/reorder-python-imports - repo: https://github.com/asottile/reorder-python-imports
rev: v3.16.0 rev: v3.14.0
hooks: hooks:
- id: reorder-python-imports - id: reorder-python-imports
args: [ args: [
--application-directories, '.:src', --application-directories, '.:src',
--py310-plus, --py39-plus,
--add-import, 'from __future__ import annotations', --add-import, 'from __future__ import annotations',
] ]
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v3.21.2 rev: v3.19.1
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py310-plus] args: [--py39-plus]
- repo: https://github.com/hhatto/autopep8 - repo: https://github.com/psf/black
rev: v2.3.2 rev: 23.12.1
hooks: hooks:
- id: autopep8 - id: black
args: [--line-length=79]
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: 7.3.0 rev: 7.0.0
hooks: hooks:
- id: flake8 - id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.19.1 rev: v1.15.0
hooks: hooks:
- id: mypy - id: mypy
exclude: ^(docs/|example-plugin/) exclude: ^(docs/|example-plugin/)

View file

@ -8,5 +8,3 @@ python:
install: install:
- path: . - path: .
- requirements: docs/source/requirements.txt - requirements: docs/source/requirements.txt
sphinx:
configuration: docs/source/conf.py

View file

@ -3,9 +3,9 @@ from __future__ import annotations
import inspect import inspect
import os.path import os.path
from collections.abc import Callable
from collections.abc import Generator from collections.abc import Generator
from typing import Any from typing import Any
from typing import Callable
from typing import NamedTuple from typing import NamedTuple
import pycodestyle import pycodestyle

View file

@ -1,15 +0,0 @@
7.3.0 -- 2025-06-20
-------------------
You can view the `7.3.0 milestone`_ on GitHub for more details.
New Dependency Information
~~~~~~~~~~~~~~~~~~~~~~~~~~
- Added support for python 3.14 (See also :pull:`1983`).
- pycodestyle has been updated to >= 2.14.0, < 2.15.0 (See also :pull:`1985`).
- Pyflakes has been updated to >= 3.4.0, < 3.5.0 (See also :pull:`1985`).
.. all links
.. _7.3.0 milestone:
https://github.com/PyCQA/flake8/milestone/54

View file

@ -9,19 +9,18 @@ with the newest releases first.
================== ==================
.. toctree:: .. toctree::
7.3.0
7.2.0
7.1.2
7.1.1
7.1.0
7.0.0 7.0.0
7.1.0
7.1.1
7.1.2
7.2.0
6.x Release Series 6.x Release Series
================== ==================
.. toctree:: .. toctree::
6.1.0
6.0.0 6.0.0
6.1.0
5.x Release Series 5.x Release Series
================== ==================

View file

@ -59,8 +59,6 @@ generates its own :term:`error code`\ s for ``pyflakes``:
+------+---------------------------------------------------------------------+ +------+---------------------------------------------------------------------+
| F541 | f-string without any placeholders | | F541 | f-string without any placeholders |
+------+---------------------------------------------------------------------+ +------+---------------------------------------------------------------------+
| F542 | t-string without any placeholders |
+------+---------------------------------------------------------------------+
+------+---------------------------------------------------------------------+ +------+---------------------------------------------------------------------+
| F601 | dictionary key ``name`` repeated with different values | | F601 | dictionary key ``name`` repeated with different values |
+------+---------------------------------------------------------------------+ +------+---------------------------------------------------------------------+
@ -104,9 +102,6 @@ generates its own :term:`error code`\ s for ``pyflakes``:
+------+---------------------------------------------------------------------+ +------+---------------------------------------------------------------------+
| F823 | local variable ``name`` ... referenced before assignment | | F823 | local variable ``name`` ... referenced before assignment |
+------+---------------------------------------------------------------------+ +------+---------------------------------------------------------------------+
| F824 | ``global name`` / ``nonlocal name`` is unused: name is never |
| | assigned in scope |
+------+---------------------------------------------------------------------+
| F831 | duplicate argument ``name`` in function definition | | F831 | duplicate argument ``name`` in function definition |
+------+---------------------------------------------------------------------+ +------+---------------------------------------------------------------------+
| F841 | local variable ``name`` is assigned to but never used | | F841 | local variable ``name`` is assigned to but never used |

View file

@ -16,6 +16,7 @@ classifiers =
Environment :: Console Environment :: Console
Framework :: Flake8 Framework :: Flake8
Intended Audience :: Developers Intended Audience :: Developers
License :: OSI Approved :: MIT License
Programming Language :: Python Programming Language :: Python
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 :: Only
@ -28,9 +29,9 @@ classifiers =
packages = find: packages = find:
install_requires = install_requires =
mccabe>=0.7.0,<0.8.0 mccabe>=0.7.0,<0.8.0
pycodestyle>=2.14.0,<2.15.0 pycodestyle>=2.13.0,<2.14.0
pyflakes>=3.4.0,<3.5.0 pyflakes>=3.3.0,<3.4.0
python_requires = >=3.10 python_requires = >=3.9
package_dir = package_dir =
=src =src

View file

@ -17,7 +17,7 @@ import sys
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
LOG.addHandler(logging.NullHandler()) LOG.addHandler(logging.NullHandler())
__version__ = "7.3.0" __version__ = "7.2.0"
__version_info__ = tuple(int(i) for i in __version__.split(".") if i.isdigit()) __version_info__ = tuple(int(i) for i in __version__.split(".") if i.isdigit())
_VERBOSITY_TO_LOG_LEVEL = { _VERBOSITY_TO_LOG_LEVEL = {
@ -66,5 +66,5 @@ def configure_logging(
LOG.addHandler(handler) LOG.addHandler(handler)
LOG.setLevel(log_level) LOG.setLevel(log_level)
LOG.debug( LOG.debug(
"Added a %s logging handler to logger root at %s", filename, __name__, "Added a %s logging handler to logger root at %s", filename, __name__
) )

View file

@ -9,10 +9,3 @@ if sys.version_info >= (3, 12): # pragma: >=3.12 cover
FSTRING_END = tokenize.FSTRING_END FSTRING_END = tokenize.FSTRING_END
else: # pragma: <3.12 cover else: # pragma: <3.12 cover
FSTRING_START = FSTRING_MIDDLE = FSTRING_END = -1 FSTRING_START = FSTRING_MIDDLE = FSTRING_END = -1
if sys.version_info >= (3, 14): # pragma: >=3.14 cover
TSTRING_START = tokenize.TSTRING_START
TSTRING_MIDDLE = tokenize.TSTRING_MIDDLE
TSTRING_END = tokenize.TSTRING_END
else: # pragma: <3.14 cover
TSTRING_START = TSTRING_MIDDLE = TSTRING_END = -1

View file

@ -135,7 +135,7 @@ class StyleGuide:
stdin_display_name=self.options.stdin_display_name, stdin_display_name=self.options.stdin_display_name,
filename_patterns=self.options.filename, filename_patterns=self.options.filename,
exclude=self.options.exclude, exclude=self.options.exclude,
), )
) )
return not paths return not paths
@ -153,7 +153,7 @@ class StyleGuide:
if not issubclass(reporter, formatter.BaseFormatter): if not issubclass(reporter, formatter.BaseFormatter):
raise ValueError( raise ValueError(
"Report should be subclass of " "Report should be subclass of "
"flake8.formatter.BaseFormatter.", "flake8.formatter.BaseFormatter."
) )
self._application.formatter = reporter(self.options) self._application.formatter = reporter(self.options)
self._application.guide = None self._application.guide = None

View file

@ -19,7 +19,6 @@ from flake8 import exceptions
from flake8 import processor from flake8 import processor
from flake8 import utils from flake8 import utils
from flake8._compat import FSTRING_START from flake8._compat import FSTRING_START
from flake8._compat import TSTRING_START
from flake8.discover_files import expand_paths from flake8.discover_files import expand_paths
from flake8.options.parse_args import parse_args from flake8.options.parse_args import parse_args
from flake8.plugins.finder import Checkers from flake8.plugins.finder import Checkers
@ -45,39 +44,40 @@ SERIAL_RETRY_ERRNOS = {
# noise in diffs. # noise in diffs.
} }
_mp: tuple[Checkers, argparse.Namespace] | None = None _mp_plugins: Checkers
_mp_options: argparse.Namespace
@contextlib.contextmanager @contextlib.contextmanager
def _mp_prefork( def _mp_prefork(
plugins: Checkers, options: argparse.Namespace, plugins: Checkers, options: argparse.Namespace
) -> Generator[None]: ) -> Generator[None]:
# we can save significant startup work w/ `fork` multiprocessing # we can save significant startup work w/ `fork` multiprocessing
global _mp global _mp_plugins, _mp_options
_mp = plugins, options _mp_plugins, _mp_options = plugins, options
try: try:
yield yield
finally: finally:
_mp = None del _mp_plugins, _mp_options
def _mp_init(argv: Sequence[str]) -> None: def _mp_init(argv: Sequence[str]) -> None:
global _mp global _mp_plugins, _mp_options
# Ensure correct signaling of ^C using multiprocessing.Pool. # Ensure correct signaling of ^C using multiprocessing.Pool.
signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN)
# for `fork` this'll already be set try:
if _mp is None: # for `fork` this'll already be set
_mp_plugins, _mp_options # noqa: B018
except NameError:
plugins, options = parse_args(argv) plugins, options = parse_args(argv)
_mp = plugins.checkers, options _mp_plugins, _mp_options = plugins.checkers, options
def _mp_run(filename: str) -> tuple[str, Results, dict[str, int]]: def _mp_run(filename: str) -> tuple[str, Results, dict[str, int]]:
assert _mp is not None, _mp
plugins, options = _mp
return FileChecker( return FileChecker(
filename=filename, plugins=plugins, options=options, filename=filename, plugins=_mp_plugins, options=_mp_options
).run_checks() ).run_checks()
@ -137,7 +137,7 @@ class Manager:
if utils.is_using_stdin(self.options.filenames): if utils.is_using_stdin(self.options.filenames):
LOG.warning( LOG.warning(
"The --jobs option is not compatible with supplying " "The --jobs option is not compatible with supplying "
"input using - . Ignoring --jobs arguments.", "input using - . Ignoring --jobs arguments."
) )
return 0 return 0
@ -252,7 +252,7 @@ class Manager:
stdin_display_name=self.options.stdin_display_name, stdin_display_name=self.options.stdin_display_name,
filename_patterns=self.options.filename, filename_patterns=self.options.filename,
exclude=self.exclude, exclude=self.exclude,
), )
) )
self.jobs = min(len(self.filenames), self.jobs) self.jobs = min(len(self.filenames), self.jobs)
@ -332,11 +332,11 @@ class FileChecker:
assert self.processor is not None, self.filename assert self.processor is not None, self.filename
try: try:
params = self.processor.keyword_arguments_for( params = self.processor.keyword_arguments_for(
plugin.parameters, arguments, plugin.parameters, arguments
) )
except AttributeError as ae: except AttributeError as ae:
raise exceptions.PluginRequestedUnknownParameters( raise exceptions.PluginRequestedUnknownParameters(
plugin_name=plugin.display_name, exception=ae, plugin_name=plugin.display_name, exception=ae
) )
try: try:
return plugin.obj(**arguments, **params) return plugin.obj(**arguments, **params)
@ -372,6 +372,43 @@ class FileChecker:
token = () token = ()
row, column = (1, 0) row, column = (1, 0)
if (
column > 0
and token
and isinstance(exception, SyntaxError)
and len(token) == 4 # Python 3.9 or earlier
):
# NOTE(sigmavirus24): SyntaxErrors report 1-indexed column
# numbers. We need to decrement the column number by 1 at
# least.
column_offset = 1
row_offset = 0
# See also: https://github.com/pycqa/flake8/issues/169,
# https://github.com/PyCQA/flake8/issues/1372
# On Python 3.9 and earlier, token will be a 4-item tuple with the
# last item being the string. Starting with 3.10, they added to
# the tuple so now instead of it ending with the code that failed
# to parse, it ends with the end of the section of code that
# failed to parse. Luckily the absolute position in the tuple is
# stable across versions so we can use that here
physical_line = token[3]
# NOTE(sigmavirus24): Not all "tokens" have a string as the last
# argument. In this event, let's skip trying to find the correct
# column and row values.
if physical_line is not None:
# NOTE(sigmavirus24): SyntaxErrors also don't exactly have a
# "physical" line so much as what was accumulated by the point
# tokenizing failed.
# See also: https://github.com/pycqa/flake8/issues/169
lines = physical_line.rstrip("\n").split("\n")
row_offset = len(lines) - 1
logical_line = lines[0]
logical_line_length = len(logical_line)
if column > logical_line_length:
column = logical_line_length
row -= row_offset
column -= column_offset
return row, column return row, column
def run_ast_checks(self) -> None: def run_ast_checks(self) -> None:
@ -511,14 +548,12 @@ class FileChecker:
self.run_logical_checks() self.run_logical_checks()
def check_physical_eol( def check_physical_eol(
self, token: tokenize.TokenInfo, prev_physical: str, self, token: tokenize.TokenInfo, prev_physical: str
) -> None: ) -> None:
"""Run physical checks if and only if it is at the end of the line.""" """Run physical checks if and only if it is at the end of the line."""
assert self.processor is not None assert self.processor is not None
if token.type == FSTRING_START: # pragma: >=3.12 cover if token.type == FSTRING_START: # pragma: >=3.12 cover
self.processor.fstring_start(token.start[0]) self.processor.fstring_start(token.start[0])
elif token.type == TSTRING_START: # pragma: >=3.14 cover
self.processor.tstring_start(token.start[0])
# a newline token ends a single physical line. # a newline token ends a single physical line.
elif processor.is_eol_token(token): elif processor.is_eol_token(token):
# if the file does not end with a newline, the NEWLINE # if the file does not end with a newline, the NEWLINE
@ -561,7 +596,7 @@ def _try_initialize_processpool(
def find_offset( def find_offset(
offset: int, mapping: processor._LogicalMapping, offset: int, mapping: processor._LogicalMapping
) -> tuple[int, int]: ) -> tuple[int, int]:
"""Find the offset tuple for a single offset.""" """Find the offset tuple for a single offset."""
if isinstance(offset, tuple): if isinstance(offset, tuple):

View file

@ -3,9 +3,9 @@ from __future__ import annotations
import logging import logging
import os.path import os.path
from collections.abc import Callable
from collections.abc import Generator from collections.abc import Generator
from collections.abc import Sequence from collections.abc import Sequence
from typing import Callable
from flake8 import utils from flake8 import utils

View file

@ -110,7 +110,7 @@ class BaseFormatter:
The formatted error string. The formatted error string.
""" """
raise NotImplementedError( raise NotImplementedError(
"Subclass of BaseFormatter did not implement" " format.", "Subclass of BaseFormatter did not implement" " format."
) )
def show_statistics(self, statistics: Statistics) -> None: def show_statistics(self, statistics: Statistics) -> None:

View file

@ -76,7 +76,7 @@ class Application:
assert self.formatter is not None assert self.formatter is not None
assert self.options is not None assert self.options is not None
self.guide = style_guide.StyleGuideManager( self.guide = style_guide.StyleGuideManager(
self.options, self.formatter, self.options, self.formatter
) )
def make_file_checker_manager(self, argv: Sequence[str]) -> None: def make_file_checker_manager(self, argv: Sequence[str]) -> None:

View file

@ -14,7 +14,7 @@ def information(version: str, plugins: Plugins) -> dict[str, Any]:
(loaded.plugin.package, loaded.plugin.version) (loaded.plugin.package, loaded.plugin.version)
for loaded in plugins.all_plugins() for loaded in plugins.all_plugins()
if loaded.plugin.package not in {"flake8", "local"} if loaded.plugin.package not in {"flake8", "local"}
}, }
) )
return { return {
"version": version, "version": version,

View file

@ -32,7 +32,7 @@ def stage1_arg_parser() -> argparse.ArgumentParser:
) )
parser.add_argument( parser.add_argument(
"--output-file", default=None, help="Redirect report to a file.", "--output-file", default=None, help="Redirect report to a file."
) )
# Config file options # Config file options

View file

@ -78,7 +78,7 @@ def load_config(
if config is not None: if config is not None:
if not cfg.read(config, encoding="UTF-8"): if not cfg.read(config, encoding="UTF-8"):
raise exceptions.ExecutionError( raise exceptions.ExecutionError(
f"The specified config file does not exist: {config}", f"The specified config file does not exist: {config}"
) )
cfg_dir = os.path.dirname(config) cfg_dir = os.path.dirname(config)
else: else:
@ -89,7 +89,7 @@ def load_config(
for filename in extra: for filename in extra:
if not cfg.read(filename, encoding="UTF-8"): if not cfg.read(filename, encoding="UTF-8"):
raise exceptions.ExecutionError( raise exceptions.ExecutionError(
f"The specified config file does not exist: {filename}", f"The specified config file does not exist: {filename}"
) )
return cfg, cfg_dir return cfg, cfg_dir
@ -131,7 +131,7 @@ def parse_config(
raise ValueError( raise ValueError(
f"Error code {error_code!r} " f"Error code {error_code!r} "
f"supplied to {option_name!r} option " f"supplied to {option_name!r} option "
f"does not match {VALID_CODE_PREFIX.pattern!r}", f"does not match {VALID_CODE_PREFIX.pattern!r}"
) )
assert option.config_name is not None assert option.config_name is not None

View file

@ -5,9 +5,9 @@ import argparse
import enum import enum
import functools import functools
import logging import logging
from collections.abc import Callable
from collections.abc import Sequence from collections.abc import Sequence
from typing import Any from typing import Any
from typing import Callable
from flake8 import utils from flake8 import utils
from flake8.plugins.finder import Plugins from flake8.plugins.finder import Plugins
@ -165,7 +165,7 @@ class Option:
if long_option_name is _ARG.NO: if long_option_name is _ARG.NO:
raise ValueError( raise ValueError(
"When specifying parse_from_config=True, " "When specifying parse_from_config=True, "
"a long_option_name must also be specified.", "a long_option_name must also be specified."
) )
self.config_name = long_option_name[2:].replace("-", "_") self.config_name = long_option_name[2:].replace("-", "_")

View file

@ -83,8 +83,8 @@ class Plugins(NamedTuple):
f"{loaded.plugin.package}: {loaded.plugin.version}" f"{loaded.plugin.package}: {loaded.plugin.version}"
for loaded in self.all_plugins() for loaded in self.all_plugins()
if loaded.plugin.package not in {"flake8", "local"} if loaded.plugin.package not in {"flake8", "local"}
}, }
), )
) )
@ -167,7 +167,7 @@ def _flake8_plugins(
# ideally pycodestyle's plugin entrypoints would exactly represent # ideally pycodestyle's plugin entrypoints would exactly represent
# the codes they produce... # the codes they produce...
yield Plugin( yield Plugin(
pycodestyle_meta["name"], pycodestyle_meta["version"], ep, pycodestyle_meta["name"], pycodestyle_meta["version"], ep
) )
else: else:
yield Plugin(name, version, ep) yield Plugin(name, version, ep)
@ -240,7 +240,7 @@ def _check_required_plugins(
f"required plugins were not installed!\n" f"required plugins were not installed!\n"
f"- installed: {', '.join(sorted(plugin_names))}\n" f"- installed: {', '.join(sorted(plugin_names))}\n"
f"- expected: {', '.join(sorted(expected_names))}\n" f"- expected: {', '.join(sorted(expected_names))}\n"
f"- missing: {', '.join(sorted(missing_plugins))}", f"- missing: {', '.join(sorted(missing_plugins))}"
) )
@ -338,7 +338,7 @@ def _classify_plugins(
if not VALID_CODE_PREFIX.match(loaded.entry_name): if not VALID_CODE_PREFIX.match(loaded.entry_name):
raise ExecutionError( raise ExecutionError(
f"plugin code for `{loaded.display_name}` does not match " f"plugin code for `{loaded.display_name}` does not match "
f"{VALID_CODE_PREFIX.pattern}", f"{VALID_CODE_PREFIX.pattern}"
) )
return Plugins( return Plugins(

View file

@ -36,7 +36,6 @@ FLAKE8_PYFLAKES_CODES = {
"StringDotFormatMissingArgument": "F524", "StringDotFormatMissingArgument": "F524",
"StringDotFormatMixingAutomatic": "F525", "StringDotFormatMixingAutomatic": "F525",
"FStringMissingPlaceholders": "F541", "FStringMissingPlaceholders": "F541",
"TStringMissingPlaceholders": "F542",
"MultiValueRepeatedKeyLiteral": "F601", "MultiValueRepeatedKeyLiteral": "F601",
"MultiValueRepeatedKeyVariable": "F602", "MultiValueRepeatedKeyVariable": "F602",
"TooManyExpressionsInStarredAssignment": "F621", "TooManyExpressionsInStarredAssignment": "F621",
@ -72,7 +71,7 @@ class FlakesChecker(pyflakes.checker.Checker):
def __init__(self, tree: ast.AST, filename: str) -> None: def __init__(self, tree: ast.AST, filename: str) -> None:
"""Initialize the PyFlakes plugin with an AST tree and filename.""" """Initialize the PyFlakes plugin with an AST tree and filename."""
super().__init__( super().__init__(
tree, filename=filename, withDoctest=self.with_doctest, tree, filename=filename, withDoctest=self.with_doctest
) )
@classmethod @classmethod

View file

@ -13,15 +13,13 @@ from flake8 import defaults
from flake8 import utils from flake8 import utils
from flake8._compat import FSTRING_END from flake8._compat import FSTRING_END
from flake8._compat import FSTRING_MIDDLE from flake8._compat import FSTRING_MIDDLE
from flake8._compat import TSTRING_END
from flake8._compat import TSTRING_MIDDLE
from flake8.plugins.finder import LoadedPlugin from flake8.plugins.finder import LoadedPlugin
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE]) NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE])
SKIP_TOKENS = frozenset( SKIP_TOKENS = frozenset(
[tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT], [tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT]
) )
_LogicalMapping = list[tuple[int, tuple[int, int]]] _LogicalMapping = list[tuple[int, tuple[int, int]]]
@ -115,7 +113,7 @@ class FileProcessor:
self.verbose = options.verbose self.verbose = options.verbose
#: Statistics dictionary #: Statistics dictionary
self.statistics = {"logical lines": 0} self.statistics = {"logical lines": 0}
self._fstring_start = self._tstring_start = -1 self._fstring_start = -1
@functools.cached_property @functools.cached_property
def file_tokens(self) -> list[tokenize.TokenInfo]: def file_tokens(self) -> list[tokenize.TokenInfo]:
@ -127,16 +125,10 @@ class FileProcessor:
"""Signal the beginning of an fstring.""" """Signal the beginning of an fstring."""
self._fstring_start = lineno self._fstring_start = lineno
def tstring_start(self, lineno: int) -> None: # pragma: >=3.14 cover
"""Signal the beginning of an tstring."""
self._tstring_start = lineno
def multiline_string(self, token: tokenize.TokenInfo) -> Generator[str]: def multiline_string(self, token: tokenize.TokenInfo) -> Generator[str]:
"""Iterate through the lines of a multiline string.""" """Iterate through the lines of a multiline string."""
if token.type == FSTRING_END: # pragma: >=3.12 cover if token.type == FSTRING_END: # pragma: >=3.12 cover
start = self._fstring_start start = self._fstring_start
elif token.type == TSTRING_END: # pragma: >=3.14 cover
start = self._tstring_start
else: else:
start = token.start[0] start = token.start[0]
@ -173,7 +165,7 @@ class FileProcessor:
"""Update the checker_state attribute for the plugin.""" """Update the checker_state attribute for the plugin."""
if "checker_state" in plugin.parameters: if "checker_state" in plugin.parameters:
self.checker_state = self._checker_states.setdefault( self.checker_state = self._checker_states.setdefault(
plugin.entry_name, {}, plugin.entry_name, {}
) )
def next_logical_line(self) -> None: def next_logical_line(self) -> None:
@ -206,10 +198,7 @@ class FileProcessor:
continue continue
if token_type == tokenize.STRING: if token_type == tokenize.STRING:
text = mutate_string(text) text = mutate_string(text)
elif token_type in { elif token_type == FSTRING_MIDDLE: # pragma: >=3.12 cover
FSTRING_MIDDLE,
TSTRING_MIDDLE,
}: # pragma: >=3.12 cover # noqa: E501
# A curly brace in an FSTRING_MIDDLE token must be an escaped # A curly brace in an FSTRING_MIDDLE token must be an escaped
# curly brace. Both 'text' and 'end' will account for the # curly brace. Both 'text' and 'end' will account for the
# escaped version of the token (i.e. a single brace) rather # escaped version of the token (i.e. a single brace) rather
@ -280,7 +269,7 @@ class FileProcessor:
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) line_range = range(min_line, max_line + 1)
joined = "".join(self.lines[min_line - 1: max_line]) joined = "".join(self.lines[min_line - 1 : max_line])
return dict.fromkeys(line_range, joined) return dict.fromkeys(line_range, joined)
@functools.cached_property @functools.cached_property
@ -367,7 +356,7 @@ class FileProcessor:
elif any(defaults.NOQA_FILE.search(line) for line in self.lines): elif any(defaults.NOQA_FILE.search(line) for line in self.lines):
LOG.warning( LOG.warning(
"Detected `flake8: noqa` on line with code. To ignore an " "Detected `flake8: noqa` on line with code. To ignore an "
"error on a line use `noqa` instead.", "error on a line use `noqa` instead."
) )
return False return False
else: else:
@ -388,12 +377,12 @@ class FileProcessor:
def is_eol_token(token: tokenize.TokenInfo) -> bool: def is_eol_token(token: tokenize.TokenInfo) -> bool:
"""Check if the token is an end-of-line token.""" """Check if the token is an end-of-line token."""
return token[0] in NEWLINE or token[4][token[3][1]:].lstrip() == "\\\n" return token[0] in NEWLINE or token[4][token[3][1] :].lstrip() == "\\\n"
def is_multiline_string(token: tokenize.TokenInfo) -> bool: def is_multiline_string(token: tokenize.TokenInfo) -> bool:
"""Check if this is a multiline string.""" """Check if this is a multiline string."""
return token.type in {FSTRING_END, TSTRING_END} or ( return token.type == FSTRING_END or (
token.type == tokenize.STRING and "\n" in token.string token.type == tokenize.STRING and "\n" in token.string
) )

View file

@ -35,7 +35,7 @@ class Statistics:
self._store[key].increment() self._store[key].increment()
def statistics_for( def statistics_for(
self, prefix: str, filename: str | None = None, self, prefix: str, filename: str | None = None
) -> Generator[Statistic]: ) -> Generator[Statistic]:
"""Generate statistics for the prefix and filename. """Generate statistics for the prefix and filename.
@ -108,7 +108,7 @@ class Statistic:
""" """
def __init__( def __init__(
self, error_code: str, filename: str, message: str, count: int, self, error_code: str, filename: str, message: str, count: int
) -> None: ) -> None:
"""Initialize our Statistic.""" """Initialize our Statistic."""
self.error_code = error_code self.error_code = error_code

View file

@ -218,7 +218,7 @@ class StyleGuideManager:
self.decider = decider or DecisionEngine(options) self.decider = decider or DecisionEngine(options)
self.style_guides: list[StyleGuide] = [] self.style_guides: list[StyleGuide] = []
self.default_style_guide = StyleGuide( self.default_style_guide = StyleGuide(
options, formatter, self.stats, decider=decider, options, formatter, self.stats, decider=decider
) )
self.style_guides = [ self.style_guides = [
self.default_style_guide, self.default_style_guide,
@ -228,7 +228,7 @@ class StyleGuideManager:
self.style_guide_for = functools.cache(self._style_guide_for) self.style_guide_for = functools.cache(self._style_guide_for)
def populate_style_guides_with( def populate_style_guides_with(
self, options: argparse.Namespace, self, options: argparse.Namespace
) -> Generator[StyleGuide]: ) -> Generator[StyleGuide]:
"""Generate style guides from the per-file-ignores option. """Generate style guides from the per-file-ignores option.
@ -240,7 +240,7 @@ class StyleGuideManager:
per_file = utils.parse_files_to_codes_mapping(options.per_file_ignores) per_file = utils.parse_files_to_codes_mapping(options.per_file_ignores)
for filename, violations in per_file: for filename, violations in per_file:
yield self.default_style_guide.copy( yield self.default_style_guide.copy(
filename=filename, extend_ignore_with=violations, filename=filename, extend_ignore_with=violations
) )
def _style_guide_for(self, filename: str) -> StyleGuide: def _style_guide_for(self, filename: str) -> StyleGuide:
@ -288,7 +288,7 @@ class StyleGuideManager:
""" """
guide = self.style_guide_for(filename) guide = self.style_guide_for(filename)
return guide.handle_error( return guide.handle_error(
code, filename, line_number, column_number, text, physical_line, code, filename, line_number, column_number, text, physical_line
) )
@ -330,7 +330,7 @@ class StyleGuide:
options.extend_ignore = options.extend_ignore or [] options.extend_ignore = options.extend_ignore or []
options.extend_ignore.extend(extend_ignore_with or []) options.extend_ignore.extend(extend_ignore_with or [])
return StyleGuide( return StyleGuide(
options, self.formatter, self.stats, filename=filename, options, self.formatter, self.stats, filename=filename
) )
@contextlib.contextmanager @contextlib.contextmanager

View file

@ -23,7 +23,7 @@ NORMALIZE_PACKAGE_NAME_RE = re.compile(r"[-_.]+")
def parse_comma_separated_list( def parse_comma_separated_list(
value: str, regexp: Pattern[str] = COMMA_SEPARATED_LIST_RE, value: str, regexp: Pattern[str] = COMMA_SEPARATED_LIST_RE
) -> list[str]: ) -> list[str]:
"""Parse a comma-separated list. """Parse a comma-separated list.
@ -115,7 +115,7 @@ def parse_files_to_codes_mapping( # noqa: C901
f"Expected `per-file-ignores` to be a mapping from file exclude " f"Expected `per-file-ignores` to be a mapping from file exclude "
f"patterns to ignore codes.\n\n" f"patterns to ignore codes.\n\n"
f"Configured `per-file-ignores` setting:\n\n" f"Configured `per-file-ignores` setting:\n\n"
f"{textwrap.indent(value.strip(), ' ')}", f"{textwrap.indent(value.strip(), ' ')}"
) )
for token in _tokenize_files_to_codes_mapping(value): for token in _tokenize_files_to_codes_mapping(value):
@ -150,7 +150,7 @@ def parse_files_to_codes_mapping( # noqa: C901
def normalize_paths( def normalize_paths(
paths: Sequence[str], parent: str = os.curdir, paths: Sequence[str], parent: str = os.curdir
) -> list[str]: ) -> list[str]:
"""Normalize a list of paths relative to a parent directory. """Normalize a list of paths relative to a parent directory.

View file

@ -64,6 +64,6 @@ class Violation(NamedTuple):
return True return True
LOG.debug( LOG.debug(
"%r is not ignored inline with ``# noqa: %s``", self, codes_str, "%r is not ignored inline with ``# noqa: %s``", self, codes_str
) )
return False return False

View file

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
import importlib.metadata import importlib.metadata
import sys
from unittest import mock from unittest import mock
import pytest import pytest
@ -96,7 +97,7 @@ def mock_file_checker_with_plugin(plugin_target):
# Prevent it from reading lines from stdin or somewhere else # Prevent it from reading lines from stdin or somewhere else
with mock.patch( with mock.patch(
"flake8.processor.FileProcessor.read_lines", return_value=["Line 1"], "flake8.processor.FileProcessor.read_lines", return_value=["Line 1"]
): ):
file_checker = checker.FileChecker( file_checker = checker.FileChecker(
filename="-", filename="-",
@ -321,10 +322,17 @@ def test_handling_syntaxerrors_across_pythons():
We need to handle that correctly to avoid crashing. We need to handle that correctly to avoid crashing.
https://github.com/PyCQA/flake8/issues/1372 https://github.com/PyCQA/flake8/issues/1372
""" """
err = SyntaxError( if sys.version_info < (3, 10): # pragma: no cover (<3.10)
"invalid syntax", ("<unknown>", 2, 1, "bad python:\n", 2, 11), # Python 3.9 or older
) err = SyntaxError(
expected = (2, 1) "invalid syntax", ("<unknown>", 2, 5, "bad python:\n")
)
expected = (2, 4)
else: # pragma: no cover (3.10+)
err = SyntaxError(
"invalid syntax", ("<unknown>", 2, 1, "bad python:\n", 2, 11)
)
expected = (2, 1)
file_checker = checker.FileChecker( file_checker = checker.FileChecker(
filename="-", filename="-",
plugins=finder.Checkers([], [], []), plugins=finder.Checkers([], [], []),

View file

@ -168,8 +168,10 @@ def test_tokenization_error_but_not_syntax_error(tmpdir, capsys):
tmpdir.join("t.py").write("b'foo' \\\n") tmpdir.join("t.py").write("b'foo' \\\n")
assert cli.main(["t.py"]) == 1 assert cli.main(["t.py"]) == 1
if sys.implementation.name == "pypy": # pragma: no cover (pypy) if hasattr(sys, "pypy_version_info"): # pragma: no cover (pypy)
expected = "t.py:1:9: E999 SyntaxError: unexpected end of file (EOF) in multi-line statement\n" # noqa: E501 expected = "t.py:2:1: E999 SyntaxError: end of file (EOF) in multi-line statement\n" # noqa: E501
elif sys.version_info < (3, 10): # pragma: no cover (cp38+)
expected = "t.py:1:8: E999 SyntaxError: unexpected EOF while parsing\n"
else: # pragma: no cover (cp310+) else: # pragma: no cover (cp310+)
expected = "t.py:1:10: E999 SyntaxError: unexpected EOF while parsing\n" # noqa: E501 expected = "t.py:1:10: E999 SyntaxError: unexpected EOF while parsing\n" # noqa: E501
@ -184,8 +186,10 @@ def test_tokenization_error_is_a_syntax_error(tmpdir, capsys):
tmpdir.join("t.py").write("if True:\n pass\n pass\n") tmpdir.join("t.py").write("if True:\n pass\n pass\n")
assert cli.main(["t.py"]) == 1 assert cli.main(["t.py"]) == 1
if sys.implementation.name == "pypy": # pragma: no cover (pypy) if hasattr(sys, "pypy_version_info"): # pragma: no cover (pypy)
expected = "t.py:3:3: E999 IndentationError: unindent does not match any outer indentation level\n" # noqa: E501 expected = "t.py:3:2: E999 IndentationError: unindent does not match any outer indentation level\n" # noqa: E501
elif sys.version_info < (3, 10): # pragma: no cover (<cp310)
expected = "t.py:3:5: E999 IndentationError: unindent does not match any outer indentation level\n" # noqa: E501
else: # pragma: no cover (cp310+) else: # pragma: no cover (cp310+)
expected = "t.py:3:7: E999 IndentationError: unindent does not match any outer indentation level\n" # noqa: E501 expected = "t.py:3:7: E999 IndentationError: unindent does not match any outer indentation level\n" # noqa: E501
@ -310,7 +314,7 @@ def test_cli_config_option_respected(tmp_path):
"""\ """\
[flake8] [flake8]
ignore = F401 ignore = F401
""", """
) )
py_file = tmp_path / "t.py" py_file = tmp_path / "t.py"
@ -326,7 +330,7 @@ def test_cli_isolated_overrides_config_option(tmp_path):
"""\ """\
[flake8] [flake8]
ignore = F401 ignore = F401
""", """
) )
py_file = tmp_path / "t.py" py_file = tmp_path / "t.py"
@ -360,7 +364,7 @@ def test_output_file(tmpdir, capsys):
def test_early_keyboard_interrupt_does_not_crash(capsys): def test_early_keyboard_interrupt_does_not_crash(capsys):
with mock.patch.object( with mock.patch.object(
config, "load_config", side_effect=KeyboardInterrupt, config, "load_config", side_effect=KeyboardInterrupt
): ):
assert cli.main(["does-not-exist"]) == 1 assert cli.main(["does-not-exist"]) == 1
out, err = capsys.readouterr() out, err = capsys.readouterr()

View file

@ -86,7 +86,7 @@ def test_local_plugin_can_add_option(local_config):
stage1_args, rest = stage1_parser.parse_known_args(argv) stage1_args, rest = stage1_parser.parse_known_args(argv)
cfg, cfg_dir = config.load_config( cfg, cfg_dir = config.load_config(
config=stage1_args.config, extra=[], isolated=False, config=stage1_args.config, extra=[], isolated=False
) )
opts = finder.parse_plugin_options( opts = finder.parse_plugin_options(
@ -296,36 +296,3 @@ t.py:1:1: T001 "f'xxxxxxxxxxxxxxxxxxxxxxxx'"
""" """
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert out == expected assert out == expected
@pytest.mark.xfail(sys.version_info < (3, 14), reason="3.14+")
def test_tstring_logical_line(tmpdir, capsys): # pragma: >=3.14 cover
cfg_s = f"""\
[flake8]
extend-ignore = F
[flake8:local-plugins]
extension =
T = {yields_logical_line.__module__}:{yields_logical_line.__name__}
"""
cfg = tmpdir.join("tox.ini")
cfg.write(cfg_s)
src = """\
t'''
hello {world}
'''
t'{{"{hello}": "{world}"}}'
"""
t_py = tmpdir.join("t.py")
t_py.write_binary(src.encode())
with tmpdir.as_cwd():
assert main(("t.py", "--config", str(cfg))) == 1
expected = """\
t.py:1:1: T001 "t'''xxxxxxx{world}x'''"
t.py:4:1: T001 "t'xxx{hello}xxxx{world}xxx'"
"""
out, err = capsys.readouterr()
assert out == expected

View file

@ -42,7 +42,7 @@ def test_plugins_all_plugins():
logical_line_plugin = _loaded(parameters={"logical_line": True}) logical_line_plugin = _loaded(parameters={"logical_line": True})
physical_line_plugin = _loaded(parameters={"physical_line": True}) physical_line_plugin = _loaded(parameters={"physical_line": True})
report_plugin = _loaded( report_plugin = _loaded(
plugin=_plugin(ep=_ep(name="R", group="flake8.report")), plugin=_plugin(ep=_ep(name="R", group="flake8.report"))
) )
plugins = finder.Plugins( plugins = finder.Plugins(
@ -200,16 +200,14 @@ def test_flake8_plugins(flake8_dist, mock_distribution):
"flake8", "flake8",
"9001", "9001",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"default", "default", "flake8.formatting.default:Default", "flake8.report"
"flake8.formatting.default:Default",
"flake8.report",
), ),
), ),
finder.Plugin( finder.Plugin(
"flake8", "flake8",
"9001", "9001",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"pylint", "flake8.formatting.default:Pylint", "flake8.report", "pylint", "flake8.formatting.default:Pylint", "flake8.report"
), ),
), ),
} }
@ -272,7 +270,7 @@ unrelated = unrelated:main
"flake8-foo", "flake8-foo",
"1.2.3", "1.2.3",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"Q", "flake8_foo:Plugin", "flake8.extension", "Q", "flake8_foo:Plugin", "flake8.extension"
), ),
), ),
finder.Plugin( finder.Plugin(
@ -306,23 +304,21 @@ unrelated = unrelated:main
"flake8", "flake8",
"9001", "9001",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"default", "default", "flake8.formatting.default:Default", "flake8.report"
"flake8.formatting.default:Default",
"flake8.report",
), ),
), ),
finder.Plugin( finder.Plugin(
"flake8", "flake8",
"9001", "9001",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"pylint", "flake8.formatting.default:Pylint", "flake8.report", "pylint", "flake8.formatting.default:Pylint", "flake8.report"
), ),
), ),
finder.Plugin( finder.Plugin(
"flake8-foo", "flake8-foo",
"1.2.3", "1.2.3",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"foo", "flake8_foo:Formatter", "flake8.report", "foo", "flake8_foo:Formatter", "flake8.report"
), ),
), ),
} }
@ -489,30 +485,28 @@ def test_find_plugins(
"flake8", "flake8",
"9001", "9001",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"default", "default", "flake8.formatting.default:Default", "flake8.report"
"flake8.formatting.default:Default",
"flake8.report",
), ),
), ),
finder.Plugin( finder.Plugin(
"flake8", "flake8",
"9001", "9001",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"pylint", "flake8.formatting.default:Pylint", "flake8.report", "pylint", "flake8.formatting.default:Pylint", "flake8.report"
), ),
), ),
finder.Plugin( finder.Plugin(
"flake8-foo", "flake8-foo",
"1.2.3", "1.2.3",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"Q", "flake8_foo:Plugin", "flake8.extension", "Q", "flake8_foo:Plugin", "flake8.extension"
), ),
), ),
finder.Plugin( finder.Plugin(
"flake8-foo", "flake8-foo",
"1.2.3", "1.2.3",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"foo", "flake8_foo:Formatter", "flake8.report", "foo", "flake8_foo:Formatter", "flake8.report"
), ),
), ),
finder.Plugin( finder.Plugin(
@ -524,7 +518,7 @@ def test_find_plugins(
"local", "local",
"local", "local",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
"Y", "mod2:attr", "flake8.extension", "Y", "mod2:attr", "flake8.extension"
), ),
), ),
finder.Plugin( finder.Plugin(
@ -729,7 +723,7 @@ def test_import_plugins_extends_sys_path():
def test_classify_plugins(): def test_classify_plugins():
report_plugin = _loaded( report_plugin = _loaded(
plugin=_plugin(ep=_ep(name="R", group="flake8.report")), plugin=_plugin(ep=_ep(name="R", group="flake8.report"))
) )
tree_plugin = _loaded(parameters={"tree": True}) tree_plugin = _loaded(parameters={"tree": True})
logical_line_plugin = _loaded(parameters={"logical_line": True}) logical_line_plugin = _loaded(parameters={"logical_line": True})

View file

@ -25,7 +25,7 @@ def reporters():
"flake8", "flake8",
"123", "123",
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
name, f"{cls.__module__}:{cls.__name__}", "flake8.report", name, f"{cls.__module__}:{cls.__name__}", "flake8.report"
), ),
), ),
cls, cls,
@ -72,5 +72,5 @@ def test_make_formatter_format_string(reporters, caplog):
"flake8.plugins.reporter", "flake8.plugins.reporter",
30, 30,
"'hi %(code)s' is an unknown formatter. Falling back to default.", "'hi %(code)s' is an unknown formatter. Falling back to default.",
), )
] ]

View file

@ -36,7 +36,7 @@ def application():
], ],
) )
def test_application_exit_code( def test_application_exit_code(
result_count, catastrophic, exit_zero, value, application, result_count, catastrophic, exit_zero, value, application
): ):
"""Verify Application.exit_code returns the correct value.""" """Verify Application.exit_code returns the correct value."""
application.result_count = result_count application.result_count = result_count

View file

@ -50,7 +50,7 @@ def test_format_needs_to_be_implemented():
formatter = base.BaseFormatter(options()) formatter = base.BaseFormatter(options())
with pytest.raises(NotImplementedError): with pytest.raises(NotImplementedError):
formatter.format( formatter.format(
Violation("A000", "file.py", 1, 1, "error text", None), Violation("A000", "file.py", 1, 1, "error text", None)
) )
@ -59,7 +59,7 @@ def test_show_source_returns_nothing_when_not_showing_source():
formatter = base.BaseFormatter(options(show_source=False)) formatter = base.BaseFormatter(options(show_source=False))
assert ( assert (
formatter.show_source( formatter.show_source(
Violation("A000", "file.py", 1, 1, "error text", "line"), Violation("A000", "file.py", 1, 1, "error text", "line")
) )
== "" == ""
) )
@ -70,7 +70,7 @@ def test_show_source_returns_nothing_when_there_is_source():
formatter = base.BaseFormatter(options(show_source=True)) formatter = base.BaseFormatter(options(show_source=True))
assert ( assert (
formatter.show_source( formatter.show_source(
Violation("A000", "file.py", 1, 1, "error text", None), Violation("A000", "file.py", 1, 1, "error text", None)
) )
== "" == ""
) )

View file

@ -41,11 +41,9 @@ def test_oserrors_are_reraised():
err = OSError(errno.EAGAIN, "Ominous message") err = OSError(errno.EAGAIN, "Ominous message")
with mock.patch("_multiprocessing.SemLock", side_effect=err): with mock.patch("_multiprocessing.SemLock", side_effect=err):
manager = _parallel_checker_manager() manager = _parallel_checker_manager()
with ( with mock.patch.object(manager, "run_serial") as serial:
mock.patch.object(manager, "run_serial") as serial, with pytest.raises(OSError):
pytest.raises(OSError), manager.run()
):
manager.run()
assert serial.call_count == 0 assert serial.call_count == 0

View file

@ -14,7 +14,7 @@ def test_debug_information():
pkg, pkg,
version, version,
importlib.metadata.EntryPoint( importlib.metadata.EntryPoint(
ep_name, "dne:dne", "flake8.extension", ep_name, "dne:dne", "flake8.extension"
), ),
), ),
None, None,

View file

@ -35,7 +35,7 @@ def create_options(**kwargs):
def test_was_ignored_ignores_errors(ignore_list, extend_ignore, error_code): def test_was_ignored_ignores_errors(ignore_list, extend_ignore, error_code):
"""Verify we detect users explicitly ignoring an error.""" """Verify we detect users explicitly ignoring an error."""
decider = style_guide.DecisionEngine( decider = style_guide.DecisionEngine(
create_options(ignore=ignore_list, extend_ignore=extend_ignore), create_options(ignore=ignore_list, extend_ignore=extend_ignore)
) )
assert decider.was_ignored(error_code) is style_guide.Ignored.Explicitly assert decider.was_ignored(error_code) is style_guide.Ignored.Explicitly
@ -53,11 +53,11 @@ def test_was_ignored_ignores_errors(ignore_list, extend_ignore, error_code):
], ],
) )
def test_was_ignored_implicitly_selects_errors( def test_was_ignored_implicitly_selects_errors(
ignore_list, extend_ignore, error_code, ignore_list, extend_ignore, error_code
): ):
"""Verify we detect users does not explicitly ignore an error.""" """Verify we detect users does not explicitly ignore an error."""
decider = style_guide.DecisionEngine( decider = style_guide.DecisionEngine(
create_options(ignore=ignore_list, extend_ignore=extend_ignore), create_options(ignore=ignore_list, extend_ignore=extend_ignore)
) )
assert decider.was_ignored(error_code) is style_guide.Selected.Implicitly assert decider.was_ignored(error_code) is style_guide.Selected.Implicitly
@ -179,7 +179,7 @@ def test_was_selected_excludes_errors(select_list, error_code):
], ],
) )
def test_decision_for( def test_decision_for(
select_list, ignore_list, extend_ignore, error_code, expected, select_list, ignore_list, extend_ignore, error_code, expected
): ):
"""Verify we decide when to report an error.""" """Verify we decide when to report an error."""
decider = style_guide.DecisionEngine( decider = style_guide.DecisionEngine(
@ -187,7 +187,7 @@ def test_decision_for(
select=select_list, select=select_list,
ignore=ignore_list, ignore=ignore_list,
extend_ignore=extend_ignore, extend_ignore=extend_ignore,
), )
) )
assert decider.decision_for(error_code) is expected assert decider.decision_for(error_code) is expected

View file

@ -47,7 +47,7 @@ def test_filenames_from_a_directory_with_a_predicate():
_filenames_from( _filenames_from(
arg=_normpath("a/b/"), arg=_normpath("a/b/"),
predicate=lambda path: path.endswith(_normpath("b/c.py")), predicate=lambda path: path.endswith(_normpath("b/c.py")),
), )
) )
# should not include c.py # should not include c.py
expected = _normpaths(("a/b/d.py", "a/b/e/f.py")) expected = _normpaths(("a/b/d.py", "a/b/e/f.py"))
@ -61,7 +61,7 @@ def test_filenames_from_a_directory_with_a_predicate_from_the_current_dir():
_filenames_from( _filenames_from(
arg=_normpath("./a/b"), arg=_normpath("./a/b"),
predicate=lambda path: path == "c.py", predicate=lambda path: path == "c.py",
), )
) )
# none should have matched the predicate so all returned # none should have matched the predicate so all returned
expected = _normpaths(("./a/b/c.py", "./a/b/d.py", "./a/b/e/f.py")) expected = _normpaths(("./a/b/c.py", "./a/b/d.py", "./a/b/e/f.py"))
@ -132,7 +132,7 @@ def _expand_paths(
stdin_display_name=stdin_display_name, stdin_display_name=stdin_display_name,
filename_patterns=filename_patterns, filename_patterns=filename_patterns,
exclude=exclude, exclude=exclude,
), )
) )

View file

@ -28,7 +28,7 @@ def _lines_from_file(tmpdir, contents, options):
def test_read_lines_universal_newlines(tmpdir, default_options): def test_read_lines_universal_newlines(tmpdir, default_options):
r"""Verify that line endings are translated to \n.""" r"""Verify that line endings are translated to \n."""
lines = _lines_from_file( lines = _lines_from_file(
tmpdir, b"# coding: utf-8\r\nx = 1\r\n", default_options, tmpdir, b"# coding: utf-8\r\nx = 1\r\n", default_options
) )
assert lines == ["# coding: utf-8\n", "x = 1\n"] assert lines == ["# coding: utf-8\n", "x = 1\n"]
@ -36,7 +36,7 @@ def test_read_lines_universal_newlines(tmpdir, default_options):
def test_read_lines_incorrect_utf_16(tmpdir, default_options): def test_read_lines_incorrect_utf_16(tmpdir, default_options):
"""Verify that an incorrectly encoded file is read as latin-1.""" """Verify that an incorrectly encoded file is read as latin-1."""
lines = _lines_from_file( lines = _lines_from_file(
tmpdir, b"# coding: utf16\nx = 1\n", default_options, tmpdir, b"# coding: utf16\nx = 1\n", default_options
) )
assert lines == ["# coding: utf16\n", "x = 1\n"] assert lines == ["# coding: utf16\n", "x = 1\n"]
@ -44,7 +44,7 @@ def test_read_lines_incorrect_utf_16(tmpdir, default_options):
def test_read_lines_unknown_encoding(tmpdir, default_options): def test_read_lines_unknown_encoding(tmpdir, default_options):
"""Verify that an unknown encoding is still read as latin-1.""" """Verify that an unknown encoding is still read as latin-1."""
lines = _lines_from_file( lines = _lines_from_file(
tmpdir, b"# coding: fake-encoding\nx = 1\n", default_options, tmpdir, b"# coding: fake-encoding\nx = 1\n", default_options
) )
assert lines == ["# coding: fake-encoding\n", "x = 1\n"] assert lines == ["# coding: fake-encoding\n", "x = 1\n"]
@ -289,7 +289,7 @@ def test_processor_split_line(default_options):
def test_build_ast(default_options): def test_build_ast(default_options):
"""Verify the logic for how we build an AST for plugins.""" """Verify the logic for how we build an AST for plugins."""
file_processor = processor.FileProcessor( file_processor = processor.FileProcessor(
"-", default_options, lines=["a = 1\n"], "-", default_options, lines=["a = 1\n"]
) )
module = file_processor.build_ast() module = file_processor.build_ast()
@ -299,7 +299,7 @@ def test_build_ast(default_options):
def test_next_logical_line_updates_the_previous_logical_line(default_options): def test_next_logical_line_updates_the_previous_logical_line(default_options):
"""Verify that we update our tracking of the previous logical line.""" """Verify that we update our tracking of the previous logical line."""
file_processor = processor.FileProcessor( file_processor = processor.FileProcessor(
"-", default_options, lines=["a = 1\n"], "-", default_options, lines=["a = 1\n"]
) )
file_processor.indent_level = 1 file_processor.indent_level = 1
@ -315,7 +315,7 @@ def test_next_logical_line_updates_the_previous_logical_line(default_options):
def test_visited_new_blank_line(default_options): def test_visited_new_blank_line(default_options):
"""Verify we update the number of blank lines seen.""" """Verify we update the number of blank lines seen."""
file_processor = processor.FileProcessor( file_processor = processor.FileProcessor(
"-", default_options, lines=["a = 1\n"], "-", default_options, lines=["a = 1\n"]
) )
assert file_processor.blank_lines == 0 assert file_processor.blank_lines == 0

View file

@ -6,7 +6,7 @@ from flake8.main import options
def test_stage1_arg_parser(): def test_stage1_arg_parser():
stage1_parser = options.stage1_arg_parser() stage1_parser = options.stage1_arg_parser()
opts, args = stage1_parser.parse_known_args( opts, args = stage1_parser.parse_known_args(
["--foo", "--verbose", "src", "setup.py", "--statistics", "--version"], ["--foo", "--verbose", "src", "setup.py", "--statistics", "--version"]
) )
assert opts.verbose assert opts.verbose

View file

@ -122,7 +122,7 @@ def test_parse_args_handles_comma_separated_defaults(optmanager):
assert optmanager.config_options_dict == {} assert optmanager.config_options_dict == {}
optmanager.add_option( optmanager.add_option(
"--exclude", default="E123,W234", comma_separated_list=True, "--exclude", default="E123,W234", comma_separated_list=True
) )
options = optmanager.parse_args([]) options = optmanager.parse_args([])
@ -135,7 +135,7 @@ def test_parse_args_handles_comma_separated_lists(optmanager):
assert optmanager.config_options_dict == {} assert optmanager.config_options_dict == {}
optmanager.add_option( optmanager.add_option(
"--exclude", default="E123,W234", comma_separated_list=True, "--exclude", default="E123,W234", comma_separated_list=True
) )
options = optmanager.parse_args(["--exclude", "E201,W111,F280"]) options = optmanager.parse_args(["--exclude", "E201,W111,F280"])
@ -148,11 +148,11 @@ def test_parse_args_normalize_paths(optmanager):
assert optmanager.config_options_dict == {} assert optmanager.config_options_dict == {}
optmanager.add_option( optmanager.add_option(
"--extra-config", normalize_paths=True, comma_separated_list=True, "--extra-config", normalize_paths=True, comma_separated_list=True
) )
options = optmanager.parse_args( options = optmanager.parse_args(
["--extra-config", "../config.ini,tox.ini,flake8/some-other.cfg"], ["--extra-config", "../config.ini,tox.ini,flake8/some-other.cfg"]
) )
assert options.extra_config == [ assert options.extra_config == [
os.path.abspath("../config.ini"), os.path.abspath("../config.ini"),

View file

@ -169,7 +169,7 @@ def test_load_extra_config_utf8(tmpdir):
@pytest.fixture @pytest.fixture
def opt_manager(): def opt_manager():
ret = OptionManager( ret = OptionManager(
version="123", plugin_versions="", parents=[], formatter_names=[], version="123", plugin_versions="", parents=[], formatter_names=[]
) )
register_default_options(ret) register_default_options(ret)
return ret return ret
@ -213,7 +213,7 @@ def test_parse_config_ignores_unknowns(tmp_path, opt_manager, caplog):
"flake8.options.config", "flake8.options.config",
10, 10,
'Option "wat" is not registered. Ignoring.', 'Option "wat" is not registered. Ignoring.',
), )
] ]

View file

@ -36,7 +36,7 @@ def test_handle_error_does_not_raise_type_errors():
) )
assert 1 == guide.handle_error( assert 1 == guide.handle_error(
"T111", "file.py", 1, 1, "error found", "a = 1", "T111", "file.py", 1, 1, "error found", "a = 1"
) )
@ -110,7 +110,7 @@ def test_style_guide_manager_pre_file_ignores_parsing():
], ],
) )
def test_style_guide_manager_pre_file_ignores( def test_style_guide_manager_pre_file_ignores(
ignores, violation, filename, handle_error_return, ignores, violation, filename, handle_error_return
): ):
"""Verify how the StyleGuideManager creates a default style guide.""" """Verify how the StyleGuideManager creates a default style guide."""
formatter = mock.create_autospec(base.BaseFormatter, instance=True) formatter = mock.create_autospec(base.BaseFormatter, instance=True)