Preserve legacy API options in workers

This commit is contained in:
Sean Doherty 2026-05-16 22:43:12 -05:00
parent ee03327c82
commit 4ae06799c3
6 changed files with 53 additions and 7 deletions

View file

@ -204,13 +204,15 @@ def get_style_guide(**kwargs: Any) -> StyleGuide:
# options set instead before we make our formatter, notifier, internal # options set instead before we make our formatter, notifier, internal
# style guide and file checker manager. # style guide and file checker manager.
options = application.options options = application.options
option_overrides = {}
for key, value in kwargs.items(): for key, value in kwargs.items():
try: try:
getattr(options, key) getattr(options, key)
setattr(options, key, value) setattr(options, key, value)
option_overrides[key] = value
except AttributeError: except AttributeError:
LOG.error('Could not update option "%s"', key) LOG.error('Could not update option "%s"', key)
application.make_formatter() application.make_formatter()
application.make_guide() application.make_guide()
application.make_file_checker_manager([]) application.make_file_checker_manager([], option_overrides)
return StyleGuide(application) return StyleGuide(application)

View file

@ -61,7 +61,10 @@ def _mp_prefork(
_mp = None _mp = None
def _mp_init(argv: Sequence[str]) -> None: def _mp_init(
argv: Sequence[str],
option_overrides: dict[str, Any] | None = None,
) -> None:
global _mp global _mp
# Ensure correct signaling of ^C using multiprocessing.Pool. # Ensure correct signaling of ^C using multiprocessing.Pool.
@ -70,6 +73,9 @@ def _mp_init(argv: Sequence[str]) -> None:
# for `fork` this'll already be set # for `fork` this'll already be set
if _mp is None: if _mp is None:
plugins, options = parse_args(argv) plugins, options = parse_args(argv)
if option_overrides:
for key, value in option_overrides.items():
setattr(options, key, value)
_mp = plugins.checkers, options _mp = plugins.checkers, options
@ -105,6 +111,7 @@ class Manager:
style_guide: StyleGuideManager, style_guide: StyleGuideManager,
plugins: Checkers, plugins: Checkers,
argv: Sequence[str], argv: Sequence[str],
option_overrides: dict[str, Any] | None = None,
) -> None: ) -> None:
"""Initialize our Manager instance.""" """Initialize our Manager instance."""
self.style_guide = style_guide self.style_guide = style_guide
@ -119,6 +126,7 @@ class Manager:
} }
self.exclude = (*self.options.exclude, *self.options.extend_exclude) self.exclude = (*self.options.exclude, *self.options.extend_exclude)
self.argv = argv self.argv = argv
self.option_overrides = option_overrides
self.results: list[tuple[str, Results, dict[str, int]]] = [] self.results: list[tuple[str, Results, dict[str, int]]] = []
def _process_statistics(self) -> None: def _process_statistics(self) -> None:
@ -192,7 +200,9 @@ class Manager:
def run_parallel(self) -> None: def run_parallel(self) -> None:
"""Run the checkers in parallel.""" """Run the checkers in parallel."""
with _mp_prefork(self.plugins, self.options): with _mp_prefork(self.plugins, self.options):
pool = _try_initialize_processpool(self.jobs, self.argv) pool = _try_initialize_processpool(
self.jobs, self.argv, self.option_overrides,
)
if pool is None: if pool is None:
self.run_serial() self.run_serial()
@ -547,10 +557,13 @@ class FileChecker:
def _try_initialize_processpool( def _try_initialize_processpool(
job_count: int, job_count: int,
argv: Sequence[str], argv: Sequence[str],
option_overrides: dict[str, Any] | None = None,
) -> multiprocessing.pool.Pool | None: ) -> multiprocessing.pool.Pool | None:
"""Return a new process pool instance if we are able to create one.""" """Return a new process pool instance if we are able to create one."""
try: try:
return multiprocessing.Pool(job_count, _mp_init, initargs=(argv,)) return multiprocessing.Pool(
job_count, _mp_init, initargs=(argv, option_overrides),
)
except OSError as err: except OSError as err:
if err.errno not in SERIAL_RETRY_ERRNOS: if err.errno not in SERIAL_RETRY_ERRNOS:
raise raise

View file

@ -6,6 +6,7 @@ import json
import logging import logging
import time import time
from collections.abc import Sequence from collections.abc import Sequence
from typing import Any
import flake8 import flake8
from flake8 import checker from flake8 import checker
@ -79,7 +80,11 @@ class Application:
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],
option_overrides: dict[str, Any] | None = None,
) -> None:
"""Initialize our FileChecker Manager.""" """Initialize our FileChecker Manager."""
assert self.guide is not None assert self.guide is not None
assert self.plugins is not None assert self.plugins is not None
@ -87,6 +92,7 @@ class Application:
style_guide=self.guide, style_guide=self.guide,
plugins=self.plugins.checkers, plugins=self.plugins.checkers,
argv=argv, argv=argv,
option_overrides=option_overrides,
) )
def run_checks(self) -> None: def run_checks(self) -> None:

View file

@ -13,3 +13,17 @@ def test_legacy_api(tmpdir):
style_guide = legacy.get_style_guide() style_guide = legacy.get_style_guide()
report = style_guide.check_files([t_py.strpath]) report = style_guide.check_files([t_py.strpath])
assert report.total_errors == 1 assert report.total_errors == 1
def test_legacy_api_parallel_checks_use_option_overrides(tmpdir):
long_line = f"x = \"{'a' * 80}\"\n"
assert len(long_line.rstrip()) == 86
file1 = tmpdir.join("file1.py")
file1.write(long_line)
file2 = tmpdir.join("file2.py")
file2.write(long_line)
style_guide = legacy.get_style_guide(max_line_length=88)
report = style_guide.check_files([file1.strpath, file2.strpath])
assert report.total_errors == 0

View file

@ -291,7 +291,7 @@ def test_acquire_when_multiprocessing_pool_can_initialize():
with mock.patch("multiprocessing.Pool") as pool: with mock.patch("multiprocessing.Pool") as pool:
result = checker._try_initialize_processpool(2, []) result = checker._try_initialize_processpool(2, [])
pool.assert_called_once_with(2, checker._mp_init, initargs=([],)) pool.assert_called_once_with(2, checker._mp_init, initargs=([], None))
assert result is pool.return_value assert result is pool.return_value
@ -310,7 +310,7 @@ def test_acquire_when_multiprocessing_pool_can_not_initialize():
with mock.patch("multiprocessing.Pool", side_effect=ImportError) as pool: with mock.patch("multiprocessing.Pool", side_effect=ImportError) as pool:
result = checker._try_initialize_processpool(2, []) result = checker._try_initialize_processpool(2, [])
pool.assert_called_once_with(2, checker._mp_init, initargs=([],)) pool.assert_called_once_with(2, checker._mp_init, initargs=([], None))
assert result is None assert result is None

View file

@ -63,6 +63,17 @@ def test_multiprocessing_cpu_count_not_implemented():
assert manager.jobs == 0 assert manager.jobs == 0
def test_mp_init_applies_option_overrides():
checker._mp = None
try:
checker._mp_init([], {"max_line_length": 88})
assert checker._mp is not None
_, options = checker._mp
assert options.max_line_length == 88
finally:
checker._mp = None
def test_jobs_count_limited_to_file_count(): def test_jobs_count_limited_to_file_count():
style_guide = style_guide_mock() style_guide = style_guide_mock()
style_guide.options.jobs = JobsArgument("4") style_guide.options.jobs = JobsArgument("4")