Skip to content

Commit 32daaa2

Browse files
yoneymeta-codesync[bot]
authored andcommitted
Handle missing fork in run_in_subprocess
Summary: Centralize fork availability handling in run_in_subprocess and remove redundant test decorators. Reviewed By: alexmalyshev Differential Revision: D115912872 fbshipit-source-id: 92220ed28404ca364a344cdfac26cd1b5f4964e6
1 parent d3e4109 commit 32daaa2

7 files changed

Lines changed: 11 additions & 39 deletions

File tree

cinderx/PythonLib/cinderx/test_support.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,12 +338,16 @@ def __init__(self, exc: Exception) -> None:
338338
self.exc = exc
339339

340340

341-
def run_in_subprocess(func: Callable[..., TRet]) -> Callable[..., TRet]:
341+
def run_in_subprocess(func: Callable[..., None]) -> Callable[..., None]:
342342
"""
343-
Run a function in a subprocess. This enables modifying process state in a
344-
test without affecting other test functions.
343+
Run a test function in a subprocess. This enables modifying process state
344+
without affecting other test functions. Pass the test without running it
345+
when fork is unavailable.
345346
"""
346347

348+
if "fork" not in multiprocessing.get_all_start_methods():
349+
return passAlways(f"fork is unavailable on {platform.system()}")(func)
350+
347351
queue: multiprocessing.Queue = multiprocessing.Queue()
348352

349353
def wrapper(queue: multiprocessing.Queue, *args: object) -> None:
@@ -353,15 +357,14 @@ def wrapper(queue: multiprocessing.Queue, *args: object) -> None:
353357
except Exception as e:
354358
queue.put(_ExceptionResult(e), timeout=SUBPROCESS_TIMEOUT_SEC)
355359

356-
def wrapped(*args: object) -> TRet:
360+
def wrapped(*args: object) -> None:
357361
fork = multiprocessing.get_context("fork")
358362
p = fork.Process(target=wrapper, args=(queue, *args))
359363
p.start()
360364
value = queue.get(timeout=SUBPROCESS_TIMEOUT_SEC)
361365
p.join(timeout=SUBPROCESS_TIMEOUT_SEC)
362366
if isinstance(value, _ExceptionResult):
363367
raise value.exc
364-
return value
365368

366369
return wrapped
367370

cinderx/PythonLib/test_cinderx/test_free_threading/test_free_threading.py

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

33
# pyre-strict
44

5-
import os
65
import threading
76
import unittest
87
from concurrent.futures import ThreadPoolExecutor
@@ -18,7 +17,6 @@ def fibonacci(n: int) -> int:
1817

1918

2019
class FunctionWatcherTest(unittest.TestCase):
21-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
2220
@run_in_subprocess
2321
def test_concurrent_qualname_updates(self) -> None:
2422
worker_count = 10
@@ -53,7 +51,6 @@ def setUp(self):
5351
def tearDown(self):
5452
cinderx.jit.background_compile(self.bg_compile)
5553

56-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
5754
@run_in_subprocess
5855
def test_concurrent_force_compile(self) -> None:
5956
worker_count = 10
@@ -87,7 +84,6 @@ def recompile_and_call(_: int) -> list[int]:
8784
)
8885
self.assertTrue(cinderx.jit.is_jit_compiled(fibonacci))
8986

90-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
9187
@run_in_subprocess
9288
def test_concurrent_calls_trigger_jit_compilation(self) -> None:
9389
cinderx.jit.compile_after_n_calls(3)

cinderx/PythonLib/test_cinderx/test_free_threading/test_jit_cells.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"""Free-threaded JIT regression tests for closure cell access."""
66

77
import gc
8-
import os
98
import queue
109
import threading
1110
import unittest
@@ -18,7 +17,6 @@
1817
class JITCellTest(unittest.TestCase):
1918
"""Exercises closure cells shared by concurrently executing JIT code."""
2019

21-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
2220
@run_in_subprocess
2321
def test_concurrent_load_deref(self) -> None:
2422
"""A loaded cell value must remain alive while writers replace it."""
@@ -63,7 +61,6 @@ def writer(prefix: str) -> None:
6361

6462
self.assertTrue(value.startswith("w"))
6563

66-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
6764
@run_in_subprocess
6865
def test_concurrent_store_deref_releases_each_value_once(self) -> None:
6966
"""Concurrent cell stores must transfer ownership exactly once."""

cinderx/PythonLib/test_cinderx/test_free_threading/test_jit_lists.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"""Free-threaded JIT regression tests for list subscripts."""
66

77
import dis
8-
import os
98
import threading
109
import unittest
1110
from collections.abc import Callable
@@ -71,7 +70,6 @@ def writer(prefix: str) -> None:
7170

