diff --git a/src/flake8/checker.py b/src/flake8/checker.py index d008b98..cbff46f 100644 --- a/src/flake8/checker.py +++ b/src/flake8/checker.py @@ -3,6 +3,7 @@ import collections import errno import itertools import logging +import multiprocessing.pool import signal import tokenize from typing import Dict @@ -15,11 +16,6 @@ from flake8 import exceptions from flake8 import processor from flake8 import utils -try: - import multiprocessing.pool -except ImportError: - multiprocessing = None # type: ignore - Results = List[Tuple[str, int, int, str, Optional[str]]] LOG = logging.getLogger(__name__) @@ -40,13 +36,6 @@ SERIAL_RETRY_ERRNOS = { } -def _multiprocessing_is_fork() -> bool: - """Class state is only preserved when using the `fork` strategy.""" - return bool( - multiprocessing and multiprocessing.get_start_method() == "fork" - ) - - class Manager: """Manage the parallelism and checker instances for each plugin and file. @@ -113,7 +102,9 @@ class Manager: # - we're processing a diff, which again does not work well with # multiprocessing and which really shouldn't require multiprocessing # - the user provided some awful input - if not _multiprocessing_is_fork(): + + # class state is only preserved when using the `fork` strategy. + if multiprocessing.get_start_method() != "fork": LOG.warning( "The multiprocessing module is not available. " "Ignoring --jobs arguments." diff --git a/tests/integration/test_main.py b/tests/integration/test_main.py index 8ad7654..6c92c4a 100644 --- a/tests/integration/test_main.py +++ b/tests/integration/test_main.py @@ -227,6 +227,29 @@ def test_bug_report_successful(capsys): assert err == "" +def test_benchmark_successful(tmp_path, capsys): + """Test that --benchmark does not crash.""" + fname = tmp_path.joinpath("t.py") + fname.write_text("print('hello world')\n") + + _call_main(["--benchmark", str(fname)]) + + out, err = capsys.readouterr() + parts = [line.split(maxsplit=1) for line in out.splitlines()] + assert parts == [ + [mock.ANY, "seconds elapsed"], + ["1", "total logical lines processed"], + [mock.ANY, "logical lines processed per second"], + ["1", "total physical lines processed"], + [mock.ANY, "physical lines processed per second"], + ["5", "total tokens processed"], + [mock.ANY, "tokens processed per second"], + ["1", "total files processed"], + [mock.ANY, "files processed per second"], + ] + assert err == "" + + def test_specific_noqa_does_not_clobber_pycodestyle_noqa(tmpdir, capsys): """See https://github.com/pycqa/flake8/issues/1104.""" with tmpdir.as_cwd(): diff --git a/tests/unit/test_checker_manager.py b/tests/unit/test_checker_manager.py index f82dc49..8e45675 100644 --- a/tests/unit/test_checker_manager.py +++ b/tests/unit/test_checker_manager.py @@ -1,5 +1,6 @@ """Tests for the Manager object for FileCheckers.""" import errno +import multiprocessing from unittest import mock import pytest @@ -37,8 +38,8 @@ def test_oserrors_cause_serial_fall_back(): assert serial.call_count == 1 -@mock.patch("flake8.checker._multiprocessing_is_fork", return_value=True) -def test_oserrors_are_reraised(is_windows): +@mock.patch.object(multiprocessing, "get_start_method", return_value="fork") +def test_oserrors_are_reraised(_): """Verify that unexpected OSErrors will cause the Manager to reraise.""" err = OSError(errno.EAGAIN, "Ominous message") with mock.patch("_multiprocessing.SemLock", side_effect=err): @@ -49,15 +50,30 @@ def test_oserrors_are_reraised(is_windows): assert serial.call_count == 0 -def test_multiprocessing_is_disabled(): +@mock.patch.object(multiprocessing, "get_start_method", return_value="spawn") +def test_multiprocessing_is_disabled(_): """Verify not being able to import multiprocessing forces jobs to 0.""" style_guide = style_guide_mock() - with mock.patch("flake8.checker.multiprocessing", None): + manager = checker.Manager(style_guide, [], []) + assert manager.jobs == 0 + + +def test_multiprocessing_cpu_count_not_implemented(): + """Verify that jobs is 0 if cpu_count is unavailable.""" + style_guide = style_guide_mock() + style_guide.options.jobs = JobsArgument("auto") + + with mock.patch.object( + multiprocessing, + "cpu_count", + side_effect=NotImplementedError, + ): manager = checker.Manager(style_guide, [], []) - assert manager.jobs == 0 + assert manager.jobs == 0 -def test_make_checkers(): +@mock.patch.object(multiprocessing, "get_start_method", return_value="spawn") +def test_make_checkers(_): """Verify that we create a list of FileChecker instances.""" style_guide = style_guide_mock() files = ["file1", "file2"] @@ -67,8 +83,7 @@ def test_make_checkers(): "logical_line_plugins": [], "physical_line_plugins": [], } - with mock.patch("flake8.checker.multiprocessing", None): - manager = checker.Manager(style_guide, files, checkplugins) + manager = checker.Manager(style_guide, files, checkplugins) with mock.patch("flake8.utils.filenames_from") as filenames_from: filenames_from.side_effect = [["file1"], ["file2"]]