https://github.com/python/cpython/commit/391659b3da570bfa28fed5fbdb6f2d9c26ab3dd0
commit: 391659b3da570bfa28fed5fbdb6f2d9c26ab3dd0
branch: main
author: Russell Keith-Magee <[email protected]>
committer: erlend-aasland <[email protected]>
date: 2024-02-05T01:04:57+01:00
summary:

gh-114099: Add test exclusions to support running the test suite on iOS 
(#114889)

Add test annotations required to run the test suite on iOS (PEP 730).

The majority of the change involve annotating tests that use subprocess,
but are skipped on Emscripten/WASI for other reasons, and including
iOS/tvOS/watchOS under the same umbrella as macOS/darwin checks.

`is_apple` and `is_apple_mobile` test helpers have been added to
identify *any* Apple platform, and "any Apple platform except macOS",
respectively.

files:
A Misc/NEWS.d/next/Tests/2024-02-02-13-18-55.gh-issue-114099.C_ycWg.rst
M Lib/test/support/__init__.py
M Lib/test/support/os_helper.py
M Lib/test/test_asyncio/test_events.py
M Lib/test/test_asyncio/test_streams.py
M Lib/test/test_asyncio/test_subprocess.py
M Lib/test/test_asyncio/test_unix_events.py
M Lib/test/test_cmd_line_script.py
M Lib/test/test_code_module.py
M Lib/test/test_fcntl.py
M Lib/test/test_ftplib.py
M Lib/test/test_genericpath.py
M Lib/test/test_httpservers.py
M Lib/test/test_io.py
M Lib/test/test_marshal.py
M Lib/test/test_mmap.py
M Lib/test/test_os.py
M Lib/test/test_pdb.py
M Lib/test/test_platform.py
M Lib/test/test_posix.py
M Lib/test/test_pty.py
M Lib/test/test_selectors.py
M Lib/test/test_shutil.py
M Lib/test/test_signal.py
M Lib/test/test_socket.py
M Lib/test/test_sqlite3/test_dbapi.py
M Lib/test/test_stat.py
M Lib/test/test_sys_settrace.py
M Lib/test/test_unicode_file_functions.py
M Lib/test/test_urllib2.py
M Lib/test/test_venv.py

diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index f2e6af078a5f29..5b091fb2fd32dc 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -43,7 +43,7 @@
     "requires_limited_api", "requires_specialization",
     # sys
     "MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi",
-    "check_impl_detail", "unix_shell", "setswitchinterval",
+    "is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval",
     # os
     "get_pagesize",
     # network
@@ -522,7 +522,7 @@ def requires_debug_ranges(reason='requires co_positions / 
debug_ranges'):
 
 is_android = hasattr(sys, 'getandroidapilevel')
 
-if sys.platform not in ('win32', 'vxworks'):
+if sys.platform not in {"win32", "vxworks", "ios", "tvos", "watchos"}:
     unix_shell = '/system/bin/sh' if is_android else '/bin/sh'
 else:
     unix_shell = None
@@ -532,19 +532,35 @@ def requires_debug_ranges(reason='requires co_positions / 
debug_ranges'):
 is_emscripten = sys.platform == "emscripten"
 is_wasi = sys.platform == "wasi"
 
-has_fork_support = hasattr(os, "fork") and not is_emscripten and not is_wasi
+# Apple mobile platforms (iOS/tvOS/watchOS) are POSIX-like but do not
+# have subprocess or fork support.
+is_apple_mobile = sys.platform in {"ios", "tvos", "watchos"}
+is_apple = is_apple_mobile or sys.platform == "darwin"
+
+has_fork_support = hasattr(os, "fork") and not (
+    is_emscripten
+    or is_wasi
+    or is_apple_mobile
+)
 
 def requires_fork():
     return unittest.skipUnless(has_fork_support, "requires working os.fork()")
 
-has_subprocess_support = not is_emscripten and not is_wasi
+has_subprocess_support = not (
+    is_emscripten
+    or is_wasi
+    or is_apple_mobile
+)
 
 def requires_subprocess():
     """Used for subprocess, os.spawn calls, fd inheritance"""
     return unittest.skipUnless(has_subprocess_support, "requires subprocess 
support")
 
 # Emscripten's socket emulation and WASI sockets have limitations.
-has_socket_support = not is_emscripten and not is_wasi
+has_socket_support = not (
+    is_emscripten
+    or is_wasi
+)
 
 def requires_working_socket(*, module=False):
     """Skip tests or modules that require working sockets
diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py
index 20f38fd36a8876..22787e32b5f3ab 100644
--- a/Lib/test/support/os_helper.py
+++ b/Lib/test/support/os_helper.py
@@ -22,8 +22,8 @@
 
 # TESTFN_UNICODE is a non-ascii filename
 TESTFN_UNICODE = TESTFN_ASCII + "-\xe0\xf2\u0258\u0141\u011f"
-if sys.platform == 'darwin':
-    # In Mac OS X's VFS API file names are, by definition, canonically
+if support.is_apple:
+    # On Apple's VFS API file names are, by definition, canonically
     # decomposed Unicode, encoded using UTF-8. See QA1173:
     # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html
     import unicodedata
@@ -48,8 +48,8 @@
                   'encoding (%s). Unicode filename tests may not be effective'
                   % (TESTFN_UNENCODABLE, sys.getfilesystemencoding()))
             TESTFN_UNENCODABLE = None
-# macOS and Emscripten deny unencodable filenames (invalid utf-8)
-elif sys.platform not in {'darwin', 'emscripten', 'wasi'}:
+# Apple and Emscripten deny unencodable filenames (invalid utf-8)
+elif not support.is_apple and sys.platform not in {"emscripten", "wasi"}:
     try:
         # ascii and utf-8 cannot encode the byte 0xff
         b'\xff'.decode(sys.getfilesystemencoding())
diff --git a/Lib/test/test_asyncio/test_events.py 
b/Lib/test/test_asyncio/test_events.py
index b25c0975736e20..c92c88bd5b2429 100644
--- a/Lib/test/test_asyncio/test_events.py
+++ b/Lib/test/test_asyncio/test_events.py
@@ -1815,6 +1815,7 @@ def check_killed(self, returncode):
         else:
             self.assertEqual(-signal.SIGKILL, returncode)
 
+    @support.requires_subprocess()
     def test_subprocess_exec(self):
         prog = os.path.join(os.path.dirname(__file__), 'echo.py')
 
@@ -1836,6 +1837,7 @@ def test_subprocess_exec(self):
         self.check_killed(proto.returncode)
         self.assertEqual(b'Python The Winner', proto.data[1])
 
+    @support.requires_subprocess()
     def test_subprocess_interactive(self):
         prog = os.path.join(os.path.dirname(__file__), 'echo.py')
 
@@ -1863,6 +1865,7 @@ def test_subprocess_interactive(self):
         self.loop.run_until_complete(proto.completed)
         self.check_killed(proto.returncode)
 
+    @support.requires_subprocess()
     def test_subprocess_shell(self):
         connect = self.loop.subprocess_shell(
                         functools.partial(MySubprocessProtocol, self.loop),
@@ -1879,6 +1882,7 @@ def test_subprocess_shell(self):
         self.assertEqual(proto.data[2], b'')
         transp.close()
 
+    @support.requires_subprocess()
     def test_subprocess_exitcode(self):
         connect = self.loop.subprocess_shell(
                         functools.partial(MySubprocessProtocol, self.loop),
@@ -1890,6 +1894,7 @@ def test_subprocess_exitcode(self):
         self.assertEqual(7, proto.returncode)
         transp.close()
 
+    @support.requires_subprocess()
     def test_subprocess_close_after_finish(self):
         connect = self.loop.subprocess_shell(
                         functools.partial(MySubprocessProtocol, self.loop),
@@ -1904,6 +1909,7 @@ def test_subprocess_close_after_finish(self):
         self.assertEqual(7, proto.returncode)
         self.assertIsNone(transp.close())
 
+    @support.requires_subprocess()
     def test_subprocess_kill(self):
         prog = os.path.join(os.path.dirname(__file__), 'echo.py')
 
@@ -1920,6 +1926,7 @@ def test_subprocess_kill(self):
         self.check_killed(proto.returncode)
         transp.close()
 
+    @support.requires_subprocess()
     def test_subprocess_terminate(self):
         prog = os.path.join(os.path.dirname(__file__), 'echo.py')
 
@@ -1937,6 +1944,7 @@ def test_subprocess_terminate(self):
         transp.close()
 
     @unittest.skipIf(sys.platform == 'win32', "Don't have SIGHUP")
+    @support.requires_subprocess()
     def test_subprocess_send_signal(self):
         # bpo-31034: Make sure that we get the default signal handler (killing
         # the process). The parent process may have decided to ignore SIGHUP,
@@ -1961,6 +1969,7 @@ def test_subprocess_send_signal(self):
         finally:
             signal.signal(signal.SIGHUP, old_handler)
 
+    @support.requires_subprocess()
     def test_subprocess_stderr(self):
         prog = os.path.join(os.path.dirname(__file__), 'echo2.py')
 
@@ -1982,6 +1991,7 @@ def test_subprocess_stderr(self):
         self.assertTrue(proto.data[2].startswith(b'ERR:test'), proto.data[2])
         self.assertEqual(0, proto.returncode)
 
+    @support.requires_subprocess()
     def test_subprocess_stderr_redirect_to_stdout(self):
         prog = os.path.join(os.path.dirname(__file__), 'echo2.py')
 
@@ -2007,6 +2017,7 @@ def test_subprocess_stderr_redirect_to_stdout(self):
         transp.close()
         self.assertEqual(0, proto.returncode)
 
+    @support.requires_subprocess()
     def test_subprocess_close_client_stream(self):
         prog = os.path.join(os.path.dirname(__file__), 'echo3.py')
 
@@ -2041,6 +2052,7 @@ def test_subprocess_close_client_stream(self):
         self.loop.run_until_complete(proto.completed)
         self.check_killed(proto.returncode)
 
+    @support.requires_subprocess()
     def test_subprocess_wait_no_same_group(self):
         # start the new process in a new session
         connect = self.loop.subprocess_shell(
@@ -2053,6 +2065,7 @@ def test_subprocess_wait_no_same_group(self):
         self.assertEqual(7, proto.returncode)
         transp.close()
 
+    @support.requires_subprocess()
     def test_subprocess_exec_invalid_args(self):
         async def connect(**kwds):
             await self.loop.subprocess_exec(
@@ -2066,6 +2079,7 @@ async def connect(**kwds):
         with self.assertRaises(ValueError):
             self.loop.run_until_complete(connect(shell=True))
 
+    @support.requires_subprocess()
     def test_subprocess_shell_invalid_args(self):
 
         async def connect(cmd=None, **kwds):
diff --git a/Lib/test/test_asyncio/test_streams.py 
b/Lib/test/test_asyncio/test_streams.py
index 3c8cc5f3649180..210990593adfa9 100644
--- a/Lib/test/test_asyncio/test_streams.py
+++ b/Lib/test/test_asyncio/test_streams.py
@@ -10,7 +10,6 @@
 import unittest
 from unittest import mock
 import warnings
-from test.support import socket_helper
 try:
     import ssl
 except ImportError:
@@ -18,6 +17,7 @@
 
 import asyncio
 from test.test_asyncio import utils as test_utils
+from test.support import requires_subprocess, socket_helper
 
 
 def tearDownModule():
@@ -770,6 +770,7 @@ async def client(addr):
         self.assertEqual(msg2, b"hello world 2!\n")
 
     @unittest.skipIf(sys.platform == 'win32', "Don't have pipes")
+    @requires_subprocess()
     def test_read_all_from_pipe_reader(self):
         # See asyncio issue 168.  This test is derived from the example
         # subprocess_attach_read_pipe.py, but we configure the
diff --git a/Lib/test/test_asyncio/test_subprocess.py 
b/Lib/test/test_asyncio/test_subprocess.py
index 808b21c6617551..f50a9ebc031ba8 100644
--- a/Lib/test/test_asyncio/test_subprocess.py
+++ b/Lib/test/test_asyncio/test_subprocess.py
@@ -47,6 +47,7 @@ def _start(self, *args, **kwargs):
         self._proc.pid = -1
 
 
[email protected]_subprocess()
 class SubprocessTransportTests(test_utils.TestCase):
     def setUp(self):
         super().setUp()
@@ -110,6 +111,7 @@ def test_subprocess_repr(self):
         transport.close()
 
 
[email protected]_subprocess()
 class SubprocessMixin:
 
     def test_stdin_stdout(self):
diff --git a/Lib/test/test_asyncio/test_unix_events.py 
b/Lib/test/test_asyncio/test_unix_events.py
index d2c8cba6acfa31..59ef9f5f58cabc 100644
--- a/Lib/test/test_asyncio/test_unix_events.py
+++ b/Lib/test/test_asyncio/test_unix_events.py
@@ -1874,7 +1874,7 @@ async def runner():
             wsock.close()
 
 
[email protected](hasattr(os, 'fork'), 'requires os.fork()')
[email protected]_fork()
 class TestFork(unittest.IsolatedAsyncioTestCase):
 
     async def test_fork_not_share_event_loop(self):
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
index 48754d5a63da3b..3a5a8abf81e43d 100644
--- a/Lib/test/test_cmd_line_script.py
+++ b/Lib/test/test_cmd_line_script.py
@@ -14,8 +14,7 @@
 
 import textwrap
 from test import support
-from test.support import import_helper
-from test.support import os_helper
+from test.support import import_helper, is_apple, os_helper
 from test.support.script_helper import (
     make_pkg, make_script, make_zip_pkg, make_zip_script,
     assert_python_ok, assert_python_failure, spawn_python, kill_python)
@@ -557,12 +556,17 @@ def test_pep_409_verbiage(self):
             self.assertTrue(text[3].startswith('NameError'))
 
     def test_non_ascii(self):
-        # Mac OS X denies the creation of a file with an invalid UTF-8 name.
+        # Apple platforms deny the creation of a file with an invalid UTF-8 
name.
         # Windows allows creating a name with an arbitrary bytes name, but
         # Python cannot a undecodable bytes argument to a subprocess.
-        # WASI does not permit invalid UTF-8 names.
-        if (os_helper.TESTFN_UNDECODABLE
-        and sys.platform not in ('win32', 'darwin', 'emscripten', 'wasi')):
+        # Emscripten/WASI does not permit invalid UTF-8 names.
+        if (
+            os_helper.TESTFN_UNDECODABLE
+            and sys.platform not in {
+                "win32", "emscripten", "wasi"
+            }
+            and not is_apple
+        ):
             name = os.fsdecode(os_helper.TESTFN_UNDECODABLE)
         elif os_helper.TESTFN_NONASCII:
             name = os_helper.TESTFN_NONASCII
diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py
index 747c0f9683c19c..259778a5cade98 100644
--- a/Lib/test/test_code_module.py
+++ b/Lib/test/test_code_module.py
@@ -160,6 +160,7 @@ def setUp(self):
         self.console = code.InteractiveConsole(local_exit=True)
         self.mock_sys()
 
+    @unittest.skipIf(sys.flags.no_site, "exit() isn't defined unless there's a 
site module")
     def test_exit(self):
         # default exit message
         self.infunc.side_effect = ["exit()"]
diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py
index 203dd6fe57dcd9..6d734d052454d3 100644
--- a/Lib/test/test_fcntl.py
+++ b/Lib/test/test_fcntl.py
@@ -6,7 +6,9 @@
 import struct
 import sys
 import unittest
-from test.support import verbose, cpython_only, get_pagesize
+from test.support import (
+    cpython_only, get_pagesize, is_apple, requires_subprocess, verbose
+)
 from test.support.import_helper import import_module
 from test.support.os_helper import TESTFN, unlink
 
@@ -56,8 +58,10 @@ def get_lockdata():
         else:
             start_len = "qq"
 
-        if (sys.platform.startswith(('netbsd', 'freebsd', 'openbsd'))
-            or sys.platform == 'darwin'):
+        if (
+            sys.platform.startswith(('netbsd', 'freebsd', 'openbsd'))
+            or is_apple
+        ):
             if struct.calcsize('l') == 8:
                 off_t = 'l'
                 pid_t = 'i'
@@ -157,6 +161,7 @@ def test_flock(self):
         self.assertRaises(TypeError, fcntl.flock, 'spam', fcntl.LOCK_SH)
 
     @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError")
+    @requires_subprocess()
     def test_lockf_exclusive(self):
         self.f = open(TESTFN, 'wb+')
         cmd = fcntl.LOCK_EX | fcntl.LOCK_NB
@@ -169,6 +174,7 @@ def test_lockf_exclusive(self):
         self.assertEqual(p.exitcode, 0)
 
     @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError")
+    @requires_subprocess()
     def test_lockf_share(self):
         self.f = open(TESTFN, 'wb+')
         cmd = fcntl.LOCK_SH | fcntl.LOCK_NB
diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py
index 2f191ea7a44c16..81115e9db888cf 100644
--- a/Lib/test/test_ftplib.py
+++ b/Lib/test/test_ftplib.py
@@ -18,6 +18,7 @@
 
 from unittest import TestCase, skipUnless
 from test import support
+from test.support import requires_subprocess
 from test.support import threading_helper
 from test.support import socket_helper
 from test.support import warnings_helper
@@ -900,6 +901,7 @@ def retr():
 
 
 @skipUnless(ssl, "SSL not available")
+@requires_subprocess()
 class TestTLS_FTPClassMixin(TestFTPClass):
     """Repeat TestFTPClass tests starting the TLS layer for both control
     and data connections first.
@@ -916,6 +918,7 @@ def setUp(self, encoding=DEFAULT_ENCODING):
 
 
 @skipUnless(ssl, "SSL not available")
+@requires_subprocess()
 class TestTLS_FTPClass(TestCase):
     """Specific TLS_FTP class tests."""
 
diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py
index 4f311c2d498e9f..b77cd4c67d6b2a 100644
--- a/Lib/test/test_genericpath.py
+++ b/Lib/test/test_genericpath.py
@@ -7,9 +7,9 @@
 import sys
 import unittest
 import warnings
-from test.support import is_emscripten
-from test.support import os_helper
-from test.support import warnings_helper
+from test.support import (
+    is_apple, is_emscripten, os_helper, warnings_helper
+)
 from test.support.script_helper import assert_python_ok
 from test.support.os_helper import FakePath
 
@@ -483,12 +483,16 @@ def test_abspath_issue3426(self):
                     self.assertIsInstance(abspath(path), str)
 
     def test_nonascii_abspath(self):
-        if (os_helper.TESTFN_UNDECODABLE
-        # macOS and Emscripten deny the creation of a directory with an
-        # invalid UTF-8 name. Windows allows creating a directory with an
-        # arbitrary bytes name, but fails to enter this directory
-        # (when the bytes name is used).
-        and sys.platform not in ('win32', 'darwin', 'emscripten', 'wasi')):
+        if (
+            os_helper.TESTFN_UNDECODABLE
+            # Apple platforms and Emscripten/WASI deny the creation of a
+            # directory with an invalid UTF-8 name. Windows allows creating a
+            # directory with an arbitrary bytes name, but fails to enter this
+            # directory (when the bytes name is used).
+            and sys.platform not in {
+                "win32", "emscripten", "wasi"
+            } and not is_apple
+        ):
             name = os_helper.TESTFN_UNDECODABLE
         elif os_helper.TESTFN_NONASCII:
             name = os_helper.TESTFN_NONASCII
diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py
index 9fa6ecf9c08e27..d762ec6102ab8a 100644
--- a/Lib/test/test_httpservers.py
+++ b/Lib/test/test_httpservers.py
@@ -30,8 +30,9 @@
 
 import unittest
 from test import support
-from test.support import os_helper
-from test.support import threading_helper
+from test.support import (
+    is_apple, os_helper, requires_subprocess, threading_helper
+)
 
 support.requires_working_socket(module=True)
 
@@ -410,8 +411,8 @@ def close_conn():
         reader.close()
         return body
 
-    @unittest.skipIf(sys.platform == 'darwin',
-                     'undecodable name cannot always be decoded on macOS')
+    @unittest.skipIf(is_apple,
+                     'undecodable name cannot always be decoded on Apple 
platforms')
     @unittest.skipIf(sys.platform == 'win32',
                      'undecodable name cannot be decoded on win32')
     @unittest.skipUnless(os_helper.TESTFN_UNDECODABLE,
@@ -422,11 +423,11 @@ def test_undecodable_filename(self):
         with open(os.path.join(self.tempdir, filename), 'wb') as f:
             f.write(os_helper.TESTFN_UNDECODABLE)
         response = self.request(self.base_url + '/')
-        if sys.platform == 'darwin':
-            # On Mac OS the HFS+ filesystem replaces bytes that aren't valid
-            # UTF-8 into a percent-encoded value.
+        if is_apple:
+            # On Apple platforms the HFS+ filesystem replaces bytes that
+            # aren't valid UTF-8 into a percent-encoded value.
             for name in os.listdir(self.tempdir):
-                if name != 'test': # Ignore a filename created in setUp().
+                if name != 'test':  # Ignore a filename created in setUp().
                     filename = name
                     break
         body = self.check_status_and_reason(response, HTTPStatus.OK)
@@ -697,6 +698,7 @@ def test_html_escape_filename(self):
 
 @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
         "This test can't be run reliably as root (issue #13308).")
+@requires_subprocess()
 class CGIHTTPServerTestCase(BaseTestCase):
     class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler):
         _test_case_self = None  # populated by each setUp() method call.
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 9e28b936e00bd5..73669ecc792776 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -39,11 +39,9 @@
 from test import support
 from test.support.script_helper import (
     assert_python_ok, assert_python_failure, run_python_until_end)
-from test.support import import_helper
-from test.support import os_helper
-from test.support import threading_helper
-from test.support import warnings_helper
-from test.support import skip_if_sanitizer
+from test.support import (
+    import_helper, is_apple, os_helper, skip_if_sanitizer, threading_helper, 
warnings_helper
+)
 from test.support.os_helper import FakePath
 
 import codecs
@@ -606,10 +604,10 @@ def test_raw_bytes_io(self):
         self.read_ops(f, True)
 
     def test_large_file_ops(self):
-        # On Windows and Mac OSX this test consumes large resources; It takes
-        # a long time to build the >2 GiB file and takes >2 GiB of disk space
-        # therefore the resource must be enabled to run this test.
-        if sys.platform[:3] == 'win' or sys.platform == 'darwin':
+        # On Windows and Apple platforms this test consumes large resources; It
+        # takes a long time to build the >2 GiB file and takes >2 GiB of disk
+        # space therefore the resource must be enabled to run this test.
+        if sys.platform[:3] == 'win' or is_apple:
             support.requires(
                 'largefile',
                 'test requires %s bytes and a long time to run' % self.LARGE)
diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py
index 6e17e010e7f355..615568e6af2102 100644
--- a/Lib/test/test_marshal.py
+++ b/Lib/test/test_marshal.py
@@ -1,5 +1,5 @@
 from test import support
-from test.support import os_helper, requires_debug_ranges
+from test.support import is_apple_mobile, os_helper, requires_debug_ranges
 from test.support.script_helper import assert_python_ok
 import array
 import io
@@ -286,7 +286,7 @@ def test_recursion_limit(self):
         #if os.name == 'nt' and support.Py_DEBUG:
         if os.name == 'nt':
             MAX_MARSHAL_STACK_DEPTH = 1000
-        elif sys.platform == 'wasi':
+        elif sys.platform == 'wasi' or is_apple_mobile:
             MAX_MARSHAL_STACK_DEPTH = 1500
         else:
             MAX_MARSHAL_STACK_DEPTH = 2000
diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py
index b89621e08577be..ac759757d24659 100644
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -1,5 +1,5 @@
 from test.support import (
-    requires, _2G, _4G, gc_collect, cpython_only, is_emscripten
+    requires, _2G, _4G, gc_collect, cpython_only, is_emscripten, is_apple,
 )
 from test.support.import_helper import import_module
 from test.support.os_helper import TESTFN, unlink
@@ -1067,7 +1067,7 @@ def tearDown(self):
         unlink(TESTFN)
 
     def _make_test_file(self, num_zeroes, tail):
-        if sys.platform[:3] == 'win' or sys.platform == 'darwin':
+        if sys.platform[:3] == 'win' or is_apple:
             requires('largefile',
                 'test requires %s bytes and a long time to run' % 
str(0x180000000))
         f = open(TESTFN, 'w+b')
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index ed79a2c24ef30b..86af1a8ed8ee15 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -3848,6 +3848,7 @@ def test_does_not_crash(self):
         self.assertGreaterEqual(size.columns, 0)
         self.assertGreaterEqual(size.lines, 0)
 
+    @support.requires_subprocess()
     def test_stty_match(self):
         """Check if stty returns the same results
 
@@ -4577,7 +4578,8 @@ def test_posix_pty_functions(self):
         self.addCleanup(os.close, son_fd)
         self.assertEqual(os.ptsname(mother_fd), os.ttyname(son_fd))
 
-    @unittest.skipUnless(hasattr(os, 'spawnl'), "need os.openpty()")
+    @unittest.skipUnless(hasattr(os, 'spawnl'), "need os.spawnl()")
+    @support.requires_subprocess()
     def test_pipe_spawnl(self):
         # gh-77046: On Windows, os.pipe() file descriptors must be created with
         # _O_NOINHERIT to make them non-inheritable. UCRT has no public API to
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index b2283cff6cb462..2b0795cdad707e 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -779,58 +779,62 @@ def test_pdb_where_command():
     (Pdb) continue
     """
 
-def test_pdb_interact_command():
-    """Test interact command
 
-    >>> g = 0
-    >>> dict_g = {}
+# skip this test if sys.flags.no_site = True;
+# exit() isn't defined unless there's a site module.
+if not sys.flags.no_site:
+    def test_pdb_interact_command():
+        """Test interact command
 
-    >>> def test_function():
-    ...     x = 1
-    ...     lst_local = []
-    ...     import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+        >>> g = 0
+        >>> dict_g = {}
 
-    >>> with PdbTestInput([  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
-    ...     'interact',
-    ...     'x',
-    ...     'g',
-    ...     'x = 2',
-    ...     'g = 3',
-    ...     'dict_g["a"] = True',
-    ...     'lst_local.append(x)',
-    ...     'exit()',
-    ...     'p x',
-    ...     'p g',
-    ...     'p dict_g',
-    ...     'p lst_local',
-    ...     'continue',
-    ... ]):
-    ...    test_function()
-    --Return--
-    > <doctest 
test.test_pdb.test_pdb_interact_command[2]>(4)test_function()->None
-    -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
-    (Pdb) interact
-    *pdb interact start*
-    ... x
-    1
-    ... g
-    0
-    ... x = 2
-    ... g = 3
-    ... dict_g["a"] = True
-    ... lst_local.append(x)
-    ... exit()
-    *exit from pdb interact command*
-    (Pdb) p x
-    1
-    (Pdb) p g
-    0
-    (Pdb) p dict_g
-    {'a': True}
-    (Pdb) p lst_local
-    [2]
-    (Pdb) continue
-    """
+        >>> def test_function():
+        ...     x = 1
+        ...     lst_local = []
+        ...     import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+
+        >>> with PdbTestInput([  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+        ...     'interact',
+        ...     'x',
+        ...     'g',
+        ...     'x = 2',
+        ...     'g = 3',
+        ...     'dict_g["a"] = True',
+        ...     'lst_local.append(x)',
+        ...     'exit()',
+        ...     'p x',
+        ...     'p g',
+        ...     'p dict_g',
+        ...     'p lst_local',
+        ...     'continue',
+        ... ]):
+        ...    test_function()
+        --Return--
+        > <doctest 
test.test_pdb.test_pdb_interact_command[2]>(4)test_function()->None
+        -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+        (Pdb) interact
+        *pdb interact start*
+        ... x
+        1
+        ... g
+        0
+        ... x = 2
+        ... g = 3
+        ... dict_g["a"] = True
+        ... lst_local.append(x)
+        ... exit()
+        *exit from pdb interact command*
+        (Pdb) p x
+        1
+        (Pdb) p g
+        0
+        (Pdb) p dict_g
+        {'a': True}
+        (Pdb) p lst_local
+        [2]
+        (Pdb) continue
+        """
 
 def test_convenience_variables():
     """Test convenience variables
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index 216973350319fe..648e18d0150ef0 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -472,7 +472,8 @@ def test_macos(self):
                   'root:xnu-4570.71.2~1/RELEASE_X86_64'),
                  'x86_64', 'i386')
         arch = ('64bit', '')
-        with mock.patch.object(platform, 'uname', return_value=uname), \
+        with mock.patch.object(sys, "platform", "darwin"), \
+             mock.patch.object(platform, 'uname', return_value=uname), \
              mock.patch.object(platform, 'architecture', return_value=arch):
             for mac_ver, expected_terse, expected in [
                 # darwin: mac_ver() returns empty strings
diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py
index 9c382ace806e0f..72e348fbbdcbc1 100644
--- a/Lib/test/test_posix.py
+++ b/Lib/test/test_posix.py
@@ -1,7 +1,7 @@
 "Test posix functions"
 
 from test import support
-from test.support import import_helper
+from test.support import is_apple
 from test.support import os_helper
 from test.support import warnings_helper
 from test.support.script_helper import assert_python_ok
@@ -781,9 +781,10 @@ def check_stat(uid, gid):
             check_stat(uid, gid)
             self.assertRaises(OSError, chown_func, first_param, 0, -1)
             check_stat(uid, gid)
-            if 0 not in os.getgroups():
-                self.assertRaises(OSError, chown_func, first_param, -1, 0)
-                check_stat(uid, gid)
+            if hasattr(os, 'getgroups'):
+                if 0 not in os.getgroups():
+                    self.assertRaises(OSError, chown_func, first_param, -1, 0)
+                    check_stat(uid, gid)
         # test illegal types
         for t in str, float:
             self.assertRaises(TypeError, chown_func, first_param, t(uid), gid)
@@ -1256,8 +1257,8 @@ def test_sched_priority(self):
         self.assertIsInstance(lo, int)
         self.assertIsInstance(hi, int)
         self.assertGreaterEqual(hi, lo)
-        # OSX evidently just returns 15 without checking the argument.
-        if sys.platform != "darwin":
+        # Apple plaforms return 15 without checking the argument.
+        if not is_apple:
             self.assertRaises(OSError, posix.sched_get_priority_min, -23)
             self.assertRaises(OSError, posix.sched_get_priority_max, -23)
 
@@ -2028,11 +2029,13 @@ def test_dup2(self):
 
 
 @unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn")
[email protected]_subprocess()
 class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin):
     spawn_func = getattr(posix, 'posix_spawn', None)
 
 
 @unittest.skipUnless(hasattr(os, 'posix_spawnp'), "test needs os.posix_spawnp")
[email protected]_subprocess()
 class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin):
     spawn_func = getattr(posix, 'posix_spawnp', None)
 
diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py
index 51e3a46d0df178..3f2bac0155fd9e 100644
--- a/Lib/test/test_pty.py
+++ b/Lib/test/test_pty.py
@@ -1,12 +1,17 @@
-from test.support import verbose, reap_children
-from test.support.os_helper import TESTFN, unlink
+import sys
+import unittest
+from test.support import (
+    is_apple_mobile, is_emscripten, is_wasi, reap_children, verbose
+)
 from test.support.import_helper import import_module
+from test.support.os_helper import TESTFN, unlink
 
-# Skip these tests if termios or fcntl are not available
+# Skip these tests if termios is not available
 import_module('termios')
-# fcntl is a proxy for not being one of the wasm32 platforms even though we
-# don't use this module... a proper check for what crashes those is needed.
-import_module("fcntl")
+
+# Skip tests on WASM platforms, plus iOS/tvOS/watchOS
+if is_apple_mobile or is_emscripten or is_wasi:
+    raise unittest.SkipTest(f"pty tests not required on {sys.platform}")
 
 import errno
 import os
@@ -17,7 +22,6 @@
 import signal
 import socket
 import io # readline
-import unittest
 import warnings
 
 TEST_STRING_1 = b"I wish to buy a fish license.\n"
diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py
index 677349c2bfca93..643775597c56c6 100644
--- a/Lib/test/test_selectors.py
+++ b/Lib/test/test_selectors.py
@@ -6,8 +6,7 @@
 import socket
 import sys
 from test import support
-from test.support import os_helper
-from test.support import socket_helper
+from test.support import is_apple, os_helper, socket_helper
 from time import sleep
 import unittest
 import unittest.mock
@@ -526,7 +525,7 @@ def test_above_fd_setsize(self):
         try:
             fds = s.select()
         except OSError as e:
-            if e.errno == errno.EINVAL and sys.platform == 'darwin':
+            if e.errno == errno.EINVAL and is_apple:
                 # unexplainable errors on macOS don't need to fail the test
                 self.skipTest("Invalid argument error calling poll()")
             raise
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index 8edd75e9907ec0..d96dad4eb9475d 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -2148,6 +2148,7 @@ def check_chown(path, uid=None, gid=None):
             check_chown(dirname, uid, gid)
 
 
[email protected]_subprocess()
 class TestWhich(BaseTest, unittest.TestCase):
 
     def setUp(self):
@@ -3181,6 +3182,7 @@ def test_bad_environ(self):
         self.assertGreaterEqual(size.lines, 0)
 
     @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty")
+    @support.requires_subprocess()
     @unittest.skipUnless(hasattr(os, 'get_terminal_size'),
                          'need os.get_terminal_size()')
     def test_stty_match(self):
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py
index 637a0ca3b36972..61fb047caf6dab 100644
--- a/Lib/test/test_signal.py
+++ b/Lib/test/test_signal.py
@@ -13,9 +13,10 @@
 import time
 import unittest
 from test import support
-from test.support import os_helper
+from test.support import (
+    is_apple, is_apple_mobile, os_helper, threading_helper
+)
 from test.support.script_helper import assert_python_ok, spawn_python
-from test.support import threading_helper
 try:
     import _testcapi
 except ImportError:
@@ -832,7 +833,7 @@ def test_itimer_real(self):
         self.assertEqual(self.hndl_called, True)
 
     # Issue 3864, unknown if this affects earlier versions of freebsd also
-    @unittest.skipIf(sys.platform in ('netbsd5',),
+    @unittest.skipIf(sys.platform in ('netbsd5',) or is_apple_mobile,
         'itimer not reliable (does not mix well with threading) on some BSDs.')
     def test_itimer_virtual(self):
         self.itimer = signal.ITIMER_VIRTUAL
@@ -1344,7 +1345,7 @@ def handler(signum, frame):
         # Python handler
         self.assertEqual(len(sigs), N, "Some signals were lost")
 
-    @unittest.skipIf(sys.platform == "darwin", "crashes due to system bug 
(FB13453490)")
+    @unittest.skipIf(is_apple, "crashes due to system bug (FB13453490)")
     @unittest.skipUnless(hasattr(signal, "SIGUSR1"),
                          "test needs SIGUSR1")
     @threading_helper.requires_working_threading()
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 231448c75f01db..17964234992062 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -1,9 +1,8 @@
 import unittest
 from test import support
-from test.support import os_helper
-from test.support import socket_helper
-from test.support import threading_helper
-from test.support import refleak_helper
+from test.support import (
+    is_apple, os_helper, refleak_helper, socket_helper, threading_helper
+)
 
 import _thread as thread
 import array
@@ -1196,8 +1195,11 @@ def testGetServBy(self):
         # Find one service that exists, then check all the related interfaces.
         # I've ordered this by protocols that have both a tcp and udp
         # protocol, at least for modern Linuxes.
-        if (sys.platform.startswith(('freebsd', 'netbsd', 'gnukfreebsd'))
-            or sys.platform in ('linux', 'darwin')):
+        if (
+            sys.platform.startswith(('freebsd', 'netbsd', 'gnukfreebsd'))
+            or sys.platform == 'linux'
+            or is_apple
+        ):
             # avoid the 'echo' service on this platform, as there is an
             # assumption breaking non-standard port/protocol entry
             services = ('daytime', 'qotd', 'domain')
@@ -3708,7 +3710,7 @@ def testFDPassCMSG_LEN(self):
     def _testFDPassCMSG_LEN(self):
         self.createAndSendFDs(1)
 
-    @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958")
+    @unittest.skipIf(is_apple, "skipping, see issue #12958")
     @unittest.skipIf(AIX, "skipping, see issue #22397")
     @requireAttrs(socket, "CMSG_SPACE")
     def testFDPassSeparate(self):
@@ -3719,7 +3721,7 @@ def testFDPassSeparate(self):
                              maxcmsgs=2)
 
     @testFDPassSeparate.client_skip
-    @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958")
+    @unittest.skipIf(is_apple, "skipping, see issue #12958")
     @unittest.skipIf(AIX, "skipping, see issue #22397")
     def _testFDPassSeparate(self):
         fd0, fd1 = self.newFDs(2)
@@ -3732,7 +3734,7 @@ def _testFDPassSeparate(self):
                                           array.array("i", [fd1]))]),
             len(MSG))
 
-    @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958")
+    @unittest.skipIf(is_apple, "skipping, see issue #12958")
     @unittest.skipIf(AIX, "skipping, see issue #22397")
     @requireAttrs(socket, "CMSG_SPACE")
     def testFDPassSeparateMinSpace(self):
@@ -3746,7 +3748,7 @@ def testFDPassSeparateMinSpace(self):
                              maxcmsgs=2, ignoreflags=socket.MSG_CTRUNC)
 
     @testFDPassSeparateMinSpace.client_skip
-    @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958")
+    @unittest.skipIf(is_apple, "skipping, see issue #12958")
     @unittest.skipIf(AIX, "skipping, see issue #22397")
     def _testFDPassSeparateMinSpace(self):
         fd0, fd1 = self.newFDs(2)
@@ -3770,7 +3772,7 @@ def sendAncillaryIfPossible(self, msg, ancdata):
             nbytes = self.sendmsgToServer([msg])
         self.assertEqual(nbytes, len(msg))
 
-    @unittest.skipIf(sys.platform == "darwin", "see issue #24725")
+    @unittest.skipIf(is_apple, "skipping, see issue #12958")
     def testFDPassEmpty(self):
         # Try to pass an empty FD array.  Can receive either no array
         # or an empty array.
diff --git a/Lib/test/test_sqlite3/test_dbapi.py 
b/Lib/test/test_sqlite3/test_dbapi.py
index f3efe0f52f4fd7..588272448bbfda 100644
--- a/Lib/test/test_sqlite3/test_dbapi.py
+++ b/Lib/test/test_sqlite3/test_dbapi.py
@@ -31,7 +31,7 @@
 
 from test.support import (
     SHORT_TIMEOUT, check_disallow_instantiation, requires_subprocess,
-    is_emscripten, is_wasi
+    is_apple, is_emscripten, is_wasi
 )
 from test.support import gc_collect
 from test.support import threading_helper
@@ -667,7 +667,7 @@ def test_open_with_path_like_object(self):
             cx.execute(self._sql)
 
     @unittest.skipIf(sys.platform == "win32", "skipped on Windows")
-    @unittest.skipIf(sys.platform == "darwin", "skipped on macOS")
+    @unittest.skipIf(is_apple, "skipped on Apple platforms")
     @unittest.skipIf(is_emscripten or is_wasi, "not supported on 
Emscripten/WASI")
     @unittest.skipUnless(TESTFN_UNDECODABLE, "only works if there are 
undecodable paths")
     def test_open_with_undecodable_path(self):
@@ -713,7 +713,7 @@ def test_open_uri_readonly(self):
                 cx.execute(self._sql)
 
     @unittest.skipIf(sys.platform == "win32", "skipped on Windows")
-    @unittest.skipIf(sys.platform == "darwin", "skipped on macOS")
+    @unittest.skipIf(is_apple, "skipped on Apple platforms")
     @unittest.skipIf(is_emscripten or is_wasi, "not supported on 
Emscripten/WASI")
     @unittest.skipUnless(TESTFN_UNDECODABLE, "only works if there are 
undecodable paths")
     def test_open_undecodable_uri(self):
diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py
index d6b6dd6e741700..49013a4bcd8af6 100644
--- a/Lib/test/test_stat.py
+++ b/Lib/test/test_stat.py
@@ -2,8 +2,7 @@
 import os
 import socket
 import sys
-from test.support import os_helper
-from test.support import socket_helper
+from test.support import is_apple, os_helper, socket_helper
 from test.support.import_helper import import_fresh_module
 from test.support.os_helper import TESTFN
 
@@ -247,7 +246,7 @@ def test_flags_consistent(self):
         for flag in self.file_flags:
             if flag.startswith("UF"):
                 self.assertTrue(getattr(self.statmod, flag) & 
self.statmod.UF_SETTABLE, f"{flag} not in UF_SETTABLE")
-            elif sys.platform == 'darwin' and self.statmod is c_stat and flag 
== 'SF_DATALESS':
+            elif is_apple and self.statmod is c_stat and flag == 'SF_DATALESS':
                 self.assertTrue(self.statmod.SF_DATALESS & 
self.statmod.SF_SYNTHETIC, "SF_DATALESS not in SF_SYNTHETIC")
                 self.assertFalse(self.statmod.SF_DATALESS & 
self.statmod.SF_SETTABLE, "SF_DATALESS in SF_SETTABLE")
             else:
diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py
index ae6e192a7ab6ef..125f40227118f6 100644
--- a/Lib/test/test_sys_settrace.py
+++ b/Lib/test/test_sys_settrace.py
@@ -7,7 +7,7 @@
 import gc
 from functools import wraps
 import asyncio
-from test.support import import_helper
+from test.support import import_helper, requires_subprocess
 import contextlib
 import os
 import tempfile
@@ -1810,6 +1810,7 @@ def compare_events(self, line_offset, events, 
expected_events):
     def make_tracer():
         return Tracer(trace_opcode_events=True)
 
+    @requires_subprocess()
     def test_trace_opcodes_after_settrace(self):
         """Make sure setting f_trace_opcodes after starting trace works even
         if it's the first time f_trace_opcodes is being set. GH-103615"""
diff --git a/Lib/test/test_unicode_file_functions.py 
b/Lib/test/test_unicode_file_functions.py
index 47619c8807bafe..25c16e3a0b7e43 100644
--- a/Lib/test/test_unicode_file_functions.py
+++ b/Lib/test/test_unicode_file_functions.py
@@ -5,7 +5,7 @@
 import unittest
 import warnings
 from unicodedata import normalize
-from test.support import os_helper
+from test.support import is_apple, os_helper
 from test import support
 
 
@@ -23,13 +23,13 @@
     '10_\u1fee\u1ffd',
     ]
 
-# Mac OS X decomposes Unicode names, using Normal Form D.
+# Apple platforms decompose Unicode names, using Normal Form D.
 # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html
 # "However, most volume formats do not follow the exact specification for
 # these normal forms.  For example, HFS Plus uses a variant of Normal Form D
 # in which U+2000 through U+2FFF, U+F900 through U+FAFF, and U+2F800 through
 # U+2FAFF are not decomposed."
-if sys.platform != 'darwin':
+if not is_apple:
     filenames.extend([
         # Specific code points: NFC(fn), NFD(fn), NFKC(fn) and NFKD(fn) all 
different
         '11_\u0385\u03d3\u03d4',
@@ -119,11 +119,11 @@ def test_open(self):
             os.stat(name)
             self._apply_failure(os.listdir, name, self._listdir_failure)
 
-    # Skip the test on darwin, because darwin does normalize the filename to
+    # Skip the test on Apple platforms, because they don't normalize the 
filename to
     # NFD (a variant of Unicode NFD form). Normalize the filename to NFC, NFKC,
     # NFKD in Python is useless, because darwin will normalize it later and so
     # open(), os.stat(), etc. don't raise any exception.
-    @unittest.skipIf(sys.platform == 'darwin', 'irrelevant test on Mac OS X')
+    @unittest.skipIf(is_apple, 'irrelevant test on Apple platforms')
     @unittest.skipIf(
         support.is_emscripten or support.is_wasi,
         "test fails on Emscripten/WASI when host platform is macOS."
@@ -142,10 +142,10 @@ def test_normalize(self):
             self._apply_failure(os.remove, name)
             self._apply_failure(os.listdir, name)
 
-    # Skip the test on darwin, because darwin uses a normalization different
+    # Skip the test on Apple platforms, because they use a normalization 
different
     # than Python NFD normalization: filenames are different even if we use
     # Python NFD normalization.
-    @unittest.skipIf(sys.platform == 'darwin', 'irrelevant test on Mac OS X')
+    @unittest.skipIf(is_apple, 'irrelevant test on Apple platforms')
     def test_listdir(self):
         sf0 = set(self.files)
         with warnings.catch_warnings():
diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py
index 99c9e24994732f..fa528a675892b5 100644
--- a/Lib/test/test_urllib2.py
+++ b/Lib/test/test_urllib2.py
@@ -1,6 +1,7 @@
 import unittest
 from test import support
 from test.support import os_helper
+from test.support import requires_subprocess
 from test.support import warnings_helper
 from test import test_urllib
 from unittest import mock
@@ -998,6 +999,7 @@ def test_http_body_fileobj(self):
 
         file_obj.close()
 
+    @requires_subprocess()
     def test_http_body_pipe(self):
         # A file reading from a pipe.
         # A pipe cannot be seek'ed.  There is no way to determine the
diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
index 6dda00efd7bbb6..ba31beb81e80b0 100644
--- a/Lib/test/test_venv.py
+++ b/Lib/test/test_venv.py
@@ -19,8 +19,8 @@
 import tempfile
 from test.support import (captured_stdout, captured_stderr,
                           skip_if_broken_multiprocessing_synchronize, verbose,
-                          requires_subprocess, is_emscripten, is_wasi,
-                          requires_venv_with_pip, TEST_HOME_DIR,
+                          requires_subprocess, is_apple_mobile, is_emscripten,
+                          is_wasi, requires_venv_with_pip, TEST_HOME_DIR,
                           requires_resource, copy_python_src_ignore)
 from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree)
 import unittest
@@ -39,8 +39,10 @@
     or sys._base_executable != sys.executable,
     'cannot run venv.create from within a venv on this platform')
 
-if is_emscripten or is_wasi:
-    raise unittest.SkipTest("venv is not available on Emscripten/WASI.")
+# Skip tests on WASM platforms, plus iOS/tvOS/watchOS
+if is_apple_mobile or is_emscripten or is_wasi:
+    raise unittest.SkipTest(f"venv tests not required on {sys.platform}")
+
 
 @requires_subprocess()
 def check_output(cmd, encoding=None):
diff --git 
a/Misc/NEWS.d/next/Tests/2024-02-02-13-18-55.gh-issue-114099.C_ycWg.rst 
b/Misc/NEWS.d/next/Tests/2024-02-02-13-18-55.gh-issue-114099.C_ycWg.rst
new file mode 100644
index 00000000000000..487cd5062fc75b
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2024-02-02-13-18-55.gh-issue-114099.C_ycWg.rst
@@ -0,0 +1 @@
+Added test exclusions required to run the test suite on iOS.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to