Skip to content

bpo-40280: Add requires_fork test helper (GH-30622) #30622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@
"requires_gzip", "requires_bz2", "requires_lzma",
"bigmemtest", "bigaddrspacetest", "cpython_only", "get_attribute",
"requires_IEEE_754", "requires_zlib",
"has_fork_support", "requires_fork",
"anticipate_failure", "load_package_tests", "detect_api_mismatch",
"check__all__", "skip_if_buggy_ucrt_strfptime",
"check_disallow_instantiation",
# sys
"is_jython", "is_android", "check_impl_detail", "unix_shell",
"setswitchinterval",
"is_jython", "is_android", "is_emscripten",
"check_impl_detail", "unix_shell", "setswitchinterval",
# network
"open_urlresource",
# processes
Expand Down Expand Up @@ -466,6 +467,15 @@ def requires_debug_ranges(reason='requires co_positions / debug_ranges'):
else:
unix_shell = None

# wasm32-emscripten is POSIX-like but does not provide a
# working fork() or subprocess API.
is_emscripten = sys.platform == "emscripten"

has_fork_support = hasattr(os, "fork") and not is_emscripten

def requires_fork():
return unittest.skipUnless(has_fork_support, "requires working os.fork()")

# Define the URL of a dedicated HTTP server for the network tests.
# The URL must use clear-text HTTP: no redirection to encrypted HTTPS.
TEST_HTTP_URL = "http://www.pythontest.net"
Expand Down
4 changes: 3 additions & 1 deletion Lib/test/test_fork1.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@


# Skip test if fork does not exist.
support.get_attribute(os, 'fork')
if not support.has_fork_support:
raise unittest.SkipTest("test module requires working os.fork")


class ForkTest(ForkWait):
def test_threaded_import_lock_fork(self):
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -1293,7 +1293,7 @@ def test__all__(self):
# tests validity but not completeness of the __all__ list
self.assertTrue(set(random.__all__) <= set(dir(random)))

@unittest.skipUnless(hasattr(os, "fork"), "fork() required")
@test.support.requires_fork()
def test_after_fork(self):
# Test the global Random instance gets reseeded in child
r, w = os.pipe()
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def test_temp_dir__existing_dir__quiet_true(self):
f'temporary directory {path!r}: '),
warn)

@unittest.skipUnless(hasattr(os, "fork"), "test requires os.fork")
@support.requires_fork()
def test_temp_dir__forked_child(self):
"""Test that a forked child process does not remove the directory."""
# See bpo-30028 for details.
Expand Down Expand Up @@ -447,6 +447,7 @@ def test_check__all__(self):

@unittest.skipUnless(hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG'),
'need os.waitpid() and os.WNOHANG')
@support.requires_fork()
def test_reap_children(self):
# Make sure that there is no other pending child process
support.reap_children()
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_sysconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ def test_SO_value(self):
'EXT_SUFFIX required for this test')
def test_EXT_SUFFIX_in_vars(self):
import _imp
if not _imp.extension_suffixes():
self.skipTest("stub loader has no suffixes")
vars = sysconfig.get_config_vars()
self.assertIsNotNone(vars['SO'])
self.assertEqual(vars['SO'], vars['EXT_SUFFIX'])
Expand Down
3 changes: 1 addition & 2 deletions Lib/test/test_tempfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,7 @@ def supports_iter(self):
if i == 20:
break

@unittest.skipUnless(hasattr(os, 'fork'),
"os.fork is required for this test")
@support.requires_fork()
def test_process_awareness(self):
# ensure that the random source differs between
# child and parent.
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ class TestForkInThread(unittest.TestCase):
def setUp(self):
self.read_fd, self.write_fd = os.pipe()

@unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork')
@support.requires_fork()
@threading_helper.reap_threads
def test_forkinthread(self):
pid = None
Expand Down
18 changes: 9 additions & 9 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ def test_daemon_param(self):
t = threading.Thread(daemon=True)
self.assertTrue(t.daemon)

@unittest.skipUnless(hasattr(os, 'fork'), 'needs os.fork()')
@support.requires_fork()
def test_fork_at_exit(self):
# bpo-42350: Calling os.fork() after threading._shutdown() must
# not log an error.
Expand Down Expand Up @@ -533,7 +533,7 @@ def exit_handler():
self.assertEqual(out, b'')
self.assertEqual(err.rstrip(), b'child process ok')

@unittest.skipUnless(hasattr(os, 'fork'), 'test needs fork()')
@support.requires_fork()
def test_dummy_thread_after_fork(self):
# Issue #14308: a dummy thread in the active list doesn't mess up
# the after-fork mechanism.
Expand All @@ -560,7 +560,7 @@ def background_thread(evt):
self.assertEqual(out, b'')
self.assertEqual(err, b'')

@unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
@support.requires_fork()
def test_is_alive_after_fork(self):
# Try hard to trigger #18418: is_alive() could sometimes be True on
# threads that vanished after a fork.
Expand Down Expand Up @@ -594,7 +594,7 @@ def f():
th.start()
th.join()

@unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
@support.requires_fork()
@unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()")
def test_main_thread_after_fork(self):
code = """if 1:
Expand All @@ -616,7 +616,7 @@ def test_main_thread_after_fork(self):
self.assertEqual(data, "MainThread\nTrue\nTrue\n")

@unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug")
@unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
@support.requires_fork()
@unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()")
def test_main_thread_after_fork_from_nonmain_thread(self):
code = """if 1:
Expand Down Expand Up @@ -993,7 +993,7 @@ def test_1_join_on_shutdown(self):
"""
self._run_and_join(script)

@unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
@support.requires_fork()
@unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug")
def test_2_join_in_forked_process(self):
# Like the test above, but from a forked interpreter
Expand All @@ -1014,7 +1014,7 @@ def test_2_join_in_forked_process(self):
"""
self._run_and_join(script)

@unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
@support.requires_fork()
@unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug")
def test_3_join_in_forked_from_thread(self):
# Like the test above, but fork() was called from a worker thread
Expand Down Expand Up @@ -1085,7 +1085,7 @@ def main():
rc, out, err = assert_python_ok('-c', script)
self.assertFalse(err)

@unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
@support.requires_fork()
@unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug")
def test_reinit_tls_after_fork(self):
# Issue #13817: fork() would deadlock in a multithreaded program with
Expand All @@ -1109,7 +1109,7 @@ def do_fork_and_wait():
for t in threads:
t.join()

@unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()")
@support.requires_fork()
def test_clear_threads_states_after_fork(self):
# Issue #17094: check that threads states are cleared after fork()

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ def test_uuid5(self):
equal(u, self.uuid.UUID(v))
equal(str(u), v)

@unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork')
@support.requires_fork()
def testIssue8621(self):
# On at least some versions of OSX self.uuid.uuid4 generates
# the same sequence of UUIDs in the parent and any
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :func:`test.support.requires_fork` decorators to mark tests that require
a working :func:`os.fork`.