[Python-checkins] bpo-40280: Add requires_fork test helper (GH-30622)

tiran webhook-mailer at python.org
Sun Jan 16 16:52:55 EST 2022


https://github.com/python/cpython/commit/91e33ac3d08a1c6004c469da2c0e2a97b5bdc53c
commit: 91e33ac3d08a1c6004c469da2c0e2a97b5bdc53c
branch: main
author: Christian Heimes <christian at python.org>
committer: tiran <christian at 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`.



More information about the Python-checkins mailing list