Enforce usage of fork method on macOS

This commit is contained in:
Florent 2022-07-24 14:06:56 +02:00
parent e8f9eb369a
commit 79a4316055
3 changed files with 30 additions and 8 deletions

View file

@ -5,6 +5,7 @@ import errno
import itertools import itertools
import logging import logging
import multiprocessing.pool import multiprocessing.pool
import platform
import signal import signal
import tokenize import tokenize
from typing import Any from typing import Any
@ -101,7 +102,11 @@ class Manager:
# - the user provided some awful input # - the user provided some awful input
# class state is only preserved when using the `fork` strategy. # class state is only preserved when using the `fork` strategy.
if multiprocessing.get_start_method() != "fork": # since Python 3.8, macOS has the method `spawn` as the default value
if (
platform.system() != "Darwin"
and multiprocessing.get_start_method() != "fork"
):
LOG.warning( LOG.warning(
"The multiprocessing module is not available. " "The multiprocessing module is not available. "
"Ignoring --jobs arguments." "Ignoring --jobs arguments."
@ -595,7 +600,8 @@ def _try_initialize_processpool(
) -> Optional[multiprocessing.pool.Pool]: ) -> Optional[multiprocessing.pool.Pool]:
"""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, _pool_init) context = multiprocessing.get_context("fork")
return context.Pool(job_count, _pool_init)
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

@ -292,11 +292,11 @@ def test_acquire_when_multiprocessing_pool_can_initialize():
This simulates the behaviour on most common platforms. This simulates the behaviour on most common platforms.
""" """
with mock.patch("multiprocessing.Pool") as pool: with mock.patch("multiprocessing.get_context") as context:
result = checker._try_initialize_processpool(2) result = checker._try_initialize_processpool(2)
pool.assert_called_once_with(2, checker._pool_init) context.return_value.Pool.assert_called_once_with(2, checker._pool_init)
assert result is pool.return_value assert result is context.return_value.Pool.return_value
def test_acquire_when_multiprocessing_pool_can_not_initialize(): def test_acquire_when_multiprocessing_pool_can_not_initialize():
@ -311,10 +311,11 @@ def test_acquire_when_multiprocessing_pool_can_not_initialize():
https://github.com/python/cpython/blob/4e02981de0952f54bf87967f8e10d169d6946b40/Lib/multiprocessing/synchronize.py#L30-L33 https://github.com/python/cpython/blob/4e02981de0952f54bf87967f8e10d169d6946b40/Lib/multiprocessing/synchronize.py#L30-L33
""" """
with mock.patch("multiprocessing.Pool", side_effect=ImportError) as pool: with mock.patch("multiprocessing.get_context") as context:
context.return_value.Pool.side_effect = ImportError
result = checker._try_initialize_processpool(2) result = checker._try_initialize_processpool(2)
pool.assert_called_once_with(2, checker._pool_init) context.return_value.Pool.assert_called_once_with(2, checker._pool_init)
assert result is None assert result is None

View file

@ -1,6 +1,7 @@
"""Tests for the Manager object for FileCheckers.""" """Tests for the Manager object for FileCheckers."""
import errno import errno
import multiprocessing import multiprocessing
import platform
from unittest import mock from unittest import mock
import pytest import pytest
@ -51,14 +52,28 @@ def test_oserrors_are_reraised(_):
assert serial.call_count == 0 assert serial.call_count == 0
@mock.patch.object(platform, "system")
@mock.patch.object(multiprocessing, "get_start_method", return_value="spawn") @mock.patch.object(multiprocessing, "get_start_method", return_value="spawn")
def test_multiprocessing_is_disabled(_): def test_multiprocessing_is_disabled(_, mock_system):
"""Verify not being able to import multiprocessing forces jobs to 0.""" """Verify not being able to import multiprocessing forces jobs to 0."""
style_guide = style_guide_mock() style_guide = style_guide_mock()
manager = checker.Manager(style_guide, finder.Checkers([], [], [])) manager = checker.Manager(style_guide, finder.Checkers([], [], []))
assert manager.jobs == 0 assert manager.jobs == 0
@mock.patch.object(platform, "system", return_value="Darwin")
@mock.patch.object(multiprocessing, "get_start_method", return_value="spawn")
def test_multiprocessing_is_enabled_for_macos(_, mock_system):
"""Verify jobs are returned on macOS.
Since Python 3.8, `spawn` is the default value on macOS, which is
not currently supported by flake8.
"""
style_guide = style_guide_mock()
manager = checker.Manager(style_guide, finder.Checkers([], [], []))
assert manager.jobs > 0
def test_multiprocessing_cpu_count_not_implemented(): def test_multiprocessing_cpu_count_not_implemented():
"""Verify that jobs is 0 if cpu_count is unavailable.""" """Verify that jobs is 0 if cpu_count is unavailable."""
style_guide = style_guide_mock() style_guide = style_guide_mock()