7271
self.assertTrue(values[0].startswith("w"))
7372

74-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
7573
@run_in_subprocess
7674
def test_concurrent_subscript_without_specialized_opcodes(self) -> None:
7775
"""Keep generic HIR when compiling an adaptive list opcode."""
@@ -92,7 +90,6 @@ def read_item(values: list[str]) -> str:
9290

9391
self.exercise_concurrent_access(read_item)
9492

95-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
9693
@unittest.skipUnless(FREE_THREADING_BUILD, "requires free-threaded build")
9794
@run_in_subprocess
9895
def test_concurrent_subscript_with_simplify(self) -> None:

cinderx/PythonLib/test_cinderx/test_free_threading/test_jit_tuples.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"""Free-threaded JIT regression tests for tuple subscripts."""
66

77
import dis
8-
import os
98
import threading
109
import unittest
1110
from collections.abc import Callable, Sequence
@@ -57,7 +56,6 @@ def reader(_: int) -> bool:
5756
self.assertEqual(results, [True] * worker_count)
5857
return worker_count * iterations
5958

60-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
6159
@run_in_subprocess
6260
def test_concurrent_subscript_without_specialized_opcodes(self) -> None:
6361
"""Keep generic HIR when compiling an adaptive tuple opcode."""
@@ -78,7 +76,6 @@ def read_item(values: Sequence[object]) -> object:
7876
values = (object(),)
7977
self.exercise_concurrent_reads(read_item, values)
8078

