Skip to content

Commit 7707f44

Browse files
committed
fix: improve setproctitle workaround
1 parent 14e3255 commit 7707f44

File tree

5 files changed

+38
-12
lines changed

5 files changed

+38
-12
lines changed

README.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,25 @@ to failing tests.
208208
debug=true
209209
210210
211+
Disable setproctitle (macOS)
212+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
213+
214+
Mutmut uses ``setproctitle`` to show the current mutant name in the process
215+
list, which is helpful for monitoring long runs. However, ``setproctitle``
216+
uses CoreFoundation APIs on macOS that are not fork-safe, causing segfaults
217+
in child processes.
218+
219+
By default, mutmut automatically disables ``setproctitle`` on macOS and
220+
enables it on other platforms. If you need to override this (e.g. to enable it on
221+
macOS at your own risk, or to disable it on other platforms), set ``use_setproctitle``:
222+
223+
.. code-block:: toml
224+
225+
# pyproject.toml
226+
[tool.mutmut]
227+
use_setproctitle = false
228+
229+
211230
Whitelisting
212231
~~~~~~~~~~~~
213232

src/mutmut/configuration.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import fnmatch
44
import os
5+
import platform
56
import sys
67
from collections.abc import Callable
78
from configparser import ConfigParser
@@ -110,6 +111,9 @@ def _load_config() -> Config:
110111
pytest_add_cli_args=s("pytest_add_cli_args", []),
111112
pytest_add_cli_args_test_selection=s("pytest_add_cli_args_test_selection", []),
112113
type_check_command=s("type_check_command", []),
114+
use_setproctitle=s(
115+
"use_setproctitle", not platform.system() == "Darwin"
116+
), # False on Mac, true otherwise as default (https://github.com/boxed/mutmut/pull/450#issuecomment-4002571055)
113117
)
114118

115119

@@ -128,6 +132,7 @@ class Config:
128132
tests_dir: list[str]
129133
mutate_only_covered_lines: bool
130134
type_check_command: list[str]
135+
use_setproctitle: bool
131136

132137
def should_ignore_for_mutation(self, path: Path | str) -> bool:
133138
path_str = str(path)

src/mutmut/utils/safe_setproctitle.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@
66
This module provides a safe_setproctitle() function that:
77
- Works normally on Linux
88
- Is a no-op on macOS to avoid crashes after fork()
9-
"""
10-
11-
import platform
12-
import sys
139
14-
from setproctitle import setproctitle as _setproctitle
10+
Related: https://github.com/boxed/mutmut/pull/450#issuecomment-4002571055
11+
"""
1512

16-
_use_setproctitle = not (sys.version_info >= (3, 10) and platform.system() == "Darwin")
13+
from mutmut.configuration import Config
1714

18-
if _use_setproctitle:
15+
if Config.get().use_setproctitle:
16+
from setproctitle import setproctitle as _setproctitle
1917

2018
def safe_setproctitle(title: str) -> None:
2119
"""Set the process title."""

tests/test_configuration.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ def test_ignores_non_python_files(self):
6565
tests_dir=[],
6666
mutate_only_covered_lines=False,
6767
type_check_command=[],
68+
use_setproctitle=False,
6869
)
6970
assert config.should_ignore_for_mutation("foo.txt") is True
7071
assert config.should_ignore_for_mutation("foo.js") is True
@@ -82,6 +83,7 @@ def test_does_not_ignore_python_files(self):
8283
tests_dir=[],
8384
mutate_only_covered_lines=False,
8485
type_check_command=[],
86+
use_setproctitle=False,
8587
)
8688
assert config.should_ignore_for_mutation("foo.py") is False
8789
assert config.should_ignore_for_mutation("src/foo.py") is False
@@ -98,6 +100,7 @@ def test_respects_do_not_mutate_exact_match(self):
98100
tests_dir=[],
99101
mutate_only_covered_lines=False,
100102
type_check_command=[],
103+
use_setproctitle=False,
101104
)
102105
assert config.should_ignore_for_mutation("foo.py") is True
103106
assert config.should_ignore_for_mutation("bar.py") is False
@@ -114,6 +117,7 @@ def test_respects_do_not_mutate_glob_pattern(self):
114117
tests_dir=[],
115118
mutate_only_covered_lines=False,
116119
type_check_command=[],
120+
use_setproctitle=False,
117121
)
118122
assert config.should_ignore_for_mutation("tests/test_foo.py") is True
119123
assert config.should_ignore_for_mutation("src/ignore_me.py") is True
@@ -131,6 +135,7 @@ def test_accepts_path_objects(self):
131135
tests_dir=[],
132136
mutate_only_covered_lines=False,
133137
type_check_command=[],
138+
use_setproctitle=False,
134139
)
135140
assert config.should_ignore_for_mutation(Path("foo.py")) is True
136141
assert config.should_ignore_for_mutation(Path("bar.py")) is False

tests/utils/test_safe_setproctitle.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,17 @@
88
import os
99
import platform
1010
import signal
11-
import sys
1211

1312
import pytest
1413
from setproctitle import setproctitle
1514

1615
from mutmut.utils.safe_setproctitle import safe_setproctitle
1716

18-
# Only run this test on macOS with Python 3.14+
19-
IS_MACOS_314 = sys.version_info >= (3, 14) and platform.system() == "Darwin"
17+
# Only run this test on macOS
18+
IS_MACOS = platform.system() == "Darwin"
2019

2120

22-
@pytest.mark.skipif(not IS_MACOS_314, reason="setproctitle only crashes after fork on macOS Python 3.14+")
21+
@pytest.mark.skipif(not IS_MACOS, reason="setproctitle only crashes after fork on macOS")
2322
def test_setproctitle_crashes_after_fork_with_corefoundation_loaded():
2423
"""Verify setproctitle segfaults after fork when CoreFoundation is loaded.
2524
@@ -63,7 +62,7 @@ def test_setproctitle_crashes_after_fork_with_corefoundation_loaded():
6362
)
6463

6564

66-
@pytest.mark.skipif(not IS_MACOS_314, reason="safe_setproctitle workaround only applies to macOS Python 3.14+")
65+
@pytest.mark.skipif(not IS_MACOS, reason="safe_setproctitle workaround only applies to macOS")
6766
def test_safe_setproctitle_does_not_crash_after_fork():
6867
"""Verify our safe_setproctitle wrapper doesn't crash after fork."""
6968
pid = os.fork()

0 commit comments

Comments
 (0)