Mailing List Archive

bpo-40280: Add requires_fork test helper (GH-30622)
https://github.com/python/cpython/commit/91e33ac3d08a1c6004c469da2c0e2a97b5bdc53c
commit: 91e33ac3d08a1c6004c469da2c0e2a97b5bdc53c
branch: main
author: Christian Heimes <christian@python.org>
committer: tiran <christian@python.org>
date: 2022-01-16T22:52:43+01:00
summary:

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

files:
A Misc/NEWS.d/next/Tests/2022-01-16-14-11-57.bpo-40280.fNnFfx.rst
M Lib/test/support/__init__.py
M Lib/test/test_fork1.py
M Lib/test/test_random.py
M Lib/test/test_support.py
M Lib/test/test_sysconfig.py
M Lib/test/test_tempfile.py
M Lib/test/test_thread.py
M Lib/test/test_threading.py
M Lib/test/test_uuid.py

diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index f8faa41ad439c..ca903d302bdd3 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -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
@@ -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"
diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py
index a2f7cfee9cf69..a6523bbc51817 100644
--- a/Lib/test/test_fork1.py
+++ b/Lib/test/test_fork1.py
@@ -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):
diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py
index b80aeca26cf48..f980c5b8df0d2 100644
--- a/Lib/test/test_random.py
+++ b/Lib/test/test_random.py
@@ -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()
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
index d5a1d447f0563..4dac7f6cd4200 100644
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -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.
@@ -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()
diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py
index 506266d08185d..6fbb80d77f793 100644
--- a/Lib/test/test_sysconfig.py
+++ b/Lib/test/test_sysconfig.py
@@ -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'])
diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py
index 2b0ec46a10327..25fddaec6d317 100644
--- a/Lib/test/test_tempfile.py
+++ b/Lib/test/test_tempfile.py
@@ -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.
diff --git a/Lib/test/test_thread.py b/Lib/test/test_thread.py
index 4ae8a833b990d..d55fb731b6df5 100644
--- a/Lib/test/test_thread.py
+++ b/Lib/test/test_thread.py
@@ -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
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index a8f3c139b24be..f03a64232e17c 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -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.
@@ -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.
@@ -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.
@@ -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:
@@ -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:
@@ -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
@@ -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
@@ -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
@@ -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()

diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py
index 3f56192c70e84..411eec0f40621 100755
--- a/Lib/test/test_uuid.py
+++ b/Lib/test/test_uuid.py
@@ -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
diff --git a/Misc/NEWS.d/next/Tests/2022-01-16-14-11-57.bpo-40280.fNnFfx.rst b/Misc/NEWS.d/next/Tests/2022-01-16-14-11-57.bpo-40280.fNnFfx.rst
new file mode 100644
index 0000000000000..2d66db1210854
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2022-01-16-14-11-57.bpo-40280.fNnFfx.rst
@@ -0,0 +1,2 @@
+Add :func:`test.support.requires_fork` decorators to mark tests that require
+a working :func:`os.fork`.

_______________________________________________
Python-checkins mailing list
Python-checkins@python.org
https://mail.python.org/mailman/listinfo/python-checkins