81-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
8279
@run_in_subprocess
8380
def test_concurrent_subscript_with_simplify(self) -> None:
8481
"""Keep the direct LoadArrayItem path for exact tuples.
@@ -104,7 +101,6 @@ def read_item(values: Sequence[object]) -> object:
104101
values = (object(),)
105102
self.exercise_concurrent_reads(read_item, values)
106103

107-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
108104
@run_in_subprocess
109105
def test_concurrent_deopt_on_guard_failure(self) -> None:
110106
"""Concurrent list calls deopt at the exact-tuple guard."""

cinderx/PythonLib/test_cinderx/test_immortalize.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# pyre-unsafe
33

44
import gc
5-
import os
65
import sys
76
import unittest
87

@@ -24,14 +23,12 @@ def test_default_not_immortal(self) -> None:
2423
obj = []
2524
self.assertFalse(cinderx.is_immortal(obj))
2625

27-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
2826
@run_in_subprocess
2927
def test_is_immortal(self) -> None:
3028
obj = []
3129
cinderx.immortalize_heap()
3230
self.assertTrue(cinderx.is_immortal(obj))
3331

34-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
3532
@run_in_subprocess
3633
def test_post_immortalize(self) -> None:
3734
cinderx.immortalize_heap()
@@ -42,7 +39,6 @@ def test_post_immortalize(self) -> None:
4239
_PY_DEBUG_BUILD,
4340
"Python 3.12 debug builds only allow interned immortal unicode",
4441
)
45-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
4642
@run_in_subprocess
4743
def test_immortalize_exact_dict_unicode_keys(self) -> None:
4844
key = "".join(("qe2_", "param"))
@@ -61,7 +57,6 @@ def test_immortalize_exact_dict_unicode_keys(self) -> None:
6157
_PY_DEBUG_BUILD,
6258
"Python 3.12 debug builds only allow interned immortal unicode",
6359
)
64-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
6560
@run_in_subprocess
6661
def test_immortalize_gc_collected_exact_dict_entries(self) -> None:
6762
key = "".join(("gc_collected_", "param"))
@@ -83,7 +78,6 @@ def test_immortalize_gc_collected_exact_dict_entries(self) -> None:
8378
_PY_DEBUG_BUILD,
8479
"Python 3.12 debug builds only allow interned immortal unicode",
8580
)
86-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
8781
@run_in_subprocess
8882
def test_immortalize_nested_exact_dict_entries(self) -> None:
8983
outer_key = "".join(("outer_", "param"))
@@ -106,7 +100,6 @@ def test_immortalize_nested_exact_dict_entries(self) -> None:
106100
_PY_DEBUG_BUILD,
107101
"Python 3.12 debug builds only allow interned immortal unicode",
108102
)
109-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
110103
@run_in_subprocess
111104
def test_immortalize_self_referential_exact_dict_entries(self) -> None:
112105
key = "".join(("self_", "param"))
@@ -125,7 +118,6 @@ def test_immortalize_self_referential_exact_dict_entries(self) -> None:
125118
self.assertTrue(cinderx.is_immortal(self_key))
126119
self.assertIs(mapping[self_key], mapping)
127120

128-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
129121
@run_in_subprocess
130122
def test_immortalize_code_consts_entries(self) -> None:
131123
def target() -> None:
@@ -143,7 +135,6 @@ def target() -> None:
143135
self.assertTrue(cinderx.is_immortal(consts))
144136
self.assertTrue(cinderx.is_immortal(const))
145137

146-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
147138
@run_in_subprocess
148139
def test_immortalize_code_consts_tuple_entry(self) -> None:
149140
def target() -> None:
@@ -162,7 +153,6 @@ def target() -> None:
162153
self.assertTrue(cinderx.is_immortal(consts))
163154
self.assertTrue(cinderx.is_immortal(tuple_const))
164155

165-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
166156
@run_in_subprocess
167157
def test_immortalize_code_consts_nested_exact_dict_entries(self) -> None:
168158
def target() -> None:
@@ -184,7 +174,6 @@ def target() -> None:
184174
self.assertTrue(cinderx.is_immortal(key))
185175
self.assertTrue(cinderx.is_immortal(value))
186176

187-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
188177
@run_in_subprocess
189178
def test_immortalize_nested_code_object_consts(self) -> None:
190179
def inner() -> None:
@@ -210,7 +199,6 @@ def outer() -> None:
210199
_PY_DEBUG_BUILD,
211200
"Python 3.12 debug builds only allow interned immortal unicode",
212201
)
213-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
214202
@run_in_subprocess
215203
def test_immortalize_code_name_tuple_entries(self) -> None:
216204
def target() -> None:
@@ -233,7 +221,6 @@ def target() -> None:
233221
self.assertTrue(cinderx.is_immortal(name))
234222
self.assertTrue(cinderx.is_immortal(local_name))
235223

236-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
237224
@run_in_subprocess
238225
def test_immortalize_code_exceptiontable(self) -> None:
239226
def target() -> None:
@@ -251,7 +238,6 @@ def target() -> None:
251238
self.assertTrue(cinderx.is_immortal(target.__code__))
252239
self.assertTrue(cinderx.is_immortal(exceptiontable))
253240

254-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
255241
@run_in_subprocess
256242
def test_immortalize_refcount(self) -> None:
257243
from cinderx import jit
@@ -274,7 +260,6 @@ def f():
274260
_PY_DEBUG_BUILD,
275261
"Python 3.12 debug builds only allow interned immortal unicode",
276262
)
277-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
278263
@run_in_subprocess
279264
def test_immortalize_code_qualname(self) -> None:
280265
def target() -> None:

cinderx/PythonLib/test_cinderx/test_jit_global_cache.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
# pyre-unsafe
66

77
import builtins
8-
import os
98
import unittest
109
from textwrap import dedent
1110

@@ -142,7 +141,6 @@ def _test_unwatch_builtins(self):
142141
# pyrefly: ignore [unsupported-operation]
143142
builtins.__dict__[42] = 42
144143

145-
@unittest.skipUnless(hasattr(os, "fork"), "fork not available on Windows")
146144
@run_in_subprocess
147145
def test_unwatch_builtins(self):
148146
try:
@@ -152,8 +150,8 @@ def test_unwatch_builtins(self):
152150
del builtins.__dict__[42]
153151

154152
@skip_unless_lazy_imports()
155-
@failUnlessHasOpcodes("LOAD_GLOBAL")
156153
@run_in_subprocess
154+
@failUnlessHasOpcodes("LOAD_GLOBAL")
157155
def test_preload_side_effect_modifies_globals(self):
158156
with cinder_support.temp_sys_path() as tmp:
159157
(tmp / "tmp_a.py").write_text(
@@ -226,8 +224,8 @@ def get_a():
226224
self.assertEqual(relevant_deopts, [])
227225

228226
@skip_unless_lazy_imports()
229-
@failUnlessHasOpcodes("LOAD_GLOBAL")
230227
@run_in_subprocess
228+
@failUnlessHasOpcodes("LOAD_GLOBAL")
231229
def test_preload_side_effect_makes_globals_unwatchable(self):
232230
with cinder_support.temp_sys_path() as tmp:
233231
(tmp / "tmp_a.py").write_text(
@@ -274,8 +272,8 @@ def get_a():
274272
)
275273

276274
@skip_unless_lazy_imports()
277-
@failUnlessHasOpcodes("LOAD_GLOBAL")
278275
@run_in_subprocess
276+
@failUnlessHasOpcodes("LOAD_GLOBAL")
279277
def test_preload_side_effect_makes_builtins_unwatchable(self):
280278
with cinder_support.temp_sys_path() as tmp:
281279
(tmp / "tmp_a.py").write_text(

0 commit comments

Comments
 (0)