From 3730a2a95eec8707de81480cfd8def62a35227f8 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Sun, 15 May 2022 10:41:08 +0800 Subject: [PATCH 01/37] Improved subprocess handling. --- README.rst | 1 - patch/Python/Python.patch | 68 ++++---------------------------- patch/Python/Setup.embedded | 1 + patch/Python/Setup.macOS | 1 - tests/testbed/src/testbed/app.py | 4 +- 5 files changed, 10 insertions(+), 65 deletions(-) diff --git a/README.rst b/README.rst index 835a6288..86d3639e 100644 --- a/README.rst +++ b/README.rst @@ -24,7 +24,6 @@ The following standard library modules are available on macOS, but not the other Apple platforms: * curses * posixshmem - * posixsubprocess The binaries support x86_64 and arm64 for macOS; arm64 for iOS and appleTV devices; and arm64_32 for watchOS. It also supports device simulators on both diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index d81fb822..12737b70 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -699,7 +699,7 @@ index 49bcaea78d..891356e54d 100644 + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) diff --git a/Lib/os.py b/Lib/os.py -index d26cfc9993..b4db279e00 100644 +index d26cfc9993..ae6629424e 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -36,7 +36,7 @@ @@ -716,7 +716,7 @@ index d26cfc9993..b4db279e00 100644 del _fscodec + -+if sys.platform in ('iOS', 'tvos', 'watchos'): ++if sys.platform in ('ios', 'tvos', 'watchos'): + allows_subprocesses = False +else: + allows_subprocesses = True @@ -834,74 +834,20 @@ index 939893eb5e..8c550ed95a 100644 return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' diff --git a/Lib/subprocess.py b/Lib/subprocess.py -index ccb46a6337..6193bcbeaf 100644 +index ccb46a6337..45fefd522d 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py -@@ -71,9 +71,13 @@ - _mswindows = True - except ModuleNotFoundError: - _mswindows = False -- import _posixsubprocess -- import select -- import selectors -+ try: -+ import _posixsubprocess -+ import select -+ import selectors -+ except ModuleNotFoundError: -+ # iOS *has* subprocesses, but doesn't support them. -+ _posixsubprocess = None - else: - from _winapi import (CREATE_NEW_CONSOLE, CREATE_NEW_PROCESS_GROUP, - STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, -@@ -208,7 +212,7 @@ - return "%s(%d)" % (self.__class__.__name__, int(self)) - - __del__ = Close --else: -+elif _posixsubprocess: - # When select or poll has indicated that the file is writable, - # we can write up to _PIPE_BUF bytes without risk of blocking. - # POSIX defines PIPE_BUF as >= 512. -@@ -236,7 +240,7 @@ - - def _cleanup(): - pass --else: -+elif _posixsubprocess: - # This lists holds Popen instances for which the underlying process had not - # exited at the time its __del__ method got called: those processes are - # wait()ed for synchronously from _cleanup() when a new Popen object is -@@ -255,6 +259,9 @@ - # This can happen if two threads create a new Popen instance. - # It's harmless that it was already removed, so ignore. - pass -+else: -+ def _cleanup(): -+ pass - - PIPE = -1 - STDOUT = -2 -@@ -656,7 +663,7 @@ - Prefer an implementation which can use vfork() in some cases for best - performance. - """ -- if _mswindows or not hasattr(os, 'posix_spawn'): -+ if _mswindows or _posixsubprocess is None or not hasattr(os, 'posix_spawn'): - # os.posix_spawn() is not available - return False - -@@ -759,6 +766,9 @@ +@@ -759,6 +759,9 @@ pass_fds=(), *, user=None, group=None, extra_groups=None, encoding=None, errors=None, text=None, umask=-1, pipesize=-1): """Create new Popen instance.""" -+ if not _mswindows and _posixsubprocess is None: ++ if not os.allows_subprocesses: + raise RuntimeError(f"Subprocesses are not supported on {sys.platform}") + _cleanup() # Held while anything is calling waitpid before returncode has been # updated to prevent clobbering returncode if wait() or poll() are -@@ -1855,7 +1865,7 @@ +@@ -1855,7 +1858,7 @@ else: self.returncode = waitstatus_to_exitcode(sts) @@ -910,7 +856,7 @@ index ccb46a6337..6193bcbeaf 100644 _WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD): """Check if child process has terminated. Returns returncode attribute. -@@ -1864,6 +1874,8 @@ +@@ -1864,6 +1867,8 @@ outside of the local scope (nor can any methods it calls). """ diff --git a/patch/Python/Setup.embedded b/patch/Python/Setup.embedded index a028e359..9b764c64 100644 --- a/patch/Python/Setup.embedded +++ b/patch/Python/Setup.embedded @@ -36,6 +36,7 @@ _multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c _opcode _opcode.c _operator _operator.c _pickle _pickle.c -DPy_BUILD_CORE_MODULE +_posixsubprocess _posixsubprocess.c _queue _queuemodule.c _random _randommodule.c -DPy_BUILD_CORE_MODULE _sha1 sha1module.c diff --git a/patch/Python/Setup.macOS b/patch/Python/Setup.macOS index 872218d1..e5e9387a 100644 --- a/patch/Python/Setup.macOS +++ b/patch/Python/Setup.macOS @@ -37,7 +37,6 @@ _decimal _decimal/_decimal.c \ -DCONFIG_64=1 -DANSI=1 -DHAVE_UINT128_T=1 _posixshmem -I$(srcdir)/Modules/_multiprocessing _multiprocessing/posixshmem.c -_posixsubprocess _posixsubprocess.c _scproxy _scproxy.c -framework SystemConfiguration -framework CoreFoundation diff --git a/tests/testbed/src/testbed/app.py b/tests/testbed/src/testbed/app.py index b6caf97d..0f641091 100644 --- a/tests/testbed/src/testbed/app.py +++ b/tests/testbed/src/testbed/app.py @@ -48,11 +48,11 @@ def main(): print(f"{sys_platform}: {test.__name__}", end="...") test() print(" ok") - except Exception as e: + except Exception: failures += 1 print(" FAILED!") print("-" * 80) - traceback.print_exception(e) + traceback.print_exc() print("-" * 80) print("=" * 80) From 9548669136d4863989521e7fc7c56dbd206d94b8 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 28 Jul 2022 14:26:00 +0800 Subject: [PATCH 02/37] Update patch to 3.11.0b5. --- Makefile | 4 +- patch/Python/Python.patch | 219 ++++++++++++++++++-------------------- 2 files changed, 107 insertions(+), 116 deletions(-) diff --git a/Makefile b/Makefile index 006eb1dc..e1596357 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ BUILD_NUMBER=custom # PYTHON_VERSION is the full version number (e.g., 3.10.0b3) # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.11.0b1 +PYTHON_VERSION=3.11.0b5 PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) @@ -48,7 +48,7 @@ BZIP2_VERSION=1.0.8 XZ_VERSION=5.2.5 OPENSSL_VERSION_NUMBER=1.1.1 -OPENSSL_REVISION=o +OPENSSL_REVISION=q OPENSSL_VERSION=$(OPENSSL_VERSION_NUMBER)$(OPENSSL_REVISION) LIBFFI_VERSION=3.4.2 diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 60158d5f..98830ef5 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,5 +1,5 @@ diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py -index f9d27cb89d..da172b345a 100644 +index 9c39179d2a..459e0f476f 100644 --- a/Lib/ctypes/test/test_as_parameter.py +++ b/Lib/ctypes/test/test_as_parameter.py @@ -1,9 +1,11 @@ @@ -29,7 +29,7 @@ index 66acd62e68..95216920e6 100644 ##for n in "ABCDEFGHIMNOPQRS": diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py -index 1099cf9a69..e9caf8032d 100644 +index 8f95a24443..58a80e3dbb 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -1,4 +1,5 @@ @@ -38,7 +38,7 @@ index 1099cf9a69..e9caf8032d 100644 import unittest from test import support -@@ -161,7 +162,7 @@ +@@ -164,7 +165,7 @@ def test_integrate(self): # Derived from some then non-working code, posted by David Foster @@ -47,7 +47,7 @@ index 1099cf9a69..e9caf8032d 100644 # The function prototype called by 'integrate': double func(double); CALLBACK = CFUNCTYPE(c_double, c_double) -@@ -212,7 +213,7 @@ +@@ -215,7 +216,7 @@ def test_callback_register_int(self): # Issue #8275: buggy handling of callback args under Win64 # NOTE: should be run on release builds as well @@ -56,7 +56,7 @@ index 1099cf9a69..e9caf8032d 100644 CALLBACK = CFUNCTYPE(c_int, c_int, c_int, c_int, c_int, c_int) # All this function does is call the callback with its args squared func = dll._testfunc_cbk_reg_int -@@ -228,7 +229,7 @@ +@@ -231,7 +232,7 @@ def test_callback_register_double(self): # Issue #8275: buggy handling of callback args under Win64 # NOTE: should be run on release builds as well @@ -66,7 +66,7 @@ index 1099cf9a69..e9caf8032d 100644 c_double, c_double) # All this function does is call the callback with its args squared diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py -index ac2240fa19..b08b5f7581 100644 +index 09b06840bf..613d02e1ef 100644 --- a/Lib/ctypes/test/test_cfuncs.py +++ b/Lib/ctypes/test/test_cfuncs.py @@ -1,6 +1,7 @@ @@ -86,7 +86,7 @@ index ac2240fa19..b08b5f7581 100644 def S(self): return c_longlong.in_dll(self._dll, "last_tf_arg_s").value -@@ -206,7 +207,7 @@ +@@ -212,7 +213,7 @@ @need_symbol('WinDLL') class stdcallCFunctions(CFunctions): @@ -135,7 +135,7 @@ index e0b9b54e97..1c05dcc22c 100644 class CFuncPtrTestCase(unittest.TestCase): def test_basic(self): diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py -index f9e92e1cc6..0e2255b62e 100644 +index fc571700ce..b3c7a1ead1 100644 --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -7,6 +7,7 @@ @@ -541,7 +541,7 @@ index 2ce5c5b64d..6e10a0c4a5 100644 import pwd os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py -index 5f67226adf..96f771fbe6 100644 +index f603a89f7f..faea51d1b5 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -52,7 +52,7 @@ @@ -554,10 +554,10 @@ index 5f67226adf..96f771fbe6 100644 + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) diff --git a/Lib/platform.py b/Lib/platform.py -index 3f3f25a2c9..deb4165665 100755 +index c272c407c7..e76bc7127f 100755 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -447,6 +447,47 @@ +@@ -451,6 +451,47 @@ # If that also doesn't work return the default values return release, versioninfo, machine @@ -605,7 +605,7 @@ index 3f3f25a2c9..deb4165665 100755 def _java_getprop(name, default): from java.lang import System -@@ -603,7 +644,7 @@ +@@ -607,7 +648,7 @@ default in case the command should fail. """ @@ -614,7 +614,7 @@ index 3f3f25a2c9..deb4165665 100755 # XXX Others too ? return default -@@ -745,6 +786,13 @@ +@@ -749,6 +790,13 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' @@ -628,7 +628,7 @@ index 3f3f25a2c9..deb4165665 100755 def from_subprocess(): """ Fall back to `uname -p` -@@ -1208,11 +1256,13 @@ +@@ -1212,11 +1260,13 @@ system, release, version = system_alias(system, release, version) if system == 'Darwin': @@ -648,7 +648,7 @@ index 3f3f25a2c9..deb4165665 100755 if system == 'Windows': # MS platforms diff --git a/Lib/site.py b/Lib/site.py -index b11cd48e69..3c0fb81ccb 100644 +index 69670d9d7f..8287267e64 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -294,6 +294,9 @@ @@ -662,28 +662,21 @@ index b11cd48e69..3c0fb81ccb 100644 return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' diff --git a/Lib/subprocess.py b/Lib/subprocess.py -index 6e61cc2e5e..994e2d0518 100644 +index 7ae8df154b..4c3121de39 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py -@@ -97,7 +97,7 @@ - "CREATE_NO_WINDOW", "DETACHED_PROCESS", - "CREATE_DEFAULT_ERROR_MODE", "CREATE_BREAKAWAY_FROM_JOB"]) +@@ -74,8 +74,8 @@ else: -- if sys.platform in {"emscripten", "wasi"}: -+ if sys.platform in {"emscripten", "wasi", "ios", "tvos", "watchos"}: - def _fork_exec(*args, **kwargs): - raise OSError( - errno.ENOTSUP, f"{sys.platform} does not support processes." -@@ -1896,7 +1896,7 @@ - else: - self.returncode = waitstatus_to_exitcode(sts) - -- def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid, -+ def _internal_poll(self, _deadstate=None, _waitpid=None, - _WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD): - """Check if child process has terminated. Returns returncode - attribute. -@@ -1905,6 +1905,8 @@ + _mswindows = True + +-# wasm32-emscripten and wasm32-wasi do not support processes +-_can_fork_exec = sys.platform not in {"emscripten", "wasi"} ++# some platforms do not support processes ++_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos"} + + if _mswindows: + import _winapi +@@ -1921,6 +1921,8 @@ outside of the local scope (nor can any methods it calls). """ @@ -693,7 +686,7 @@ index 6e61cc2e5e..994e2d0518 100644 if not self._waitpid_lock.acquire(False): # Something else is busy calling waitpid. Don't allow two diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py -index e21b7303fe..026934bb34 100644 +index ebe3711827..6d20f3c2d2 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -95,6 +95,33 @@ @@ -730,7 +723,7 @@ index e21b7303fe..026934bb34 100644 } # For the OS-native venv scheme, we essentially provide an alias: -@@ -282,12 +309,19 @@ +@@ -283,12 +310,19 @@ 'home': 'posix_home', 'user': 'nt_user', } @@ -751,7 +744,7 @@ index e21b7303fe..026934bb34 100644 'prefix': 'posix_prefix', 'home': 'posix_home', diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index 3b2f33979d..86e7fb2d47 100644 +index 39b3530905..ef1fa18624 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -47,7 +47,7 @@ @@ -763,7 +756,7 @@ index 3b2f33979d..86e7fb2d47 100644 "check_impl_detail", "unix_shell", "setswitchinterval", # network "open_urlresource", -@@ -500,7 +500,7 @@ +@@ -507,7 +507,7 @@ is_android = hasattr(sys, 'getandroidapilevel') @@ -772,7 +765,7 @@ index 3b2f33979d..86e7fb2d47 100644 unix_shell = '/system/bin/sh' if is_android else '/bin/sh' else: unix_shell = None -@@ -510,12 +510,25 @@ +@@ -517,12 +517,25 @@ is_emscripten = sys.platform == "emscripten" is_wasi = sys.platform == "wasi" @@ -952,7 +945,7 @@ index fc8c39365f..00e988fdcc 100644 off_t = 'l' pid_t = 'i' diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py -index 1f041aa121..3af807f6f9 100644 +index a937258069..bf011b0662 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -401,7 +401,7 @@ @@ -965,7 +958,7 @@ index 1f041aa121..3af807f6f9 100644 # UTF-8 into a percent-encoded value. for name in os.listdir(self.tempdir): diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py -index b6663a4484..fbd553a629 100644 +index 1d5b6e7a5d..6fd6a543bf 100644 --- a/Lib/test/test_importlib/extension/test_finder.py +++ b/Lib/test/test_importlib/extension/test_finder.py @@ -2,10 +2,13 @@ @@ -974,7 +967,7 @@ index b6663a4484..fbd553a629 100644 +import sys import unittest - import warnings + import sys +@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), @@ -983,7 +976,7 @@ index b6663a4484..fbd553a629 100644 """Test the finder for extension modules.""" diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py -index 5080009bee..9cf9fb3d8a 100644 +index 8570c6bc90..7de327ebf3 100644 --- a/Lib/test/test_importlib/extension/test_loader.py +++ b/Lib/test/test_importlib/extension/test_loader.py @@ -13,6 +13,8 @@ @@ -995,7 +988,7 @@ index 5080009bee..9cf9fb3d8a 100644 class LoaderTests(abc.LoaderTests): """Test load_module() for extension modules.""" -@@ -90,6 +92,9 @@ +@@ -94,6 +96,9 @@ Source_LoaderTests ) = util.test_both(LoaderTests, machinery=machinery) @@ -1006,10 +999,10 @@ index 5080009bee..9cf9fb3d8a 100644 # Test loading extension modules with multi-phase initialization (PEP 489). diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py -index 5528c461e5..42fcfe8c64 100644 +index daccbae5b4..7158b41fef 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py -@@ -608,7 +608,7 @@ +@@ -609,7 +609,7 @@ # 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. @@ -1019,7 +1012,7 @@ index 5528c461e5..42fcfe8c64 100644 'largefile', 'test requires %s bytes and a long time to run' % self.LARGE) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py -index 5d4ddedd05..b119a43d8e 100644 +index 71926a5432..2918759262 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1787,9 +1787,21 @@ @@ -1063,19 +1056,17 @@ index 5d4ddedd05..b119a43d8e 100644 """Test for SysLogHandler with Unix sockets.""" diff --git a/Lib/test/test_mailcap.py b/Lib/test/test_mailcap.py -index 97a8fac6e0..63274a7b52 100644 +index 8185f4a780..510ab8d3dd 100644 --- a/Lib/test/test_mailcap.py +++ b/Lib/test/test_mailcap.py -@@ -219,7 +219,8 @@ +@@ -223,7 +223,6 @@ ] self._run_cases(cases) - @unittest.skipUnless(os.name == "posix", "Requires 'test' command on system") -+ @unittest.skipUnless(os.name == "posix" and sys.platform not in ('ios', 'tvos', 'watchos'), -+ "Requires 'test' command on system") @unittest.skipIf(sys.platform == "vxworks", "'test' command is not supported on VxWorks") - def test_test(self): - # findmatch() will automatically check any "test" conditions and skip + @unittest.skipUnless( + test.support.has_subprocess_support, diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index aae86cc257..bb48a85517 100644 --- a/Lib/test/test_marshal.py @@ -1139,10 +1130,10 @@ index 9b2cd201f3..dcd3a7a241 100644 @unittest.skipUnless(sys.platform == 'darwin', "OSX only test") def test_mac_ver_with_fork(self): diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py -index f44b8d0403..50a9bfa5b2 100644 +index ae25ef5588..8f2b1ffa69 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py -@@ -75,12 +75,19 @@ +@@ -69,12 +69,19 @@ "getpid", "getpgrp", "getppid", "getuid", "sync", ] @@ -1164,7 +1155,7 @@ index f44b8d0403..50a9bfa5b2 100644 @unittest.skipUnless(hasattr(posix, 'getresuid'), 'test needs posix.getresuid()') -@@ -780,9 +787,10 @@ +@@ -776,9 +783,10 @@ check_stat(uid, gid) self.assertRaises(OSError, chown_func, first_param, 0, -1) check_stat(uid, gid) @@ -1188,10 +1179,10 @@ index f44b8d0403..50a9bfa5b2 100644 self.assertRaises(OSError, posix.sched_get_priority_max, -23) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py -index 7003386345..55c83ec81e 100644 +index a2c4ab5081..eb427dfd72 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py -@@ -1758,6 +1758,8 @@ +@@ -1776,6 +1776,8 @@ check_chown(dirname, uid, gid) @@ -1200,7 +1191,7 @@ index 7003386345..55c83ec81e 100644 class TestWhich(BaseTest, unittest.TestCase): def setUp(self): -@@ -2625,6 +2627,7 @@ +@@ -2642,6 +2644,7 @@ self.assertGreaterEqual(size.lines, 0) @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty") @@ -1209,10 +1200,10 @@ index 7003386345..55c83ec81e 100644 'need os.get_terminal_size()') def test_stty_match(self): diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 613363722c..ac48352d0f 100755 +index a2938cd834..2d3a5c72fb 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py -@@ -1146,7 +1146,7 @@ +@@ -1144,7 +1144,7 @@ # 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')) @@ -1221,7 +1212,7 @@ index 613363722c..ac48352d0f 100755 # avoid the 'echo' service on this platform, as there is an # assumption breaking non-standard port/protocol entry services = ('daytime', 'qotd', 'domain') -@@ -3541,7 +3541,8 @@ +@@ -3539,7 +3539,8 @@ def _testFDPassCMSG_LEN(self): self.createAndSendFDs(1) @@ -1231,7 +1222,7 @@ index 613363722c..ac48352d0f 100755 @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparate(self): -@@ -3552,7 +3553,8 @@ +@@ -3550,7 +3551,8 @@ maxcmsgs=2) @testFDPassSeparate.client_skip @@ -1241,7 +1232,7 @@ index 613363722c..ac48352d0f 100755 @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparate(self): fd0, fd1 = self.newFDs(2) -@@ -3565,7 +3567,8 @@ +@@ -3563,7 +3565,8 @@ array.array("i", [fd1]))]), len(MSG)) @@ -1251,7 +1242,7 @@ index 613363722c..ac48352d0f 100755 @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparateMinSpace(self): -@@ -3579,7 +3582,8 @@ +@@ -3577,7 +3580,8 @@ maxcmsgs=2, ignoreflags=socket.MSG_CTRUNC) @testFDPassSeparateMinSpace.client_skip @@ -1261,7 +1252,7 @@ index 613363722c..ac48352d0f 100755 @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparateMinSpace(self): fd0, fd1 = self.newFDs(2) -@@ -3603,7 +3607,8 @@ +@@ -3601,7 +3605,8 @@ nbytes = self.sendmsgToServer([msg]) self.assertEqual(nbytes, len(msg)) @@ -1271,7 +1262,7 @@ index 613363722c..ac48352d0f 100755 def testFDPassEmpty(self): # Try to pass an empty FD array. Can receive either no array # or an empty array. -@@ -4423,28 +4428,38 @@ +@@ -4421,28 +4426,38 @@ pass @requireAttrs(socket.socket, "sendmsg") @@ -1361,10 +1352,10 @@ index de2e7305cc..ccf240397d 100644 import distutils.text_file import distutils.unixccompiler diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py -index f2b93706b2..8bcc5ae09f 100644 +index 578ac1db50..daf61113b2 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py -@@ -333,7 +333,7 @@ +@@ -336,7 +336,7 @@ self.assertTrue(os.path.isfile(config_h), config_h) def test_get_scheme_names(self): @@ -1374,10 +1365,10 @@ index f2b93706b2..8bcc5ae09f 100644 wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py -index f7dea136a8..feea7146e6 100644 +index 9c6561c099..f49aafcd9e 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py -@@ -1170,6 +1170,8 @@ +@@ -1171,6 +1171,8 @@ os.set_blocking(r, False) return (r, w) @@ -1386,7 +1377,7 @@ index f7dea136a8..feea7146e6 100644 def test_threads_join(self): # Non-daemon threads should be joined at subinterpreter shutdown # (issue #18808) -@@ -1198,6 +1200,8 @@ +@@ -1199,6 +1201,8 @@ # The thread was joined properly. self.assertEqual(os.read(r, 1), b"x") @@ -1396,29 +1387,29 @@ index f7dea136a8..feea7146e6 100644 # Same as above, but a delay gets introduced after the thread's # Python code returned but before the thread state is deleted. diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py -index d96cf1e6c7..0878f1bdd1 100644 +index 74039f59f7..36dc82f871 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py -@@ -16,7 +16,7 @@ +@@ -19,7 +19,7 @@ import tempfile from test.support import (captured_stdout, captured_stderr, requires_zlib, skip_if_broken_multiprocessing_synchronize, verbose, -- requires_subprocess, is_emscripten) -+ requires_subprocess, is_apple_mobile, is_emscripten) +- requires_subprocess, is_emscripten, is_wasi, ++ requires_subprocess, is_apple_mobile, is_emscripten, is_wasi, + requires_venv_with_pip) from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) import unittest - import venv -@@ -34,7 +34,7 @@ - or sys._base_executable != sys.executable, - 'cannot run venv.create from within a venv on this platform') +@@ -40,6 +40,8 @@ --if is_emscripten: -+if is_emscripten or is_apple_mobile: - raise unittest.SkipTest("venv is not available on Emscripten.") + if is_emscripten or is_wasi: + raise unittest.SkipTest("venv is not available on Emscripten/WASI.") ++if is_apple_mobile: ++ raise unittest.SkipTest("venv is not available on mobile Apple platforms.") @requires_subprocess() + def check_output(cmd, encoding=None): diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py -index 848bf4f76d..ed6969348c 100644 +index f4c11d88c8..fb1ef8b417 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -1166,6 +1166,7 @@ @@ -1628,7 +1619,7 @@ index 9132f13e81..36e00f7bfd 100644 diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c -index 08c40834c4..5c110d7211 100644 +index 3026bb6a34..42f503abe6 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -1,3 +1,7 @@ @@ -1639,7 +1630,7 @@ index 08c40834c4..5c110d7211 100644 #include "Python.h" #include "pycore_initconfig.h" // _PyStatus_ERR #include "pycore_pyerrors.h" // _Py_DumpExtensionModules -@@ -21,6 +25,11 @@ +@@ -19,6 +23,11 @@ # include #endif @@ -1652,10 +1643,10 @@ index 08c40834c4..5c110d7211 100644 and sigaction() SA_ONSTACK */ #if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) diff --git a/Modules/makesetup b/Modules/makesetup -index 9b20e3c9f6..23be532f6a 100755 +index 08303814c8..56d35ab407 100755 --- a/Modules/makesetup +++ b/Modules/makesetup -@@ -166,7 +166,7 @@ +@@ -167,7 +167,7 @@ esac case $arg in -framework) libs="$libs $arg"; skip=libs; @@ -1664,7 +1655,7 @@ index 9b20e3c9f6..23be532f6a 100755 ;; -[IDUCfF]*) cpps="$cpps $arg";; -Xcompiler) skip=cpps;; -@@ -186,6 +186,7 @@ +@@ -187,6 +187,7 @@ *.c++) srcs="$srcs $arg";; *.cxx) srcs="$srcs $arg";; *.cpp) srcs="$srcs $arg";; @@ -1672,7 +1663,7 @@ index 9b20e3c9f6..23be532f6a 100755 \$\(*_CFLAGS\)) cpps="$cpps $arg";; \$\(*_INCLUDES\)) cpps="$cpps $arg";; \$\(*_LIBS\)) libs="$libs $arg";; -@@ -220,7 +221,16 @@ +@@ -221,7 +222,16 @@ done case $doconfig in yes) @@ -1685,12 +1676,12 @@ index 9b20e3c9f6..23be532f6a 100755 + # requires escaping for when this script runs, and escaping for when + # the output is run through sed. + if test "$libs"; then -+ LIBS="$LIBS \\\\\\\\\\$NL\t$libs" ++ LIBS="$LIBS \\\\\\$NL\t$libs" + fi MODS="$MODS $mods" BUILT="$BUILT $mods" ;; -@@ -245,6 +255,7 @@ +@@ -246,6 +256,7 @@ *.C) obj=`basename $src .C`.o; cc='$(CXX)';; *.cxx) obj=`basename $src .cxx`.o; cc='$(CXX)';; *.cpp) obj=`basename $src .cpp`.o; cc='$(CXX)';; @@ -1714,7 +1705,7 @@ index aa93e756c6..fcf3784c2f 100644 sin(pi*x), giving accurate results for all finite x (especially x integral or close to an integer). This is here for use in the diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c -index 4015889441..b9d6633213 100644 +index d7cac2b67f..f7d49ac2c0 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -69,6 +69,8 @@ @@ -1779,7 +1770,7 @@ index 4015889441..b9d6633213 100644 return d; } -@@ -4931,6 +4957,9 @@ +@@ -4935,6 +4961,9 @@ /*[clinic end generated code: output=290fc437dd4f33a0 input=86a58554ba6094af]*/ { long result; @@ -1789,7 +1780,7 @@ index 4015889441..b9d6633213 100644 const char *bytes = PyBytes_AsString(command); if (PySys_Audit("os.system", "(O)", command) < 0) { -@@ -4940,6 +4969,7 @@ +@@ -4944,6 +4973,7 @@ Py_BEGIN_ALLOW_THREADS result = system(bytes); Py_END_ALLOW_THREADS @@ -1797,7 +1788,7 @@ index 4015889441..b9d6633213 100644 return result; } #endif -@@ -13693,6 +13723,7 @@ +@@ -13697,6 +13727,7 @@ int is_symlink; int need_stat; #endif @@ -1805,7 +1796,7 @@ index 4015889441..b9d6633213 100644 #ifdef MS_WINDOWS unsigned long dir_bits; #endif -@@ -13753,6 +13784,7 @@ +@@ -13757,6 +13788,7 @@ #endif return result; @@ -1875,7 +1866,7 @@ index a757380bd0..5a20864a1c 100644 "getpwnam(): name not found: %R", name); } diff --git a/Modules/timemodule.c b/Modules/timemodule.c -index 7475ef344b..2ebafe993b 100644 +index 18f9ddb909..2ed289a92f 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -62,6 +62,11 @@ @@ -3603,10 +3594,10 @@ index d74fb6deac..249b391d71 100755 | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ diff --git a/configure b/configure -index b57c6f3a45..b4f2e1a026 100755 +index 078bb5bef1..107dccb7a5 100755 --- a/configure +++ b/configure -@@ -3814,6 +3814,15 @@ +@@ -3820,6 +3820,15 @@ *-*-cygwin*) ac_sys_system=Cygwin ;; @@ -3622,7 +3613,7 @@ index b57c6f3a45..b4f2e1a026 100755 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -3870,6 +3879,15 @@ +@@ -3876,6 +3885,15 @@ *-*-cygwin*) _host_cpu= ;; @@ -3638,7 +3629,7 @@ index b57c6f3a45..b4f2e1a026 100755 *-*-vxworks*) _host_cpu=$host_cpu ;; -@@ -3948,6 +3966,13 @@ +@@ -3954,6 +3972,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -3652,7 +3643,7 @@ index b57c6f3a45..b4f2e1a026 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -6152,6 +6177,12 @@ +@@ -6191,6 +6216,12 @@ case $ac_sys_system in #( Darwin*) : MULTIARCH="" ;; #( @@ -3665,7 +3656,7 @@ index b57c6f3a45..b4f2e1a026 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -6945,11 +6976,17 @@ +@@ -7048,11 +7079,17 @@ fi if test "$cross_compiling" = yes; then @@ -3688,7 +3679,7 @@ index b57c6f3a45..b4f2e1a026 100755 fi -@@ -14783,6 +14820,10 @@ +@@ -14908,6 +14945,10 @@ then case $ac_sys_system/$ac_sys_release in hp*|HP*) DYNLOADFILE="dynload_hpux.o";; @@ -3699,7 +3690,7 @@ index b57c6f3a45..b4f2e1a026 100755 *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub # out any dynamic loading -@@ -26385,7 +26426,7 @@ +@@ -26543,7 +26584,7 @@ $as_echo "$as_me: creating Modules/Setup.local" >&6;} if test ! -f Modules/Setup.local then @@ -3709,10 +3700,10 @@ index b57c6f3a45..b4f2e1a026 100755 { $as_echo "$as_me:${as_lineno-$LINENO}: creating Makefile" >&5 diff --git a/configure.ac b/configure.ac -index 07b8885f1e..b660ad6475 100644 +index 09f3f902a6..271fd4be6d 100644 --- a/configure.ac +++ b/configure.ac -@@ -500,6 +500,15 @@ +@@ -545,6 +545,15 @@ *-*-cygwin*) ac_sys_system=Cygwin ;; @@ -3728,7 +3719,7 @@ index 07b8885f1e..b660ad6475 100644 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -555,6 +564,15 @@ +@@ -600,6 +609,15 @@ *-*-cygwin*) _host_cpu= ;; @@ -3744,7 +3735,7 @@ index 07b8885f1e..b660ad6475 100644 *-*-vxworks*) _host_cpu=$host_cpu ;; -@@ -630,6 +648,13 @@ +@@ -675,6 +693,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -3758,7 +3749,7 @@ index 07b8885f1e..b660ad6475 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -1011,6 +1036,9 @@ +@@ -1085,6 +1110,9 @@ AC_MSG_CHECKING([for multiarch]) AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], @@ -3768,7 +3759,7 @@ index 07b8885f1e..b660ad6475 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1472,11 +1500,17 @@ +@@ -1591,11 +1619,17 @@ AC_CHECK_TOOLS([READELF], [readelf], [:]) if test "$cross_compiling" = yes; then @@ -3791,7 +3782,7 @@ index 07b8885f1e..b660ad6475 100644 fi AC_SUBST(READELF) -@@ -4362,6 +4396,10 @@ +@@ -4501,6 +4535,10 @@ then case $ac_sys_system/$ac_sys_release in hp*|HP*) DYNLOADFILE="dynload_hpux.o";; @@ -3802,7 +3793,7 @@ index 07b8885f1e..b660ad6475 100644 *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub # out any dynamic loading -@@ -6919,7 +6957,7 @@ +@@ -7049,7 +7087,7 @@ AC_MSG_NOTICE([creating Modules/Setup.local]) if test ! -f Modules/Setup.local then From eb454970045747c19b7c608626e9fcac07332921 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 29 Jul 2022 10:18:29 +0800 Subject: [PATCH 03/37] Fixes #149 -- Isolate the path to the bare minimum. This removes the potential influence of Homebrew, user-provided Python installs, etc. --- Makefile | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index e1596357..79e8a6cc 100644 --- a/Makefile +++ b/Makefile @@ -106,6 +106,11 @@ MACHINE_SIMPLE-arm64_32=arm # The architecture of the machine doing the build HOST_ARCH=$(shell uname -m) +# Force the path to be minimal. This ensures that anything in the user environment +# (in particular, homebrew and user-provided Python installs) aren't inadvertently +# linked into the support package. +PATH=/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin + # Build for all operating systems all: $(OS_LIST) @@ -131,9 +136,15 @@ downloads: \ update-patch: # Generate a diff from the clone of the python/cpython Github repository - # Requireds patchutils (installable via `brew install patchutils`) + # Requires patchutils (installable via `brew install patchutils`); this + # also means we need to re-introduce homebrew to the path for the filterdiff + # call if [ -z "$(PYTHON_REPO_DIR)" ]; then echo "\n\nPYTHON_REPO_DIR must be set to the root of your Python github checkout\n\n"; fi - cd $(PYTHON_REPO_DIR) && git diff -D v$(PYTHON_VERSION) $(PYTHON_VER) | filterdiff -X $(PROJECT_DIR)/patch/Python/diff.exclude -p 1 --clean > $(PROJECT_DIR)/patch/Python/Python.patch + cd $(PYTHON_REPO_DIR) && \ + git diff -D v$(PYTHON_VERSION) $(PYTHON_VER) \ + | PATH="/usr/local/bin:/opt/homebrew/bin:$(PATH)" filterdiff \ + -X $(PROJECT_DIR)/patch/Python/diff.exclude -p 1 --clean \ + > $(PROJECT_DIR)/patch/Python/Python.patch ########################################################################### # Setup: BZip2 From 2aea01c3574b5086b4c75ab4b2890f0212d25b21 Mon Sep 17 00:00:00 2001 From: Alexis Hildebrandt Date: Mon, 15 Aug 2022 12:25:30 +0200 Subject: [PATCH 04/37] Improve download related targets --- Makefile | 69 ++++++++++++++++++++++---------------------------------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index 79e8a6cc..697ea701 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,8 @@ LIBFFI_VERSION=3.4.2 PRODUCTS=BZip2 XZ OpenSSL libFFI Python OS_LIST=macOS iOS tvOS watchOS +CURL_FLAGS=--fail --location --create-dirs --progress-bar + # macOS targets TARGETS-macOS=macosx.x86_64 macosx.arm64 CFLAGS-macOS=-mmacosx-version-min=10.15 @@ -128,10 +130,10 @@ distclean: clean rm -rf downloads downloads: \ - downloads/bzip2-$(BZIP2_VERSION).tgz \ - downloads/xz-$(XZ_VERSION).tgz \ - downloads/openssl-$(OPENSSL_VERSION).tgz \ - downloads/libffi-$(LIBFFI_VERSION).tgz \ + downloads/bzip2-$(BZIP2_VERSION).tar.gz \ + downloads/xz-$(XZ_VERSION).tar.gz \ + downloads/openssl-$(OPENSSL_VERSION).tar.gz \ + downloads/libffi-$(LIBFFI_VERSION).tar.gz \ downloads/Python-$(PYTHON_VERSION).tgz update-patch: @@ -151,26 +153,20 @@ update-patch: ########################################################################### # Download original BZip2 source code archive. -downloads/bzip2-$(BZIP2_VERSION).tgz: +downloads/bzip2-$(BZIP2_VERSION).tar.gz: @echo ">>> Download BZip2 sources" - mkdir -p downloads - if [ ! -e downloads/bzip2-$(BZIP2_VERSION).tgz ]; then \ - curl --fail -L https://sourceware.org/pub/bzip2/bzip2-$(BZIP2_VERSION).tar.gz \ - -o downloads/bzip2-$(BZIP2_VERSION).tgz; \ - fi + curl $(CURL_FLAGS) -o $@ \ + https://sourceware.org/pub/bzip2/$(notdir $@) ########################################################################### # Setup: XZ (LZMA) ########################################################################### # Download original XZ source code archive. -downloads/xz-$(XZ_VERSION).tgz: +downloads/xz-$(XZ_VERSION).tar.gz: @echo ">>> Download XZ sources" - mkdir -p downloads - if [ ! -e downloads/xz-$(XZ_VERSION).tgz ]; then \ - curl --fail -L http://tukaani.org/xz/xz-$(XZ_VERSION).tar.gz \ - -o downloads/xz-$(XZ_VERSION).tgz; \ - fi + curl $(CURL_FLAGS) -o $@ \ + https://tukaani.org/xz/$(notdir $@) ########################################################################### # Setup: OpenSSL @@ -179,30 +175,22 @@ downloads/xz-$(XZ_VERSION).tgz: ########################################################################### # Download original OpenSSL source code archive. -downloads/openssl-$(OPENSSL_VERSION).tgz: +downloads/openssl-$(OPENSSL_VERSION).tar.gz: @echo ">>> Download OpenSSL sources" - mkdir -p downloads - -if [ ! -e downloads/openssl-$(OPENSSL_VERSION).tgz ]; then \ - curl --fail -L http://openssl.org/source/openssl-$(OPENSSL_VERSION).tar.gz \ - -o downloads/openssl-$(OPENSSL_VERSION).tgz; \ - fi - if [ ! -e downloads/openssl-$(OPENSSL_VERSION).tgz ]; then \ - curl --fail -L http://openssl.org/source/old/$(OPENSSL_VERSION_NUMBER)/openssl-$(OPENSSL_VERSION).tar.gz \ - -o downloads/openssl-$(OPENSSL_VERSION).tgz; \ - fi + curl $(CURL_FLAGS) -o $@ \ + https://openssl.org/source/$(notdir $@) \ + || curl $(CURL_FLAGS) -o $@ \ + https://openssl.org/source/old/$(notdir $@) ########################################################################### # Setup: libFFI ########################################################################### -# Download original XZ source code archive. -downloads/libffi-$(LIBFFI_VERSION).tgz: +# Download original libFFI source code archive. +downloads/libffi-$(LIBFFI_VERSION).tar.gz: @echo ">>> Download libFFI sources" - mkdir -p downloads - if [ ! -e downloads/libffi-$(LIBFFI_VERSION).tgz ]; then \ - curl --fail -L http://github.com/libffi/libffi/releases/download/v$(LIBFFI_VERSION)/libffi-$(LIBFFI_VERSION).tar.gz \ - -o downloads/libffi-$(LIBFFI_VERSION).tgz; \ - fi + curl $(CURL_FLAGS) -o $@ \ + https://github.com/libffi/libffi/releases/download/v$(LIBFFI_VERSION)/$(notdir $@) ########################################################################### # Setup: Python @@ -211,11 +199,8 @@ downloads/libffi-$(LIBFFI_VERSION).tgz: # Download original Python source code archive. downloads/Python-$(PYTHON_VERSION).tgz: @echo ">>> Download Python sources" - mkdir -p downloads - if [ ! -e downloads/Python-$(PYTHON_VERSION).tgz ]; then \ - curl --fail -L https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/Python-$(PYTHON_VERSION).tgz \ - -o downloads/Python-$(PYTHON_VERSION).tgz; \ - fi + curl $(CURL_FLAGS) -o $@ \ + https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/$(notdir $@) ########################################################################### # Build for specified target (from $(TARGETS-*)) @@ -265,10 +250,10 @@ LDFLAGS-$(target)=-arch $$(ARCH-$(target)) -isysroot=$$(SDK_ROOT-$(target)) BZIP2_DIR-$(target)=build/$(os)/bzip2-$(BZIP2_VERSION)-$(target) BZIP2_LIB-$(target)=$$(BZIP2_DIR-$(target))/_install/lib/libbz2.a -$$(BZIP2_DIR-$(target))/Makefile: downloads/bzip2-$(BZIP2_VERSION).tgz +$$(BZIP2_DIR-$(target))/Makefile: downloads/bzip2-$(BZIP2_VERSION).tar.gz @echo ">>> Unpack BZip2 sources for $(target)" mkdir -p $$(BZIP2_DIR-$(target)) - tar zxf downloads/bzip2-$(BZIP2_VERSION).tgz --strip-components 1 -C $$(BZIP2_DIR-$(target)) + tar zxf $$^ --strip-components 1 -C $$(BZIP2_DIR-$(target)) # Touch the makefile to ensure that Make identifies it as up to date. touch $$(BZIP2_DIR-$(target))/Makefile @@ -315,10 +300,10 @@ OPENSSL_DIR-$(target)=build/$(os)/openssl-$(OPENSSL_VERSION)-$(target) OPENSSL_SSL_LIB-$(target)=$$(OPENSSL_DIR-$(target))/_install/lib/libssl.a OPENSSL_CRYPTO_LIB-$(target)=$$(OPENSSL_DIR-$(target))/_install/lib/libcrypto.a -$$(OPENSSL_DIR-$(target))/is_configured: downloads/openssl-$(OPENSSL_VERSION).tgz +$$(OPENSSL_DIR-$(target))/is_configured: downloads/openssl-$(OPENSSL_VERSION).tar.gz @echo ">>> Unpack and configure OpenSSL sources for $(target)" mkdir -p $$(OPENSSL_DIR-$(target)) - tar zxf downloads/openssl-$(OPENSSL_VERSION).tgz --strip-components 1 -C $$(OPENSSL_DIR-$(target)) + tar zxf $$^ --strip-components 1 -C $$(OPENSSL_DIR-$(target)) ifeq ($$(findstring simulator,$$(SDK-$(target))),) # Tweak ui_openssl.c From 9514d46245c6051e9aec09d0e06aa85cc4d7ca32 Mon Sep 17 00:00:00 2001 From: Alexis Hildebrandt Date: Tue, 16 Aug 2022 09:48:06 +0200 Subject: [PATCH 05/37] Use $$< instead of $$^ tar's -f option expects a single archive-file, therefore the name of the first prerequisite ($^) of download related targets should be passed instead of the names of all the prerequisites ($^). --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 697ea701..5238e689 100644 --- a/Makefile +++ b/Makefile @@ -253,7 +253,7 @@ BZIP2_LIB-$(target)=$$(BZIP2_DIR-$(target))/_install/lib/libbz2.a $$(BZIP2_DIR-$(target))/Makefile: downloads/bzip2-$(BZIP2_VERSION).tar.gz @echo ">>> Unpack BZip2 sources for $(target)" mkdir -p $$(BZIP2_DIR-$(target)) - tar zxf $$^ --strip-components 1 -C $$(BZIP2_DIR-$(target)) + tar zxf $$< --strip-components 1 -C $$(BZIP2_DIR-$(target)) # Touch the makefile to ensure that Make identifies it as up to date. touch $$(BZIP2_DIR-$(target))/Makefile @@ -303,7 +303,7 @@ OPENSSL_CRYPTO_LIB-$(target)=$$(OPENSSL_DIR-$(target))/_install/lib/libcrypto.a $$(OPENSSL_DIR-$(target))/is_configured: downloads/openssl-$(OPENSSL_VERSION).tar.gz @echo ">>> Unpack and configure OpenSSL sources for $(target)" mkdir -p $$(OPENSSL_DIR-$(target)) - tar zxf $$^ --strip-components 1 -C $$(OPENSSL_DIR-$(target)) + tar zxf $$< --strip-components 1 -C $$(OPENSSL_DIR-$(target)) ifeq ($$(findstring simulator,$$(SDK-$(target))),) # Tweak ui_openssl.c From 6d1511b7232f87cca4018a9a0451500622496838 Mon Sep 17 00:00:00 2001 From: Alexis Hildebrandt Date: Tue, 16 Aug 2022 09:49:33 +0200 Subject: [PATCH 06/37] Change order of prerequisites so the name of the first prerequisite ($<) can be used as an argument to tar's -f option. --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 5238e689..c15e08e8 100644 --- a/Makefile +++ b/Makefile @@ -405,15 +405,15 @@ PYTHON_LIB-$(target)=$$(PYTHON_DIR-$(target))/_install/lib/libpython$(PYTHON_VER PYCONFIG_H-$(target)=build/$(os)/python/$$(SDK-$(target))/include/python$(PYTHON_VER)/pyconfig-$$(ARCH-$(target)).h $$(PYTHON_DIR-$(target))/Makefile: \ + downloads/Python-$(PYTHON_VERSION).tgz \ $$(BZIP2_XCFRAMEWORK-$(os)) \ $$(XZ_XCFRAMEWORK-$(os)) \ $$(OPENSSL_XCFRAMEWORK-$(os)) \ $$(LIBFFI_XCFRAMEWORK-$(os)) \ $$(PYTHON_XCFRAMEWORK-macOS) \ - downloads/Python-$(PYTHON_VERSION).tgz @echo ">>> Unpack and configure Python for $(target)" mkdir -p $$(PYTHON_DIR-$(target)) - tar zxf downloads/Python-$(PYTHON_VERSION).tgz --strip-components 1 -C $$(PYTHON_DIR-$(target)) + tar zxf $$< --strip-components 1 -C $$(PYTHON_DIR-$(target)) # Apply target Python patches cd $$(PYTHON_DIR-$(target)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch # Generate the embedded module configuration @@ -758,13 +758,13 @@ PYTHON_DIR-$(os)=$$(PYTHON_DIR-$$(firstword $$(TARGETS-$(os)))) PYTHON_LIB-$(os)=$$(PYTHON_LIB-$$(firstword $$(TARGETS-$(os)))) $$(PYTHON_DIR-$(os))/Makefile: \ + downloads/Python-$(PYTHON_VERSION).tgz \ $$(BZIP2_XCFRAMEWORK-$(os)) \ $$(XZ_XCFRAMEWORK-$(os)) \ $$(OPENSSL_XCFRAMEWORK-$(os)) \ - downloads/Python-$(PYTHON_VERSION).tgz @echo ">>> Unpack and configure Python for $(os)" mkdir -p $$(PYTHON_DIR-$(os)) - tar zxf downloads/Python-$(PYTHON_VERSION).tgz --strip-components 1 -C $$(PYTHON_DIR-$(os)) + tar zxf $$< --strip-components 1 -C $$(PYTHON_DIR-$(os)) # Apply target Python patches cd $$(PYTHON_DIR-$(os)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch cat $(PROJECT_DIR)/patch/Python/Setup.embedded \ From fac33f993e7d9038f55d79bd1b61fd32c2742d8d Mon Sep 17 00:00:00 2001 From: Alexis Hildebrandt Date: Tue, 16 Aug 2022 09:52:26 +0200 Subject: [PATCH 07/37] Fix libffi and xz download file extensions and use name of first prerequisite as an argument to tar's -f option. This is a follow-up on PR #155. --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index c15e08e8..fa32ecba 100644 --- a/Makefile +++ b/Makefile @@ -272,10 +272,10 @@ $$(BZIP2_LIB-$(target)): $$(BZIP2_DIR-$(target))/Makefile XZ_DIR-$(target)=build/$(os)/xz-$(XZ_VERSION)-$(target) XZ_LIB-$(target)=$$(XZ_DIR-$(target))/_install/lib/liblzma.a -$$(XZ_DIR-$(target))/Makefile: downloads/xz-$(XZ_VERSION).tgz +$$(XZ_DIR-$(target))/Makefile: downloads/xz-$(XZ_VERSION).tar.gz @echo ">>> Unpack XZ sources for $(target)" mkdir -p $$(XZ_DIR-$(target)) - tar zxf downloads/xz-$(XZ_VERSION).tgz --strip-components 1 -C $$(XZ_DIR-$(target)) + tar zxf $$< --strip-components 1 -C $$(XZ_DIR-$(target)) # Configure the build cd $$(XZ_DIR-$(target)) && \ ./configure \ @@ -706,10 +706,10 @@ ifneq ($(os),macOS) LIBFFI_XCFRAMEWORK-$(os)=build/$(os)/Support/libFFI.xcframework LIBFFI_DIR-$(os)=build/$(os)/libffi-$(LIBFFI_VERSION) -$$(LIBFFI_DIR-$(os))/darwin_common/include/ffi.h: downloads/libffi-$(LIBFFI_VERSION).tgz $$(PYTHON_XCFRAMEWORK-macOS) +$$(LIBFFI_DIR-$(os))/darwin_common/include/ffi.h: downloads/libffi-$(LIBFFI_VERSION).tar.gz $$(PYTHON_XCFRAMEWORK-macOS) @echo ">>> Unpack and configure libFFI sources on $(os)" mkdir -p $$(LIBFFI_DIR-$(os)) - tar zxf downloads/libffi-$(LIBFFI_VERSION).tgz --strip-components 1 -C $$(LIBFFI_DIR-$(os)) + tar zxf $$< --strip-components 1 -C $$(LIBFFI_DIR-$(os)) # Patch the build to add support for new platforms cd $$(LIBFFI_DIR-$(os)) && patch -p1 < $(PROJECT_DIR)/patch/libffi.patch # Configure the build From 17eb57d40b4be7195888bd012fdf9366262a26a0 Mon Sep 17 00:00:00 2001 From: Alexis Hildebrandt Date: Tue, 16 Aug 2022 10:10:49 +0200 Subject: [PATCH 08/37] Fix typo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fa32ecba..07c612ea 100644 --- a/Makefile +++ b/Makefile @@ -410,7 +410,7 @@ $$(PYTHON_DIR-$(target))/Makefile: \ $$(XZ_XCFRAMEWORK-$(os)) \ $$(OPENSSL_XCFRAMEWORK-$(os)) \ $$(LIBFFI_XCFRAMEWORK-$(os)) \ - $$(PYTHON_XCFRAMEWORK-macOS) \ + $$(PYTHON_XCFRAMEWORK-macOS) @echo ">>> Unpack and configure Python for $(target)" mkdir -p $$(PYTHON_DIR-$(target)) tar zxf $$< --strip-components 1 -C $$(PYTHON_DIR-$(target)) From 0936e8f05f78b1dd9f806e241a409f259b81916c Mon Sep 17 00:00:00 2001 From: Alexis Hildebrandt Date: Tue, 16 Aug 2022 10:11:57 +0200 Subject: [PATCH 09/37] Fix another typo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 07c612ea..0710cc10 100644 --- a/Makefile +++ b/Makefile @@ -761,7 +761,7 @@ $$(PYTHON_DIR-$(os))/Makefile: \ downloads/Python-$(PYTHON_VERSION).tgz \ $$(BZIP2_XCFRAMEWORK-$(os)) \ $$(XZ_XCFRAMEWORK-$(os)) \ - $$(OPENSSL_XCFRAMEWORK-$(os)) \ + $$(OPENSSL_XCFRAMEWORK-$(os)) @echo ">>> Unpack and configure Python for $(os)" mkdir -p $$(PYTHON_DIR-$(os)) tar zxf $$< --strip-components 1 -C $$(PYTHON_DIR-$(os)) From 7485a168ed847cbe8c481efa960fd5b67852f4e6 Mon Sep 17 00:00:00 2001 From: Alexis Hildebrandt Date: Tue, 16 Aug 2022 10:41:50 +0200 Subject: [PATCH 10/37] Fix blankspace and newline at end of file --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 697ea701..77a33c6d 100644 --- a/Makefile +++ b/Makefile @@ -810,7 +810,7 @@ $$(PYTHON_XCFRAMEWORK-$(os)): $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_FATLIB-$$( xcodebuild -create-xcframework \ -output $$@ $$(foreach sdk,$$(SDKS-$(os)),-library $$(PYTHON_FATLIB-$$(sdk)) -headers $$(PYTHON_DIR-$$(sdk))/include/python$(PYTHON_VER)) \ 2>&1 | tee -a build/$(os)/python-$(os).xcframework.log - # Copy the standard library from the first target listed + # Copy the standard library from the first target listed mkdir -p $$(PYTHON_RESOURCES-$(os)) cp -f -r $$(PYTHON_DIR-$$(firstword $$(PYTHON_TARGETS-$(os))))/_install/lib/python$(PYTHON_VER) \ $$(PYTHON_RESOURCES-$(os)) @@ -918,4 +918,4 @@ clean-Python: $(foreach os,$(OS_LIST),clean-Python-$(os)) dev-clean-Python: $(foreach os,$(OS_LIST),dev-clean-Python-$(os)) # Expand the build macro for every OS -$(foreach os,$(OS_LIST),$(eval $(call build,$(os)))) \ No newline at end of file +$(foreach os,$(OS_LIST),$(eval $(call build,$(os)))) From 5d9773546f841c4b876c4c095c673593bf2a6493 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 29 Jul 2022 14:20:15 +0800 Subject: [PATCH 11/37] Minor patch cleanups. --- patch/Python/Python.patch | 49 ++++----------------------------------- 1 file changed, 5 insertions(+), 44 deletions(-) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 98830ef5..f18b4968 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -662,7 +662,7 @@ index 69670d9d7f..8287267e64 100644 return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' diff --git a/Lib/subprocess.py b/Lib/subprocess.py -index 7ae8df154b..4c3121de39 100644 +index 7ae8df154b..08899c9921 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -74,8 +74,8 @@ @@ -676,15 +676,6 @@ index 7ae8df154b..4c3121de39 100644 if _mswindows: import _winapi -@@ -1921,6 +1921,8 @@ - outside of the local scope (nor can any methods it calls). - - """ -+ if _waitpid is None: -+ _waitpid = os.waitpid - if self.returncode is None: - if not self._waitpid_lock.acquire(False): - # Something else is busy calling waitpid. Don't allow two diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index ebe3711827..6d20f3c2d2 100644 --- a/Lib/sysconfig.py @@ -1055,18 +1046,6 @@ index 71926a5432..2918759262 100644 class UnixSysLogHandlerTest(SysLogHandlerTest): """Test for SysLogHandler with Unix sockets.""" -diff --git a/Lib/test/test_mailcap.py b/Lib/test/test_mailcap.py -index 8185f4a780..510ab8d3dd 100644 ---- a/Lib/test/test_mailcap.py -+++ b/Lib/test/test_mailcap.py -@@ -223,7 +223,6 @@ - ] - self._run_cases(cases) - -- @unittest.skipUnless(os.name == "posix", "Requires 'test' command on system") - @unittest.skipIf(sys.platform == "vxworks", "'test' command is not supported on VxWorks") - @unittest.skipUnless( - test.support.has_subprocess_support, diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index aae86cc257..bb48a85517 100644 --- a/Lib/test/test_marshal.py @@ -1179,7 +1158,7 @@ index ae25ef5588..8f2b1ffa69 100644 self.assertRaises(OSError, posix.sched_get_priority_max, -23) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py -index a2c4ab5081..eb427dfd72 100644 +index a2c4ab5081..265b5618df 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1776,6 +1776,8 @@ @@ -1195,7 +1174,7 @@ index a2c4ab5081..eb427dfd72 100644 self.assertGreaterEqual(size.lines, 0) @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(support.has_subprocess_support, 'Test requires support for subprocesses.') @unittest.skipUnless(hasattr(os, 'get_terminal_size'), 'need os.get_terminal_size()') def test_stty_match(self): @@ -3594,7 +3573,7 @@ index d74fb6deac..249b391d71 100755 | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ diff --git a/configure b/configure -index 078bb5bef1..107dccb7a5 100755 +index 078bb5bef1..3bb80ee5bc 100755 --- a/configure +++ b/configure @@ -3820,6 +3820,15 @@ @@ -3690,17 +3669,8 @@ index 078bb5bef1..107dccb7a5 100755 *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub # out any dynamic loading -@@ -26543,7 +26584,7 @@ - $as_echo "$as_me: creating Modules/Setup.local" >&6;} - if test ! -f Modules/Setup.local - then -- echo "# Edit this file for local setup changes" >Modules/Setup.local -+ echo "# Edit this file for local setup changes" >Modules/Setup.local - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: creating Makefile" >&5 diff --git a/configure.ac b/configure.ac -index 09f3f902a6..271fd4be6d 100644 +index 09f3f902a6..b3bab951eb 100644 --- a/configure.ac +++ b/configure.ac @@ -545,6 +545,15 @@ @@ -3793,15 +3763,6 @@ index 09f3f902a6..271fd4be6d 100644 *) # use dynload_shlib.c and dlopen() if we have it; otherwise stub # out any dynamic loading -@@ -7049,7 +7087,7 @@ - AC_MSG_NOTICE([creating Modules/Setup.local]) - if test ! -f Modules/Setup.local - then -- echo "# Edit this file for local setup changes" >Modules/Setup.local -+ echo "# Edit this file for local setup changes" >Modules/Setup.local - fi - - AC_MSG_NOTICE([creating Makefile]) --- /dev/null +++ b/iOS/Info.plist @@ -0,0 +1,20 @@ From 0a9ffe42bbcb88ceb70694e33c5a84ec342bc654 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 2 Aug 2022 13:06:40 +0800 Subject: [PATCH 12/37] Revert to dynamic modules for macOS builds. --- Makefile | 129 +++++++++++------------ patch/Python/Setup.embedded | 123 +-------------------- patch/Python/Setup.iOS | 18 ---- patch/Python/Setup.macOS | 45 -------- patch/Python/Setup.tvOS | 18 ---- patch/Python/Setup.watchos.arm64_32 | 23 ---- patch/Python/Setup.watchsimulator.arm64 | 23 ---- patch/Python/Setup.watchsimulator.x86_64 | 23 ---- patch/Python/release.common.exclude | 6 +- 9 files changed, 64 insertions(+), 344 deletions(-) delete mode 100644 patch/Python/Setup.watchos.arm64_32 delete mode 100644 patch/Python/Setup.watchsimulator.arm64 delete mode 100644 patch/Python/Setup.watchsimulator.x86_64 diff --git a/Makefile b/Makefile index f880ac98..c3bcc62c 100644 --- a/Makefile +++ b/Makefile @@ -134,7 +134,7 @@ downloads: \ downloads/xz-$(XZ_VERSION).tar.gz \ downloads/openssl-$(OPENSSL_VERSION).tar.gz \ downloads/libffi-$(LIBFFI_VERSION).tar.gz \ - downloads/Python-$(PYTHON_VERSION).tgz + downloads/Python-$(PYTHON_VERSION).tar.gz update-patch: # Generate a diff from the clone of the python/cpython Github repository @@ -197,7 +197,7 @@ downloads/libffi-$(LIBFFI_VERSION).tar.gz: ########################################################################### # Download original Python source code archive. -downloads/Python-$(PYTHON_VERSION).tgz: +downloads/Python-$(PYTHON_VERSION).tar.gz: @echo ">>> Download Python sources" curl $(CURL_FLAGS) -o $@ \ https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/$(notdir $@) @@ -405,12 +405,11 @@ PYTHON_LIB-$(target)=$$(PYTHON_DIR-$(target))/_install/lib/libpython$(PYTHON_VER PYCONFIG_H-$(target)=build/$(os)/python/$$(SDK-$(target))/include/python$(PYTHON_VER)/pyconfig-$$(ARCH-$(target)).h $$(PYTHON_DIR-$(target))/Makefile: \ - downloads/Python-$(PYTHON_VERSION).tgz \ - $$(BZIP2_XCFRAMEWORK-$(os)) \ - $$(XZ_XCFRAMEWORK-$(os)) \ - $$(OPENSSL_XCFRAMEWORK-$(os)) \ - $$(LIBFFI_XCFRAMEWORK-$(os)) \ - $$(PYTHON_XCFRAMEWORK-macOS) + downloads/Python-$(PYTHON_VERSION).tar.gz + $$(BZIP2_FATLIB-$$(SDK-$(target))) \ + $$(XZ_FATLIB-$$(SDK-$(target))) \ + $$(OPENSSL_FAT_INCLUDE-$$(SDK-$(target))) $$(OPENSSL_SSL_FATLIB-$$(SDK-$(target))) $$(OPENSSL_CRYPTO_FATLIB-$$(SDK-$(target))) \ + $$(LIBFFI_FATLIB-$$(SDK-$(target))) \ @echo ">>> Unpack and configure Python for $(target)" mkdir -p $$(PYTHON_DIR-$(target)) tar zxf $$< --strip-components 1 -C $$(PYTHON_DIR-$(target)) @@ -418,21 +417,28 @@ $$(PYTHON_DIR-$(target))/Makefile: \ cd $$(PYTHON_DIR-$(target)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch # Generate the embedded module configuration cat $(PROJECT_DIR)/patch/Python/Setup.embedded \ - $(PROJECT_DIR)/patch/Python/Setup.$(os) \ - $(PROJECT_DIR)/patch/Python/Setup.$(target) | \ + $(PROJECT_DIR)/patch/Python/Setup.$(os) | \ sed -e "s/{{slice}}/$$(SLICE-$$(SDK-$(target)))/g" \ > $$(PYTHON_DIR-$(target))/Modules/Setup.local # Configure target Python cd $$(PYTHON_DIR-$(target)) && \ ./configure \ - CC="$$(CC-$(target))" LD="$$(CC-$(target))" \ + CC="$$(CC-$(target))" \ + LD="$$(CC-$(target))" \ + LIBLZMA_CFLAGS="-I../xz/$(target)/include" \ + LIBLZMA_LIBS="-L../xz/$(target)/lib -lxz" \ + BZIP2_CFLAGS="-I../bzip2/$(target)/include" \ + BZIP2_LIBS="-L../bzip2/$(target)/lib -lbzip2" \ --host=$$(MACHINE_DETAILED-$(target))-apple-$(shell echo $(os) | tr '[:upper:]' '[:lower:]') \ --build=$(HOST_ARCH)-apple-darwin \ --with-build-python=$(PROJECT_DIR)/$(PYTHON_DIR-macOS)/_install/bin/python$(PYTHON_VER) \ --prefix="$(PROJECT_DIR)/$$(PYTHON_DIR-$(target))/_install" \ - --without-doc-strings --enable-ipv6 --without-ensurepip \ + --enable-ipv6 \ --with-openssl=../openssl/$$(SDK-$(target)) \ - ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no \ + --without-doc-strings \ + --without-ensurepip \ + ac_cv_file__dev_ptmx=no \ + ac_cv_file__dev_ptc=no \ $$(PYTHON_CONFIGURE-$(os)) \ 2>&1 | tee -a ../python-$(target).config.log @@ -530,16 +536,28 @@ $$(XZ_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(XZ_LIB-$$(targ # SDK: OpenSSL ########################################################################### -OPENSSL_FATLIB-$(sdk)=build/$(os)/openssl/$(sdk)/lib/libOpenSSL.a +OPENSSL_FAT_INCLUDE-$(sdk)=build/$(os)/openssl/$(sdk)/include +OPENSSL_SSL_FATLIB-$(sdk)=build/$(os)/openssl/$(sdk)/lib/libssl.a +OPENSSL_CRYPTO_FATLIB-$(sdk)=build/$(os)/openssl/$(sdk)/lib/libcrypto.a -$$(OPENSSL_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) - @echo ">>> Build OpenSSL fat library for $(sdk)" +$$(OPENSSL_FAT_INCLUDE-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) + @echo ">>> Copy OpenSSL headers from the first target associated with the SDK" + mkdir -p build/$(os)/openssl/$(sdk) + cp -r $$(OPENSSL_DIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/_install/include build/$(os)/openssl/$(sdk) + +$$(OPENSSL_SSL_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) + @echo ">>> Build OpenSSL ssl fat library for $(sdk)" mkdir -p build/$(os)/openssl/$(sdk)/lib xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ \ - $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target)) $$(OPENSSL_CRYPTO_LIB-$$(target))) \ - 2>&1 | tee -a build/$(os)/openssl-$(sdk).libtool.log - # Copy headers from the first target associated with the SDK - cp -r $$(OPENSSL_DIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/_install/include build/$(os)/openssl/$(sdk) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) \ + 2>&1 | tee -a build/$(os)/openssl-$(sdk)-ssl.libtool.log + +$$(OPENSSL_CRYPTO_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) + @echo ">>> Build OpenSSL crypto fat library for $(sdk)" + mkdir -p build/$(os)/openssl/$(sdk)/lib + xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ \ + $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) \ + 2>&1 | tee -a build/$(os)/openssl-$(sdk)-crypto.libtool.log ########################################################################### # SDK: libFFI @@ -599,7 +617,9 @@ vars-$(sdk): @echo "SDK_ARCHES-$(sdk): $$(SDK_ARCHES-$(sdk))" @echo "BZIP2_FATLIB-$(sdk): $$(BZIP2_FATLIB-$(sdk))" @echo "XZ_FATLIB-$(sdk): $$(XZ_FATLIB-$(sdk))" - @echo "OPENSSL_FATLIB-$(sdk): $$(OPENSSL_FATLIB-$(sdk))" + @echo "OPENSSL_FAT_INCLUDE-$(sdk): $$(OPENSSL_FAT_INCLUDE-$(sdk))" + @echo "OPENSSL_SSL_FATLIB-$(sdk): $$(OPENSSL_SSL_FATLIB-$(sdk))" + @echo "OPENSSL_CRYPTO_FATLIB-$(sdk): $$(OPENSSL_CRYPTO_FATLIB-$(sdk))" @echo "LIBFFI_FATLIB-$(sdk): $$(LIBFFI_FATLIB-$(sdk))" @echo "PYTHON_DIR-$(sdk): $$(PYTHON_DIR-$(sdk))" @echo "PYTHON_FATLIB-$(sdk): $$(PYTHON_FATLIB-$(sdk))" @@ -633,67 +653,37 @@ $$(foreach sdk,$$(SDKS-$(os)),$$(eval $$(call build-sdk,$$(sdk),$(os)))) # Build: BZip2 ########################################################################### -BZIP2_XCFRAMEWORK-$(os)=build/$(os)/Support/BZip2.xcframework - -$$(BZIP2_XCFRAMEWORK-$(os)): $$(foreach sdk,$$(SDKS-$(os)),$$(BZIP2_FATLIB-$$(sdk))) - @echo ">>> Create BZip2.XCFramework on $(os)" - mkdir -p $$(BZIP2_XCFRAMEWORK-$(os)) - xcodebuild -create-xcframework \ - -output $$@ $$(foreach sdk,$$(SDKS-$(os)),-library $$(BZIP2_FATLIB-$$(sdk)) -headers build/$(os)/bzip2/$$(sdk)/include) \ - 2>&1 | tee -a build/$(os)/bzip2-$(os).xcframework.log - -BZip2-$(os): $$(BZIP2_XCFRAMEWORK-$(os)) +BZip2-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(BZIP2_FATLIB-$$(sdk))) clean-BZip2-$(os): @echo ">>> Clean BZip2 build products on $(os)" rm -rf build/$(os)/bzip2-$(BZIP2_VERSION)-* \ build/$(os)/bzip2 \ build/$(os)/bzip2-*.log \ - build/$(os)/Support/BZip2.xcframework ########################################################################### # Build: XZ (LZMA) ########################################################################### -XZ_XCFRAMEWORK-$(os)=build/$(os)/Support/XZ.xcframework - -$$(XZ_XCFRAMEWORK-$(os)): $$(foreach sdk,$$(SDKS-$(os)),$$(XZ_FATLIB-$$(sdk))) - @echo ">>> Create XZ.XCFramework on $(os)" - mkdir -p $$(XZ_XCFRAMEWORK-$(os)) - xcodebuild -create-xcframework \ - -output $$@ $$(foreach sdk,$$(SDKS-$(os)),-library $$(XZ_FATLIB-$$(sdk)) -headers build/$(os)/xz/$$(sdk)/include) \ - 2>&1 | tee -a build/$(os)/xz-$(os).xcframework.log - -XZ-$(os): $$(XZ_XCFRAMEWORK-$(os)) +XZ-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(XZ_FATLIB-$$(sdk))) clean-XZ-$(os): @echo ">>> Clean XZ build products on $(os)" rm -rf build/$(os)/xz-$(XZ_VERSION)-* \ build/$(os)/xz \ build/$(os)/xz-*.log \ - build/$(os)/Support/XZ.xcframework ########################################################################### # Build: OpenSSL ########################################################################### -OPENSSL_XCFRAMEWORK-$(os)=build/$(os)/Support/OpenSSL.xcframework - -$$(OPENSSL_XCFRAMEWORK-$(os)): $$(foreach sdk,$$(SDKS-$(os)),$$(OPENSSL_FATLIB-$$(sdk))) - @echo ">>> Create OpenSSL.XCFramework on $(os)" - mkdir -p $$(OPENSSL_XCFRAMEWORK-$(os)) - xcodebuild -create-xcframework \ - -output $$@ $$(foreach sdk,$$(SDKS-$(os)),-library $$(OPENSSL_FATLIB-$$(sdk)) -headers build/$(os)/openssl/$$(sdk)/include) \ - 2>&1 | tee -a build/$(os)/openssl-$(os).xcframework.log - -OpenSSL-$(os): $$(OPENSSL_XCFRAMEWORK-$(os)) +OpenSSL-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(OPENSSL_FAT_INCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk))) clean-OpenSSL-$(os): @echo ">>> Clean OpenSSL build products on $(os)" rm -rf build/$(os)/openssl-$(OPENSSL_VERSION)-* \ build/$(os)/openssl \ build/$(os)/openssl-*.log \ - build/$(os)/Support/OpenSSL.xcframework ########################################################################### # Build: libFFI @@ -733,7 +723,6 @@ clean-libFFI-$(os): @echo ">>> Clean libFFI build products on $(os)" rm -rf build/$(os)/libffi-$(LIBFFI_VERSION) \ build/$(os)/libffi-*.log \ - build/$(os)/Support/libFFI.xcframework ########################################################################### @@ -758,10 +747,10 @@ PYTHON_DIR-$(os)=$$(PYTHON_DIR-$$(firstword $$(TARGETS-$(os)))) PYTHON_LIB-$(os)=$$(PYTHON_LIB-$$(firstword $$(TARGETS-$(os)))) $$(PYTHON_DIR-$(os))/Makefile: \ - downloads/Python-$(PYTHON_VERSION).tgz \ - $$(BZIP2_XCFRAMEWORK-$(os)) \ - $$(XZ_XCFRAMEWORK-$(os)) \ - $$(OPENSSL_XCFRAMEWORK-$(os)) + downloads/Python-$(PYTHON_VERSION).tar.gz + $$(foreach sdk,$$(SDKS-$(os)),$$(BZIP2_FATLIB-$$(sdk))) \ + $$(foreach sdk,$$(SDKS-$(os)),$$(XZ_FATLIB-$$(sdk))) \ + $$(foreach sdk,$$(SDKS-$(os)),$$(OPENSSL_FAT_INCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk))) \ @echo ">>> Unpack and configure Python for $(os)" mkdir -p $$(PYTHON_DIR-$(os)) tar zxf $$< --strip-components 1 -C $$(PYTHON_DIR-$(os)) @@ -774,10 +763,19 @@ $$(PYTHON_DIR-$(os))/Makefile: \ # Configure target Python cd $$(PYTHON_DIR-$(os)) && \ ./configure \ - CC="$(CC-macosx)" LD="$(CC-macosx)" \ + CC="$(CC-macosx)" \ + LD="$(CC-macosx)" \ + LIBLZMA_CFLAGS="-I../xz/macosx/include" \ + LIBLZMA_LIBS="-L../xz/macosx/lib -lxz" \ + BZIP2_CFLAGS="-I../bzip2/macosx/include" \ + BZIP2_LIBS="-L../bzip2/macosx/lib -lbzip2" \ --prefix="$(PROJECT_DIR)/$$(PYTHON_DIR-$(os))/_install" \ - --without-doc-strings --enable-ipv6 --without-ensurepip --enable-universalsdk --with-universal-archs=universal2 \ + --enable-ipv6 \ + --enable-universalsdk \ --with-openssl=../openssl/macosx \ + --with-universal-archs=universal2 \ + --without-doc-strings \ + --without-ensurepip \ $$(PYTHON_CONFIGURE-$(os)) \ 2>&1 | tee -a ../python-$(os).config.log @@ -820,7 +818,7 @@ Python-$(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz clean-Python-$(os): @echo ">>> Clean Python build products on $(os)" rm -rf \ - dist/Python-$(PYTHON_VER)-$(os) \ + dist/Python-$(PYTHON_VER)-$(os)-* \ build/$(os)/Python-$(PYTHON_VERSION)-* \ build/$(os)/python \ build/$(os)/python-*.log \ @@ -842,12 +840,7 @@ dev-clean-Python-$(os): # Build ########################################################################### -dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: \ - $$(BZIP2_XCFRAMEWORK-$(os)) \ - $$(XZ_XCFRAMEWORK-$(os)) \ - $$(OPENSSL_XCFRAMEWORK-$(os)) \ - $$(LIBFFI_XCFRAMEWORK-$(os)) \ - $$(PYTHON_XCFRAMEWORK-$(os)) +dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: $$(PYTHON_XCFRAMEWORK-$(os)) @echo ">>> Create final distribution artefact for $(os)" mkdir -p dist echo "Python version: $(PYTHON_VERSION) " > build/$(os)/Support/VERSIONS diff --git a/patch/Python/Setup.embedded b/patch/Python/Setup.embedded index 18657425..e5e379e0 100644 --- a/patch/Python/Setup.embedded +++ b/patch/Python/Setup.embedded @@ -1,123 +1,7 @@ ##################################################################### -# Static compilation instructions for all binary modules. +# Compilation instructions for all binary modules. ##################################################################### -*static* - -_asyncio _asynciomodule.c -_bisect _bisectmodule.c -_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c -_bz2 _bz2module.c -I$(srcdir)/../Support/BZip2.xcframework/{{slice}}/Headers -L$(srcdir)/../Support/BZip2.xcframework/{{slice}} -lbzip2 -_codecs_cn cjkcodecs/_codecs_cn.c -_codecs_hk cjkcodecs/_codecs_hk.c -_codecs_iso2022 cjkcodecs/_codecs_iso2022.c -_codecs_jp cjkcodecs/_codecs_jp.c -_codecs_kr cjkcodecs/_codecs_kr.c -_codecs_tw cjkcodecs/_codecs_tw.c -_contextvars _contextvarsmodule.c -_csv _csv.c -_dbm _dbmmodule.c -DUSE_NDBM -_datetime _datetimemodule.c -_elementtree _elementtree.c \ - -I$(srcdir)/Modules/expat - -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI -_hashlib _hashopenssl.c -I$(srcdir)/../Support/OpenSSL.xcframework/{{slice}}/Headers -L$(srcdir)/../Support/OpenSSL.xcframework/{{slice}} -lOpenSSL -DUSE_SSL -_heapq _heapqmodule.c -_json -I$(srcdir)/Include/internal -DPy_BUILD_CORE_BUILTIN _json.c -_lsprof _lsprof.c rotatingtree.c -_lzma _lzmamodule.c -I$(srcdir)/../Support/XZ.xcframework/{{slice}}/Headers -L$(srcdir)/../Support/XZ.xcframework/{{slice}} -lxz -_md5 md5module.c -_multibytecodec cjkcodecs/multibytecodec.c -_multiprocessing -I$(srcdir)/Modules/_multiprocessing _multiprocessing/multiprocessing.c _multiprocessing/semaphore.c -_opcode _opcode.c -_pickle _pickle.c -DPy_BUILD_CORE_MODULE -_posixsubprocess _posixsubprocess.c -_queue _queuemodule.c -_random _randommodule.c -DPy_BUILD_CORE_MODULE -_sha1 sha1module.c -_sha3 _sha3/sha3module.c -_sha256 sha256module.c -DPy_BUILD_CORE_MODULE -_sha512 sha512module.c -DPy_BUILD_CORE_MODULE -_socket socketmodule.c -_sqlite3 -I$(srcdir)/Modules/_sqlite -DSQLITE_OMIT_LOAD_EXTENSION -lsqlite3 \ - _sqlite/blob.c \ - _sqlite/connection.c \ - _sqlite/cursor.c \ - _sqlite/microprotocols.c \ - _sqlite/module.c \ - _sqlite/prepare_protocol.c \ - _sqlite/row.c \ - _sqlite/statement.c \ - _sqlite/util.c -_ssl _ssl.c -I$(srcdir)/../Support/OpenSSL.xcframework/{{slice}}/Headers -L$(srcdir)/../Support/OpenSSL.xcframework/{{slice}} -lOpenSSL -DUSE_SSL -_statistics _statisticsmodule.c -_struct _struct.c -_typing _typingmodule.c -_uuid _uuidmodule.c -_zoneinfo _zoneinfo.c -DPy_BUILD_CORE_MODULE -array arraymodule.c -DPy_BUILD_CORE_MODULE -binascii binascii.c -cmath cmathmodule.c -fcntl fcntlmodule.c -grp grpmodule.c -math mathmodule.c -mmap mmapmodule.c -pyexpat expat/xmlparse.c \ - expat/xmlrole.c \ - expat/xmltok.c \ - pyexpat.c \ - -I$(srcdir)/Modules/expat \ - -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI -DXML_DEV_URANDOM -resource resource.c -select selectmodule.c -syslog syslogmodule.c -termios termios.c -unicodedata unicodedata.c -zlib zlibmodule.c -I$(prefix)/include -lz - -##################################################################### -# Modules that will be added by Setup.bootstrap -##################################################################### - -# _abc -# _codecs -# _collections -# _functools -# _io -# _locale -# _operator -# _signal -# _sre -# _stat -# _symtable -# _thread -# _tracemalloc -# _weakref -# atexit -# errno -# faulthandler -# itertools -# posix -# pwd -# time - -##################################################################### -# DISABLED Testing modules -##################################################################### - -*disabled* - -_ctypes_test -_testbuffer -_testcapi -_testimportmultiple -_testinternalcapi -_testmultiphase -_xxsubinterpreters -_xxtestfuzz -xxlimited -xxlimited_35 - ##################################################################### # DISABLED Modules that require additional frameworks ##################################################################### @@ -133,11 +17,6 @@ readline # removed in Python 3.13 ##################################################################### -*static* - -_crypt _cryptmodule.c -audioop audioop.c - *disabled* nis diff --git a/patch/Python/Setup.iOS b/patch/Python/Setup.iOS index 5633c2c5..7e9847e5 100644 --- a/patch/Python/Setup.iOS +++ b/patch/Python/Setup.iOS @@ -20,24 +20,6 @@ _ctypes _ctypes/_ctypes.c \ -L$(srcdir)/../Support/libFFI.xcframework/{{slice}} \ -lFFI -_decimal _decimal/_decimal.c \ - _decimal/libmpdec/basearith.c \ - _decimal/libmpdec/constants.c \ - _decimal/libmpdec/context.c \ - _decimal/libmpdec/convolute.c \ - _decimal/libmpdec/crt.c \ - _decimal/libmpdec/difradix2.c \ - _decimal/libmpdec/fnt.c \ - _decimal/libmpdec/fourstep.c \ - _decimal/libmpdec/io.c \ - _decimal/libmpdec/mpalloc.c \ - _decimal/libmpdec/mpdecimal.c \ - _decimal/libmpdec/numbertheory.c \ - _decimal/libmpdec/sixstep.c \ - _decimal/libmpdec/transpose.c \ - -I$(srcdir)/Modules/_decimal/libmpdec \ - -DCONFIG_64 -DANSI -DHAVE_UINT128_T - ##################################################################### # DISABLED Modules that require additional frameworks ##################################################################### diff --git a/patch/Python/Setup.macOS b/patch/Python/Setup.macOS index 293fffe5..7c8229ad 100644 --- a/patch/Python/Setup.macOS +++ b/patch/Python/Setup.macOS @@ -1,48 +1,3 @@ ##################################################################### # macOS: Platform specific configuration ##################################################################### - -*static* - -_ctypes _ctypes/_ctypes.c \ - _ctypes/callbacks.c \ - _ctypes/callproc.c \ - _ctypes/stgdict.c \ - _ctypes/cfield.c \ - _ctypes/malloc_closure.c \ - -I$(srcdir)/Modules/_ctypes/darwin \ - -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ffi \ - -DPy_BUILD_CORE_MODULE \ - -DHAVE_FFI_CLOSURE_ALLOC \ - -DHAVE_FFI_PREP_CIF_VAR \ - -DHAVE_FFI_PREP_CLOSURE_LOC \ - -DMACOSX \ - -DUSING_APPLE_OS_LIBFFI \ - -DUSING_MALLOC_CLOSURE_DOT_C \ - -lffi - -_decimal _decimal/_decimal.c \ - _decimal/libmpdec/basearith.c \ - _decimal/libmpdec/constants.c \ - _decimal/libmpdec/context.c \ - _decimal/libmpdec/convolute.c \ - _decimal/libmpdec/crt.c \ - _decimal/libmpdec/difradix2.c \ - _decimal/libmpdec/fnt.c \ - _decimal/libmpdec/fourstep.c \ - _decimal/libmpdec/io.c \ - _decimal/libmpdec/mpalloc.c \ - _decimal/libmpdec/mpdecimal.c \ - _decimal/libmpdec/numbertheory.c \ - _decimal/libmpdec/sixstep.c \ - _decimal/libmpdec/transpose.c \ - -I$(srcdir)/Modules/_decimal/libmpdec \ - -DCONFIG_64 -DANSI -DHAVE_UINT128_T - -_posixshmem -I$(srcdir)/Modules/_multiprocessing _multiprocessing/posixshmem.c - -_scproxy _scproxy.c -framework SystemConfiguration -framework CoreFoundation - -_curses _cursesmodule.c -lcurses -ltermcap - -_curses_panel _curses_panel.c -lpanel -lncurses diff --git a/patch/Python/Setup.tvOS b/patch/Python/Setup.tvOS index b9c2a7a0..67202998 100644 --- a/patch/Python/Setup.tvOS +++ b/patch/Python/Setup.tvOS @@ -20,24 +20,6 @@ _ctypes _ctypes/_ctypes.c \ -L$(srcdir)/../Support/libFFI.xcframework/{{slice}} \ -lFFI -_decimal _decimal/_decimal.c \ - _decimal/libmpdec/basearith.c \ - _decimal/libmpdec/constants.c \ - _decimal/libmpdec/context.c \ - _decimal/libmpdec/convolute.c \ - _decimal/libmpdec/crt.c \ - _decimal/libmpdec/difradix2.c \ - _decimal/libmpdec/fnt.c \ - _decimal/libmpdec/fourstep.c \ - _decimal/libmpdec/io.c \ - _decimal/libmpdec/mpalloc.c \ - _decimal/libmpdec/mpdecimal.c \ - _decimal/libmpdec/numbertheory.c \ - _decimal/libmpdec/sixstep.c \ - _decimal/libmpdec/transpose.c \ - -I$(srcdir)/Modules/_decimal/libmpdec \ - -DCONFIG_64 -DANSI -DHAVE_UINT128_T - ##################################################################### # DISABLED Modules that require additional frameworks ##################################################################### diff --git a/patch/Python/Setup.watchos.arm64_32 b/patch/Python/Setup.watchos.arm64_32 deleted file mode 100644 index a693c508..00000000 --- a/patch/Python/Setup.watchos.arm64_32 +++ /dev/null @@ -1,23 +0,0 @@ -##################################################################### -# watchOS ARM64_32: Target specific configuration -##################################################################### - -*static* - -_decimal _decimal/_decimal.c \ - _decimal/libmpdec/basearith.c \ - _decimal/libmpdec/constants.c \ - _decimal/libmpdec/context.c \ - _decimal/libmpdec/convolute.c \ - _decimal/libmpdec/crt.c \ - _decimal/libmpdec/difradix2.c \ - _decimal/libmpdec/fnt.c \ - _decimal/libmpdec/fourstep.c \ - _decimal/libmpdec/io.c \ - _decimal/libmpdec/mpalloc.c \ - _decimal/libmpdec/mpdecimal.c \ - _decimal/libmpdec/numbertheory.c \ - _decimal/libmpdec/sixstep.c \ - _decimal/libmpdec/transpose.c \ - -I$(srcdir)/Modules/_decimal/libmpdec \ - -DCONFIG_32 -DANSI diff --git a/patch/Python/Setup.watchsimulator.arm64 b/patch/Python/Setup.watchsimulator.arm64 deleted file mode 100644 index 2f55b527..00000000 --- a/patch/Python/Setup.watchsimulator.arm64 +++ /dev/null @@ -1,23 +0,0 @@ -##################################################################### -# watchOS Simulator ARM64: Target specific configuration -##################################################################### - -*static* - -_decimal _decimal/_decimal.c \ - _decimal/libmpdec/basearith.c \ - _decimal/libmpdec/constants.c \ - _decimal/libmpdec/context.c \ - _decimal/libmpdec/convolute.c \ - _decimal/libmpdec/crt.c \ - _decimal/libmpdec/difradix2.c \ - _decimal/libmpdec/fnt.c \ - _decimal/libmpdec/fourstep.c \ - _decimal/libmpdec/io.c \ - _decimal/libmpdec/mpalloc.c \ - _decimal/libmpdec/mpdecimal.c \ - _decimal/libmpdec/numbertheory.c \ - _decimal/libmpdec/sixstep.c \ - _decimal/libmpdec/transpose.c \ - -I$(srcdir)/Modules/_decimal/libmpdec \ - -DCONFIG_64 -DANSI -DHAVE_UINT128_T diff --git a/patch/Python/Setup.watchsimulator.x86_64 b/patch/Python/Setup.watchsimulator.x86_64 deleted file mode 100644 index f43c2000..00000000 --- a/patch/Python/Setup.watchsimulator.x86_64 +++ /dev/null @@ -1,23 +0,0 @@ -##################################################################### -# watchOS simulator x86_64: Target specific configuration -##################################################################### - -*static* - -_decimal _decimal/_decimal.c \ - _decimal/libmpdec/basearith.c \ - _decimal/libmpdec/constants.c \ - _decimal/libmpdec/context.c \ - _decimal/libmpdec/convolute.c \ - _decimal/libmpdec/crt.c \ - _decimal/libmpdec/difradix2.c \ - _decimal/libmpdec/fnt.c \ - _decimal/libmpdec/fourstep.c \ - _decimal/libmpdec/io.c \ - _decimal/libmpdec/mpalloc.c \ - _decimal/libmpdec/mpdecimal.c \ - _decimal/libmpdec/numbertheory.c \ - _decimal/libmpdec/sixstep.c \ - _decimal/libmpdec/transpose.c \ - -I$(srcdir)/Modules/_decimal/libmpdec \ - -DCONFIG_64 -DANSI -DHAVE_UINT128_T diff --git a/patch/Python/release.common.exclude b/patch/Python/release.common.exclude index a4ac3703..0e0f9a90 100644 --- a/patch/Python/release.common.exclude +++ b/patch/Python/release.common.exclude @@ -17,19 +17,17 @@ Python/Resources/lib/python*/distutils/tests Python/Resources/lib/python*/lib2to3/tests Python/Resources/lib/python*/sqlite3/test Python/Resources/lib/python*/test -# Remove compiled test and example modules. -Python/Resources/lib/python*/lib-dynload/*.so # Remove config-* directory, which is used for compiling C extension modules. Python/Resources/lib/python*/config-* # Remove ensurepip. If user code needs pip, it can add it to Python/Resources/lib/python*/ensurepip +# Remove libraries supporting IDLE. We don't need to ship an IDE +Python/Resources/lib/python*/idlelib # Remove Tcl/Tk GUI code. We don't build against Tcl/Tk at the moment, so this # will not work. -Python/Resources/lib/python*/idlelib Python/Resources/lib/python*/tkinter Python/Resources/lib/python*/turtle.py Python/Resources/lib/python*/turtledemo -Python/Resources/lib/python*/lib-dynload/_tkinter*.so # Remove site-packages directory. The template unpacks user code and # dependencies to a different path. Python/Resources/lib/python*/site-packages From 99af12b40c3ab4bada95e131dc5e1cab65a9fea9 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 5 Aug 2022 11:23:29 +0800 Subject: [PATCH 13/37] Build completing for iOS dynamic modules. --- Makefile | 88 ++-- patch/Python/Python.patch | 940 ++++++++++++++++++++++++++++++++++-- patch/Python/Setup.embedded | 24 - patch/Python/Setup.iOS | 31 -- patch/Python/Setup.macOS | 3 - patch/Python/Setup.tvOS | 31 -- patch/Python/Setup.watchOS | 31 -- 7 files changed, 938 insertions(+), 210 deletions(-) delete mode 100644 patch/Python/Setup.embedded delete mode 100644 patch/Python/Setup.iOS delete mode 100644 patch/Python/Setup.macOS delete mode 100644 patch/Python/Setup.tvOS delete mode 100644 patch/Python/Setup.watchOS diff --git a/Makefile b/Makefile index c3bcc62c..48cd3a3f 100644 --- a/Makefile +++ b/Makefile @@ -64,45 +64,36 @@ TARGETS-macOS=macosx.x86_64 macosx.arm64 CFLAGS-macOS=-mmacosx-version-min=10.15 CFLAGS-macosx.x86_64= CFLAGS-macosx.arm64= -SLICE-macosx=macos-arm64_x86_64 SDK_ROOT-macosx=$(shell xcrun --sdk macosx --show-sdk-path) CC-macosx=xcrun --sdk macosx clang --sysroot=$(SDK_ROOT-macosx) $(CFLAGS-macOS) # iOS targets TARGETS-iOS=iphonesimulator.x86_64 iphonesimulator.arm64 iphoneos.arm64 -CFLAGS-iOS=-mios-version-min=12.0 -fembed-bitcode +CFLAGS-iOS=-mios-version-min=12.0 CFLAGS-iphoneos.arm64= CFLAGS-iphonesimulator.x86_64= CFLAGS-iphonesimulator.arm64= -SLICE-iphoneos=ios-arm64 -SLICE-iphonesimulator=ios-arm64_x86_64-simulator # tvOS targets TARGETS-tvOS=appletvsimulator.x86_64 appletvsimulator.arm64 appletvos.arm64 -CFLAGS-tvOS=-mtvos-version-min=9.0 -fembed-bitcode +CFLAGS-tvOS=-mtvos-version-min=9.0 CFLAGS-appletvos.arm64= CFLAGS-appletvsimulator.x86_64= CFLAGS-appletvsimulator.arm64= -SLICE-appletvos=tvos-arm64 -SLICE-appletvsimulator=tvos-arm64_x86_64-simulator PYTHON_CONFIGURE-tvOS=ac_cv_func_sigaltstack=no # watchOS targets TARGETS-watchOS=watchsimulator.x86_64 watchsimulator.arm64 watchos.arm64_32 -CFLAGS-watchOS=-mwatchos-version-min=4.0 -fembed-bitcode +CFLAGS-watchOS=-mwatchos-version-min=4.0 CFLAGS_watchsimulator.x86_64= CFLAGS-watchsimulator.arm64= CFLAGS-watchos.arm64_32= -SLICE-watchos=watchos-arm64_32 -SLICE-watchsimulator=watchos-arm64_x86_64-simulator PYTHON_CONFIGURE-watchOS=ac_cv_func_sigaltstack=no # override machine types for arm64 -MACHINE_DETAILED-arm64=aarch64 MACHINE_SIMPLE-arm64=arm # override machine types for arm64_32 -MACHINE_DETAILED-arm64_32=aarch64 MACHINE_SIMPLE-arm64_32=arm # The architecture of the machine doing the build @@ -219,11 +210,6 @@ os=$2 SDK-$(target)=$$(basename $(target)) ARCH-$(target)=$$(subst .,,$$(suffix $(target))) -ifdef MACHINE_DETAILED-$$(ARCH-$(target)) -MACHINE_DETAILED-$(target)=$$(MACHINE_DETAILED-$$(ARCH-$(target))) -else -MACHINE_DETAILED-$(target)=$$(ARCH-$(target)) -endif ifdef MACHINE_SIMPLE-$$(ARCH-$(target)) MACHINE_SIMPLE-$(target)=$$(MACHINE_SIMPLE-$$(ARCH-$(target))) else @@ -372,7 +358,7 @@ $$(OPENSSL_SSL_LIB-$(target)): $$(OPENSSL_DIR-$(target))/libssl.a # a per-target build on macOS ifneq ($(os),macOS) -LIBFFI_DIR-$(os)=build/$(os)/libffi-$(LIBFFI_VERSION) +LIBFFI_DIR-$(os)=build/$(os)/libffi LIBFFI_DIR-$(target)=$$(LIBFFI_DIR-$(os))/build_$$(SDK-$(target))-$$(ARCH-$(target)) LIBFFI_LIB-$(target)=$$(LIBFFI_DIR-$(target))/.libs/libffi.a @@ -415,21 +401,18 @@ $$(PYTHON_DIR-$(target))/Makefile: \ tar zxf $$< --strip-components 1 -C $$(PYTHON_DIR-$(target)) # Apply target Python patches cd $$(PYTHON_DIR-$(target)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch - # Generate the embedded module configuration - cat $(PROJECT_DIR)/patch/Python/Setup.embedded \ - $(PROJECT_DIR)/patch/Python/Setup.$(os) | \ - sed -e "s/{{slice}}/$$(SLICE-$$(SDK-$(target)))/g" \ - > $$(PYTHON_DIR-$(target))/Modules/Setup.local # Configure target Python cd $$(PYTHON_DIR-$(target)) && \ ./configure \ CC="$$(CC-$(target))" \ - LD="$$(CC-$(target))" \ - LIBLZMA_CFLAGS="-I../xz/$(target)/include" \ - LIBLZMA_LIBS="-L../xz/$(target)/lib -lxz" \ - BZIP2_CFLAGS="-I../bzip2/$(target)/include" \ - BZIP2_LIBS="-L../bzip2/$(target)/lib -lbzip2" \ - --host=$$(MACHINE_DETAILED-$(target))-apple-$(shell echo $(os) | tr '[:upper:]' '[:lower:]') \ + LIBLZMA_CFLAGS="-I../xz/$$(SDK-$(target))/include" \ + LIBLZMA_LIBS="-L../xz/$$(SDK-$(target))/lib -lxz" \ + BZIP2_CFLAGS="-I../bzip2/$$(SDK-$(target))/include" \ + BZIP2_LIBS="-L../bzip2/$$(SDK-$(target))/lib -lbzip2" \ + LIBFFI_INCLUDEDIR="../libffi/_install/$$(SDK-$(target))/include" \ + LIBFFI_LIBDIR="../libffi/_install/$$(SDK-$(target))/lib" \ + LIBFFI_LIB="ffi" \ + --host=$$(TARGET_TRIPLE-$(target)) \ --build=$(HOST_ARCH)-apple-darwin \ --with-build-python=$(PROJECT_DIR)/$(PYTHON_DIR-macOS)/_install/bin/python$(PYTHON_VER) \ --prefix="$(PROJECT_DIR)/$$(PYTHON_DIR-$(target))/_install" \ @@ -497,6 +480,25 @@ endef # build-target # - $2 OS (e.g., iOS, tvOS) # ########################################################################### +define targets-sdk +sdk=$1 +os=$2 + +BZIP2_FATLIB-$(sdk)=build/$(os)/bzip2/$(sdk)/lib/libbzip2.a + +XZ_FATLIB-$(sdk)=build/$(os)/xz/$(sdk)/lib/libxz.a + +OPENSSL_FAT_INCLUDE-$(sdk)=build/$(os)/openssl/$(sdk)/include +OPENSSL_SSL_FATLIB-$(sdk)=build/$(os)/openssl/$(sdk)/lib/libssl.a +OPENSSL_CRYPTO_FATLIB-$(sdk)=build/$(os)/openssl/$(sdk)/lib/libcrypto.a + +LIBFFI_FATLIB-$(sdk)=$$(LIBFFI_DIR-$(os))/_install/$(sdk)/lib/libffi.a + +PYTHON_DIR-$(sdk)=build/$(os)/python/$(sdk) +PYTHON_FATLIB-$(sdk)=$$(PYTHON_DIR-$(sdk))/lib/libPython.a + +endef + define build-sdk sdk=$1 os=$2 @@ -508,8 +510,6 @@ SDK_ARCHES-$(sdk)=$$(sort $$(subst .,,$$(suffix $$(SDK_TARGETS-$(sdk))))) # SDK: BZip2 ########################################################################### -BZIP2_FATLIB-$(sdk)=build/$(os)/bzip2/$(sdk)/lib/libbzip2.a - $$(BZIP2_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(BZIP2_LIB-$$(target))) @echo ">>> Build BZip2 fat library for $(sdk)" mkdir -p build/$(os)/bzip2/$(sdk)/lib @@ -522,8 +522,6 @@ $$(BZIP2_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(BZIP2_LIB-$ # SDK: XZ (LZMA) ########################################################################### -XZ_FATLIB-$(sdk)=build/$(os)/xz/$(sdk)/lib/libxz.a - $$(XZ_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(XZ_LIB-$$(target))) @echo ">>> Build XZ fat library for $(sdk)" mkdir -p build/$(os)/xz/$(sdk)/lib @@ -536,10 +534,6 @@ $$(XZ_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(XZ_LIB-$$(targ # SDK: OpenSSL ########################################################################### -OPENSSL_FAT_INCLUDE-$(sdk)=build/$(os)/openssl/$(sdk)/include -OPENSSL_SSL_FATLIB-$(sdk)=build/$(os)/openssl/$(sdk)/lib/libssl.a -OPENSSL_CRYPTO_FATLIB-$(sdk)=build/$(os)/openssl/$(sdk)/lib/libcrypto.a - $$(OPENSSL_FAT_INCLUDE-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) @echo ">>> Copy OpenSSL headers from the first target associated with the SDK" mkdir -p build/$(os)/openssl/$(sdk) @@ -563,11 +557,9 @@ $$(OPENSSL_CRYPTO_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OP # SDK: libFFI ########################################################################### -LIBFFI_FATLIB-$(sdk)=$$(LIBFFI_DIR-$(os))/_install/$(sdk)/libFFI.a - $$(LIBFFI_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(LIBFFI_LIB-$$(target))) @echo ">>> Build libFFI fat library for $(sdk)" - mkdir -p $$(LIBFFI_DIR-$(os))/_install/$(sdk) + mkdir -p $$(LIBFFI_DIR-$(os))/_install/$(sdk)/lib xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ $$^ \ 2>&1 | tee -a build/$(os)/libffi-$(sdk).libtool.log # Copy headers from the first target associated with the SDK @@ -580,9 +572,6 @@ $$(LIBFFI_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(LIBFFI_LIB # SDK: Python ########################################################################### -PYTHON_DIR-$(sdk)=build/$(os)/python/$(sdk) -PYTHON_FATLIB-$(sdk)=$$(PYTHON_DIR-$(sdk))/lib/libPython.a - ifeq ($(os),macOS) # There's a single OS-level build for macOS; the fat library is a direct copy of # OS build, and the headers are the unmodifed headers produced by the OS build. @@ -642,11 +631,15 @@ os=$1 # Build: Macro Expansions ########################################################################### +SDKS-$(os)=$$(sort $$(basename $$(TARGETS-$(os)))) + +# Expand the targets-sdk macro for all the sdks on this OS (e.g., iphoneos, iphonesimulator) +$$(foreach sdk,$$(SDKS-$(os)),$$(eval $$(call targets-sdk,$$(sdk),$(os)))) + # Expand the build-target macro for target on this OS $$(foreach target,$$(TARGETS-$(os)),$$(eval $$(call build-target,$$(target),$(os)))) # Expand the build-sdk macro for all the sdks on this OS (e.g., iphoneos, iphonesimulator) -SDKS-$(os)=$$(sort $$(basename $$(TARGETS-$(os)))) $$(foreach sdk,$$(SDKS-$(os)),$$(eval $$(call build-sdk,$$(sdk),$(os)))) ########################################################################### @@ -694,7 +687,7 @@ clean-OpenSSL-$(os): ifneq ($(os),macOS) LIBFFI_XCFRAMEWORK-$(os)=build/$(os)/Support/libFFI.xcframework -LIBFFI_DIR-$(os)=build/$(os)/libffi-$(LIBFFI_VERSION) +LIBFFI_DIR-$(os)=build/$(os)/libffi $$(LIBFFI_DIR-$(os))/darwin_common/include/ffi.h: downloads/libffi-$(LIBFFI_VERSION).tar.gz $$(PYTHON_XCFRAMEWORK-macOS) @echo ">>> Unpack and configure libFFI sources on $(os)" @@ -756,15 +749,10 @@ $$(PYTHON_DIR-$(os))/Makefile: \ tar zxf $$< --strip-components 1 -C $$(PYTHON_DIR-$(os)) # Apply target Python patches cd $$(PYTHON_DIR-$(os)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch - cat $(PROJECT_DIR)/patch/Python/Setup.embedded \ - $(PROJECT_DIR)/patch/Python/Setup.$(os) | \ - sed -e "s/{{slice}}/$$(SLICE-macosx)/g" \ - > $$(PYTHON_DIR-$(os))/Modules/Setup.local # Configure target Python cd $$(PYTHON_DIR-$(os)) && \ ./configure \ CC="$(CC-macosx)" \ - LD="$(CC-macosx)" \ LIBLZMA_CFLAGS="-I../xz/macosx/include" \ LIBLZMA_LIBS="-L../xz/macosx/lib -lxz" \ BZIP2_CFLAGS="-I../bzip2/macosx/include" \ diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index f18b4968..6b31d972 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1469,6 +1469,19 @@ index 44974d433b..ae4e18802b 100755 # # Platform support for Windows +diff --git a/Makefile.pre.in b/Makefile.pre.in +index e145315c45..9ad1a257a0 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -344,6 +344,8 @@ + ########################################################################## + + LIBFFI_INCLUDEDIR= @LIBFFI_INCLUDEDIR@ ++LIBFFI_LIBDIR= @LIBFFI_LIBDIR@ ++LIBFFI_LIB=@LIBFFI_LIB@ + + ########################################################################## + # Parser diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 9132f13e81..36e00f7bfd 100644 --- a/Modules/_posixsubprocess.c @@ -3559,10 +3572,188 @@ index 90a4405091..65dd71d868 100644 + exit(ret); + return ret; +} +diff --git a/aclocal.m4 b/aclocal.m4 +index 6a33c0cc9d..09ae5d1aa8 100644 +--- a/aclocal.m4 ++++ b/aclocal.m4 +@@ -1,6 +1,6 @@ +-# generated automatically by aclocal 1.16.3 -*- Autoconf -*- ++# generated automatically by aclocal 1.16.5 -*- Autoconf -*- + +-# Copyright (C) 1996-2020 Free Software Foundation, Inc. ++# Copyright (C) 1996-2021 Free Software Foundation, Inc. + + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -184,7 +184,7 @@ + # and this notice are preserved. This file is offered as-is, without any + # warranty. + +-#serial 10 ++#serial 11 + + AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL]) + AC_DEFUN([AX_CHECK_OPENSSL], [ +@@ -227,7 +227,7 @@ + if ! $found; then + OPENSSL_INCLUDES= + for ssldir in $ssldirs; do +- AC_MSG_CHECKING([for openssl/ssl.h in $ssldir]) ++ AC_MSG_CHECKING([for include/openssl/ssl.h in $ssldir]) + if test -f "$ssldir/include/openssl/ssl.h"; then + OPENSSL_INCLUDES="-I$ssldir/include" + OPENSSL_LDFLAGS="-L$ssldir/lib" +@@ -276,7 +276,7 @@ + ]) + + # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +-# serial 11 (pkg-config-0.29.1) ++# serial 12 (pkg-config-0.29.2) + + dnl Copyright © 2004 Scott James Remnant . + dnl Copyright © 2012-2015 Dan Nicholson +@@ -318,7 +318,7 @@ + dnl See the "Since" comment for each macro you use to see what version + dnl of the macros you require. + m4_defun([PKG_PREREQ], +-[m4_define([PKG_MACROS_VERSION], [0.29.1]) ++[m4_define([PKG_MACROS_VERSION], [0.29.2]) + m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, + [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) + ])dnl PKG_PREREQ +@@ -419,7 +419,7 @@ + AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + + pkg_failed=no +-AC_MSG_CHECKING([for $1]) ++AC_MSG_CHECKING([for $2]) + + _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) + _PKG_CONFIG([$1][_LIBS], [libs], [$2]) +@@ -429,11 +429,11 @@ + See the pkg-config man page for more details.]) + + if test $pkg_failed = yes; then +- AC_MSG_RESULT([no]) ++ AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` +- else ++ else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs +@@ -450,7 +450,7 @@ + _PKG_TEXT])[]dnl + ]) + elif test $pkg_failed = untried; then +- AC_MSG_RESULT([no]) ++ AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( + [The pkg-config script could not be found or is too old. Make sure it + is in your PATH or set the PKG_CONFIG environment variable to the full +@@ -551,77 +551,9 @@ + AS_VAR_IF([$1], [""], [$5], [$4])dnl + ])dnl PKG_CHECK_VAR + +-dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, +-dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], +-dnl [DESCRIPTION], [DEFAULT]) +-dnl ------------------------------------------ +-dnl +-dnl Prepare a "--with-" configure option using the lowercase +-dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and +-dnl PKG_CHECK_MODULES in a single macro. +-AC_DEFUN([PKG_WITH_MODULES], +-[ +-m4_pushdef([with_arg], m4_tolower([$1])) +- +-m4_pushdef([description], +- [m4_default([$5], [build with ]with_arg[ support])]) +- +-m4_pushdef([def_arg], [m4_default([$6], [auto])]) +-m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) +-m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) +- +-m4_case(def_arg, +- [yes],[m4_pushdef([with_without], [--without-]with_arg)], +- [m4_pushdef([with_without],[--with-]with_arg)]) +- +-AC_ARG_WITH(with_arg, +- AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, +- [AS_TR_SH([with_]with_arg)=def_arg]) +- +-AS_CASE([$AS_TR_SH([with_]with_arg)], +- [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], +- [auto],[PKG_CHECK_MODULES([$1],[$2], +- [m4_n([def_action_if_found]) $3], +- [m4_n([def_action_if_not_found]) $4])]) +- +-m4_popdef([with_arg]) +-m4_popdef([description]) +-m4_popdef([def_arg]) +- +-])dnl PKG_WITH_MODULES +- +-dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +-dnl [DESCRIPTION], [DEFAULT]) +-dnl ----------------------------------------------- +-dnl +-dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES +-dnl check._[VARIABLE-PREFIX] is exported as make variable. +-AC_DEFUN([PKG_HAVE_WITH_MODULES], +-[ +-PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) +- +-AM_CONDITIONAL([HAVE_][$1], +- [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) +-])dnl PKG_HAVE_WITH_MODULES +- +-dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +-dnl [DESCRIPTION], [DEFAULT]) +-dnl ------------------------------------------------------ +-dnl +-dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after +-dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make +-dnl and preprocessor variable. +-AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], +-[ +-PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) +- +-AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], +- [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) +-])dnl PKG_HAVE_DEFINE_WITH_MODULES +- + # AM_CONDITIONAL -*- Autoconf -*- + +-# Copyright (C) 1997-2020 Free Software Foundation, Inc. ++# Copyright (C) 1997-2021 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -652,7 +584,7 @@ + Usually this means the macro was only invoked conditionally.]]) + fi])]) + +-# Copyright (C) 2006-2020 Free Software Foundation, Inc. ++# Copyright (C) 2006-2021 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, diff --git a/config.sub b/config.sub -index d74fb6deac..249b391d71 100755 +index d74fb6deac..09ebc4287c 100755 --- a/config.sub +++ b/config.sub +@@ -1121,7 +1121,7 @@ + xscale-* | xscalee[bl]-*) + cpu=`echo "$cpu" | sed 's/^xscale/arm/'` + ;; +- arm64-*) ++ arm64-* | arm64_32-*) + cpu=aarch64 + ;; + @@ -1723,7 +1723,7 @@ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ @@ -3572,27 +3763,94 @@ index d74fb6deac..249b391d71 100755 | mpw* | magic* | mmixware* | mon960* | lnews* \ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ +@@ -1786,6 +1786,8 @@ + ;; + *-eabi* | *-gnueabi*) + ;; ++ ios*-simulator | tvos*-simulator | watchos*-simulator) ++ ;; + -*) + # Blank kernel with real OS is always fine. + ;; diff --git a/configure b/configure -index 078bb5bef1..3bb80ee5bc 100755 +index 078bb5bef1..84ef6459fb 100755 --- a/configure +++ b/configure -@@ -3820,6 +3820,15 @@ +@@ -836,6 +836,8 @@ + LIBMPDEC_INTERNAL + LIBMPDEC_LDFLAGS + LIBMPDEC_CFLAGS ++LIBFFI_LIB ++LIBFFI_LIBDIR + LIBFFI_INCLUDEDIR + LIBEXPAT_INTERNAL + LIBEXPAT_LDFLAGS +@@ -988,7 +990,6 @@ + docdir + oldincludedir + includedir +-runstatedir + localstatedir + sharedstatedir + sysconfdir +@@ -1139,7 +1140,6 @@ + sysconfdir='${prefix}/etc' + sharedstatedir='${prefix}/com' + localstatedir='${prefix}/var' +-runstatedir='${localstatedir}/run' + includedir='${prefix}/include' + oldincludedir='/usr/include' + docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +@@ -1392,15 +1392,6 @@ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + +- -runstatedir | --runstatedir | --runstatedi | --runstated \ +- | --runstate | --runstat | --runsta | --runst | --runs \ +- | --run | --ru | --r) +- ac_prev=runstatedir ;; +- -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ +- | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ +- | --run=* | --ru=* | --r=*) +- runstatedir=$ac_optarg ;; +- + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ +@@ -1538,7 +1529,7 @@ + for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ +- libdir localedir mandir runstatedir ++ libdir localedir mandir + do + eval ac_val=\$$ac_var + # Remove trailing slashes. +@@ -1691,7 +1682,6 @@ + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] +- --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] +@@ -3820,6 +3810,15 @@ *-*-cygwin*) ac_sys_system=Cygwin ;; -+ *-apple-ios) ++ *-apple-ios*) + ac_sys_system=iOS + ;; -+ *-apple-tvos) ++ *-apple-tvos*) + ac_sys_system=tvOS + ;; -+ *-apple-watchos) ++ *-apple-watchos*) + ac_sys_system=watchOS + ;; *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -3876,6 +3885,15 @@ +@@ -3876,6 +3875,15 @@ *-*-cygwin*) _host_cpu= ;; @@ -3608,7 +3866,7 @@ index 078bb5bef1..3bb80ee5bc 100755 *-*-vxworks*) _host_cpu=$host_cpu ;; -@@ -3954,6 +3972,13 @@ +@@ -3954,6 +3962,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -3622,7 +3880,39 @@ index 078bb5bef1..3bb80ee5bc 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -6191,6 +6216,12 @@ +@@ -6146,7 +6161,30 @@ + #elif defined(__gnu_hurd__) + i386-gnu + #elif defined(__APPLE__) +- darwin ++# include "TargetConditionals.h" ++# if TARGET_OS_IOS ++# if TARGET_OS_SIMULATOR ++ iphonesimulator ++# else ++ iphoneos ++# endif ++# elif TARGET_OS_TV ++# if TARGET_OS_SIMULATOR ++ appletvsimulator ++# else ++ appletvos ++# endif ++# elif TARGET_OS_WATCH ++# if TARGET_OS_SIMULATOR ++ watchsimulator ++# else ++ watchos ++# endif ++# elif TARGET_OS_OSX ++ darwin ++# else ++# error unknown Apple platform ++# endif + #elif defined(__VXWORKS__) + vxworks + #elif defined(__wasm32__) +@@ -6191,6 +6229,12 @@ case $ac_sys_system in #( Darwin*) : MULTIARCH="" ;; #( @@ -3635,7 +3925,16 @@ index 078bb5bef1..3bb80ee5bc 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7048,11 +7079,17 @@ +@@ -6249,7 +6293,7 @@ + x86_64-*-freebsd*/clang) : + PY_SUPPORT_TIER=3 ;; #( + *) : +- PY_SUPPORT_TIER=0 ++ PY_SUPPORT_TIER=0 + ;; + esac + +@@ -7048,11 +7092,23 @@ fi if test "$cross_compiling" = yes; then @@ -3645,9 +3944,15 @@ index 078bb5bef1..3bb80ee5bc 100755 - ;; - esac + case "$host" in -+ *-apple-*os) ++ *-apple-ios*) + # readelf not required for iOS cross builds. + ;; ++ *-apple-tvos*) ++ # readelf not required for tvOS cross builds. ++ ;; ++ *-apple-watchos*) ++ # readelf not required for watchOS cross builds. ++ ;; + *) + case "$READELF" in + readelf|:) @@ -3658,32 +3963,404 @@ index 078bb5bef1..3bb80ee5bc 100755 fi -@@ -14908,6 +14945,10 @@ - then - case $ac_sys_system/$ac_sys_release in - hp*|HP*) DYNLOADFILE="dynload_hpux.o";; -+ # Disable dynamic loading on iOS -+ iOS/*) DYNLOADFILE="dynload_stub.o";; -+ tvOS/*) DYNLOADFILE="dynload_stub.o";; -+ watchOS/*) DYNLOADFILE="dynload_stub.o";; - *) - # use dynload_shlib.c and dlopen() if we have it; otherwise stub - # out any dynamic loading +@@ -10704,6 +10760,9 @@ + BLDSHARED="$LDSHARED" + fi + ;; ++ iOS/*|tvOS/*|watchOS/*) ++ LDSHARED='$(CC) -bundle -undefined dynamic_lookup' ++ LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup';; + Emscripten|WASI) + LDSHARED='$(CC) -shared' + LDCXXSHARED='$(CXX) -shared';; +@@ -11112,8 +11171,8 @@ + + + pkg_failed=no +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBUUID" >&5 +-$as_echo_n "checking for LIBUUID... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uuid >= 2.20" >&5 ++$as_echo_n "checking for uuid >= 2.20... " >&6; } + + if test -n "$LIBUUID_CFLAGS"; then + pkg_cv_LIBUUID_CFLAGS="$LIBUUID_CFLAGS" +@@ -11153,7 +11212,7 @@ + + + if test $pkg_failed = yes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then +@@ -11296,7 +11355,7 @@ + + + elif test $pkg_failed = untried; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + save_CFLAGS=$CFLAGS +@@ -11947,23 +12006,35 @@ + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_system_ffi" >&5 + $as_echo "$with_system_ffi" >&6; } + else +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +-$as_echo "yes" >&6; } +- if test "$with_system_ffi" != "" ++ if test "$ac_sys_system" = "iOS" || test "$ac_sys_system" = "tvOS" || test "$ac_sys_system" = "watchOS" + then +- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with(out)-system-ffi is ignored on this platform" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Using user-provided libffi configuration" >&5 ++$as_echo "$as_me: WARNING: Using user-provided libffi configuration" >&2;} ++ LIBFFI_LIBDIR="${LIBFFI_LIBDIR}" ++ LIBFFI_LIB="${LIBFFI_LIB}" ++ else ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 ++$as_echo "yes" >&6; } ++ if test "$with_system_ffi" != "" ++ then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with(out)-system-ffi is ignored on this platform" >&5 + $as_echo "$as_me: WARNING: --with(out)-system-ffi is ignored on this platform" >&2;} ++ fi ++ with_system_ffi="yes" + fi +- with_system_ffi="yes" + fi + + if test "$with_system_ffi" = "yes" && test -n "$PKG_CONFIG"; then + LIBFFI_INCLUDEDIR="`"$PKG_CONFIG" libffi --cflags-only-I 2>/dev/null | sed -e 's/^-I//;s/ *$//'`" + else +- LIBFFI_INCLUDEDIR="" ++ LIBFFI_INCLUDEDIR="${LIBFFI_INCLUDEDIR}" + fi + + ++ ++ + # Check for use of the system libmpdec library + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-system-libmpdec" >&5 + $as_echo_n "checking for --with-system-libmpdec... " >&6; } +@@ -12104,8 +12175,8 @@ + + + pkg_failed=no +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBNSL" >&5 +-$as_echo_n "checking for LIBNSL... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libnsl" >&5 ++$as_echo_n "checking for libnsl... " >&6; } + + if test -n "$LIBNSL_CFLAGS"; then + pkg_cv_LIBNSL_CFLAGS="$LIBNSL_CFLAGS" +@@ -12145,7 +12216,7 @@ + + + if test $pkg_failed = yes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then +@@ -12246,7 +12317,7 @@ + LIBNSL_LIBS=${LIBNSL_LIBS-$libnsl} + + elif test $pkg_failed = untried; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + LIBNSL_CFLAGS=${LIBNSL_CFLAGS-""} +@@ -12394,8 +12465,8 @@ + + + pkg_failed=no +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBSQLITE3" >&5 +-$as_echo_n "checking for LIBSQLITE3... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3 >= 3.7.15" >&5 ++$as_echo_n "checking for sqlite3 >= 3.7.15... " >&6; } + + if test -n "$LIBSQLITE3_CFLAGS"; then + pkg_cv_LIBSQLITE3_CFLAGS="$LIBSQLITE3_CFLAGS" +@@ -12435,7 +12506,7 @@ + + + if test $pkg_failed = yes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then +@@ -12457,7 +12528,7 @@ + + + elif test $pkg_failed = untried; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + LIBSQLITE3_CFLAGS=${LIBSQLITE3_CFLAGS-""} +@@ -13226,8 +13297,8 @@ + + + pkg_failed=no +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for TCLTK" >&5 +-$as_echo_n "checking for TCLTK... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $_QUERY" >&5 ++$as_echo_n "checking for $_QUERY... " >&6; } + + if test -n "$TCLTK_CFLAGS"; then + pkg_cv_TCLTK_CFLAGS="$TCLTK_CFLAGS" +@@ -13267,7 +13338,7 @@ + + + if test $pkg_failed = yes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then +@@ -13285,7 +13356,7 @@ + + found_tcltk=no + elif test $pkg_failed = untried; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + found_tcltk=no + else +@@ -13321,8 +13392,8 @@ + + + pkg_failed=no +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for X11" >&5 +-$as_echo_n "checking for X11... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for x11" >&5 ++$as_echo_n "checking for x11... " >&6; } + + if test -n "$X11_CFLAGS"; then + pkg_cv_X11_CFLAGS="$X11_CFLAGS" +@@ -13362,7 +13433,7 @@ + + + if test $pkg_failed = yes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then +@@ -13389,7 +13460,7 @@ + and X11_LIBS to avoid the need to call pkg-config. + See the pkg-config man page for more details." "$LINENO" 5 + elif test $pkg_failed = untried; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 + $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +@@ -15952,8 +16023,8 @@ + + + pkg_failed=no +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZLIB" >&5 +-$as_echo_n "checking for ZLIB... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for zlib >= 1.2.0" >&5 ++$as_echo_n "checking for zlib >= 1.2.0... " >&6; } + + if test -n "$ZLIB_CFLAGS"; then + pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" +@@ -15993,7 +16064,7 @@ + + + if test $pkg_failed = yes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then +@@ -16137,7 +16208,7 @@ + + + elif test $pkg_failed = untried; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + save_CFLAGS=$CFLAGS +@@ -16300,8 +16371,8 @@ + + + pkg_failed=no +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BZIP2" >&5 +-$as_echo_n "checking for BZIP2... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for bzip2" >&5 ++$as_echo_n "checking for bzip2... " >&6; } + + if test -n "$BZIP2_CFLAGS"; then + pkg_cv_BZIP2_CFLAGS="$BZIP2_CFLAGS" +@@ -16341,7 +16412,7 @@ + + + if test $pkg_failed = yes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then +@@ -16438,7 +16509,7 @@ + + + elif test $pkg_failed = untried; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + save_CFLAGS=$CFLAGS +@@ -16530,8 +16601,8 @@ + + + pkg_failed=no +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBLZMA" >&5 +-$as_echo_n "checking for LIBLZMA... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for liblzma" >&5 ++$as_echo_n "checking for liblzma... " >&6; } + + if test -n "$LIBLZMA_CFLAGS"; then + pkg_cv_LIBLZMA_CFLAGS="$LIBLZMA_CFLAGS" +@@ -16571,7 +16642,7 @@ + + + if test $pkg_failed = yes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then +@@ -16668,7 +16739,7 @@ + + + elif test $pkg_failed = untried; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + save_CFLAGS=$CFLAGS +@@ -17267,8 +17338,8 @@ + + + pkg_failed=no +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBCRYPT" >&5 +-$as_echo_n "checking for LIBCRYPT... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libxcrypt >= 3.1.1" >&5 ++$as_echo_n "checking for libxcrypt >= 3.1.1... " >&6; } + + if test -n "$LIBCRYPT_CFLAGS"; then + pkg_cv_LIBCRYPT_CFLAGS="$LIBCRYPT_CFLAGS" +@@ -17308,7 +17379,7 @@ + + + if test $pkg_failed = yes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then +@@ -17405,7 +17476,7 @@ + + + elif test $pkg_failed = untried; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + save_CFLAGS=$CFLAGS +@@ -22177,8 +22248,8 @@ + if ! $found; then + OPENSSL_INCLUDES= + for ssldir in $ssldirs; do +- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h in $ssldir" >&5 +-$as_echo_n "checking for openssl/ssl.h in $ssldir... " >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for include/openssl/ssl.h in $ssldir" >&5 ++$as_echo_n "checking for include/openssl/ssl.h in $ssldir... " >&6; } + if test -f "$ssldir/include/openssl/ssl.h"; then + OPENSSL_INCLUDES="-I$ssldir/include" + OPENSSL_LDFLAGS="-L$ssldir/lib" +@@ -22559,8 +22630,8 @@ + + + pkg_failed=no +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBB2" >&5 +-$as_echo_n "checking for LIBB2... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libb2" >&5 ++$as_echo_n "checking for libb2... " >&6; } + + if test -n "$LIBB2_CFLAGS"; then + pkg_cv_LIBB2_CFLAGS="$LIBB2_CFLAGS" +@@ -22600,7 +22671,7 @@ + + + if test $pkg_failed = yes; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then +@@ -22618,7 +22689,7 @@ + + have_libb2=no + elif test $pkg_failed = untried; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } + have_libb2=no + else +@@ -22689,6 +22760,29 @@ + py_cv_module_ossaudiodev=n/a + py_cv_module_spwd=n/a + ;; #( ++ iOS|tvOS|watchOS) : ++ ++ ++ ++ py_cv_module__curses=n/a ++ py_cv_module__curses_panel=n/a ++ py_cv_module__gdbm=n/a ++ py_cv_module__multiprocessing=n/a ++ py_cv_module__posixshmem=n/a ++ py_cv_module__posixsubprocess=n/a ++ py_cv_module__scproxy=n/a ++ py_cv_module__tkinter=n/a ++ py_cv_module__xxsubinterpreters=n/a ++ py_cv_module_grp=n/a ++ py_cv_module_nis=n/a ++ py_cv_module_ossaudiodev=n/a ++ py_cv_module_readline=n/a ++ py_cv_module_pwd=n/a ++ py_cv_module_spwd=n/a ++ py_cv_module_syslog=n/a ++ py_cv_module_=n/a ++ ++ ;; #( + CYGWIN*) : + + diff --git a/configure.ac b/configure.ac -index 09f3f902a6..b3bab951eb 100644 +index 09f3f902a6..8563e88760 100644 --- a/configure.ac +++ b/configure.ac @@ -545,6 +545,15 @@ *-*-cygwin*) ac_sys_system=Cygwin ;; -+ *-apple-ios) ++ *-apple-ios*) + ac_sys_system=iOS + ;; -+ *-apple-tvos) ++ *-apple-tvos*) + ac_sys_system=tvOS + ;; -+ *-apple-watchos) ++ *-apple-watchos*) + ac_sys_system=watchOS + ;; *-*-vxworks*) @@ -3719,7 +4396,39 @@ index 09f3f902a6..b3bab951eb 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -1085,6 +1110,9 @@ +@@ -1044,7 +1069,30 @@ + #elif defined(__gnu_hurd__) + i386-gnu + #elif defined(__APPLE__) +- darwin ++# include "TargetConditionals.h" ++# if TARGET_OS_IOS ++# if TARGET_OS_SIMULATOR ++ iphonesimulator ++# else ++ iphoneos ++# endif ++# elif TARGET_OS_TV ++# if TARGET_OS_SIMULATOR ++ appletvsimulator ++# else ++ appletvos ++# endif ++# elif TARGET_OS_WATCH ++# if TARGET_OS_SIMULATOR ++ watchsimulator ++# else ++ watchos ++# endif ++# elif TARGET_OS_OSX ++ darwin ++# else ++# error unknown Apple platform ++# endif + #elif defined(__VXWORKS__) + vxworks + #elif defined(__wasm32__) +@@ -1085,6 +1133,9 @@ AC_MSG_CHECKING([for multiarch]) AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], @@ -3729,7 +4438,23 @@ index 09f3f902a6..b3bab951eb 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1591,11 +1619,17 @@ +@@ -1129,6 +1180,15 @@ + dnl [wasm32-unknown-emscripten/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly Emscripten + dnl [wasm32-unknown-wasi/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly System Interface + [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 ++ dnl [aarch64-apple-ios/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 ++ dnl [aarch64-apple-ios-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 ++ dnl [x86_64-apple-ios-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on x86_64 ++ dnl [aarch64-apple-tvos/clang], [PY_SUPPORT_TIER=3], dnl Apple tvOS on ARM64 ++ dnl [aarch64-apple-tvos-simulator/clang], [PY_SUPPORT_TIER=3], dnl Apple tvOS Simulator on ARM64 ++ dnl [x86_64-apple-tvos-simulator/clang], [PY_SUPPORT_TIER=3], dnl Apple tvOS Simulator on x86_64 ++ dnl [arm64_32-apple-watchos/clang], [PY_SUPPORT_TIER=3], dnl Apple watchOS on ARM64_32 ++ dnl [aarch64-apple-watchos-simulator/clang], [PY_SUPPORT_TIER=3], dnl Apple watchOS Simulator on ARM64 ++ dnl [x86_64-apple-watchos-simulator/clang], [PY_SUPPORT_TIER=3], dnl Apple watchOS Simulator on x86_64 + [PY_SUPPORT_TIER=0] + ) + +@@ -1591,11 +1651,23 @@ AC_CHECK_TOOLS([READELF], [readelf], [:]) if test "$cross_compiling" = yes; then @@ -3739,9 +4464,15 @@ index 09f3f902a6..b3bab951eb 100644 - ;; - esac + case "$host" in -+ *-apple-*os) ++ *-apple-ios*) + # readelf not required for iOS cross builds. + ;; ++ *-apple-tvos*) ++ # readelf not required for tvOS cross builds. ++ ;; ++ *-apple-watchos*) ++ # readelf not required for watchOS cross builds. ++ ;; + *) + case "$READELF" in + readelf|:) @@ -3752,17 +4483,92 @@ index 09f3f902a6..b3bab951eb 100644 fi AC_SUBST(READELF) -@@ -4501,6 +4535,10 @@ - then - case $ac_sys_system/$ac_sys_release in - hp*|HP*) DYNLOADFILE="dynload_hpux.o";; -+ # Disable dynamic loading on iOS -+ iOS/*) DYNLOADFILE="dynload_stub.o";; -+ tvOS/*) DYNLOADFILE="dynload_stub.o";; -+ watchOS/*) DYNLOADFILE="dynload_stub.o";; - *) - # use dynload_shlib.c and dlopen() if we have it; otherwise stub - # out any dynamic loading +@@ -2066,7 +2138,7 @@ + dnl build with WASM debug info if either Py_DEBUG is set or the target is + dnl node-debug or browser-debug. + AS_VAR_IF([Py_DEBUG], [yes], [wasm_debug=yes], [wasm_debug=no]) +- ++ + dnl Start with 20 MB and allow to grow + AS_VAR_APPEND([LDFLAGS_NODIST], [" -sALLOW_MEMORY_GROWTH -sTOTAL_MEMORY=20971520"]) + +@@ -3128,6 +3200,9 @@ + BLDSHARED="$LDSHARED" + fi + ;; ++ iOS/*|tvOS/*|watchOS/*) ++ LDSHARED='$(CC) -bundle -undefined dynamic_lookup' ++ LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup';; + Emscripten|WASI) + LDSHARED='$(CC) -shared' + LDCXXSHARED='$(CXX) -shared';; +@@ -3597,20 +3672,30 @@ + esac + AC_MSG_RESULT($with_system_ffi) + else +- AC_MSG_RESULT(yes) +- if test "$with_system_ffi" != "" ++ if test "$ac_sys_system" = "iOS" || test "$ac_sys_system" = "tvOS" || test "$ac_sys_system" = "watchOS" + then +- AC_MSG_WARN([--with(out)-system-ffi is ignored on this platform]) ++ AC_MSG_RESULT(no) ++ AC_MSG_WARN([Using user-provided libffi configuration]) ++ LIBFFI_LIBDIR="${LIBFFI_LIBDIR}" ++ LIBFFI_LIB="${LIBFFI_LIB}" ++ else ++ AC_MSG_RESULT(yes) ++ if test "$with_system_ffi" != "" ++ then ++ AC_MSG_WARN([--with(out)-system-ffi is ignored on this platform]) ++ fi ++ with_system_ffi="yes" + fi +- with_system_ffi="yes" + fi + + if test "$with_system_ffi" = "yes" && test -n "$PKG_CONFIG"; then + LIBFFI_INCLUDEDIR="`"$PKG_CONFIG" libffi --cflags-only-I 2>/dev/null | sed -e 's/^-I//;s/ *$//'`" + else +- LIBFFI_INCLUDEDIR="" ++ LIBFFI_INCLUDEDIR="${LIBFFI_INCLUDEDIR}" + fi + AC_SUBST(LIBFFI_INCLUDEDIR) ++AC_SUBST(LIBFFI_LIBDIR) ++AC_SUBST(LIBFFI_LIB) + + # Check for use of the system libmpdec library + AC_MSG_CHECKING(for --with-system-libmpdec) +@@ -6764,6 +6849,30 @@ + [AIX], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])], + [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [_crypt], [termios], [grp])], + [Darwin], [PY_STDLIB_MOD_SET_NA([ossaudiodev], [spwd])], ++ [iOS|tvOS|watchOS], [ ++ dnl subprocess and multiprocessing are not supported (no fork syscall). ++ dnl curses and tkinter user interface are not available. ++ dnl gdbm, and nis aren't available ++ dnl Stub implementations are provided for pwd, grp etc APIs ++ PY_STDLIB_MOD_SET_NA( ++ [_curses], ++ [_curses_panel], ++ [_gdbm], ++ [_multiprocessing], ++ [_posixshmem], ++ [_posixsubprocess], ++ [_scproxy], ++ [_tkinter], ++ [_xxsubinterpreters], ++ [grp], ++ [nis], ++ [ossaudiodev], ++ [readline], ++ [pwd], ++ [spwd], ++ [syslog], ++ ) ++ ], + [CYGWIN*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])], + [QNX*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])], + [FreeBSD*], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])], --- /dev/null +++ b/iOS/Info.plist @@ -0,0 +1,20 @@ @@ -4621,6 +5427,60 @@ index 09f3f902a6..b3bab951eb 100644 + #import +#endif \ No newline at end of file +diff --git a/setup.py b/setup.py +index 15d0d4576a..b59083aa47 100644 +--- a/setup.py ++++ b/setup.py +@@ -82,6 +82,9 @@ + MS_WINDOWS = (HOST_PLATFORM == 'win32') + CYGWIN = (HOST_PLATFORM == 'cygwin') + MACOS = (HOST_PLATFORM == 'darwin') ++IOS = HOST_PLATFORM.startswith('ios-') ++TVOS = HOST_PLATFORM.startswith('tvos-') ++WATCHOS = HOST_PLATFORM.startswith('watchos-') + AIX = (HOST_PLATFORM.startswith('aix')) + VXWORKS = ('vxworks' in HOST_PLATFORM) + EMSCRIPTEN = HOST_PLATFORM == 'emscripten-wasm32' +@@ -1396,6 +1399,11 @@ + extra_compile_args.append('-DMACOSX') + include_dirs.append('_ctypes/darwin') + ++ elif IOS or TVOS or WATCHOS: ++ sources.append('_ctypes/malloc_closure.c') ++ extra_compile_args.append('-DUSING_MALLOC_CLOSURE_DOT_C') ++ include_dirs.append('_ctypes/darwin') ++ + elif HOST_PLATFORM == 'sunos5': + # XXX This shouldn't be necessary; it appears that some + # of the assembler code is non-PIC (i.e. it has relocations +@@ -1418,7 +1426,8 @@ + self.addext(Extension('_ctypes_test', ['_ctypes/_ctypes_test.c'])) + + ffi_inc = sysconfig.get_config_var("LIBFFI_INCLUDEDIR") +- ffi_lib = None ++ ffi_lib_dir = sysconfig.get_config_var("LIBFFI_LIBDIR") ++ ffi_lib = sysconfig.get_config_var("LIBFFI_LIB") + + ffi_inc_dirs = self.inc_dirs.copy() + if MACOS: +@@ -1447,6 +1456,7 @@ + for lib_name in ('ffi', 'ffi_pic'): + if (self.compiler.find_library_file(self.lib_dirs, lib_name)): + ffi_lib = lib_name ++ self.use_system_libffi = True + break + + if ffi_inc and ffi_lib: +@@ -1460,7 +1470,8 @@ + + ext.include_dirs.append(ffi_inc) + ext.libraries.append(ffi_lib) +- self.use_system_libffi = True ++ if ffi_lib_dir: ++ ext.library_dirs.append(ffi_lib_dir) + + if sysconfig.get_config_var('HAVE_LIBDL'): + # for dlopen, see bpo-32647 --- /dev/null +++ b/tvOS/Info.plist @@ -0,0 +1,20 @@ diff --git a/patch/Python/Setup.embedded b/patch/Python/Setup.embedded deleted file mode 100644 index e5e379e0..00000000 --- a/patch/Python/Setup.embedded +++ /dev/null @@ -1,24 +0,0 @@ -##################################################################### -# Compilation instructions for all binary modules. -##################################################################### - -##################################################################### -# DISABLED Modules that require additional frameworks -##################################################################### - -*disabled* - -_gdbm -_tkinter -readline - -##################################################################### -# Modules that exist, but are deprecated by PEP594 and will be -# removed in Python 3.13 -##################################################################### - -*disabled* - -nis -ossaudiodev -spwd diff --git a/patch/Python/Setup.iOS b/patch/Python/Setup.iOS deleted file mode 100644 index 7e9847e5..00000000 --- a/patch/Python/Setup.iOS +++ /dev/null @@ -1,31 +0,0 @@ -##################################################################### -# iOS: Platform specific configuration -##################################################################### - -*static* - -_ctypes _ctypes/_ctypes.c \ - _ctypes/callbacks.c \ - _ctypes/callproc.c \ - _ctypes/stgdict.c \ - _ctypes/cfield.c \ - _ctypes/malloc_closure.c \ - -I$(srcdir)/Modules/_ctypes/darwin \ - -I$(srcdir)/../Support/libFFI.xcframework/{{slice}}/Headers \ - -DPy_BUILD_CORE_MODULE \ - -DHAVE_FFI_CLOSURE_ALLOC \ - -DHAVE_FFI_PREP_CIF_VAR \ - -DHAVE_FFI_PREP_CLOSURE_LOC \ - -DUSING_MALLOC_CLOSURE_DOT_C \ - -L$(srcdir)/../Support/libFFI.xcframework/{{slice}} \ - -lFFI - -##################################################################### -# DISABLED Modules that require additional frameworks -##################################################################### - -*disabled* - -_curses -_curses_panel -_posixshmem diff --git a/patch/Python/Setup.macOS b/patch/Python/Setup.macOS deleted file mode 100644 index 7c8229ad..00000000 --- a/patch/Python/Setup.macOS +++ /dev/null @@ -1,3 +0,0 @@ -##################################################################### -# macOS: Platform specific configuration -##################################################################### diff --git a/patch/Python/Setup.tvOS b/patch/Python/Setup.tvOS deleted file mode 100644 index 67202998..00000000 --- a/patch/Python/Setup.tvOS +++ /dev/null @@ -1,31 +0,0 @@ -##################################################################### -# tvOS: Platform specific configuration -##################################################################### - -*static* - -_ctypes _ctypes/_ctypes.c \ - _ctypes/callbacks.c \ - _ctypes/callproc.c \ - _ctypes/stgdict.c \ - _ctypes/cfield.c \ - _ctypes/malloc_closure.c \ - -I$(srcdir)/Modules/_ctypes/darwin \ - -I$(srcdir)/../Support/libFFI.xcframework/{{slice}}/Headers \ - -DPy_BUILD_CORE_MODULE \ - -DHAVE_FFI_CLOSURE_ALLOC \ - -DHAVE_FFI_PREP_CIF_VAR \ - -DHAVE_FFI_PREP_CLOSURE_LOC \ - -DUSING_MALLOC_CLOSURE_DOT_C \ - -L$(srcdir)/../Support/libFFI.xcframework/{{slice}} \ - -lFFI - -##################################################################### -# DISABLED Modules that require additional frameworks -##################################################################### - -*disabled* - -_curses -_curses_panel -_posixshmem diff --git a/patch/Python/Setup.watchOS b/patch/Python/Setup.watchOS deleted file mode 100644 index 369dae09..00000000 --- a/patch/Python/Setup.watchOS +++ /dev/null @@ -1,31 +0,0 @@ -##################################################################### -# watchOS: Platform specific configuration -##################################################################### - -*static* - -_ctypes _ctypes/_ctypes.c \ - _ctypes/callbacks.c \ - _ctypes/callproc.c \ - _ctypes/stgdict.c \ - _ctypes/cfield.c \ - _ctypes/malloc_closure.c \ - -I$(srcdir)/Modules/_ctypes/darwin \ - -I$(srcdir)/../Support/libFFI.xcframework/{{slice}}/Headers \ - -DPy_BUILD_CORE_MODULE \ - -DHAVE_FFI_CLOSURE_ALLOC \ - -DHAVE_FFI_PREP_CIF_VAR \ - -DHAVE_FFI_PREP_CLOSURE_LOC \ - -DUSING_MALLOC_CLOSURE_DOT_C \ - -L$(srcdir)/../Support/libFFI.xcframework/{{slice}} \ - -lFFI - -##################################################################### -# DISABLED Modules that require additional frameworks -##################################################################### - -*disabled* - -_curses -_curses_panel -_posixshmem From 6ad009ade369cdc24e028d2762098b2db417b02d Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 18 Aug 2022 13:47:03 +0800 Subject: [PATCH 14/37] Restructure build products to give better control over outputs. --- .gitignore | 11 +- Makefile | 766 ++++++++++++--------- patch/Python/Python.patch | 13 +- patch/Python/release.common.exclude | 42 +- patch/Python/release.iOS.exclude | 2 +- patch/Python/test.exclude | 21 +- patch/{libffi.patch => libffi-3.4.2.patch} | 0 patch/xz-5.2.5.patch | 34 + 8 files changed, 492 insertions(+), 397 deletions(-) rename patch/{libffi.patch => libffi-3.4.2.patch} (100%) create mode 100644 patch/xz-5.2.5.patch diff --git a/.gitignore b/.gitignore index 5a32a5f6..ffdfbbf9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,17 @@ *.swo *.swp -src/* +.envrc +.vscode/ archive/* build/* -downloads/* diff/* dist/* -.envrc -.vscode/ +downloads/* +install/* local/* +merge/* +src/* +support/* *.dist-info __pycache__ tests/testbed/macOS diff --git a/Makefile b/Makefile index 48cd3a3f..eb68a6d3 100644 --- a/Makefile +++ b/Makefile @@ -61,41 +61,22 @@ CURL_FLAGS=--fail --location --create-dirs --progress-bar # macOS targets TARGETS-macOS=macosx.x86_64 macosx.arm64 -CFLAGS-macOS=-mmacosx-version-min=10.15 -CFLAGS-macosx.x86_64= -CFLAGS-macosx.arm64= -SDK_ROOT-macosx=$(shell xcrun --sdk macosx --show-sdk-path) -CC-macosx=xcrun --sdk macosx clang --sysroot=$(SDK_ROOT-macosx) $(CFLAGS-macOS) +VERSION_MIN-macOS=-mmacosx-version-min=10.15 # iOS targets TARGETS-iOS=iphonesimulator.x86_64 iphonesimulator.arm64 iphoneos.arm64 -CFLAGS-iOS=-mios-version-min=12.0 -CFLAGS-iphoneos.arm64= -CFLAGS-iphonesimulator.x86_64= -CFLAGS-iphonesimulator.arm64= +VERSION_MIN-iOS=-mios-version-min=12.0 # tvOS targets TARGETS-tvOS=appletvsimulator.x86_64 appletvsimulator.arm64 appletvos.arm64 -CFLAGS-tvOS=-mtvos-version-min=9.0 -CFLAGS-appletvos.arm64= -CFLAGS-appletvsimulator.x86_64= -CFLAGS-appletvsimulator.arm64= +VERSION_MIN-tvOS=-mtvos-version-min=9.0 PYTHON_CONFIGURE-tvOS=ac_cv_func_sigaltstack=no # watchOS targets TARGETS-watchOS=watchsimulator.x86_64 watchsimulator.arm64 watchos.arm64_32 -CFLAGS-watchOS=-mwatchos-version-min=4.0 -CFLAGS_watchsimulator.x86_64= -CFLAGS-watchsimulator.arm64= -CFLAGS-watchos.arm64_32= +VERSION_MIN-watchOS=-mwatchos-version-min=4.0 PYTHON_CONFIGURE-watchOS=ac_cv_func_sigaltstack=no -# override machine types for arm64 -MACHINE_SIMPLE-arm64=arm - -# override machine types for arm64_32 -MACHINE_SIMPLE-arm64_32=arm - # The architecture of the machine doing the build HOST_ARCH=$(shell uname -m) @@ -114,7 +95,7 @@ all: $(OS_LIST) # Clean all builds clean: - rm -rf build dist + rm -rf build install merge dist support # Full clean - includes all downloaded products distclean: clean @@ -128,7 +109,11 @@ downloads: \ downloads/Python-$(PYTHON_VERSION).tar.gz update-patch: - # Generate a diff from the clone of the python/cpython Github repository + # Generate a diff from the clone of the python/cpython Github repository, + # comparing between the current state of the 3.X branch against the v3.X.Y + # tag associated with the release being built. This allows you to + # maintain a branch that contains custom patches against the default Python. + # The patch archived in this respository is based on github.com/freakboy3742/cpython # Requires patchutils (installable via `brew install patchutils`); this # also means we need to re-introduce homebrew to the path for the filterdiff # call @@ -206,167 +191,181 @@ define build-target target=$1 os=$2 +OS_LOWER-$(target)=$(shell echo $(os) | tr '[:upper:]' '[:lower:]') + # $(target) can be broken up into is composed of $(SDK).$(ARCH) SDK-$(target)=$$(basename $(target)) ARCH-$(target)=$$(subst .,,$$(suffix $(target))) -ifdef MACHINE_SIMPLE-$$(ARCH-$(target)) -MACHINE_SIMPLE-$(target)=$$(MACHINE_SIMPLE-$$(ARCH-$(target))) -else -MACHINE_SIMPLE-$(target)=$$(ARCH-$(target)) -endif - -ifeq ($$(findstring simulator,$$(SDK-$(target))),) -TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$(shell echo $(os) | tr '[:upper:]' '[:lower:]') +ifeq ($(os),macOS) +TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-darwin else -TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$(shell echo $(os) | tr '[:upper:]' '[:lower:]')-simulator + ifeq ($$(findstring simulator,$$(SDK-$(target))),) +TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target)) + else +TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))-simulator + endif endif SDK_ROOT-$(target)=$$(shell xcrun --sdk $$(SDK-$(target)) --show-sdk-path) -CC-$(target)=xcrun --sdk $$(SDK-$(target)) clang \ +CC-$(target)=xcrun --sdk $$(SDK-$(target)) clang +CFLAGS-$(target)=\ -target $$(TARGET_TRIPLE-$(target)) \ --sysroot=$$(SDK_ROOT-$(target)) \ - $$(CFLAGS-$(os)) $$(CFLAGS-$(target)) -LDFLAGS-$(target)=-arch $$(ARCH-$(target)) -isysroot=$$(SDK_ROOT-$(target)) + $$(VERSION_MIN-$(os)) +LDFLAGS-$(target)=\ + -target $$(TARGET_TRIPLE-$(target)) \ + -isysroot $$(SDK_ROOT-$(target)) \ + $$(VERSION_MIN-$(os)) ########################################################################### # Target: BZip2 ########################################################################### -BZIP2_DIR-$(target)=build/$(os)/bzip2-$(BZIP2_VERSION)-$(target) -BZIP2_LIB-$(target)=$$(BZIP2_DIR-$(target))/_install/lib/libbz2.a +BZIP2_SRCDIR-$(target)=build/$(os)/$(target)/bzip2-$(BZIP2_VERSION) +BZIP2_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/bzip2-$(BZIP2_VERSION) +BZIP2_LIB-$(target)=$$(BZIP2_INSTALL-$(target))/lib/libbz2.a -$$(BZIP2_DIR-$(target))/Makefile: downloads/bzip2-$(BZIP2_VERSION).tar.gz +$$(BZIP2_SRCDIR-$(target))/Makefile: downloads/bzip2-$(BZIP2_VERSION).tar.gz @echo ">>> Unpack BZip2 sources for $(target)" - mkdir -p $$(BZIP2_DIR-$(target)) - tar zxf $$< --strip-components 1 -C $$(BZIP2_DIR-$(target)) + mkdir -p $$(BZIP2_SRCDIR-$(target)) + tar zxf $$< --strip-components 1 -C $$(BZIP2_SRCDIR-$(target)) # Touch the makefile to ensure that Make identifies it as up to date. - touch $$(BZIP2_DIR-$(target))/Makefile + touch $$(BZIP2_SRCDIR-$(target))/Makefile -$$(BZIP2_LIB-$(target)): $$(BZIP2_DIR-$(target))/Makefile +$$(BZIP2_LIB-$(target)): $$(BZIP2_SRCDIR-$(target))/Makefile @echo ">>> Build BZip2 for $(target)" - cd $$(BZIP2_DIR-$(target)) && \ + cd $$(BZIP2_SRCDIR-$(target)) && \ make install \ - PREFIX="$(PROJECT_DIR)/$$(BZIP2_DIR-$(target))/_install" \ + PREFIX="$$(BZIP2_INSTALL-$(target))" \ CC="$$(CC-$(target))" \ - 2>&1 | tee -a ../bzip2-$(target).build.log + CFLAGS="$$(CFLAGS-$(target))" \ + LDFLAGS="$$(LDFLAGS-$(target))" \ + 2>&1 | tee -a ../bzip2-$(BZIP2_VERSION).build.log ########################################################################### # Target: XZ (LZMA) ########################################################################### -XZ_DIR-$(target)=build/$(os)/xz-$(XZ_VERSION)-$(target) -XZ_LIB-$(target)=$$(XZ_DIR-$(target))/_install/lib/liblzma.a +XZ_SRCDIR-$(target)=build/$(os)/$(target)/xz-$(XZ_VERSION) +XZ_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/xz-$(XZ_VERSION) +XZ_LIB-$(target)=$$(XZ_INSTALL-$(target))/lib/liblzma.a -$$(XZ_DIR-$(target))/Makefile: downloads/xz-$(XZ_VERSION).tar.gz +$$(XZ_SRCDIR-$(target))/Makefile: downloads/xz-$(XZ_VERSION).tar.gz @echo ">>> Unpack XZ sources for $(target)" - mkdir -p $$(XZ_DIR-$(target)) - tar zxf $$< --strip-components 1 -C $$(XZ_DIR-$(target)) + mkdir -p $$(XZ_SRCDIR-$(target)) + tar zxf $$< --strip-components 1 -C $$(XZ_SRCDIR-$(target)) + # Patch the source to add support for new platforms + cd $$(XZ_SRCDIR-$(target)) && patch -p1 < $(PROJECT_DIR)/patch/xz-$(XZ_VERSION).patch # Configure the build - cd $$(XZ_DIR-$(target)) && \ + cd $$(XZ_SRCDIR-$(target)) && \ ./configure \ CC="$$(CC-$(target))" \ + CFLAGS="$$(CFLAGS-$(target))" \ LDFLAGS="$$(LDFLAGS-$(target))" \ - --disable-shared --enable-static \ - --host=$$(MACHINE_SIMPLE-$(target))-apple-darwin \ - --prefix="$(PROJECT_DIR)/$$(XZ_DIR-$(target))/_install" \ - 2>&1 | tee -a ../xz-$(target).config.log + --disable-shared \ + --enable-static \ + --host=$$(TARGET_TRIPLE-$(target)) \ + --prefix="$$(XZ_INSTALL-$(target))" \ + 2>&1 | tee -a ../xz-$(XZ_VERSION).config.log -$$(XZ_LIB-$(target)): $$(XZ_DIR-$(target))/Makefile +$$(XZ_LIB-$(target)): $$(XZ_SRCDIR-$(target))/Makefile @echo ">>> Build and install XZ for $(target)" - cd $$(XZ_DIR-$(target)) && \ + cd $$(XZ_SRCDIR-$(target)) && \ make install \ - 2>&1 | tee -a ../xz-$(target).build.log + 2>&1 | tee -a ../xz-$(XZ_VERSION).build.log ########################################################################### # Target: OpenSSL ########################################################################### -OPENSSL_DIR-$(target)=build/$(os)/openssl-$(OPENSSL_VERSION)-$(target) -OPENSSL_SSL_LIB-$(target)=$$(OPENSSL_DIR-$(target))/_install/lib/libssl.a -OPENSSL_CRYPTO_LIB-$(target)=$$(OPENSSL_DIR-$(target))/_install/lib/libcrypto.a +OPENSSL_SRCDIR-$(target)=build/$(os)/$(target)/openssl-$(OPENSSL_VERSION) +OPENSSL_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/openssl-$(OPENSSL_VERSION) +OPENSSL_SSL_LIB-$(target)=$$(OPENSSL_INSTALL-$(target))/lib/libssl.a +OPENSSL_CRYPTO_LIB-$(target)=$$(OPENSSL_INSTALL-$(target))/lib/libcrypto.a -$$(OPENSSL_DIR-$(target))/is_configured: downloads/openssl-$(OPENSSL_VERSION).tar.gz +$$(OPENSSL_SRCDIR-$(target))/is_configured: downloads/openssl-$(OPENSSL_VERSION).tar.gz @echo ">>> Unpack and configure OpenSSL sources for $(target)" - mkdir -p $$(OPENSSL_DIR-$(target)) - tar zxf $$< --strip-components 1 -C $$(OPENSSL_DIR-$(target)) + mkdir -p $$(OPENSSL_SRCDIR-$(target)) + tar zxf $$< --strip-components 1 -C $$(OPENSSL_SRCDIR-$(target)) ifeq ($$(findstring simulator,$$(SDK-$(target))),) # Tweak ui_openssl.c - sed -ie "s!static volatile sig_atomic_t intr_signal;!static volatile intr_signal;!" $$(OPENSSL_DIR-$(target))/crypto/ui/ui_openssl.c + sed -ie "s!static volatile sig_atomic_t intr_signal;!static volatile intr_signal;!" $$(OPENSSL_SRCDIR-$(target))/crypto/ui/ui_openssl.c endif ifeq ($$(findstring iphone,$$(SDK-$(target))),) # Patch apps/speed.c and apps/ocsp.c to not use fork() since it's not available on tvOS - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_DIR-$(target))/apps/speed.c - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_DIR-$(target))/apps/ocsp.c + sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/speed.c + sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/ocsp.c # Patch Configure to build for tvOS or watchOS, not iOS - LC_ALL=C sed -ie 's/-D_REENTRANT:iOS/-D_REENTRANT:$(os)/' $$(OPENSSL_DIR-$(target))/Configure + LC_ALL=C sed -ie 's/-D_REENTRANT:iOS/-D_REENTRANT:$(os)/' $$(OPENSSL_SRCDIR-$(target))/Configure endif # Configure the OpenSSL build ifeq ($(os),macOS) - cd $$(OPENSSL_DIR-$(target)) && \ - CC="$$(CC-$(target))" \ + cd $$(OPENSSL_SRCDIR-$(target)) && \ + CC="$$(CC-$(target)) $$(CFLAGS-$(target))" \ ./Configure darwin64-$$(ARCH-$(target))-cc no-tests \ - --prefix="$(PROJECT_DIR)/$$(OPENSSL_DIR-$(target))/_install" \ + --prefix="$$(OPENSSL_INSTALL-$(target))" \ --openssldir=/etc/ssl \ - 2>&1 | tee -a ../openssl-$(target).config.log + 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).config.log else - cd $$(OPENSSL_DIR-$(target)) && \ - CC="$$(CC-$(target))" \ + cd $$(OPENSSL_SRCDIR-$(target)) && \ + CC="$$(CC-$(target)) $$(CFLAGS-$(target))" \ CROSS_TOP="$$(dir $$(SDK_ROOT-$(target))).." \ CROSS_SDK="$$(notdir $$(SDK_ROOT-$(target)))" \ ./Configure iphoneos-cross no-asm no-tests \ - --prefix="$(PROJECT_DIR)/$$(OPENSSL_DIR-$(target))/_install" \ + --prefix="$$(OPENSSL_INSTALL-$(target))" \ --openssldir=/etc/ssl \ - 2>&1 | tee -a ../openssl-$(target).config.log + 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).config.log endif # The OpenSSL Makefile is... interesting. Invoking `make all` or `make # install` *modifies the Makefile*. Therefore, we can't use the Makefile as # a build dependency, because building/installing dirties the target that # was used as a dependency. To compensate, create a dummy file as a marker # for whether OpenSSL has been configured, and use *that* as a reference. - date > $$(OPENSSL_DIR-$(target))/is_configured + date > $$(OPENSSL_SRCDIR-$(target))/is_configured -$$(OPENSSL_DIR-$(target))/libssl.a: $$(OPENSSL_DIR-$(target))/is_configured +$$(OPENSSL_SRCDIR-$(target))/libssl.a: $$(OPENSSL_SRCDIR-$(target))/is_configured @echo ">>> Build OpenSSL for $(target)" # OpenSSL's `all` target modifies the Makefile; # use the raw targets that make up all and it's dependencies - cd $$(OPENSSL_DIR-$(target)) && \ - CC="$$(CC-$(target))" \ + cd $$(OPENSSL_SRCDIR-$(target)) && \ + CC="$$(CC-$(target)) $$(CFLAGS-$(target))" \ CROSS_TOP="$$(dir $$(SDK_ROOT-$(target))).." \ CROSS_SDK="$$(notdir $$(SDK_ROOT-$(target)))" \ make all \ - 2>&1 | tee -a ../openssl-$(target).build.log + 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).build.log -$$(OPENSSL_SSL_LIB-$(target)): $$(OPENSSL_DIR-$(target))/libssl.a +$$(OPENSSL_SSL_LIB-$(target)): $$(OPENSSL_SRCDIR-$(target))/libssl.a @echo ">>> Install OpenSSL for $(target)" # Install just the software (not the docs) - cd $$(OPENSSL_DIR-$(target)) && \ - CC="$$(CC-$(target))" \ + cd $$(OPENSSL_SRCDIR-$(target)) && \ + CC="$$(CC-$(target)) $$(CFLAGS-$(target))" \ CROSS_TOP="$$(dir $$(SDK_ROOT-$(target))).." \ CROSS_SDK="$$(notdir $$(SDK_ROOT-$(target)))" \ make install_sw \ - 2>&1 | tee -a ../openssl-$(target).install.log + 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).install.log ########################################################################### # Target: libFFI ########################################################################### # macOS builds use the system libFFI, so there's no need to do -# a per-target build on macOS +# a per-target build on macOS. +# The configure step is performed as part of the OS-level build. ifneq ($(os),macOS) -LIBFFI_DIR-$(os)=build/$(os)/libffi -LIBFFI_DIR-$(target)=$$(LIBFFI_DIR-$(os))/build_$$(SDK-$(target))-$$(ARCH-$(target)) -LIBFFI_LIB-$(target)=$$(LIBFFI_DIR-$(target))/.libs/libffi.a +LIBFFI_SRCDIR-$(os)=build/$(os)/libffi-$(LIBFFI_VERSION) +LIBFFI_SRCDIR-$(target)=$$(LIBFFI_SRCDIR-$(os))/build_$$(SDK-$(target))-$$(ARCH-$(target)) +LIBFFI_LIB-$(target)=$$(LIBFFI_SRCDIR-$(target))/.libs/libffi.a -$$(LIBFFI_LIB-$(target)): $$(LIBFFI_DIR-$(os))/darwin_common/include/ffi.h +$$(LIBFFI_LIB-$(target)): $$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h @echo ">>> Build libFFI for $(target)" - cd $$(LIBFFI_DIR-$(target)) && \ + cd $$(LIBFFI_SRCDIR-$(target)) && \ make \ - 2>&1 | tee -a ../../libffi-$(target).build.log + 2>&1 | tee -a ../../libffi-$(LIBFFI_VERSION).build.log endif @@ -374,73 +373,63 @@ endif # Target: Python ########################################################################### -# macOS builds are compiled as a single universal2 build. As a result, -# the macOS Python build is configured in the `build` macro, rather than the +# macOS builds are compiled as a single universal2 build. +# The macOS Python build is configured in the `build-sdk` macro, rather than the # `build-target` macro. -ifeq ($(os),macOS) - -# These constants will be the same for all macOS targets -PYTHON_DIR-$(target)=build/$(os)/Python-$(PYTHON_VERSION)-$(os) -PYTHON_LIB-$(target)=$$(PYTHON_DIR-$(target))/_install/lib/libpython$(PYTHON_VER).a -# PYCONFIG_H-$(target) not defined, as there's no header shim needed - -else +ifneq ($(os),macOS) -PYTHON_DIR-$(target)=build/$(os)/Python-$(PYTHON_VERSION)-$(target) -PYTHON_LIB-$(target)=$$(PYTHON_DIR-$(target))/_install/lib/libpython$(PYTHON_VER).a -PYCONFIG_H-$(target)=build/$(os)/python/$$(SDK-$(target))/include/python$(PYTHON_VER)/pyconfig-$$(ARCH-$(target)).h +PYTHON_SRCDIR-$(target)=build/$(os)/$(target)/python-$(PYTHON_VERSION) +PYTHON_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/python-$(PYTHON_VERSION) +PYTHON_LIB-$(target)=$$(PYTHON_INSTALL-$(target))/lib/libpython$(PYTHON_VER).a -$$(PYTHON_DIR-$(target))/Makefile: \ - downloads/Python-$(PYTHON_VERSION).tar.gz +$$(PYTHON_SRCDIR-$(target))/Makefile: \ + downloads/Python-$(PYTHON_VERSION).tar.gz \ $$(BZIP2_FATLIB-$$(SDK-$(target))) \ $$(XZ_FATLIB-$$(SDK-$(target))) \ - $$(OPENSSL_FAT_INCLUDE-$$(SDK-$(target))) $$(OPENSSL_SSL_FATLIB-$$(SDK-$(target))) $$(OPENSSL_CRYPTO_FATLIB-$$(SDK-$(target))) \ - $$(LIBFFI_FATLIB-$$(SDK-$(target))) \ + $$(OPENSSL_FATINCLUDE-$$(SDK-$(target))) $$(OPENSSL_SSL_FATLIB-$$(SDK-$(target))) $$(OPENSSL_CRYPTO_FATLIB-$$(SDK-$(target))) \ + $$(LIBFFI_FATLIB-$$(SDK-$(target))) @echo ">>> Unpack and configure Python for $(target)" - mkdir -p $$(PYTHON_DIR-$(target)) - tar zxf $$< --strip-components 1 -C $$(PYTHON_DIR-$(target)) + mkdir -p $$(PYTHON_SRCDIR-$(target)) + tar zxf downloads/Python-$(PYTHON_VERSION).tar.gz --strip-components 1 -C $$(PYTHON_SRCDIR-$(target)) # Apply target Python patches - cd $$(PYTHON_DIR-$(target)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch + cd $$(PYTHON_SRCDIR-$(target)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch # Configure target Python - cd $$(PYTHON_DIR-$(target)) && \ + cd $$(PYTHON_SRCDIR-$(target)) && \ ./configure \ CC="$$(CC-$(target))" \ - LIBLZMA_CFLAGS="-I../xz/$$(SDK-$(target))/include" \ - LIBLZMA_LIBS="-L../xz/$$(SDK-$(target))/lib -lxz" \ - BZIP2_CFLAGS="-I../bzip2/$$(SDK-$(target))/include" \ - BZIP2_LIBS="-L../bzip2/$$(SDK-$(target))/lib -lbzip2" \ - LIBFFI_INCLUDEDIR="../libffi/_install/$$(SDK-$(target))/include" \ - LIBFFI_LIBDIR="../libffi/_install/$$(SDK-$(target))/lib" \ + CFLAGS="$$(CFLAGS-$(target))" \ + LDFLAGS="$$(LDFLAGS-$(target))" \ + LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$$(SDK-$(target)))/include" \ + LIBLZMA_LIBS="-L$$(XZ_MERGE-$$(SDK-$(target)))/lib -lxz" \ + BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$$(SDK-$(target)))/include" \ + BZIP2_LIBS="-L$$(BZIP2_MERGE-$$(SDK-$(target)))/lib -lbzip2" \ + LIBFFI_INCLUDEDIR="$$(LIBFFI_MERGE-$$(SDK-$(target)))/include" \ + LIBFFI_LIBDIR="$$(LIBFFI_MERGE-$$(SDK-$(target)))/lib" \ LIBFFI_LIB="ffi" \ --host=$$(TARGET_TRIPLE-$(target)) \ --build=$(HOST_ARCH)-apple-darwin \ - --with-build-python=$(PROJECT_DIR)/$(PYTHON_DIR-macOS)/_install/bin/python$(PYTHON_VER) \ - --prefix="$(PROJECT_DIR)/$$(PYTHON_DIR-$(target))/_install" \ + --with-build-python=$$(PYTHON_INSTALL-macosx)/bin/python$(PYTHON_VER) \ + --prefix="$$(PYTHON_INSTALL-$(target))" \ --enable-ipv6 \ - --with-openssl=../openssl/$$(SDK-$(target)) \ + --with-openssl="$$(OPENSSL_MERGE-$$(SDK-$(target)))" \ --without-doc-strings \ --without-ensurepip \ ac_cv_file__dev_ptmx=no \ ac_cv_file__dev_ptc=no \ $$(PYTHON_CONFIGURE-$(os)) \ - 2>&1 | tee -a ../python-$(target).config.log + 2>&1 | tee -a ../python-$(PYTHON_VERSION).config.log -$$(PYTHON_DIR-$(target))/python.exe: $$(PYTHON_DIR-$(target))/Makefile +$$(PYTHON_SRCDIR-$(target))/python.exe: $$(PYTHON_SRCDIR-$(target))/Makefile @echo ">>> Build Python for $(target)" - cd $$(PYTHON_DIR-$(target)) && \ + cd $$(PYTHON_SRCDIR-$(target)) && \ make all \ - 2>&1 | tee -a ../python-$(target).build.log + 2>&1 | tee -a ../python-$(PYTHON_VERSION).build.log -$$(PYTHON_LIB-$(target)): $$(PYTHON_DIR-$(target))/python.exe +$$(PYTHON_LIB-$(target)): $$(PYTHON_SRCDIR-$(target))/python.exe @echo ">>> Install Python for $(target)" - cd $$(PYTHON_DIR-$(target)) && \ + cd $$(PYTHON_SRCDIR-$(target)) && \ make install \ - 2>&1 | tee -a ../python-$(target).install.log - -$$(PYCONFIG_H-$(target)): $$(PYTHON_LIB-$(target)) - @echo ">>> Install pyconfig headers for $(target)" - cp $(PROJECT_DIR)/patch/Python/pyconfig-$(os).h build/$(os)/python/$$(SDK-$(target))/include/python$(PYTHON_VER)/pyconfig.h - cp $$(PYTHON_DIR-$(target))/_install/include/python$(PYTHON_VER)/pyconfig.h $$(PYCONFIG_H-$(target)) + 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log endif @@ -450,23 +439,28 @@ endif vars-$(target): @echo ">>> Environment variables for $(target)" - @echo "ARCH-$(target): $$(ARCH-$(target))" - @echo "MACHINE_DETAILED-$(target): $$(MACHINE_DETAILED-$(target))" @echo "SDK-$(target): $$(SDK-$(target))" + @echo "ARCH-$(target): $$(ARCH-$(target))" + @echo "TARGET_TRIPLE-$(target): $$(TARGET_TRIPLE-$(target))" @echo "SDK_ROOT-$(target): $$(SDK_ROOT-$(target))" @echo "CC-$(target): $$(CC-$(target))" - @echo "BZIP2_DIR-$(target): $$(BZIP2_DIR-$(target))" + @echo "CFLAGS-$(target): $$(CFLAGS-$(target))" + @echo "LDFLAGS-$(target): $$(LDFLAGS-$(target))" + @echo "BZIP2_SRCDIR-$(target): $$(BZIP2_SRCDIR-$(target))" + @echo "BZIP2_INSTALL-$(target): $$(BZIP2_INSTALL-$(target))" @echo "BZIP2_LIB-$(target): $$(BZIP2_LIB-$(target))" - @echo "XZ_DIR-$(target): $$(XZ_DIR-$(target))" + @echo "XZ_SRCDIR-$(target): $$(XZ_SRCDIR-$(target))" + @echo "XZ_INSTALL-$(target): $$(XZ_INSTALL-$(target))" @echo "XZ_LIB-$(target): $$(XZ_LIB-$(target))" - @echo "OPENSSL_DIR-$(target): $$(OPENSSL_DIR-$(target))" + @echo "OPENSSL_SRCDIR-$(target): $$(OPENSSL_SRCDIR-$(target))" + @echo "OPENSSL_INSTALL-$(target): $$(OPENSSL_INSTALL-$(target))" @echo "OPENSSL_SSL_LIB-$(target): $$(OPENSSL_SSL_LIB-$(target))" @echo "OPENSSL_CRYPTO_LIB-$(target): $$(OPENSSL_CRYPTO_LIB-$(target))" - @echo "LIBFFI_DIR-$(target): $$(LIBFFI_DIR-$(target))" + @echo "LIBFFI_SRCDIR-$(target): $$(LIBFFI_SRCDIR-$(target))" @echo "LIBFFI_LIB-$(target): $$(LIBFFI_LIB-$(target))" - @echo "PYTHON_DIR-$(target): $$(PYTHON_DIR-$(target))" + @echo "PYTHON_SRCDIR-$(target): $$(PYTHON_SRCDIR-$(target))" + @echo "PYTHON_INSTALL-$(target): $$(PYTHON_INSTALL-$(target))" @echo "PYTHON_LIB-$(target): $$(PYTHON_LIB-$(target))" - @echo "PYCONFIG_H-$(target): $$(PYCONFIG_H-$(target))" @echo endef # build-target @@ -480,31 +474,53 @@ endef # build-target # - $2 OS (e.g., iOS, tvOS) # ########################################################################### -define targets-sdk +define build-sdk sdk=$1 os=$2 -BZIP2_FATLIB-$(sdk)=build/$(os)/bzip2/$(sdk)/lib/libbzip2.a +OS_LOWER-$(sdk)=$(shell echo $(os) | tr '[:upper:]' '[:lower:]') -XZ_FATLIB-$(sdk)=build/$(os)/xz/$(sdk)/lib/libxz.a +SDK_TARGETS-$(sdk)=$$(filter $(sdk).%,$$(TARGETS-$(os))) +SDK_ARCHES-$(sdk)=$$(sort $$(subst .,,$$(suffix $$(SDK_TARGETS-$(sdk))))) -OPENSSL_FAT_INCLUDE-$(sdk)=build/$(os)/openssl/$(sdk)/include -OPENSSL_SSL_FATLIB-$(sdk)=build/$(os)/openssl/$(sdk)/lib/libssl.a -OPENSSL_CRYPTO_FATLIB-$(sdk)=build/$(os)/openssl/$(sdk)/lib/libcrypto.a +ifeq ($$(findstring simulator,$(sdk)),) +SDK_SLICE-$(sdk)=$$(OS_LOWER-$(sdk))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g") +else +SDK_SLICE-$(sdk)=$$(OS_LOWER-$(sdk))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g")-simulator +endif -LIBFFI_FATLIB-$(sdk)=$$(LIBFFI_DIR-$(os))/_install/$(sdk)/lib/libffi.a +SDK_ROOT-$(sdk)=$$(shell xcrun --sdk $(sdk) --show-sdk-path) +CC-$(sdk)=xcrun --sdk $(sdk) clang +CFLAGS-$(sdk)=\ + --sysroot=$$(SDK_ROOT-$(sdk)) \ + $$(VERSION_MIN-$(os)) +LDFLAGS-$(sdk)=\ + -isysroot $$(SDK_ROOT-$(sdk)) \ + $$(VERSION_MIN-$(os)) -PYTHON_DIR-$(sdk)=build/$(os)/python/$(sdk) -PYTHON_FATLIB-$(sdk)=$$(PYTHON_DIR-$(sdk))/lib/libPython.a +# Predeclare SDK constants that are used by the build-target macro -endef +BZIP2_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION) +BZIP2_FATLIB-$(sdk)=$$(BZIP2_MERGE-$(sdk))/lib/libbzip2.a -define build-sdk -sdk=$1 -os=$2 +XZ_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/xz-$(XZ_VERSION) +XZ_FATLIB-$(sdk)=$$(XZ_MERGE-$(sdk))/lib/liblzma.a -SDK_TARGETS-$(sdk)=$$(filter $(sdk).%,$$(TARGETS-$(os))) -SDK_ARCHES-$(sdk)=$$(sort $$(subst .,,$$(suffix $$(SDK_TARGETS-$(sdk))))) +OPENSSL_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION) +OPENSSL_FATINCLUDE-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/include +OPENSSL_SSL_FATLIB-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/lib/libssl.a +OPENSSL_CRYPTO_FATLIB-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/lib/libcrypto.a + +LIBFFI_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/libffi-$(LIBFFI_VERSION) +LIBFFI_FATLIB-$(sdk)=$$(LIBFFI_MERGE-$(sdk))/lib/libffi.a + +PYTHON_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/python-$(PYTHON_VERSION) +PYTHON_FATLIB-$(sdk)=$$(PYTHON_MERGE-$(sdk))/libPython$(PYTHON_VER).a +PYTHON_FATINCLUDE-$(sdk)=$$(PYTHON_MERGE-$(sdk))/Headers +PYTHON_FATSTDLIB-$(sdk)=$$(PYTHON_MERGE-$(sdk))/python-stdlib + +# Expand the build-target macro for target on this OS +$$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(eval $$(call build-target,$$(target),$(os)))) ########################################################################### # SDK: BZip2 @@ -512,11 +528,11 @@ SDK_ARCHES-$(sdk)=$$(sort $$(subst .,,$$(suffix $$(SDK_TARGETS-$(sdk))))) $$(BZIP2_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(BZIP2_LIB-$$(target))) @echo ">>> Build BZip2 fat library for $(sdk)" - mkdir -p build/$(os)/bzip2/$(sdk)/lib + mkdir -p $$(BZIP2_MERGE-$(sdk))/lib xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ $$^ \ - 2>&1 | tee -a build/$(os)/bzip2-$(sdk).libtool.log - # Copy headers from the first target associated with the SDK - cp -r $$(BZIP2_DIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/_install/include build/$(os)/bzip2/$(sdk) + 2>&1 | tee -a merge/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION).libtool.log + # Copy headers from the first target associated with the $(sdk) SDK + cp -r $$(BZIP2_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(BZIP2_MERGE-$(sdk)) ########################################################################### # SDK: XZ (LZMA) @@ -524,34 +540,34 @@ $$(BZIP2_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(BZIP2_LIB-$ $$(XZ_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(XZ_LIB-$$(target))) @echo ">>> Build XZ fat library for $(sdk)" - mkdir -p build/$(os)/xz/$(sdk)/lib + mkdir -p $$(XZ_MERGE-$(sdk))/lib xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ $$^ \ - 2>&1 | tee -a build/$(os)/xz-$(sdk).libtool.log - # Copy headers from the first target associated with the SDK - cp -r $$(XZ_DIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/_install/include build/$(os)/xz/$(sdk) + 2>&1 | tee -a merge/$(os)/$(sdk)/xz-$(XZ_VERSION).libtool.log + # Copy headers from the first target associated with the $(sdk) SDK + cp -r $$(XZ_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(XZ_MERGE-$(sdk)) ########################################################################### # SDK: OpenSSL ########################################################################### -$$(OPENSSL_FAT_INCLUDE-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) +$$(OPENSSL_FATINCLUDE-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) @echo ">>> Copy OpenSSL headers from the first target associated with the SDK" - mkdir -p build/$(os)/openssl/$(sdk) - cp -r $$(OPENSSL_DIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/_install/include build/$(os)/openssl/$(sdk) + mkdir -p $$(OPENSSL_MERGE-$(sdk)) + cp -r $$(OPENSSL_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(OPENSSL_MERGE-$(sdk)) $$(OPENSSL_SSL_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) @echo ">>> Build OpenSSL ssl fat library for $(sdk)" - mkdir -p build/$(os)/openssl/$(sdk)/lib + mkdir -p $$(OPENSSL_MERGE-$(sdk))/lib xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ \ $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) \ - 2>&1 | tee -a build/$(os)/openssl-$(sdk)-ssl.libtool.log + 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).ssl.libtool.log $$(OPENSSL_CRYPTO_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) @echo ">>> Build OpenSSL crypto fat library for $(sdk)" - mkdir -p build/$(os)/openssl/$(sdk)/lib + mkdir -p $$(OPENSSL_MERGE-$(sdk))/lib xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ \ $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) \ - 2>&1 | tee -a build/$(os)/openssl-$(sdk)-crypto.libtool.log + 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).crypto.libtool.log ########################################################################### # SDK: libFFI @@ -559,43 +575,132 @@ $$(OPENSSL_CRYPTO_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OP $$(LIBFFI_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(LIBFFI_LIB-$$(target))) @echo ">>> Build libFFI fat library for $(sdk)" - mkdir -p $$(LIBFFI_DIR-$(os))/_install/$(sdk)/lib + mkdir -p $$(LIBFFI_MERGE-$(sdk))/lib xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ $$^ \ - 2>&1 | tee -a build/$(os)/libffi-$(sdk).libtool.log - # Copy headers from the first target associated with the SDK - cp -f -r $$(LIBFFI_DIR-$(os))/darwin_common/include \ - $$(LIBFFI_DIR-$(os))/_install/$(sdk) - cp -f -r $$(LIBFFI_DIR-$(os))/darwin_$(shell echo $(os) | tr '[:upper:]' '[:lower:]')/include/* \ - $$(LIBFFI_DIR-$(os))/_install/$(sdk)/include + 2>&1 | tee -a merge/$(os)/$(sdk)/libffi-$(LIBFFI_VERSION).libtool.log + # Copy headers from the first target associated with the $(sdk) SDK + cp -f -r $$(LIBFFI_SRCDIR-$(os))/darwin_common/include \ + $$(LIBFFI_MERGE-$(sdk)) + cp -f -r $$(LIBFFI_SRCDIR-$(os))/darwin_$$(OS_LOWER-$(sdk))/include/* \ + $$(LIBFFI_MERGE-$(sdk))/include ########################################################################### # SDK: Python ########################################################################### +# macOS builds are compiled as a single universal2 build. The fat library is a +# direct copy of OS build, and the headers and standard library are unmodified +# from the versions produced by the OS build. ifeq ($(os),macOS) -# There's a single OS-level build for macOS; the fat library is a direct copy of -# OS build, and the headers are the unmodifed headers produced by the OS build. -$$(PYTHON_FATLIB-$(sdk)): $$(PYTHON_LIB-$$(firstword $$(SDK_TARGETS-$(sdk)))) +PYTHON_SRCDIR-$(sdk)=build/$(os)/$(sdk)/python-$(PYTHON_VERSION) +PYTHON_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/python-$(PYTHON_VERSION) +PYTHON_LIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/libpython$(PYTHON_VER).a + +$$(PYTHON_SRCDIR-$(sdk))/Makefile: \ + $$(BZIP2_FATLIB-$$(sdk)) \ + $$(XZ_FATLIB-$$(sdk)) \ + $$(OPENSSL_FATINCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk)) \ + downloads/Python-$(PYTHON_VERSION).tar.gz + @echo ">>> Unpack and configure Python for $(sdk)" + mkdir -p $$(PYTHON_SRCDIR-$(sdk)) + tar zxf downloads/Python-$(PYTHON_VERSION).tar.gz --strip-components 1 -C $$(PYTHON_SRCDIR-$(sdk)) + # Apply target Python patches + cd $$(PYTHON_SRCDIR-$(sdk)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch + # Configure target Python + cd $$(PYTHON_SRCDIR-$(sdk)) && \ + ./configure \ + CC="$$(CC-$(sdk))" \ + CFLAGS="$$(CFLAGS-$(sdk))" \ + LDFLAGS="$$(LDFLAGS-$(sdk))" \ + LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$(sdk))/include" \ + LIBLZMA_LIBS="-L$$(XZ_MERGE-$(sdk))/lib -llzma" \ + BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$(sdk))/include" \ + BZIP2_LIBS="-L$$(BZIP2_MERGE-$(sdk))/lib -lbzip2" \ + --prefix="$$(PYTHON_INSTALL-$(sdk))" \ + --enable-ipv6 \ + --enable-universalsdk \ + --with-openssl="$$(OPENSSL_MERGE-$(sdk))" \ + --with-universal-archs=universal2 \ + --without-doc-strings \ + --without-ensurepip \ + 2>&1 | tee -a ../python-$(PYTHON_VERSION).config.log + +$$(PYTHON_SRCDIR-$(sdk))/python.exe: \ + $$(PYTHON_SRCDIR-$(sdk))/Makefile + @echo ">>> Build Python for $(sdk)" + cd $$(PYTHON_SRCDIR-$(sdk)) && \ + make all \ + 2>&1 | tee -a ../python-$(PYTHON_VERSION).build.log + +$$(PYTHON_LIB-$(sdk)): $$(PYTHON_SRCDIR-$(sdk))/python.exe + @echo ">>> Install Python for $(sdk)" + cd $$(PYTHON_SRCDIR-$(sdk)) && \ + make install \ + 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log + +$$(PYTHON_FATLIB-$(sdk)): $$(PYTHON_LIB-$(sdk)) @echo ">>> Build Python fat library for $(sdk)" - # Copy the OS-level library - mkdir -p build/$(os)/python/$(sdk)/lib - cp $$(PYTHON_LIB-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_FATLIB-$(sdk)) - # Copy headers from the OS-level build - cp -r $$(PYTHON_DIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/_install/include build/$(os)/python/$(sdk) + mkdir -p $$(dir $$(PYTHON_FATLIB-$(sdk))) + # The macosx static library is already fat; copy it as-is + cp $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FATLIB-$(sdk)) + +$$(PYTHON_FATINCLUDE-$(sdk)): $$(PYTHON_LIB-$(sdk)) + @echo ">>> Build Python fat library for $(sdk)" + # The macosx headers are already fat; copy as-is + cp -r $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) $$(PYTHON_FATINCLUDE-$(sdk)) + +$$(PYTHON_FATSTDLIB-$(sdk)): $$(PYTHON_LIB-$(sdk)) + @echo ">>> Build Python stdlib library for $(sdk)" + # The macosx stdlib is already fat; copy it as-is + cp -r $$(PYTHON_INSTALL-$(sdk))/lib/python$(PYTHON_VER) $$(PYTHON_FATSTDLIB-$(sdk)) else +# Non-macOS builds need to be merged on a per-SDK basis. The merge covers: +# * Merging a fat libPython.a +# * Installing an architecture-sensitive pyconfig.h +# * Merging fat versions of the standard library lib-dynload folder + $$(PYTHON_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$(target))) - @echo ">>> Build Python fat library for $(sdk)" - mkdir -p build/$(os)/python/$(sdk)/lib + @echo ">>> Build Python fat library for the $(sdk) SDK" + mkdir -p $$(dir $$(PYTHON_FATLIB-$(sdk))) xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ $$^ \ - 2>&1 | tee -a build/$(os)/python-$(sdk).libtool.log - # Copy headers from the first target associated with the SDK - cp -r $$(PYTHON_DIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/_install/include build/$(os)/python/$(sdk) + 2>&1 | tee -a merge/$(os)/$(sdk)/python-$(PYTHON_VERSION).libtool.log + +$$(PYTHON_FATINCLUDE-$(sdk)): $$(PYTHON_LIB-$(sdk)) + @echo ">>> Build Python fat headers for the $(sdk) SDK" + # Copy headers as-is from the first target in the $(sdk) SDK + cp -r $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include/python$(PYTHON_VER) $$(PYTHON_FATINCLUDE-$(sdk)) + # Copy the cross-target header from the patch folder + cp $(PROJECT_DIR)/patch/Python/pyconfig-$(os).h $$(PYTHON_FATINCLUDE-$(sdk))/pyconfig.h + # Add the individual headers from each target in an arch-specific name + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/include/python$(PYTHON_VER)/pyconfig.h $$(PYTHON_FATINCLUDE-$(sdk))/pyconfig-$$(ARCH-$$(target)).h; ) + +$$(PYTHON_FATSTDLIB-$(sdk)): $$(PYTHON_FATLIB-$(sdk)) + @echo ">>> Build Python stdlib for the $(sdk) SDK" + mkdir -p $$(PYTHON_FATSTDLIB-$(sdk)) + # Copy stdlib from the first target associated with the $(sdk) SDK + cp -r $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/ $$(PYTHON_FATSTDLIB-$(sdk)) + + # Delete the single-SDK parts of the standard library + rm -rf \ + $$(PYTHON_FATSTDLIB-$(sdk))/_sysconfigdata__*.py \ + $$(PYTHON_FATSTDLIB-$(sdk))/config-* \ + $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload/* + + # Copy the individual _sysconfigdata modules into names that include the architecture + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/_sysconfigdata__$$(OS_LOWER-$(sdk))_$(sdk).py $$(PYTHON_FATSTDLIB-$(sdk))/_sysconfigdata__$$(OS_LOWER-$(sdk))_$(sdk)_$$(ARCH-$$(target)).py; ) + + # Copy the individual config modules directories into names that include the architecture + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp -r $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/config-$(PYTHON_VER)-$(sdk) $$(PYTHON_FATSTDLIB-$(sdk))/config-$(PYTHON_VER)-$$(target); ) + + # Merge the binary modules from each target in the $(sdk) SDK into a single binary + $$(foreach module,$$(wildcard $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/lib-dynload/*),xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/lib-dynload/$$(notdir $$(module))); ) endif + ########################################################################### # SDK: Debug ########################################################################### @@ -604,14 +709,28 @@ vars-$(sdk): @echo ">>> Environment variables for $(sdk)" @echo "SDK_TARGETS-$(sdk): $$(SDK_TARGETS-$(sdk))" @echo "SDK_ARCHES-$(sdk): $$(SDK_ARCHES-$(sdk))" + @echo "SDK_SLICE-$(sdk): $$(SDK_SLICE-$(sdk))" + @echo "SDK_ROOT-$(sdk): $$(SDK_ROOT-$(sdk))" + @echo "CC-$(sdk): $$(CC-$(sdk))" + @echo "CFLAGS-$(sdk): $$(CFLAGS-$(sdk))" + @echo "LDFLAGS-$(sdk): $$(LDFLAGS-$(sdk))" + @echo "BZIP2_MERGE-$(sdk): $$(BZIP2_MERGE-$(sdk))" @echo "BZIP2_FATLIB-$(sdk): $$(BZIP2_FATLIB-$(sdk))" + @echo "XZ_MERGE-$(sdk): $$(XZ_MERGE-$(sdk))" @echo "XZ_FATLIB-$(sdk): $$(XZ_FATLIB-$(sdk))" - @echo "OPENSSL_FAT_INCLUDE-$(sdk): $$(OPENSSL_FAT_INCLUDE-$(sdk))" + @echo "OPENSSL_MERGE-$(sdk): $$(OPENSSL_MERGE-$(sdk))" + @echo "OPENSSL_FATINCLUDE-$(sdk): $$(OPENSSL_FATINCLUDE-$(sdk))" @echo "OPENSSL_SSL_FATLIB-$(sdk): $$(OPENSSL_SSL_FATLIB-$(sdk))" @echo "OPENSSL_CRYPTO_FATLIB-$(sdk): $$(OPENSSL_CRYPTO_FATLIB-$(sdk))" + @echo "LIBFFI_MERGE-$(sdk): $$(LIBFFI_MERGE-$(sdk))" @echo "LIBFFI_FATLIB-$(sdk): $$(LIBFFI_FATLIB-$(sdk))" - @echo "PYTHON_DIR-$(sdk): $$(PYTHON_DIR-$(sdk))" + @echo "PYTHON_MERGE-$(sdk): $$(PYTHON_MERGE-$(sdk))" @echo "PYTHON_FATLIB-$(sdk): $$(PYTHON_FATLIB-$(sdk))" + @echo "PYTHON_FATINCLUDE-$(sdk): $$(PYTHON_FATINCLUDE-$(sdk))" + @echo "PYTHON_FATSTDLIB-$(sdk): $$(PYTHON_FATSTDLIB-$(sdk))" + @echo "PYTHON_SRCDIR-$(sdk): $$(PYTHON_SRCDIR-$(sdk))" + @echo "PYTHON_INSTALL-$(sdk): $$(PYTHON_INSTALL-$(sdk))" + @echo "PYTHON_LIB-$(sdk): $$(PYTHON_LIB-$(sdk))" @echo endef # build-sdk @@ -633,11 +752,9 @@ os=$1 SDKS-$(os)=$$(sort $$(basename $$(TARGETS-$(os)))) -# Expand the targets-sdk macro for all the sdks on this OS (e.g., iphoneos, iphonesimulator) -$$(foreach sdk,$$(SDKS-$(os)),$$(eval $$(call targets-sdk,$$(sdk),$(os)))) - -# Expand the build-target macro for target on this OS -$$(foreach target,$$(TARGETS-$(os)),$$(eval $$(call build-target,$$(target),$(os)))) +# Predeclare the Python XCFramework files so they can be referenced in SDK targets +PYTHON_XCFRAMEWORK-$(os)=support/$(os)/Python.xcframework +PYTHON_STDLIB-$(os)=support/$(os)/python-stdlib # Expand the build-sdk macro for all the sdks on this OS (e.g., iphoneos, iphonesimulator) $$(foreach sdk,$$(SDKS-$(os)),$$(eval $$(call build-sdk,$$(sdk),$(os)))) @@ -650,9 +767,13 @@ BZip2-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(BZIP2_FATLIB-$$(sdk))) clean-BZip2-$(os): @echo ">>> Clean BZip2 build products on $(os)" - rm -rf build/$(os)/bzip2-$(BZIP2_VERSION)-* \ - build/$(os)/bzip2 \ - build/$(os)/bzip2-*.log \ + rm -rf \ + build/$(os)/*/bzip2-$(BZIP2_VERSION) \ + build/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ + install/$(os)/*/bzip2-$(BZIP2_VERSION) \ + install/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ + merge/$(os)/*/bzip2-$(BZIP2_VERSION) \ + merge/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ ########################################################################### # Build: XZ (LZMA) @@ -662,21 +783,29 @@ XZ-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(XZ_FATLIB-$$(sdk))) clean-XZ-$(os): @echo ">>> Clean XZ build products on $(os)" - rm -rf build/$(os)/xz-$(XZ_VERSION)-* \ - build/$(os)/xz \ - build/$(os)/xz-*.log \ + rm -rf \ + build/$(os)/*/xz-$(XZ_VERSION) \ + build/$(os)/*/xz-$(XZ_VERSION).*.log \ + install/$(os)/*/xz-$(XZ_VERSION) \ + install/$(os)/*/xz-$(XZ_VERSION).*.log \ + merge/$(os)/*/xz-$(XZ_VERSION) \ + merge/$(os)/*/xz-$(XZ_VERSION).*.log \ ########################################################################### # Build: OpenSSL ########################################################################### -OpenSSL-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(OPENSSL_FAT_INCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk))) +OpenSSL-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(OPENSSL_FATINCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk))) clean-OpenSSL-$(os): @echo ">>> Clean OpenSSL build products on $(os)" - rm -rf build/$(os)/openssl-$(OPENSSL_VERSION)-* \ - build/$(os)/openssl \ - build/$(os)/openssl-*.log \ + rm -rf \ + build/$(os)/*/openssl-$(OPENSSL_VERSION) \ + build/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ + install/$(os)/*/openssl-$(OPENSSL_VERSION) \ + install/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ + merge/$(os)/*/openssl-$(OPENSSL_VERSION) \ + merge/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ ########################################################################### # Build: libFFI @@ -686,174 +815,134 @@ clean-OpenSSL-$(os): # a libFFI framework for macOS. ifneq ($(os),macOS) -LIBFFI_XCFRAMEWORK-$(os)=build/$(os)/Support/libFFI.xcframework -LIBFFI_DIR-$(os)=build/$(os)/libffi - -$$(LIBFFI_DIR-$(os))/darwin_common/include/ffi.h: downloads/libffi-$(LIBFFI_VERSION).tar.gz $$(PYTHON_XCFRAMEWORK-macOS) +$$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h: downloads/libffi-$(LIBFFI_VERSION).tar.gz $$(PYTHON_LIB-macosx) @echo ">>> Unpack and configure libFFI sources on $(os)" - mkdir -p $$(LIBFFI_DIR-$(os)) - tar zxf $$< --strip-components 1 -C $$(LIBFFI_DIR-$(os)) + mkdir -p $$(LIBFFI_SRCDIR-$(os)) + tar zxf $$< --strip-components 1 -C $$(LIBFFI_SRCDIR-$(os)) # Patch the build to add support for new platforms - cd $$(LIBFFI_DIR-$(os)) && patch -p1 < $(PROJECT_DIR)/patch/libffi.patch + cd $$(LIBFFI_SRCDIR-$(os)) && patch -p1 < $(PROJECT_DIR)/patch/libffi-$(LIBFFI_VERSION).patch # Configure the build - cd $$(LIBFFI_DIR-$(os)) && \ - PATH="$(PROJECT_DIR)/$(PYTHON_DIR-macOS)/_install/bin:$(PATH)" \ + cd $$(LIBFFI_SRCDIR-$(os)) && \ + PATH="$(PYTHON_INSTALL-macosx)/bin:$(PATH)" \ python$(PYTHON_VER) generate-darwin-source-and-headers.py --only-$(shell echo $(os) | tr '[:upper:]' '[:lower:]') \ - 2>&1 | tee -a ../libffi-$(os).config.log - -$$(LIBFFI_XCFRAMEWORK-$(os)): $$(foreach sdk,$$(SDKS-$(os)),$$(LIBFFI_FATLIB-$$(sdk))) - @echo ">>> Create libFFI.XCFramework on $(os)" - mkdir -p $$(LIBFFI_XCFRAMEWORK-$(os)) - xcodebuild -create-xcframework \ - -output $$@ $$(foreach sdk,$$(SDKS-$(os)),-library $$(LIBFFI_FATLIB-$$(sdk)) -headers $$(LIBFFI_DIR-$(os))/_install/$$(sdk)/include) \ - 2>&1 | tee -a build/$(os)/libffi-$(os).xcframework.log + 2>&1 | tee -a ../libffi-$(LIBFFI_VERSION).config.log endif -libFFI-$(os): $$(LIBFFI_XCFRAMEWORK-$(os)) +libFFI-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(LIBFFI_FATLIB-$$(sdk))) clean-libFFI-$(os): @echo ">>> Clean libFFI build products on $(os)" - rm -rf build/$(os)/libffi-$(LIBFFI_VERSION) \ - build/$(os)/libffi-*.log \ + rm -rf \ + build/$(os)/*/libffi-$(LIBFFI_VERSION) \ + build/$(os)/*/libffi-$(LIBFFI_VERSION).*.log \ + merge/$(os)/*/libffi-$(LIBFFI_VERSION) \ + merge/$(os)/*/libffi-$(LIBFFI_VERSION).*.log \ ########################################################################### # Build: Python ########################################################################### -PYTHON_XCFRAMEWORK-$(os)=build/$(os)/Support/Python.xcframework -PYTHON_RESOURCES-$(os)=build/$(os)/Support/Python/Resources/lib - -# macOS builds a single Python universal2 target; thus it needs to be -# configured in the `build` macro, not the `build-target` macro. -ifeq ($(os),macOS) +$$(PYTHON_XCFRAMEWORK-$(os)): \ + $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_FATLIB-$$(sdk)) $$(PYTHON_FATINCLUDE-$$(sdk))) + @echo ">>> Create Python.XCFramework on $(os)" + mkdir -p $$(dir $$(PYTHON_XCFRAMEWORK-$(os))) + xcodebuild -create-xcframework \ + -output $$@ $$(foreach sdk,$$(SDKS-$(os)),-library $$(PYTHON_FATLIB-$$(sdk)) -headers $$(PYTHON_FATINCLUDE-$$(sdk))) \ + 2>&1 | tee -a support/python-$(os).xcframework.log -# On macOS, there's a single target and a single output dir, -# rather than a target for each architecture. -PYTHON_TARGETS-$(os)=macOS +$$(PYTHON_STDLIB-$(os)): \ + $$(PYTHON_XCFRAMEWORK-$(os)) \ + $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_FATSTDLIB-$$(sdk))) + @echo ">>> Create Python stdlib on $(os)" + # Copy stdlib from first SDK in $(os) + cp -r $$(PYTHON_FATSTDLIB-$$(firstword $$(SDKS-$(os)))) $$(PYTHON_STDLIB-$(os)) -# For convenience on macOS, define an OS-level PYTHON_DIR and PYTHON_LIB. -# They are proxies of the values set for the first target, since all target -# constants should have the same value for macOS builds -PYTHON_DIR-$(os)=$$(PYTHON_DIR-$$(firstword $$(TARGETS-$(os)))) -PYTHON_LIB-$(os)=$$(PYTHON_LIB-$$(firstword $$(TARGETS-$(os)))) + # Delete the single-SDK stdlib artefacts from $(os) + rm -rf \ + $$(PYTHON_STDLIB-$(os))/_sysconfigdata__*.py \ + $$(PYTHON_STDLIB-$(os))/config-* \ + $$(PYTHON_STDLIB-$(os))/lib-dynload/* -$$(PYTHON_DIR-$(os))/Makefile: \ - downloads/Python-$(PYTHON_VERSION).tar.gz - $$(foreach sdk,$$(SDKS-$(os)),$$(BZIP2_FATLIB-$$(sdk))) \ - $$(foreach sdk,$$(SDKS-$(os)),$$(XZ_FATLIB-$$(sdk))) \ - $$(foreach sdk,$$(SDKS-$(os)),$$(OPENSSL_FAT_INCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk))) \ - @echo ">>> Unpack and configure Python for $(os)" - mkdir -p $$(PYTHON_DIR-$(os)) - tar zxf $$< --strip-components 1 -C $$(PYTHON_DIR-$(os)) - # Apply target Python patches - cd $$(PYTHON_DIR-$(os)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch - # Configure target Python - cd $$(PYTHON_DIR-$(os)) && \ - ./configure \ - CC="$(CC-macosx)" \ - LIBLZMA_CFLAGS="-I../xz/macosx/include" \ - LIBLZMA_LIBS="-L../xz/macosx/lib -lxz" \ - BZIP2_CFLAGS="-I../bzip2/macosx/include" \ - BZIP2_LIBS="-L../bzip2/macosx/lib -lbzip2" \ - --prefix="$(PROJECT_DIR)/$$(PYTHON_DIR-$(os))/_install" \ - --enable-ipv6 \ - --enable-universalsdk \ - --with-openssl=../openssl/macosx \ - --with-universal-archs=universal2 \ - --without-doc-strings \ - --without-ensurepip \ - $$(PYTHON_CONFIGURE-$(os)) \ - 2>&1 | tee -a ../python-$(os).config.log + # Copy the config-* contents from every SDK in $(os) into the support folder. + $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_FATSTDLIB-$$(sdk))/config-$(PYTHON_VER)-* $$(PYTHON_STDLIB-$(os)); ) -$$(PYTHON_DIR-$(os))/python.exe: \ - $$(PYTHON_DIR-$(os))/Makefile - @echo ">>> Build Python for $(os)" - cd $$(PYTHON_DIR-$(os)) && \ - PATH="$(PROJECT_DIR)/$(PYTHON_DIR-$(os))/_install/bin:$(PATH)" \ - make all \ - 2>&1 | tee -a ../python-$(os).build.log + # Copy the _sysconfigdata modules from every SDK in $(os) into the support folder. + $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_FATSTDLIB-$$(sdk))/_sysconfigdata__*.py $$(PYTHON_STDLIB-$(os)); ) -$$(PYTHON_LIB-$(os)): $$(PYTHON_DIR-$(os))/python.exe - @echo ">>> Install Python for $(os)" - cd $$(PYTHON_DIR-$(os)) && \ - PATH="$(PROJECT_DIR)/$(PYTHON_DIR-$(os))/_install/bin:$(PATH)" \ - make install \ - 2>&1 | tee -a ../python-$(os).install.log + # Copy the lib-dynload contents from every SDK in $(os) into the support folder. + $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_FATSTDLIB-$$(sdk))/lib-dynload/* $$(PYTHON_STDLIB-$(os))/lib-dynload; ) +dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: $$(PYTHON_XCFRAMEWORK-$(os)) $$(PYTHON_STDLIB-$(os)) + @echo ">>> Create final distribution artefact for $(os)" + mkdir -p dist + echo "Python version: $(PYTHON_VERSION) " > support/$(os)/VERSIONS + echo "Build: $(BUILD_NUMBER)" >> support/$(os)/VERSIONS + echo "---------------------" >> support/$(os)/VERSIONS +ifeq ($(os),macOS) + echo "libFFI: macOS native" >> support/$(os)/VERSIONS else - -# The targets for Python on OSes other than macOS -# are the same as they are for every other library -PYTHON_TARGETS-$(os)=$$(TARGETS-$(os)) - + echo "libFFI: $(LIBFFI_VERSION)" >> support/$(os)/VERSIONS endif + echo "BZip2: $(BZIP2_VERSION)" >> support/$(os)/VERSIONS + echo "OpenSSL: $(OPENSSL_VERSION)" >> support/$(os)/VERSIONS + echo "XZ: $(XZ_VERSION)" >> support/$(os)/VERSIONS -$$(PYTHON_XCFRAMEWORK-$(os)): $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_FATLIB-$$(sdk))) $$(foreach target,$$(PYTHON_TARGETS-$(os)),$$(PYCONFIG_H-$$(target))) - @echo ">>> Create Python.XCFramework on $(os)" - mkdir -p $$(PYTHON_XCFRAMEWORK-$(os)) - xcodebuild -create-xcframework \ - -output $$@ $$(foreach sdk,$$(SDKS-$(os)),-library $$(PYTHON_FATLIB-$$(sdk)) -headers $$(PYTHON_DIR-$$(sdk))/include/python$(PYTHON_VER)) \ - 2>&1 | tee -a build/$(os)/python-$(os).xcframework.log - # Copy the standard library from the first target listed - mkdir -p $$(PYTHON_RESOURCES-$(os)) - cp -f -r $$(PYTHON_DIR-$$(firstword $$(PYTHON_TARGETS-$(os))))/_install/lib/python$(PYTHON_VER) \ - $$(PYTHON_RESOURCES-$(os)) + # Build a "full" tarball with all content for test purposes + tar zcvf dist/Python-$(PYTHON_VER)-$(os)-support.test-$(BUILD_NUMBER).tar.gz -X patch/Python/test.exclude -C support/$(os) `ls -A support/$(os)` + # Build a distributable tarball + tar zcvf $$@ -X patch/Python/release.common.exclude -X patch/Python/release.$(os).exclude -C support/$(os) `ls -A support/$(os)` Python-$(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz clean-Python-$(os): @echo ">>> Clean Python build products on $(os)" rm -rf \ - dist/Python-$(PYTHON_VER)-$(os)-* \ - build/$(os)/Python-$(PYTHON_VERSION)-* \ - build/$(os)/python \ - build/$(os)/python-*.log \ - build/$(os)/Support/Python.xcframework \ - build/$(os)/Support/Python + build/$(os)/*/python-$(PYTHON_VERSION) \ + build/$(os)/*/python-$(PYTHON_VERSION).*.log \ + install/$(os)/*/python-$(PYTHON_VERSION) \ + install/$(os)/*/python-$(PYTHON_VERSION).*.log \ + merge/$(os)/*/python-$(PYTHON_VERSION) \ + merge/$(os)/*/python-$(PYTHON_VERSION).*.log \ + support/$(os) \ + support/*-$(os).*.log \ + dist/Python-$(PYTHON_VER)-$(os)-* dev-clean-Python-$(os): @echo ">>> Partially clean Python build products on $(os) so that local code modifications can be made" rm -rf \ - dist/Python-$(PYTHON_VER)-$(os)-* \ - build/$(os)/Python-$(PYTHON_VERSION)-*/python.exe \ - build/$(os)/Python-$(PYTHON_VERSION)-*/_install \ - build/$(os)/python \ - build/$(os)/python-*.log \ - build/$(os)/Support/Python.xcframework \ - build/$(os)/Support/Python + build/$(os)/*/Python-$(PYTHON_VERSION)/python.exe \ + build/$(os)/*/python-$(PYTHON_VERSION).*.log \ + install/$(os)/*/python-$(PYTHON_VERSION) \ + install/$(os)/*/python-$(PYTHON_VERSION).*.log \ + merge/$(os)/*/python-$(PYTHON_VERSION) \ + merge/$(os)/*/python-$(PYTHON_VERSION).*.log \ + support/$(os) \ + support/*-$(os).*.log \ + dist/Python-$(PYTHON_VER)-$(os)-* + +merge-clean-Python-$(os): + @echo ">>> Partially clean Python build products on $(os) so that merge modifications can be made" + rm -rf \ + merge/$(os)/*/python-$(PYTHON_VERSION) \ + merge/$(os)/*/python-$(PYTHON_VERSION).*.log \ + support/$(os) \ + support/*-$(os).*.log \ + dist/Python-$(PYTHON_VER)-$(os)-* ########################################################################### # Build ########################################################################### -dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: $$(PYTHON_XCFRAMEWORK-$(os)) - @echo ">>> Create final distribution artefact for $(os)" - mkdir -p dist - echo "Python version: $(PYTHON_VERSION) " > build/$(os)/Support/VERSIONS - echo "Build: $(BUILD_NUMBER)" >> build/$(os)/Support/VERSIONS - echo "---------------------" >> build/$(os)/Support/VERSIONS -ifeq ($(os),macOS) - echo "libFFI: macOS native" >> build/$(os)/Support/VERSIONS -else - echo "libFFI: $(LIBFFI_VERSION)" >> build/$(os)/Support/VERSIONS -endif - echo "BZip2: $(BZIP2_VERSION)" >> build/$(os)/Support/VERSIONS - echo "OpenSSL: $(OPENSSL_VERSION)" >> build/$(os)/Support/VERSIONS - echo "XZ: $(XZ_VERSION)" >> build/$(os)/Support/VERSIONS - - # Build a "full" tarball with all content for test purposes - tar zcvf dist/Python-$(PYTHON_VER)-$(os)-support.test-$(BUILD_NUMBER).tar.gz -X patch/Python/test.exclude -C build/$(os)/Support `ls -A build/$(os)/Support` - # Build a distributable tarball - tar zcvf $$@ -X patch/Python/release.common.exclude -X patch/Python/release.$(os).exclude -C build/$(os)/Support `ls -A build/$(os)/Support` - $(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz clean-$(os): @echo ">>> Clean $(os) build products" rm -rf \ build/$(os) \ + install/$(os) \ + merge/$(os) \ dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz \ dist/Python-$(PYTHON_VER)-$(os)-support.test-$(BUILD_NUMBER).tar.gz \ @@ -864,16 +953,9 @@ clean-$(os): vars-$(os): $$(foreach target,$$(TARGETS-$(os)),vars-$$(target)) $$(foreach sdk,$$(SDKS-$(os)),vars-$$(sdk)) @echo ">>> Environment variables for $(os)" @echo "SDKS-$(os): $$(SDKS-$(os))" - @echo "BZIP2_XCFRAMEWORK-$(os): $$(BZIP2_XCFRAMEWORK-$(os))" - @echo "XZ_XCFRAMEWORK-$(os): $$(XZ_XCFRAMEWORK-$(os))" - @echo "OPENSSL_XCFRAMEWORK-$(os): $$(OPENSSL_XCFRAMEWORK-$(os))" - @echo "LIBFFI_XCFRAMEWORK-$(os): $$(LIBFFI_XCFRAMEWORK-$(os))" - @echo "LIBFFI_DIR-$(os): $$(LIBFFI_DIR-$(os))" + @echo "LIBFFI_SRCDIR-$(os): $$(LIBFFI_SRCDIR-$(os))" + @echo "LIBPYTHON_XCFRAMEWORK-$(os): $$(LIBPYTHON_XCFRAMEWORK-$(os))" @echo "PYTHON_XCFRAMEWORK-$(os): $$(PYTHON_XCFRAMEWORK-$(os))" - @echo "PYTHON_RESOURCES-$(os): $$(PYTHON_RESOURCES-$(os))" - @echo "PYTHON_TARGETS-$(os): $$(PYTHON_TARGETS-$(os))" - @echo "PYTHON_DIR-$(os): $$(PYTHON_DIR-$(os))" - @echo "PYTHON_LIB-$(os): $$(PYTHON_LIB-$(os))" @echo endef # build diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 6b31d972..70303e3d 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -3742,10 +3742,10 @@ index 6a33c0cc9d..09ae5d1aa8 100644 # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff --git a/config.sub b/config.sub -index d74fb6deac..09ebc4287c 100755 +index d74fb6deac..a30f74f03d 100755 --- a/config.sub +++ b/config.sub -@@ -1121,7 +1121,7 @@ +@@ -1121,10 +1121,9 @@ xscale-* | xscalee[bl]-*) cpu=`echo "$cpu" | sed 's/^xscale/arm/'` ;; @@ -3753,8 +3753,11 @@ index d74fb6deac..09ebc4287c 100755 + arm64-* | arm64_32-*) cpu=aarch64 ;; - -@@ -1723,7 +1723,7 @@ +- + # Recognize the canonical CPU Types that limit and/or modify the + # company names they are paired with. + cr16-*) +@@ -1723,7 +1722,7 @@ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ | hiux* | abug | nacl* | netware* | windows* \ @@ -3763,7 +3766,7 @@ index d74fb6deac..09ebc4287c 100755 | mpw* | magic* | mmixware* | mon960* | lnews* \ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ -@@ -1786,6 +1786,8 @@ +@@ -1786,6 +1785,8 @@ ;; *-eabi* | *-gnueabi*) ;; diff --git a/patch/Python/release.common.exclude b/patch/Python/release.common.exclude index 0e0f9a90..8f11eac5 100644 --- a/patch/Python/release.common.exclude +++ b/patch/Python/release.common.exclude @@ -1,38 +1,30 @@ # This is a list of support package path patterns that we exclude # from all Python-Apple-support tarballs. # It is used by `tar -X` during the Makefile build. -# -# Remove binaries; not needed for embedded builds -Python/Resources/bin -# Remove includes; use the version packaged in Headers -Python/Resources/include -# Remove the Resources version of libpython; -# we use the version packaged at the root level -Python/Resources/lib/libpython3.*.a -# Remove lib/pkgconfig files. These are used for compiling C extension modules. -Python/Resources/lib/pkgconfig # Remove standard library test suites. -Python/Resources/lib/python*/ctypes/test -Python/Resources/lib/python*/distutils/tests -Python/Resources/lib/python*/lib2to3/tests -Python/Resources/lib/python*/sqlite3/test -Python/Resources/lib/python*/test +python-stdlib/ctypes/test +python-stdlib/distutils/tests +python-stdlib/lib2to3/tests +python-stdlib/sqlite3/test +python-stdlib/test # Remove config-* directory, which is used for compiling C extension modules. -Python/Resources/lib/python*/config-* +python-stdlib/config-* # Remove ensurepip. If user code needs pip, it can add it to -Python/Resources/lib/python*/ensurepip +python-stdlib/ensurepip # Remove libraries supporting IDLE. We don't need to ship an IDE -Python/Resources/lib/python*/idlelib +python-stdlib/idlelib # Remove Tcl/Tk GUI code. We don't build against Tcl/Tk at the moment, so this # will not work. -Python/Resources/lib/python*/tkinter -Python/Resources/lib/python*/turtle.py -Python/Resources/lib/python*/turtledemo +python-stdlib/tkinter +python-stdlib/turtle.py +python-stdlib/turtledemo +# Remove the testing binary modules +python-stdlib/lib-dynload/_test*.so +python-stdlib/lib-dynload/_xx*.so +python-stdlib/lib-dynload/xx*.so # Remove site-packages directory. The template unpacks user code and # dependencies to a different path. -Python/Resources/lib/python*/site-packages -# Remove share/ directory, which contains user documentation (man pages). -Python/Resources/share +python-stdlib/site-packages # Remove pyc files. These take up space, but since most stdlib modules are # never imported by user code, they mostly have no value. -Python/Resources/*/*.pyc \ No newline at end of file +*/__pycache__ \ No newline at end of file diff --git a/patch/Python/release.iOS.exclude b/patch/Python/release.iOS.exclude index 59de6bf2..471b1907 100644 --- a/patch/Python/release.iOS.exclude +++ b/patch/Python/release.iOS.exclude @@ -3,4 +3,4 @@ # It is used by `tar -X` during the Makefile build. # # Remove command-line curses toolkit. -Python/Resources/lib/python*/curses +python-stdlib/curses diff --git a/patch/Python/test.exclude b/patch/Python/test.exclude index 664b3273..add994a7 100644 --- a/patch/Python/test.exclude +++ b/patch/Python/test.exclude @@ -2,25 +2,6 @@ # we exclude from the embedded device Python-Apple-support test tarballs. # It is used by `tar -X` during the Makefile build. # -# Remove binaries -Python/Resources/bin -# Remove include/ directory, only useful for compiling C extension modules. -Python/Resources/include -# Remove the Resources version of libpython; -# we use the version packaged at the root level -Python/Resources/lib/libpython3.*.a -# Remove lib/pkgconfig files. These are used for compiling C extension modules. -Python/Resources/lib/pkgconfig -# Remove compiled test and example modules. -Python/Resources/lib/python*/lib-dynload/_test*.so -Python/Resources/lib/python*/lib-dynload/_ctypes_test*.so -Python/Resources/lib/python*/lib-dynload/xxlimited*.so -Python/Resources/lib/python*/lib-dynload/_xxtestfuzz.so -# Remove site-packages directory. The template unpacks user code and -# dependencies to a different path. -Python/Resources/lib/python*/site-packages -# Remove share/ directory, which contains user documentation (man pages). -Python/Resources/share # Remove pyc files. These take up space, but since most stdlib modules are # never imported by user code, they mostly have no value. -Python/Resources/*/*.pyc \ No newline at end of file +*/__pycache__ \ No newline at end of file diff --git a/patch/libffi.patch b/patch/libffi-3.4.2.patch similarity index 100% rename from patch/libffi.patch rename to patch/libffi-3.4.2.patch diff --git a/patch/xz-5.2.5.patch b/patch/xz-5.2.5.patch new file mode 100644 index 00000000..42950e07 --- /dev/null +++ b/patch/xz-5.2.5.patch @@ -0,0 +1,34 @@ +diff -ru xz-5.2.5/build-aux/config.sub xz-5.2.5-patched/build-aux/config.sub +--- xz-5.2.5/build-aux/config.sub 2020-03-17 22:29:35.000000000 +0800 ++++ xz-5.2.5-patched/build-aux/config.sub 2022-08-10 08:38:41.000000000 +0800 +@@ -116,6 +116,7 @@ + case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ ++ ios*-simulator | tvos*-simulator | watchos*-simulator | \ + knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ + kopensolaris*-gnu* | cloudabi*-eabi* | \ + storm-chaos* | os2-emx* | rtmk-nova*) +@@ -450,6 +451,9 @@ + | ymp-* \ + | z8k-* | z80-*) + ;; ++ arm64-* | arm64e-* | arm64_32-*) ++ basic_machine=aarch64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ++ ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown +@@ -1520,7 +1524,11 @@ + ;; + -nacl*) + ;; +- -ios) ++ -ios | -ios-simulator) ++ ;; ++ -tvos | -tvos-simulator) ++ ;; ++ -watchos | -watchos-simulator) + ;; + -none) + ;; From 7eed1bb7490f482dd5f4e20072c1310abc633689 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 23 Aug 2022 11:49:45 +0800 Subject: [PATCH 15/37] Correct iOS naming of XZ library, and include a stub sysconfigdata module. --- Makefile | 5 ++++- patch/Python/_sysconfigdata__ios_iphoneos.py | 8 ++++++++ patch/Python/_sysconfigdata__ios_iphonesimulator.py | 10 ++++++++++ patch/Python/_sysconfigdata__tvos_appletvos.py | 8 ++++++++ patch/Python/_sysconfigdata__tvos_appletvsimulator.py | 10 ++++++++++ patch/Python/_sysconfigdata__watchos_watchos.py | 8 ++++++++ patch/Python/_sysconfigdata__watchos_watchsimulator.py | 10 ++++++++++ 7 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 patch/Python/_sysconfigdata__ios_iphoneos.py create mode 100644 patch/Python/_sysconfigdata__ios_iphonesimulator.py create mode 100644 patch/Python/_sysconfigdata__tvos_appletvos.py create mode 100644 patch/Python/_sysconfigdata__tvos_appletvsimulator.py create mode 100644 patch/Python/_sysconfigdata__watchos_watchos.py create mode 100644 patch/Python/_sysconfigdata__watchos_watchsimulator.py diff --git a/Makefile b/Makefile index eb68a6d3..7aedaf3f 100644 --- a/Makefile +++ b/Makefile @@ -400,7 +400,7 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ CFLAGS="$$(CFLAGS-$(target))" \ LDFLAGS="$$(LDFLAGS-$(target))" \ LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$$(SDK-$(target)))/include" \ - LIBLZMA_LIBS="-L$$(XZ_MERGE-$$(SDK-$(target)))/lib -lxz" \ + LIBLZMA_LIBS="-L$$(XZ_MERGE-$$(SDK-$(target)))/lib -llzma" \ BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$$(SDK-$(target)))/include" \ BZIP2_LIBS="-L$$(BZIP2_MERGE-$$(SDK-$(target)))/lib -lbzip2" \ LIBFFI_INCLUDEDIR="$$(LIBFFI_MERGE-$$(SDK-$(target)))/include" \ @@ -689,6 +689,9 @@ $$(PYTHON_FATSTDLIB-$(sdk)): $$(PYTHON_FATLIB-$(sdk)) $$(PYTHON_FATSTDLIB-$(sdk))/config-* \ $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload/* + # Copy the cross-target _sysconfigdata module from the patch folder + cp $(PROJECT_DIR)/patch/Python/_sysconfigdata__$$(OS_LOWER-$(sdk))_$(sdk).py $$(PYTHON_FATSTDLIB-$(sdk)) + # Copy the individual _sysconfigdata modules into names that include the architecture $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/_sysconfigdata__$$(OS_LOWER-$(sdk))_$(sdk).py $$(PYTHON_FATSTDLIB-$(sdk))/_sysconfigdata__$$(OS_LOWER-$(sdk))_$(sdk)_$$(ARCH-$$(target)).py; ) diff --git a/patch/Python/_sysconfigdata__ios_iphoneos.py b/patch/Python/_sysconfigdata__ios_iphoneos.py new file mode 100644 index 00000000..ab821f73 --- /dev/null +++ b/patch/Python/_sysconfigdata__ios_iphoneos.py @@ -0,0 +1,8 @@ +import platform + + +arch = platform.machine() +if arch == 'arm64': + from _sysconfigdata__ios_iphoneos_arm64 import * +else: + raise RuntimeError("Unknown iOS architecture.") diff --git a/patch/Python/_sysconfigdata__ios_iphonesimulator.py b/patch/Python/_sysconfigdata__ios_iphonesimulator.py new file mode 100644 index 00000000..09bf13f1 --- /dev/null +++ b/patch/Python/_sysconfigdata__ios_iphonesimulator.py @@ -0,0 +1,10 @@ +import platform + + +arch = platform.machine() +if arch == 'x86_64': + from _sysconfigdata__ios_iphonesimulator_x86_64 import * +elif arch == 'arm64': + from _sysconfigdata__ios_iphonesimulator_arm64 import * +else: + raise RuntimeError("Unknown iOS simulator architecture.") diff --git a/patch/Python/_sysconfigdata__tvos_appletvos.py b/patch/Python/_sysconfigdata__tvos_appletvos.py new file mode 100644 index 00000000..8914b46c --- /dev/null +++ b/patch/Python/_sysconfigdata__tvos_appletvos.py @@ -0,0 +1,8 @@ +import platform + + +arch = platform.machine() +if arch == 'arm64': + from _sysconfigdata__tvos_appletvos_arm64 import * +else: + raise RuntimeError("Unknown tvOS architecture.") diff --git a/patch/Python/_sysconfigdata__tvos_appletvsimulator.py b/patch/Python/_sysconfigdata__tvos_appletvsimulator.py new file mode 100644 index 00000000..cd0739b2 --- /dev/null +++ b/patch/Python/_sysconfigdata__tvos_appletvsimulator.py @@ -0,0 +1,10 @@ +import platform + + +arch = platform.machine() +if arch == 'x86_64': + from _sysconfigdata__tvos_appletvsimulator_x86_64 import * +elif arch == 'arm64': + from _sysconfigdata__tvos_appletvsimulator_arm64 import * +else: + raise RuntimeError("Unknown tvOS simulator architecture.") diff --git a/patch/Python/_sysconfigdata__watchos_watchos.py b/patch/Python/_sysconfigdata__watchos_watchos.py new file mode 100644 index 00000000..ec13c805 --- /dev/null +++ b/patch/Python/_sysconfigdata__watchos_watchos.py @@ -0,0 +1,8 @@ +import platform + + +arch = platform.machine() +if arch == 'arm64_32': + from _sysconfigdata__watchos_watchos_arm64_32 import * +else: + raise RuntimeError("Unknown watchOS architecture.") diff --git a/patch/Python/_sysconfigdata__watchos_watchsimulator.py b/patch/Python/_sysconfigdata__watchos_watchsimulator.py new file mode 100644 index 00000000..4fa46260 --- /dev/null +++ b/patch/Python/_sysconfigdata__watchos_watchsimulator.py @@ -0,0 +1,10 @@ +import platform + + +arch = platform.machine() +if arch == 'x86_64': + from _sysconfigdata__watchos_watchsimulator_x86_64 import * +elif arch == 'arm64': + from _sysconfigdata__watchos_watchsimulator_arm64 import * +else: + raise RuntimeError("Unknown watchOS simulator architecture.") From cd4ae500783c491bf11d0ddc782112c03dd6b522 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 23 Aug 2022 14:09:07 +0800 Subject: [PATCH 16/37] Use lipo rather than libtool to merge binaries. --- Makefile | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 7aedaf3f..ddd362e5 100644 --- a/Makefile +++ b/Makefile @@ -529,8 +529,8 @@ $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(eval $$(call build-target,$$(target) $$(BZIP2_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(BZIP2_LIB-$$(target))) @echo ">>> Build BZip2 fat library for $(sdk)" mkdir -p $$(BZIP2_MERGE-$(sdk))/lib - xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION).libtool.log + lipo -create -output $$@ $$^ \ + 2>&1 | tee -a merge/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION).lipo.log # Copy headers from the first target associated with the $(sdk) SDK cp -r $$(BZIP2_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(BZIP2_MERGE-$(sdk)) @@ -541,8 +541,8 @@ $$(BZIP2_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(BZIP2_LIB-$ $$(XZ_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(XZ_LIB-$$(target))) @echo ">>> Build XZ fat library for $(sdk)" mkdir -p $$(XZ_MERGE-$(sdk))/lib - xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/xz-$(XZ_VERSION).libtool.log + lipo -create -output $$@ $$^ \ + 2>&1 | tee -a merge/$(os)/$(sdk)/xz-$(XZ_VERSION).lipo.log # Copy headers from the first target associated with the $(sdk) SDK cp -r $$(XZ_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(XZ_MERGE-$(sdk)) @@ -558,16 +558,16 @@ $$(OPENSSL_FATINCLUDE-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENS $$(OPENSSL_SSL_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) @echo ">>> Build OpenSSL ssl fat library for $(sdk)" mkdir -p $$(OPENSSL_MERGE-$(sdk))/lib - xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ \ + lipo -create -output $$@ \ $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) \ - 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).ssl.libtool.log + 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).ssl.lipo.log $$(OPENSSL_CRYPTO_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) @echo ">>> Build OpenSSL crypto fat library for $(sdk)" mkdir -p $$(OPENSSL_MERGE-$(sdk))/lib - xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ \ + lipo -create -output $$@ \ $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) \ - 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).crypto.libtool.log + 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).crypto.lipo.log ########################################################################### # SDK: libFFI @@ -576,8 +576,8 @@ $$(OPENSSL_CRYPTO_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OP $$(LIBFFI_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(LIBFFI_LIB-$$(target))) @echo ">>> Build libFFI fat library for $(sdk)" mkdir -p $$(LIBFFI_MERGE-$(sdk))/lib - xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/libffi-$(LIBFFI_VERSION).libtool.log + lipo -create -output $$@ $$^ \ + 2>&1 | tee -a merge/$(os)/$(sdk)/libffi-$(LIBFFI_VERSION).lipo.log # Copy headers from the first target associated with the $(sdk) SDK cp -f -r $$(LIBFFI_SRCDIR-$(os))/darwin_common/include \ $$(LIBFFI_MERGE-$(sdk)) @@ -665,8 +665,8 @@ else $$(PYTHON_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$(target))) @echo ">>> Build Python fat library for the $(sdk) SDK" mkdir -p $$(dir $$(PYTHON_FATLIB-$(sdk))) - xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/python-$(PYTHON_VERSION).libtool.log + lipo -create -output $$@ $$^ \ + 2>&1 | tee -a merge/$(os)/$(sdk)/python-$(PYTHON_VERSION).lipo.log $$(PYTHON_FATINCLUDE-$(sdk)): $$(PYTHON_LIB-$(sdk)) @echo ">>> Build Python fat headers for the $(sdk) SDK" @@ -699,7 +699,7 @@ $$(PYTHON_FATSTDLIB-$(sdk)): $$(PYTHON_FATLIB-$(sdk)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp -r $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/config-$(PYTHON_VER)-$(sdk) $$(PYTHON_FATSTDLIB-$(sdk))/config-$(PYTHON_VER)-$$(target); ) # Merge the binary modules from each target in the $(sdk) SDK into a single binary - $$(foreach module,$$(wildcard $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/lib-dynload/*),xcrun --sdk $(sdk) libtool -no_warning_for_no_symbols -static -o $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/lib-dynload/$$(notdir $$(module))); ) + $$(foreach module,$$(wildcard $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/lib-dynload/*),lipo -create -output $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/lib-dynload/$$(notdir $$(module))); ) endif From d2607a0f0ca4d29e5de1b6f7ac1996089f8247d7 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 23 Aug 2022 14:09:38 +0800 Subject: [PATCH 17/37] platform.machine() doesn't work quite as expected on hardware. --- patch/Python/_sysconfigdata__ios_iphoneos.py | 11 ++++++----- patch/Python/_sysconfigdata__tvos_appletvos.py | 12 ++++++------ patch/Python/_sysconfigdata__watchos_watchos.py | 12 ++++++------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/patch/Python/_sysconfigdata__ios_iphoneos.py b/patch/Python/_sysconfigdata__ios_iphoneos.py index ab821f73..b7de2e70 100644 --- a/patch/Python/_sysconfigdata__ios_iphoneos.py +++ b/patch/Python/_sysconfigdata__ios_iphoneos.py @@ -1,8 +1,9 @@ import platform -arch = platform.machine() -if arch == 'arm64': - from _sysconfigdata__ios_iphoneos_arm64 import * -else: - raise RuntimeError("Unknown iOS architecture.") +from _sysconfigdata__ios_iphoneos_arm64 import * +# arch = platform.machine() +# if arch == 'arm64': +# from _sysconfigdata__ios_iphoneos_arm64 import * +# else: +# raise RuntimeError("Unknown iOS architecture.") diff --git a/patch/Python/_sysconfigdata__tvos_appletvos.py b/patch/Python/_sysconfigdata__tvos_appletvos.py index 8914b46c..9d3687fe 100644 --- a/patch/Python/_sysconfigdata__tvos_appletvos.py +++ b/patch/Python/_sysconfigdata__tvos_appletvos.py @@ -1,8 +1,8 @@ import platform - -arch = platform.machine() -if arch == 'arm64': - from _sysconfigdata__tvos_appletvos_arm64 import * -else: - raise RuntimeError("Unknown tvOS architecture.") +from _sysconfigdata__tvos_appletvos_arm64 import * +# arch = platform.machine() +# if arch == 'arm64': +# from _sysconfigdata__tvos_appletvos_arm64 import * +# else: +# raise RuntimeError("Unknown tvOS architecture.") diff --git a/patch/Python/_sysconfigdata__watchos_watchos.py b/patch/Python/_sysconfigdata__watchos_watchos.py index ec13c805..7b2a60e1 100644 --- a/patch/Python/_sysconfigdata__watchos_watchos.py +++ b/patch/Python/_sysconfigdata__watchos_watchos.py @@ -1,8 +1,8 @@ import platform - -arch = platform.machine() -if arch == 'arm64_32': - from _sysconfigdata__watchos_watchos_arm64_32 import * -else: - raise RuntimeError("Unknown watchOS architecture.") +from _sysconfigdata__watchos_watchos_arm64_32 import * +# arch = platform.machine() +# if arch == 'arm64_32': +# from _sysconfigdata__watchos_watchos_arm64_32 import * +# else: +# raise RuntimeError("Unknown watchOS architecture.") From 873a13433861fa870c7b26d42a1fd82d843e2a17 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 26 Aug 2022 11:40:04 +0800 Subject: [PATCH 18/37] Switch to OpenSSL 3. --- Makefile | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index ddd362e5..655a3145 100644 --- a/Makefile +++ b/Makefile @@ -47,9 +47,7 @@ BZIP2_VERSION=1.0.8 XZ_VERSION=5.2.5 -OPENSSL_VERSION_NUMBER=1.1.1 -OPENSSL_REVISION=q -OPENSSL_VERSION=$(OPENSSL_VERSION_NUMBER)$(OPENSSL_REVISION) +OPENSSL_VERSION=3.0.5 LIBFFI_VERSION=3.4.2 @@ -289,17 +287,10 @@ $$(OPENSSL_SRCDIR-$(target))/is_configured: downloads/openssl-$(OPENSSL_VERSION) mkdir -p $$(OPENSSL_SRCDIR-$(target)) tar zxf $$< --strip-components 1 -C $$(OPENSSL_SRCDIR-$(target)) -ifeq ($$(findstring simulator,$$(SDK-$(target))),) - # Tweak ui_openssl.c - sed -ie "s!static volatile sig_atomic_t intr_signal;!static volatile intr_signal;!" $$(OPENSSL_SRCDIR-$(target))/crypto/ui/ui_openssl.c -endif - -ifeq ($$(findstring iphone,$$(SDK-$(target))),) - # Patch apps/speed.c and apps/ocsp.c to not use fork() since it's not available on tvOS +ifneq ($(os),macOS) + # Patch code to disable the use of fork as it's not available on $(os) + sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/include/http_server.h sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/speed.c - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/ocsp.c - # Patch Configure to build for tvOS or watchOS, not iOS - LC_ALL=C sed -ie 's/-D_REENTRANT:iOS/-D_REENTRANT:$(os)/' $$(OPENSSL_SRCDIR-$(target))/Configure endif # Configure the OpenSSL build @@ -387,7 +378,8 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ $$(BZIP2_FATLIB-$$(SDK-$(target))) \ $$(XZ_FATLIB-$$(SDK-$(target))) \ $$(OPENSSL_FATINCLUDE-$$(SDK-$(target))) $$(OPENSSL_SSL_FATLIB-$$(SDK-$(target))) $$(OPENSSL_CRYPTO_FATLIB-$$(SDK-$(target))) \ - $$(LIBFFI_FATLIB-$$(SDK-$(target))) + $$(LIBFFI_FATLIB-$$(SDK-$(target))) \ + $$(PYTHON_XCFRAMEWORK-macOS) @echo ">>> Unpack and configure Python for $(target)" mkdir -p $$(PYTHON_SRCDIR-$(target)) tar zxf downloads/Python-$(PYTHON_VERSION).tar.gz --strip-components 1 -C $$(PYTHON_SRCDIR-$(target)) From 5e2acc047142f66d8ad4e997c93c313515a3351a Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 26 Aug 2022 12:15:20 +0800 Subject: [PATCH 19/37] Updated README, including details on how to use the support packages. --- README.rst | 60 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 11ab12a5..61e4c154 100644 --- a/README.rst +++ b/README.rst @@ -10,9 +10,11 @@ into a macOS, iOS, tvOS or watchOS project. It works by downloading, patching, and building a fat binary of Python and selected pre-requisites, and packaging them as static libraries that can be -incorporated into an XCode project. +incorporated into an XCode project. The binary modules in the Python standard +library are statically compiled, but are distribted as ``.so`` objects that +can be dynamically loaded at runtime. -It exposed *almost* all the modules in the Python standard library except for: +It exposes *almost* all the modules in the Python standard library except for: * dbm.gnu * tkinter * readline @@ -23,7 +25,11 @@ It exposed *almost* all the modules in the Python standard library except for: The following standard library modules are available on macOS, but not the other Apple platforms: * curses + * grp + * multiprocessing * posixshmem + * posixsubprocess + * syslog The binaries support x86_64 and arm64 for macOS; arm64 for iOS and appleTV devices; and arm64_32 for watchOS. It also supports device simulators on both @@ -35,7 +41,7 @@ x86_64 and M1 hardware. This should enable the code to run on: * Mac Mini (including M1 Apple Silicon Mac minis) * Mac Studio (all models) * Mac Pro (all models) -* iOS 13.0 or later, on: +* iOS 12.0 or later, on: * iPhone (6s or later) * iPad (5th gen or later) * iPad Air (all models) @@ -48,6 +54,11 @@ x86_64 and M1 hardware. This should enable the code to run on: Quickstart ---------- +The easist way to use these packages is by creating a project with `Briefcase +`__. Briefcase will download pre-compiled +versions of these support packages, and add them to an XCode project (or +pre-build stub application, in the case of macOS). + Pre-built versions of the frameworks can be downloaded `for macOS`_, `for iOS`_, `for tvOS`_, and `for watchOS`_, and added to your project. @@ -66,8 +77,47 @@ This should: 2. Patch them as required for compatibility with the selected OS 3. Build the packages as XCode-compatible XCFrameworks. -The build products will be in the `build` directory; the compiled frameworks -will be in the `dist` directory. +The resulting support packages will be packaged as a ``.tar.gz`` file +in the ``dist`` folder. + +Each support package contains: + +* ``VERSIONS``, a text file describing the specific versions of code used to + build the support package; +* ``Python.xcframework``, a multi-architecture build of libPython3.11.a +* ``python-stdlib``, the code and binary modules comprising the Python standard + library. On iOS, tvOS and watchOS, there are 2 copies of every binary module - + one for physical devices, and one for the simulator. The simulator binaries + are "fat", containing code for both x86_64 and arm64. + +To add a support package to your own Xcode project: + +1. Drag ``Python.xcframework`` and ``python-stdlib`` into your Xcode project + tree. +2. Ensure that these two objects are added to any targets that need to use + them; +3. Add a custom build phase to purge any binary modules for the platform you are + *not* targetting; and +4. Add a custom build phase to sign any of the binary modules in your app. +5. Add CPython API code to your app to create an instance of the Python + interpreter. + +For examples of the scripts needed for steps 3 and 4, and the code needed for +step 5, compare with a project generated with Briefcase. + +On macOS, you must also either: +1. Enable the "Disable Library Validation" entitlement (found on the "Signing + & Capabilities" tab in XCode); or +2. Sign your app with a Development or Distribution certificate. This will + require a paid Apple Developer subscription. + +It is not possible to use an ad-hoc signing certificate with the "Disable +Library Validation" entitlement disabled. + +On iOS/tvOS/watchOS, you can use the default developer certificate for deploying +to a device simulator. However, to deploy to a physical device (including your +own), you will require a Development or Distribution certificate, which requires +a paid Apple Developer subscription. .. _for macOS: https://briefcase-support.org/python?platform=macOS&version=3.11 .. _for iOS: https://briefcase-support.org/python?platform=iOS&version=3.11 From 7e7b1425129993dd29a5668582f50a6db5660a2d Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 31 Aug 2022 14:45:18 +0800 Subject: [PATCH 20/37] Additional tweaks picked up by testing third party binary wheels. * No need to pass an explicit sysroot * Minimum version info is perserved in the VERSIONS file * Tools not used by the Python build, but preserved in _sysconfigdata, added to the configure pass. --- Makefile | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 655a3145..6130eadd 100644 --- a/Makefile +++ b/Makefile @@ -59,20 +59,24 @@ CURL_FLAGS=--fail --location --create-dirs --progress-bar # macOS targets TARGETS-macOS=macosx.x86_64 macosx.arm64 -VERSION_MIN-macOS=-mmacosx-version-min=10.15 +VERSION_MIN-macOS=10.15 +CFLAGS-macOS=-mmacosx-version-min=$(VERSION_MIN-macOS) # iOS targets TARGETS-iOS=iphonesimulator.x86_64 iphonesimulator.arm64 iphoneos.arm64 -VERSION_MIN-iOS=-mios-version-min=12.0 +VERSION_MIN-iOS=12.0 +CFLAGS-iOS=-mios-version-min=$(VERSION_MIN-iOS) # tvOS targets TARGETS-tvOS=appletvsimulator.x86_64 appletvsimulator.arm64 appletvos.arm64 -VERSION_MIN-tvOS=-mtvos-version-min=9.0 +VERSION_MIN-tvOS=9.0 +CFLAGS-tvOS=-mtvos-version-min=$(VERSION_MIN-tvOS) PYTHON_CONFIGURE-tvOS=ac_cv_func_sigaltstack=no # watchOS targets TARGETS-watchOS=watchsimulator.x86_64 watchsimulator.arm64 watchos.arm64_32 -VERSION_MIN-watchOS=-mwatchos-version-min=4.0 +VERSION_MIN-watchOS=4.0 +CFLAGS-watchOS=-mwatchos-version-min=$(VERSION_MIN-watchOS) PYTHON_CONFIGURE-watchOS=ac_cv_func_sigaltstack=no # The architecture of the machine doing the build @@ -207,14 +211,14 @@ endif SDK_ROOT-$(target)=$$(shell xcrun --sdk $$(SDK-$(target)) --show-sdk-path) CC-$(target)=xcrun --sdk $$(SDK-$(target)) clang +CXX-$(target)=xcrun --sdk $$(SDK-$(target)) clang +AR-$(target)=xcrun --sdk $$(SDK-$(target)) ar CFLAGS-$(target)=\ -target $$(TARGET_TRIPLE-$(target)) \ - --sysroot=$$(SDK_ROOT-$(target)) \ - $$(VERSION_MIN-$(os)) + $$(CFLAGS-$(os)) LDFLAGS-$(target)=\ -target $$(TARGET_TRIPLE-$(target)) \ - -isysroot $$(SDK_ROOT-$(target)) \ - $$(VERSION_MIN-$(os)) + $$(CFLAGS-$(os)) ########################################################################### # Target: BZip2 @@ -388,7 +392,9 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ # Configure target Python cd $$(PYTHON_SRCDIR-$(target)) && \ ./configure \ + AR="$$(AR-$(target))" \ CC="$$(CC-$(target))" \ + CXX="$$(CXX-$(target))" \ CFLAGS="$$(CFLAGS-$(target))" \ LDFLAGS="$$(LDFLAGS-$(target))" \ LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$$(SDK-$(target)))/include" \ @@ -481,14 +487,11 @@ else SDK_SLICE-$(sdk)=$$(OS_LOWER-$(sdk))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g")-simulator endif -SDK_ROOT-$(sdk)=$$(shell xcrun --sdk $(sdk) --show-sdk-path) CC-$(sdk)=xcrun --sdk $(sdk) clang CFLAGS-$(sdk)=\ - --sysroot=$$(SDK_ROOT-$(sdk)) \ - $$(VERSION_MIN-$(os)) + $$(CFLAGS-$(os)) LDFLAGS-$(sdk)=\ - -isysroot $$(SDK_ROOT-$(sdk)) \ - $$(VERSION_MIN-$(os)) + $$(CFLAGS-$(os)) # Predeclare SDK constants that are used by the build-target macro @@ -705,7 +708,6 @@ vars-$(sdk): @echo "SDK_TARGETS-$(sdk): $$(SDK_TARGETS-$(sdk))" @echo "SDK_ARCHES-$(sdk): $$(SDK_ARCHES-$(sdk))" @echo "SDK_SLICE-$(sdk): $$(SDK_SLICE-$(sdk))" - @echo "SDK_ROOT-$(sdk): $$(SDK_ROOT-$(sdk))" @echo "CC-$(sdk): $$(CC-$(sdk))" @echo "CFLAGS-$(sdk): $$(CFLAGS-$(sdk))" @echo "LDFLAGS-$(sdk): $$(LDFLAGS-$(sdk))" @@ -874,6 +876,7 @@ dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: $$(PYTHON_XCFRAM mkdir -p dist echo "Python version: $(PYTHON_VERSION) " > support/$(os)/VERSIONS echo "Build: $(BUILD_NUMBER)" >> support/$(os)/VERSIONS + echo "Min $(os) version: $$(VERSION_MIN-$(os))" >> support/$(os)/VERSIONS echo "---------------------" >> support/$(os)/VERSIONS ifeq ($(os),macOS) echo "libFFI: macOS native" >> support/$(os)/VERSIONS From 4ecd795d030dbdb5060cff43d7ef1c8b28980def Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 31 Aug 2022 14:45:55 +0800 Subject: [PATCH 21/37] Correct the handling of arm64_32 in watchOS headers. --- patch/Python/pyconfig-watchOS.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/patch/Python/pyconfig-watchOS.h b/patch/Python/pyconfig-watchOS.h index 6801a6a9..ae4563f1 100644 --- a/patch/Python/pyconfig-watchOS.h +++ b/patch/Python/pyconfig-watchOS.h @@ -3,7 +3,11 @@ #endif #ifdef __arm64__ +# ifdef __LP64__ #include "pyconfig-arm64.h" +# else +#include "pyconfig-arm64_32.h" +# endif #endif #ifdef __x86_64__ From 9250f708ae4ba84fca533b0f252e3cbe0b44606b Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 1 Sep 2022 13:40:14 +0800 Subject: [PATCH 22/37] Add a templated cross-platform sitecustomize.py to non-macOS support packages. --- Makefile | 14 ++++++++++++-- README.rst | 11 +++++++++++ patch/Python/sitecustomize.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 patch/Python/sitecustomize.py diff --git a/Makefile b/Makefile index 6130eadd..9743da52 100644 --- a/Makefile +++ b/Makefile @@ -872,8 +872,7 @@ $$(PYTHON_STDLIB-$(os)): \ $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_FATSTDLIB-$$(sdk))/lib-dynload/* $$(PYTHON_STDLIB-$(os))/lib-dynload; ) dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: $$(PYTHON_XCFRAMEWORK-$(os)) $$(PYTHON_STDLIB-$(os)) - @echo ">>> Create final distribution artefact for $(os)" - mkdir -p dist + @echo ">>> Create VERSIONS file for $(os)" echo "Python version: $(PYTHON_VERSION) " > support/$(os)/VERSIONS echo "Build: $(BUILD_NUMBER)" >> support/$(os)/VERSIONS echo "Min $(os) version: $$(VERSION_MIN-$(os))" >> support/$(os)/VERSIONS @@ -887,6 +886,17 @@ endif echo "OpenSSL: $(OPENSSL_VERSION)" >> support/$(os)/VERSIONS echo "XZ: $(XZ_VERSION)" >> support/$(os)/VERSIONS +ifneq ($(os),macOS) + @echo ">>> Create cross-platform site sitecustomize.py for $(os)" + mkdir -p support/$(os)/platform-site + cat $(PROJECT_DIR)/patch/Python/sitecustomize.py \ + | sed -e "s/{{os}}/$(os)/g" \ + | sed -e "s/{{tag}}/$$(shell echo $(os) | tr '[:upper:]' '[:lower:]')_$$(shell echo $$(VERSION_MIN-$(os)) | sed "s/\./_/g")/g" \ + > support/$(os)/platform-site/sitecustomize.py +endif + + @echo ">>> Create final distribution artefact for $(os)" + mkdir -p dist # Build a "full" tarball with all content for test purposes tar zcvf dist/Python-$(PYTHON_VER)-$(os)-support.test-$(BUILD_NUMBER).tar.gz -X patch/Python/test.exclude -C support/$(os) `ls -A support/$(os)` # Build a distributable tarball diff --git a/README.rst b/README.rst index 61e4c154..3189881d 100644 --- a/README.rst +++ b/README.rst @@ -90,6 +90,17 @@ Each support package contains: one for physical devices, and one for the simulator. The simulator binaries are "fat", containing code for both x86_64 and arm64. +Non-macOS platforms also contain a ``platform-site`` folder. This contains a +site customization script that can be used to make your local Python install +look like it is an on-device install. This is needed because when you run +``pip`` you'll be on a macOS machine; if ``pip`` tries to install a binary +package, it will install a macOS binary wheel (which won't work on +iOS/tvOS/watchOS). However, if you add the ``platform-site`` folder to your +``PYTHONPATH`` when invoking pip, the site customization will make your Python +install return ``platform`` and ``sysconfig`` responses consistent with +on-device behavior, which will cause ``pip`` to install platform-appropriate +packages. + To add a support package to your own Xcode project: 1. Drag ``Python.xcframework`` and ``python-stdlib`` into your Xcode project diff --git a/patch/Python/sitecustomize.py b/patch/Python/sitecustomize.py new file mode 100644 index 00000000..58dd105d --- /dev/null +++ b/patch/Python/sitecustomize.py @@ -0,0 +1,31 @@ +# A site customization that can be used to trick pip into installing +# packages cross-platform. If the folder containing this file is on +# your PYTHONPATH when you invoke pip, pip will behave as if it were +# running on {{os}}. +import os +import platform +import sys +import sysconfig + +# Make platform.system() return "{{os}}" +def custom_system(): + return "{{os}}" + +platform.system = custom_system + +# Make sysconfig.get_platform() return "{{tag}}" +def custom_get_platform(): + return "{{tag}}" + +sysconfig.get_platform = custom_get_platform + +# Call the next sitecustomize script if there is one +# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). +del sys.modules["sitecustomize"] +this_dir = os.path.dirname(__file__) +path_index = sys.path.index(this_dir) +del sys.path[path_index] +try: + import sitecustomize # noqa: F401 +finally: + sys.path.insert(path_index, this_dir) From 468600847263384a693ad9ffa15494daf4b7e0f8 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 1 Sep 2022 15:48:39 +0800 Subject: [PATCH 23/37] Update patch to 3.11.0rc0, and xz-5.2.6. --- Makefile | 8 +- patch/Python/Python.patch | 114 +++++++++++------------ patch/{xz-5.2.5.patch => xz-5.2.6.patch} | 0 3 files changed, 61 insertions(+), 61 deletions(-) rename patch/{xz-5.2.5.patch => xz-5.2.6.patch} (100%) diff --git a/Makefile b/Makefile index 9743da52..b13dc46a 100644 --- a/Makefile +++ b/Makefile @@ -39,13 +39,13 @@ BUILD_NUMBER=custom # PYTHON_VERSION is the full version number (e.g., 3.10.0b3) # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.11.0b5 +PYTHON_VERSION=3.11.0rc1 PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) BZIP2_VERSION=1.0.8 -XZ_VERSION=5.2.5 +XZ_VERSION=5.2.6 OPENSSL_VERSION=3.0.5 @@ -158,7 +158,7 @@ downloads/openssl-$(OPENSSL_VERSION).tar.gz: curl $(CURL_FLAGS) -o $@ \ https://openssl.org/source/$(notdir $@) \ || curl $(CURL_FLAGS) -o $@ \ - https://openssl.org/source/old/$(notdir $@) + https://openssl.org/source/old/$(basename $(OPENSSL_VERSION))/$(notdir $@) ########################################################################### # Setup: libFFI @@ -178,7 +178,7 @@ downloads/libffi-$(LIBFFI_VERSION).tar.gz: downloads/Python-$(PYTHON_VERSION).tar.gz: @echo ">>> Download Python sources" curl $(CURL_FLAGS) -o $@ \ - https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/$(notdir $@) + https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/$(basename $(basename $(notdir $@))).tgz ########################################################################### # Build for specified target (from $(TARGETS-*)) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 70303e3d..c0225ab7 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -910,7 +910,7 @@ index 098a0da344..1df5654356 100644 class MyServer: diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py -index 2f68459d30..c9f9f48097 100644 +index 1d922783ce..c974cfe00a 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -276,6 +276,8 @@ @@ -1179,10 +1179,10 @@ index a2c4ab5081..265b5618df 100644 'need os.get_terminal_size()') def test_stty_match(self): diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index a2938cd834..2d3a5c72fb 100644 +index 72e3394df2..401620bae2 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py -@@ -1144,7 +1144,7 @@ +@@ -1157,7 +1157,7 @@ # 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')) @@ -1191,7 +1191,7 @@ index a2938cd834..2d3a5c72fb 100644 # avoid the 'echo' service on this platform, as there is an # assumption breaking non-standard port/protocol entry services = ('daytime', 'qotd', 'domain') -@@ -3539,7 +3539,8 @@ +@@ -3552,7 +3552,8 @@ def _testFDPassCMSG_LEN(self): self.createAndSendFDs(1) @@ -1201,7 +1201,7 @@ index a2938cd834..2d3a5c72fb 100644 @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparate(self): -@@ -3550,7 +3551,8 @@ +@@ -3563,7 +3564,8 @@ maxcmsgs=2) @testFDPassSeparate.client_skip @@ -1211,7 +1211,7 @@ index a2938cd834..2d3a5c72fb 100644 @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparate(self): fd0, fd1 = self.newFDs(2) -@@ -3563,7 +3565,8 @@ +@@ -3576,7 +3578,8 @@ array.array("i", [fd1]))]), len(MSG)) @@ -1221,7 +1221,7 @@ index a2938cd834..2d3a5c72fb 100644 @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparateMinSpace(self): -@@ -3577,7 +3580,8 @@ +@@ -3590,7 +3593,8 @@ maxcmsgs=2, ignoreflags=socket.MSG_CTRUNC) @testFDPassSeparateMinSpace.client_skip @@ -1231,7 +1231,7 @@ index a2938cd834..2d3a5c72fb 100644 @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparateMinSpace(self): fd0, fd1 = self.newFDs(2) -@@ -3601,7 +3605,8 @@ +@@ -3614,7 +3618,8 @@ nbytes = self.sendmsgToServer([msg]) self.assertEqual(nbytes, len(msg)) @@ -1241,7 +1241,7 @@ index a2938cd834..2d3a5c72fb 100644 def testFDPassEmpty(self): # Try to pass an empty FD array. Can receive either no array # or an empty array. -@@ -4421,28 +4426,38 @@ +@@ -4434,28 +4439,38 @@ pass @requireAttrs(socket.socket, "sendmsg") @@ -1470,7 +1470,7 @@ index 44974d433b..ae4e18802b 100755 # # Platform support for Windows diff --git a/Makefile.pre.in b/Makefile.pre.in -index e145315c45..9ad1a257a0 100644 +index 8fbcd7ac17..0e85788dba 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -344,6 +344,8 @@ @@ -1697,7 +1697,7 @@ index aa93e756c6..fcf3784c2f 100644 sin(pi*x), giving accurate results for all finite x (especially x integral or close to an integer). This is here for use in the diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c -index d7cac2b67f..f7d49ac2c0 100644 +index 378032501f..25a035acd7 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -69,6 +69,8 @@ @@ -1780,7 +1780,7 @@ index d7cac2b67f..f7d49ac2c0 100644 return result; } #endif -@@ -13697,6 +13727,7 @@ +@@ -13700,6 +13730,7 @@ int is_symlink; int need_stat; #endif @@ -1788,7 +1788,7 @@ index d7cac2b67f..f7d49ac2c0 100644 #ifdef MS_WINDOWS unsigned long dir_bits; #endif -@@ -13757,6 +13788,7 @@ +@@ -13760,6 +13791,7 @@ #endif return result; @@ -3776,7 +3776,7 @@ index d74fb6deac..a30f74f03d 100755 # Blank kernel with real OS is always fine. ;; diff --git a/configure b/configure -index 078bb5bef1..84ef6459fb 100755 +index 91227f00be..d75c8c48fc 100755 --- a/configure +++ b/configure @@ -836,6 +836,8 @@ @@ -3928,8 +3928,8 @@ index 078bb5bef1..84ef6459fb 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -6249,7 +6293,7 @@ - x86_64-*-freebsd*/clang) : +@@ -6253,7 +6297,7 @@ + x86_64-*-freebsd*/clang) : PY_SUPPORT_TIER=3 ;; #( *) : - PY_SUPPORT_TIER=0 @@ -3937,7 +3937,7 @@ index 078bb5bef1..84ef6459fb 100755 ;; esac -@@ -7048,11 +7092,23 @@ +@@ -7052,11 +7096,23 @@ fi if test "$cross_compiling" = yes; then @@ -3966,7 +3966,7 @@ index 078bb5bef1..84ef6459fb 100755 fi -@@ -10704,6 +10760,9 @@ +@@ -10709,6 +10765,9 @@ BLDSHARED="$LDSHARED" fi ;; @@ -3976,7 +3976,7 @@ index 078bb5bef1..84ef6459fb 100755 Emscripten|WASI) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; -@@ -11112,8 +11171,8 @@ +@@ -11117,8 +11176,8 @@ pkg_failed=no @@ -3987,7 +3987,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -n "$LIBUUID_CFLAGS"; then pkg_cv_LIBUUID_CFLAGS="$LIBUUID_CFLAGS" -@@ -11153,7 +11212,7 @@ +@@ -11158,7 +11217,7 @@ if test $pkg_failed = yes; then @@ -3996,7 +3996,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -@@ -11296,7 +11355,7 @@ +@@ -11301,7 +11360,7 @@ elif test $pkg_failed = untried; then @@ -4005,7 +4005,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } save_CFLAGS=$CFLAGS -@@ -11947,23 +12006,35 @@ +@@ -11952,23 +12011,35 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_system_ffi" >&5 $as_echo "$with_system_ffi" >&6; } else @@ -4047,7 +4047,7 @@ index 078bb5bef1..84ef6459fb 100755 # Check for use of the system libmpdec library { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-system-libmpdec" >&5 $as_echo_n "checking for --with-system-libmpdec... " >&6; } -@@ -12104,8 +12175,8 @@ +@@ -12109,8 +12180,8 @@ pkg_failed=no @@ -4058,7 +4058,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -n "$LIBNSL_CFLAGS"; then pkg_cv_LIBNSL_CFLAGS="$LIBNSL_CFLAGS" -@@ -12145,7 +12216,7 @@ +@@ -12150,7 +12221,7 @@ if test $pkg_failed = yes; then @@ -4067,7 +4067,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -@@ -12246,7 +12317,7 @@ +@@ -12251,7 +12322,7 @@ LIBNSL_LIBS=${LIBNSL_LIBS-$libnsl} elif test $pkg_failed = untried; then @@ -4076,7 +4076,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } LIBNSL_CFLAGS=${LIBNSL_CFLAGS-""} -@@ -12394,8 +12465,8 @@ +@@ -12399,8 +12470,8 @@ pkg_failed=no @@ -4087,7 +4087,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -n "$LIBSQLITE3_CFLAGS"; then pkg_cv_LIBSQLITE3_CFLAGS="$LIBSQLITE3_CFLAGS" -@@ -12435,7 +12506,7 @@ +@@ -12440,7 +12511,7 @@ if test $pkg_failed = yes; then @@ -4096,7 +4096,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -@@ -12457,7 +12528,7 @@ +@@ -12462,7 +12533,7 @@ elif test $pkg_failed = untried; then @@ -4105,7 +4105,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } LIBSQLITE3_CFLAGS=${LIBSQLITE3_CFLAGS-""} -@@ -13226,8 +13297,8 @@ +@@ -13231,8 +13302,8 @@ pkg_failed=no @@ -4116,7 +4116,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -n "$TCLTK_CFLAGS"; then pkg_cv_TCLTK_CFLAGS="$TCLTK_CFLAGS" -@@ -13267,7 +13338,7 @@ +@@ -13272,7 +13343,7 @@ if test $pkg_failed = yes; then @@ -4125,7 +4125,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -@@ -13285,7 +13356,7 @@ +@@ -13290,7 +13361,7 @@ found_tcltk=no elif test $pkg_failed = untried; then @@ -4134,7 +4134,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } found_tcltk=no else -@@ -13321,8 +13392,8 @@ +@@ -13326,8 +13397,8 @@ pkg_failed=no @@ -4145,7 +4145,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -n "$X11_CFLAGS"; then pkg_cv_X11_CFLAGS="$X11_CFLAGS" -@@ -13362,7 +13433,7 @@ +@@ -13367,7 +13438,7 @@ if test $pkg_failed = yes; then @@ -4154,7 +4154,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -@@ -13389,7 +13460,7 @@ +@@ -13394,7 +13465,7 @@ and X11_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then @@ -4163,7 +4163,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -@@ -15952,8 +16023,8 @@ +@@ -15969,8 +16040,8 @@ pkg_failed=no @@ -4174,7 +4174,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -n "$ZLIB_CFLAGS"; then pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" -@@ -15993,7 +16064,7 @@ +@@ -16010,7 +16081,7 @@ if test $pkg_failed = yes; then @@ -4183,7 +4183,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -@@ -16137,7 +16208,7 @@ +@@ -16154,7 +16225,7 @@ elif test $pkg_failed = untried; then @@ -4192,7 +4192,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } save_CFLAGS=$CFLAGS -@@ -16300,8 +16371,8 @@ +@@ -16317,8 +16388,8 @@ pkg_failed=no @@ -4203,7 +4203,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -n "$BZIP2_CFLAGS"; then pkg_cv_BZIP2_CFLAGS="$BZIP2_CFLAGS" -@@ -16341,7 +16412,7 @@ +@@ -16358,7 +16429,7 @@ if test $pkg_failed = yes; then @@ -4212,7 +4212,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -@@ -16438,7 +16509,7 @@ +@@ -16455,7 +16526,7 @@ elif test $pkg_failed = untried; then @@ -4221,7 +4221,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } save_CFLAGS=$CFLAGS -@@ -16530,8 +16601,8 @@ +@@ -16547,8 +16618,8 @@ pkg_failed=no @@ -4232,7 +4232,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -n "$LIBLZMA_CFLAGS"; then pkg_cv_LIBLZMA_CFLAGS="$LIBLZMA_CFLAGS" -@@ -16571,7 +16642,7 @@ +@@ -16588,7 +16659,7 @@ if test $pkg_failed = yes; then @@ -4241,7 +4241,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -@@ -16668,7 +16739,7 @@ +@@ -16685,7 +16756,7 @@ elif test $pkg_failed = untried; then @@ -4250,7 +4250,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } save_CFLAGS=$CFLAGS -@@ -17267,8 +17338,8 @@ +@@ -17918,8 +17989,8 @@ pkg_failed=no @@ -4261,7 +4261,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -n "$LIBCRYPT_CFLAGS"; then pkg_cv_LIBCRYPT_CFLAGS="$LIBCRYPT_CFLAGS" -@@ -17308,7 +17379,7 @@ +@@ -17959,7 +18030,7 @@ if test $pkg_failed = yes; then @@ -4270,7 +4270,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -@@ -17405,7 +17476,7 @@ +@@ -18056,7 +18127,7 @@ elif test $pkg_failed = untried; then @@ -4279,7 +4279,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } save_CFLAGS=$CFLAGS -@@ -22177,8 +22248,8 @@ +@@ -22828,8 +22899,8 @@ if ! $found; then OPENSSL_INCLUDES= for ssldir in $ssldirs; do @@ -4290,7 +4290,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -f "$ssldir/include/openssl/ssl.h"; then OPENSSL_INCLUDES="-I$ssldir/include" OPENSSL_LDFLAGS="-L$ssldir/lib" -@@ -22559,8 +22630,8 @@ +@@ -23210,8 +23281,8 @@ pkg_failed=no @@ -4301,7 +4301,7 @@ index 078bb5bef1..84ef6459fb 100755 if test -n "$LIBB2_CFLAGS"; then pkg_cv_LIBB2_CFLAGS="$LIBB2_CFLAGS" -@@ -22600,7 +22671,7 @@ +@@ -23251,7 +23322,7 @@ if test $pkg_failed = yes; then @@ -4310,7 +4310,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -@@ -22618,7 +22689,7 @@ +@@ -23269,7 +23340,7 @@ have_libb2=no elif test $pkg_failed = untried; then @@ -4319,7 +4319,7 @@ index 078bb5bef1..84ef6459fb 100755 $as_echo "no" >&6; } have_libb2=no else -@@ -22689,6 +22760,29 @@ +@@ -23340,6 +23411,29 @@ py_cv_module_ossaudiodev=n/a py_cv_module_spwd=n/a ;; #( @@ -4350,7 +4350,7 @@ index 078bb5bef1..84ef6459fb 100755 diff --git a/configure.ac b/configure.ac -index 09f3f902a6..8563e88760 100644 +index 77fb609b74..72acc6e054 100644 --- a/configure.ac +++ b/configure.ac @@ -545,6 +545,15 @@ @@ -4442,8 +4442,8 @@ index 09f3f902a6..8563e88760 100644 [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) @@ -1129,6 +1180,15 @@ - dnl [wasm32-unknown-emscripten/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly Emscripten - dnl [wasm32-unknown-wasi/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly System Interface + [wasm32-unknown-emscripten/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly Emscripten + [wasm32-unknown-wasi/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly System Interface [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 + dnl [aarch64-apple-ios/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 + dnl [aarch64-apple-ios-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 @@ -4495,7 +4495,7 @@ index 09f3f902a6..8563e88760 100644 dnl Start with 20 MB and allow to grow AS_VAR_APPEND([LDFLAGS_NODIST], [" -sALLOW_MEMORY_GROWTH -sTOTAL_MEMORY=20971520"]) -@@ -3128,6 +3200,9 @@ +@@ -3129,6 +3201,9 @@ BLDSHARED="$LDSHARED" fi ;; @@ -4505,7 +4505,7 @@ index 09f3f902a6..8563e88760 100644 Emscripten|WASI) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; -@@ -3597,20 +3672,30 @@ +@@ -3598,20 +3673,30 @@ esac AC_MSG_RESULT($with_system_ffi) else @@ -4541,7 +4541,7 @@ index 09f3f902a6..8563e88760 100644 # Check for use of the system libmpdec library AC_MSG_CHECKING(for --with-system-libmpdec) -@@ -6764,6 +6849,30 @@ +@@ -6788,6 +6873,30 @@ [AIX], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])], [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [_crypt], [termios], [grp])], [Darwin], [PY_STDLIB_MOD_SET_NA([ossaudiodev], [spwd])], diff --git a/patch/xz-5.2.5.patch b/patch/xz-5.2.6.patch similarity index 100% rename from patch/xz-5.2.5.patch rename to patch/xz-5.2.6.patch From 2f42105838ab8f6f7e703ddb929d97758a36145e Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 5 Sep 2022 13:51:38 +0800 Subject: [PATCH 24/37] Tweak the values returned by platform module. --- patch/Python/Python.patch | 156 ++++++++++++++++++++++++++++---------- 1 file changed, 115 insertions(+), 41 deletions(-) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index c0225ab7..5bf60d06 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,3 +1,42 @@ +--- /dev/null ++++ b/Lib/_ios_support.py +@@ -0,0 +1,36 @@ ++from ctypes import cdll, c_void_p, c_char_p ++from ctypes import util ++ ++ ++def get_platform_ios(): ++ objc = cdll.LoadLibrary(util.find_library(b'objc')) ++ ++ objc.objc_getClass.restype = c_void_p ++ objc.objc_getClass.argtypes = [c_char_p] ++ objc.objc_msgSend.restype = c_void_p ++ objc.objc_msgSend.argtypes = [c_void_p, c_void_p] ++ objc.sel_registerName.restype = c_void_p ++ objc.sel_registerName.argtypes = [c_char_p] ++ ++ UIDevice = c_void_p(objc.objc_getClass(b'UIDevice')) ++ SEL_currentDevice = c_void_p(objc.sel_registerName(b'currentDevice')) ++ device = c_void_p(objc.objc_msgSend(UIDevice, SEL_currentDevice)) ++ ++ SEL_systemVersion = c_void_p(objc.sel_registerName(b'systemVersion')) ++ systemVersion = c_void_p(objc.objc_msgSend(device, SEL_systemVersion)) ++ ++ SEL_systemName = c_void_p(objc.sel_registerName(b'systemName')) ++ systemName = c_void_p(objc.objc_msgSend(device, SEL_systemName)) ++ ++ SEL_model = c_void_p(objc.sel_registerName(b'model')) ++ systemModel = c_void_p(objc.objc_msgSend(device, SEL_model)) ++ ++ # UTF8String returns a const char*; ++ SEL_UTF8String = c_void_p(objc.sel_registerName(b'UTF8String')) ++ objc.objc_msgSend.restype = c_char_p ++ ++ system = objc.objc_msgSend(systemName, SEL_UTF8String).decode() ++ release = objc.objc_msgSend(systemVersion, SEL_UTF8String).decode() ++ model = objc.objc_msgSend(systemModel, SEL_UTF8String).decode() ++ ++ return system, release, model diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py index 9c39179d2a..459e0f476f 100644 --- a/Lib/ctypes/test/test_as_parameter.py @@ -554,58 +593,46 @@ index f603a89f7f..faea51d1b5 100644 + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) diff --git a/Lib/platform.py b/Lib/platform.py -index c272c407c7..e76bc7127f 100755 +index c272c407c7..1b1aa6d141 100755 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -451,6 +451,47 @@ +@@ -451,6 +451,35 @@ # If that also doesn't work return the default values return release, versioninfo, machine +def iOS_ver(): + """ Get iOS/tvOS version information, and return it as a -+ tuple (system, release). All tuple entries are strings. ++ tuple (system, release, model). All tuple entries are strings. + + Equivalent of: -+ system = [[UIDevice currentDevice].model] UTF8String] ++ system = [[UIDevice currentDevice].systemName] UTF8String] + release = [[UIDevice currentDevice].systemVersion] UTF8String] -+ ++ model = [[UIDevice currentDevice].model] UTF8String] + """ -+ from ctypes import cast, cdll, c_void_p, c_char_p -+ from ctypes import util -+ objc = cdll.LoadLibrary(util.find_library(b'objc')) -+ uikit = cdll.LoadLibrary(util.find_library(b'UIKit')) -+ -+ objc.objc_getClass.restype = c_void_p -+ objc.objc_getClass.argtypes = [c_char_p] -+ objc.objc_msgSend.restype = c_void_p -+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p] -+ objc.sel_registerName.restype = c_void_p -+ objc.sel_registerName.argtypes = [c_char_p] -+ -+ UIDevice = c_void_p(objc.objc_getClass(b'UIDevice')) -+ SEL_currentDevice = c_void_p(objc.sel_registerName(b'currentDevice')) -+ device = c_void_p(objc.objc_msgSend(UIDevice, SEL_currentDevice)) -+ -+ SEL_systemVersion = c_void_p(objc.sel_registerName(b'systemVersion')) -+ systemVersion = c_void_p(objc.objc_msgSend(device, SEL_systemVersion)) ++ import _ios_support ++ return _ios_support.get_platform_ios() + -+ SEL_systemName = c_void_p(objc.sel_registerName(b'systemName')) -+ systemName = c_void_p(objc.objc_msgSend(device, SEL_systemName)) ++def is_simulator(): ++ """Determine if the current platform is a device simulator. + -+ # UTF8String returns a const char*; -+ SEL_UTF8String = c_void_p(objc.sel_registerName(b'UTF8String')) -+ objc.objc_msgSend.restype = c_char_p ++ Only useful when working with iOS, tvOS or watchOS, because ++ Apple provides simulator platforms for those devices. + -+ system = objc.objc_msgSend(systemName, SEL_UTF8String).decode() -+ release = objc.objc_msgSend(systemVersion, SEL_UTF8String).decode() -+ -+ return system, release ++ If the platform is actual hardware, returns False. Will also ++ return False for device *emulators*, which are indistinguishable ++ from actual devices because they are reproducing actual device ++ properties. ++ """ ++ if sys.platform in ('ios', 'tvos', 'watchos'): ++ return sys.implementation._multiarch.endswith('simulator') + ++ # All other platforms aren't simulators. ++ return False + def _java_getprop(name, default): from java.lang import System -@@ -607,7 +648,7 @@ +@@ -607,7 +636,7 @@ default in case the command should fail. """ @@ -614,21 +641,47 @@ index c272c407c7..e76bc7127f 100755 # XXX Others too ? return default -@@ -749,6 +790,13 @@ +@@ -749,6 +778,24 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' -+ # iOS, tvOS and watchOS processor is the same as machine ++ # On iOS, tvOS and watchOS, os.uname returns the architecture ++ # as uname.machine. On device it doesn't; but there's only ++ # on CPU architecture on device + def get_ios(): -+ return '' ++ if sys.implementation._multiarch.endswith('simulator'): ++ return os.uname().machine ++ return 'arm64' ++ ++ def get_tvos(): ++ if sys.implementation._multiarch.endswith('simulator'): ++ return os.uname().machine ++ return 'arm64' + -+ get_tvos = get_ios -+ get_watchos = get_ios ++ def get_watchos(): ++ if sys.implementation._multiarch.endswith('simulator'): ++ return os.uname().machine ++ return 'arm64_32' + def from_subprocess(): """ Fall back to `uname -p` -@@ -1212,11 +1260,13 @@ +@@ -900,6 +947,14 @@ + system = 'Windows' + release = 'Vista' + ++ # Normalize responses on Apple mobile platforms ++ if sys.platform in ('ios', 'tvos'): ++ system, release, model = iOS_ver() ++ # Simulator devices report as "arm64" or "x86_64"; ++ # use the model as the basis for the normalized machine name. ++ if sys.implementation._multiarch.endswith('simulator'): ++ machine = f'{model} Simulator' ++ + vals = system, node, release, version, machine + # Replace 'unknown' values with the more portable '' + _uname_cache = uname_result(*map(_unknown_as_blank, vals)) +@@ -1212,11 +1267,13 @@ system, release, version = system_alias(system, release, version) if system == 'Darwin': @@ -638,7 +691,7 @@ index c272c407c7..e76bc7127f 100755 - system = 'macOS' - release = macos_release + if sys.platform in ('ios', 'tvos'): -+ system, release = iOS_ver() ++ system, release, model = iOS_ver() + else: + macos_release = mac_ver()[0] + if macos_release: @@ -677,7 +730,7 @@ index 7ae8df154b..08899c9921 100644 if _mswindows: import _winapi diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py -index ebe3711827..6d20f3c2d2 100644 +index ebe3711827..ea00c3176d 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -95,6 +95,33 @@ @@ -734,6 +787,27 @@ index ebe3711827..6d20f3c2d2 100644 return { 'prefix': 'posix_prefix', 'home': 'posix_home', +@@ -788,10 +822,16 @@ + if m: + release = m.group() + elif osname[:6] == "darwin": +- import _osx_support +- osname, release, machine = _osx_support.get_platform_osx( +- get_config_vars(), +- osname, release, machine) ++ if sys.platform in ("ios", "tvos", "watchos"): ++ import _ios_support ++ _, release, model = _ios_support.get_platform_ios() ++ osname = sys.platform ++ machine = sys.implementation._multiarch ++ else: ++ import _osx_support ++ osname, release, machine = _osx_support.get_platform_osx( ++ get_config_vars(), ++ osname, release, machine) + + return f"{osname}-{release}-{machine}" + diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 39b3530905..ef1fa18624 100644 --- a/Lib/test/support/__init__.py From e25db431b90dfff55b1fa7727009128370316df0 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 6 Sep 2022 09:53:22 +0800 Subject: [PATCH 25/37] Correct the XZ patch for the 5.2.6 update. --- patch/xz-5.2.6.patch | 59 ++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/patch/xz-5.2.6.patch b/patch/xz-5.2.6.patch index 42950e07..6876e0d5 100644 --- a/patch/xz-5.2.6.patch +++ b/patch/xz-5.2.6.patch @@ -1,34 +1,35 @@ -diff -ru xz-5.2.5/build-aux/config.sub xz-5.2.5-patched/build-aux/config.sub ---- xz-5.2.5/build-aux/config.sub 2020-03-17 22:29:35.000000000 +0800 -+++ xz-5.2.5-patched/build-aux/config.sub 2022-08-10 08:38:41.000000000 +0800 -@@ -116,6 +116,7 @@ - case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ -+ ios*-simulator | tvos*-simulator | watchos*-simulator | \ - knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ - kopensolaris*-gnu* | cloudabi*-eabi* | \ - storm-chaos* | os2-emx* | rtmk-nova*) -@@ -450,6 +451,9 @@ - | ymp-* \ - | z8k-* | z80-*) +diff -ru xz-5.2.6-orig/build-aux/config.sub xz-5.2.6/build-aux/config.sub +--- xz-5.2.6-orig/build-aux/config.sub 2022-08-12 18:56:06.000000000 +0800 ++++ xz-5.2.6/build-aux/config.sub 2022-09-01 14:36:08.000000000 +0800 +@@ -1121,10 +1121,9 @@ + xscale-* | xscalee[bl]-*) + cpu=`echo "$cpu" | sed 's/^xscale/arm/'` ;; -+ arm64-* | arm64e-* | arm64_32-*) -+ basic_machine=aarch64-`echo "$basic_machine" | sed 's/^[^-]*-//'` -+ ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown -@@ -1520,7 +1524,11 @@ +- arm64-*) ++ arm64-* | arm64_32-*) + cpu=aarch64 ;; - -nacl*) +- + # Recognize the canonical CPU Types that limit and/or modify the + # company names they are paired with. + cr16-*) +@@ -1723,7 +1722,7 @@ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ + | hiux* | abug | nacl* | netware* | windows* \ +- | os9* | macos* | osx* | ios* \ ++ | os9* | macos* | osx* | ios* | tvos* | watchos* \ + | mpw* | magic* | mmixware* | mon960* | lnews* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* | twizzler* \ +@@ -1786,6 +1785,8 @@ ;; -- -ios) -+ -ios | -ios-simulator) -+ ;; -+ -tvos | -tvos-simulator) -+ ;; -+ -watchos | -watchos-simulator) + *-eabi* | *-gnueabi*) ;; - -none) ++ ios*-simulator | tvos*-simulator | watchos*-simulator) ++ ;; + -*) + # Blank kernel with real OS is always fine. ;; +Only in xz-5.2.6/build-aux: config.sub.orig +Only in xz-5.2.6/build-aux: config.sub.rej From 59c518b36de1dde746a569219d3da7328250b56b Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 6 Sep 2022 09:59:27 +0800 Subject: [PATCH 26/37] Clean up sysconfigdata shims. --- patch/Python/_sysconfigdata__ios_iphoneos.py | 9 +-------- patch/Python/_sysconfigdata__ios_iphonesimulator.py | 5 +++-- patch/Python/_sysconfigdata__tvos_appletvos.py | 8 +------- patch/Python/_sysconfigdata__tvos_appletvsimulator.py | 5 +++-- patch/Python/_sysconfigdata__watchos_watchos.py | 8 +------- patch/Python/_sysconfigdata__watchos_watchsimulator.py | 5 +++-- 6 files changed, 12 insertions(+), 28 deletions(-) diff --git a/patch/Python/_sysconfigdata__ios_iphoneos.py b/patch/Python/_sysconfigdata__ios_iphoneos.py index b7de2e70..780bf883 100644 --- a/patch/Python/_sysconfigdata__ios_iphoneos.py +++ b/patch/Python/_sysconfigdata__ios_iphoneos.py @@ -1,9 +1,2 @@ -import platform - - +# There's only one supported architecture for iOS hardware from _sysconfigdata__ios_iphoneos_arm64 import * -# arch = platform.machine() -# if arch == 'arm64': -# from _sysconfigdata__ios_iphoneos_arm64 import * -# else: -# raise RuntimeError("Unknown iOS architecture.") diff --git a/patch/Python/_sysconfigdata__ios_iphonesimulator.py b/patch/Python/_sysconfigdata__ios_iphonesimulator.py index 09bf13f1..1bede110 100644 --- a/patch/Python/_sysconfigdata__ios_iphonesimulator.py +++ b/patch/Python/_sysconfigdata__ios_iphonesimulator.py @@ -1,7 +1,8 @@ -import platform +import os -arch = platform.machine() +# os.uname().machine is the host CPU architecture on the simulator +arch = os.uname().machine if arch == 'x86_64': from _sysconfigdata__ios_iphonesimulator_x86_64 import * elif arch == 'arm64': diff --git a/patch/Python/_sysconfigdata__tvos_appletvos.py b/patch/Python/_sysconfigdata__tvos_appletvos.py index 9d3687fe..eb7ac9cc 100644 --- a/patch/Python/_sysconfigdata__tvos_appletvos.py +++ b/patch/Python/_sysconfigdata__tvos_appletvos.py @@ -1,8 +1,2 @@ -import platform - +# There's only one supported architecture for tvOS hardware from _sysconfigdata__tvos_appletvos_arm64 import * -# arch = platform.machine() -# if arch == 'arm64': -# from _sysconfigdata__tvos_appletvos_arm64 import * -# else: -# raise RuntimeError("Unknown tvOS architecture.") diff --git a/patch/Python/_sysconfigdata__tvos_appletvsimulator.py b/patch/Python/_sysconfigdata__tvos_appletvsimulator.py index cd0739b2..29f8a67b 100644 --- a/patch/Python/_sysconfigdata__tvos_appletvsimulator.py +++ b/patch/Python/_sysconfigdata__tvos_appletvsimulator.py @@ -1,7 +1,8 @@ -import platform +import os -arch = platform.machine() +# os.uname().machine is the host CPU architecture on the simulator +arch = os.uname().machine if arch == 'x86_64': from _sysconfigdata__tvos_appletvsimulator_x86_64 import * elif arch == 'arm64': diff --git a/patch/Python/_sysconfigdata__watchos_watchos.py b/patch/Python/_sysconfigdata__watchos_watchos.py index 7b2a60e1..c1872dfc 100644 --- a/patch/Python/_sysconfigdata__watchos_watchos.py +++ b/patch/Python/_sysconfigdata__watchos_watchos.py @@ -1,8 +1,2 @@ -import platform - +# There's only one supported architecture for watchOS hardware from _sysconfigdata__watchos_watchos_arm64_32 import * -# arch = platform.machine() -# if arch == 'arm64_32': -# from _sysconfigdata__watchos_watchos_arm64_32 import * -# else: -# raise RuntimeError("Unknown watchOS architecture.") diff --git a/patch/Python/_sysconfigdata__watchos_watchsimulator.py b/patch/Python/_sysconfigdata__watchos_watchsimulator.py index 4fa46260..c34bd97b 100644 --- a/patch/Python/_sysconfigdata__watchos_watchsimulator.py +++ b/patch/Python/_sysconfigdata__watchos_watchsimulator.py @@ -1,7 +1,8 @@ -import platform +import os -arch = platform.machine() +# os.uname().machine is the host CPU architecture on the simulator +arch = os.uname().machine if arch == 'x86_64': from _sysconfigdata__watchos_watchsimulator_x86_64 import * elif arch == 'arm64': From 59f9bcb1de9cbb29deb85f585d233000ce02d52d Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 6 Sep 2022 11:55:38 +0800 Subject: [PATCH 27/37] Normalize the naming of the BZip2 merged library. --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b13dc46a..3c669aab 100644 --- a/Makefile +++ b/Makefile @@ -400,7 +400,7 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$$(SDK-$(target)))/include" \ LIBLZMA_LIBS="-L$$(XZ_MERGE-$$(SDK-$(target)))/lib -llzma" \ BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$$(SDK-$(target)))/include" \ - BZIP2_LIBS="-L$$(BZIP2_MERGE-$$(SDK-$(target)))/lib -lbzip2" \ + BZIP2_LIBS="-L$$(BZIP2_MERGE-$$(SDK-$(target)))/lib -lbz2" \ LIBFFI_INCLUDEDIR="$$(LIBFFI_MERGE-$$(SDK-$(target)))/include" \ LIBFFI_LIBDIR="$$(LIBFFI_MERGE-$$(SDK-$(target)))/lib" \ LIBFFI_LIB="ffi" \ @@ -496,7 +496,7 @@ LDFLAGS-$(sdk)=\ # Predeclare SDK constants that are used by the build-target macro BZIP2_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION) -BZIP2_FATLIB-$(sdk)=$$(BZIP2_MERGE-$(sdk))/lib/libbzip2.a +BZIP2_FATLIB-$(sdk)=$$(BZIP2_MERGE-$(sdk))/lib/libbz2.a XZ_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/xz-$(XZ_VERSION) XZ_FATLIB-$(sdk)=$$(XZ_MERGE-$(sdk))/lib/liblzma.a @@ -611,7 +611,7 @@ $$(PYTHON_SRCDIR-$(sdk))/Makefile: \ LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$(sdk))/include" \ LIBLZMA_LIBS="-L$$(XZ_MERGE-$(sdk))/lib -llzma" \ BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$(sdk))/include" \ - BZIP2_LIBS="-L$$(BZIP2_MERGE-$(sdk))/lib -lbzip2" \ + BZIP2_LIBS="-L$$(BZIP2_MERGE-$(sdk))/lib -lbz2" \ --prefix="$$(PYTHON_INSTALL-$(sdk))" \ --enable-ipv6 \ --enable-universalsdk \ From dba406c127810155f13ce15e14bcbb8418cb84b4 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 6 Sep 2022 12:59:36 +0800 Subject: [PATCH 28/37] Update patch to Python 3.10.6. --- Makefile | 32 +- README.rst | 2 +- patch/Python/Python.patch | 783 ++++++++++++++++++++++++++++++-------- 3 files changed, 649 insertions(+), 168 deletions(-) diff --git a/Makefile b/Makefile index f2eed50b..3761b9fc 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ BUILD_NUMBER=custom # PYTHON_VERSION is the full version number (e.g., 3.10.0b3) # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.10.4 +PYTHON_VERSION=3.10.6 PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) @@ -215,9 +215,11 @@ CXX-$(target)=xcrun --sdk $$(SDK-$(target)) clang AR-$(target)=xcrun --sdk $$(SDK-$(target)) ar CFLAGS-$(target)=\ -target $$(TARGET_TRIPLE-$(target)) \ + --sysroot=$$(SDK_ROOT-$(target)) \ $$(CFLAGS-$(os)) LDFLAGS-$(target)=\ -target $$(TARGET_TRIPLE-$(target)) \ + --sysroot=$$(SDK_ROOT-$(target)) \ $$(CFLAGS-$(os)) ########################################################################### @@ -391,22 +393,18 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ cd $$(PYTHON_SRCDIR-$(target)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch # Configure target Python cd $$(PYTHON_SRCDIR-$(target)) && \ + PATH="$$(PYTHON_INSTALL-macosx)/bin:$(PATH)" \ ./configure \ AR="$$(AR-$(target))" \ CC="$$(CC-$(target))" \ CXX="$$(CXX-$(target))" \ - CFLAGS="$$(CFLAGS-$(target))" \ - LDFLAGS="$$(LDFLAGS-$(target))" \ - LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$$(SDK-$(target)))/include" \ - LIBLZMA_LIBS="-L$$(XZ_MERGE-$$(SDK-$(target)))/lib -llzma" \ - BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$$(SDK-$(target)))/include" \ - BZIP2_LIBS="-L$$(BZIP2_MERGE-$$(SDK-$(target)))/lib -lbz2" \ + CFLAGS="$$(CFLAGS-$(target)) -I$$(BZIP2_MERGE-$$(SDK-$(target)))/include -I$$(XZ_MERGE-$$(SDK-$(target)))/include" \ + LDFLAGS="$$(LDFLAGS-$(target)) -L$$(BZIP2_MERGE-$$(SDK-$(target)))/lib -L$$(XZ_MERGE-$$(SDK-$(target)))/lib" \ LIBFFI_INCLUDEDIR="$$(LIBFFI_MERGE-$$(SDK-$(target)))/include" \ LIBFFI_LIBDIR="$$(LIBFFI_MERGE-$$(SDK-$(target)))/lib" \ LIBFFI_LIB="ffi" \ --host=$$(TARGET_TRIPLE-$(target)) \ --build=$(HOST_ARCH)-apple-darwin \ - --with-build-python=$$(PYTHON_INSTALL-macosx)/bin/python$(PYTHON_VER) \ --prefix="$$(PYTHON_INSTALL-$(target))" \ --enable-ipv6 \ --with-openssl="$$(OPENSSL_MERGE-$$(SDK-$(target)))" \ @@ -420,14 +418,16 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ $$(PYTHON_SRCDIR-$(target))/python.exe: $$(PYTHON_SRCDIR-$(target))/Makefile @echo ">>> Build Python for $(target)" cd $$(PYTHON_SRCDIR-$(target)) && \ - make all \ - 2>&1 | tee -a ../python-$(PYTHON_VERSION).build.log + PATH="$$(PYTHON_INSTALL-macosx)/bin:$(PATH)" \ + make all \ + 2>&1 | tee -a ../python-$(PYTHON_VERSION).build.log $$(PYTHON_LIB-$(target)): $$(PYTHON_SRCDIR-$(target))/python.exe @echo ">>> Install Python for $(target)" cd $$(PYTHON_SRCDIR-$(target)) && \ - make install \ - 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log + PATH="$$(PYTHON_INSTALL-macosx)/bin:$(PATH)" \ + make install \ + 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log endif @@ -606,12 +606,8 @@ $$(PYTHON_SRCDIR-$(sdk))/Makefile: \ cd $$(PYTHON_SRCDIR-$(sdk)) && \ ./configure \ CC="$$(CC-$(sdk))" \ - CFLAGS="$$(CFLAGS-$(sdk))" \ - LDFLAGS="$$(LDFLAGS-$(sdk))" \ - LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$(sdk))/include" \ - LIBLZMA_LIBS="-L$$(XZ_MERGE-$(sdk))/lib -llzma" \ - BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$(sdk))/include" \ - BZIP2_LIBS="-L$$(BZIP2_MERGE-$(sdk))/lib -lbz2" \ + CFLAGS="$$(CFLAGS-$(sdk)) -I$$(BZIP2_MERGE-$(sdk))/include -I$$(XZ_MERGE-$(sdk))/include" \ + LDFLAGS="$$(LDFLAGS-$(sdk)) -L$$(XZ_MERGE-$(sdk))/lib -L$$(BZIP2_MERGE-$(sdk))/lib" \ --prefix="$$(PYTHON_INSTALL-$(sdk))" \ --enable-ipv6 \ --enable-universalsdk \ diff --git a/README.rst b/README.rst index a90aec3e..03948522 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ Python Apple Support ==================== -**This repository branch builds a packaged version of Python 3.10.4**. +**This repository branch builds a packaged version of Python 3.10.6**. Other Python versions are available by cloning other branches of the main repository. diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 12737b70..b4b449ec 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,8 +1,8 @@ diff --git a/Doc/library/os.rst b/Doc/library/os.rst -index 2a1ea05c5c..ec5e12b859 100644 +index 6694768419..59b4feab56 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst -@@ -3295,6 +3295,13 @@ +@@ -3303,6 +3303,13 @@ .. versionadded:: 3.8 @@ -17,7 +17,7 @@ index 2a1ea05c5c..ec5e12b859 100644 .. data:: MFD_CLOEXEC MFD_ALLOW_SEALING diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst -index ab9f1d88a0..93f4e90926 100644 +index 45fcfc7952..04ea3b488e 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -25,6 +25,11 @@ @@ -44,6 +44,45 @@ index bb565201a1..c01db0cf51 100644 /* This block is only used as part of the public API and should not be * included in _datetimemodule.c, which does not use the C API capsule. * See bpo-35081 for more details. +--- /dev/null ++++ b/Lib/_ios_support.py +@@ -0,0 +1,36 @@ ++from ctypes import cdll, c_void_p, c_char_p ++from ctypes import util ++ ++ ++def get_platform_ios(): ++ objc = cdll.LoadLibrary(util.find_library(b'objc')) ++ ++ objc.objc_getClass.restype = c_void_p ++ objc.objc_getClass.argtypes = [c_char_p] ++ objc.objc_msgSend.restype = c_void_p ++ objc.objc_msgSend.argtypes = [c_void_p, c_void_p] ++ objc.sel_registerName.restype = c_void_p ++ objc.sel_registerName.argtypes = [c_char_p] ++ ++ UIDevice = c_void_p(objc.objc_getClass(b'UIDevice')) ++ SEL_currentDevice = c_void_p(objc.sel_registerName(b'currentDevice')) ++ device = c_void_p(objc.objc_msgSend(UIDevice, SEL_currentDevice)) ++ ++ SEL_systemVersion = c_void_p(objc.sel_registerName(b'systemVersion')) ++ systemVersion = c_void_p(objc.objc_msgSend(device, SEL_systemVersion)) ++ ++ SEL_systemName = c_void_p(objc.sel_registerName(b'systemName')) ++ systemName = c_void_p(objc.objc_msgSend(device, SEL_systemName)) ++ ++ SEL_model = c_void_p(objc.sel_registerName(b'model')) ++ systemModel = c_void_p(objc.objc_msgSend(device, SEL_model)) ++ ++ # UTF8String returns a const char*; ++ SEL_UTF8String = c_void_p(objc.sel_registerName(b'UTF8String')) ++ objc.objc_msgSend.restype = c_char_p ++ ++ system = objc.objc_msgSend(systemName, SEL_UTF8String).decode() ++ release = objc.objc_msgSend(systemVersion, SEL_UTF8String).decode() ++ model = objc.objc_msgSend(systemModel, SEL_UTF8String).decode() ++ ++ return system, release, model diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py index f9d27cb89d..da172b345a 100644 --- a/Lib/ctypes/test/test_as_parameter.py @@ -726,58 +765,45 @@ index d26cfc9993..ae6629424e 100644 if _exists("fork") and not _exists("spawnv") and _exists("execv"): diff --git a/Lib/platform.py b/Lib/platform.py -index e32f9c11cd..78afc8a9da 100755 +index e32f9c11cd..be08f544e7 100755 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -449,6 +449,47 @@ +@@ -449,6 +449,34 @@ # If that also doesn't work return the default values return release, versioninfo, machine ++ +def iOS_ver(): + """ Get iOS/tvOS version information, and return it as a -+ tuple (system, release). All tuple entries are strings. -+ ++ tuple (system, release, model). All tuple entries are strings. + Equivalent of: -+ system = [[UIDevice currentDevice].model] UTF8String] ++ system = [[UIDevice currentDevice].systemName] UTF8String] + release = [[UIDevice currentDevice].systemVersion] UTF8String] -+ ++ model = [[UIDevice currentDevice].model] UTF8String] + """ -+ from ctypes import cast, cdll, c_void_p, c_char_p -+ from ctypes import util -+ objc = cdll.LoadLibrary(util.find_library(b'objc')) -+ uikit = cdll.LoadLibrary(util.find_library(b'UIKit')) -+ -+ objc.objc_getClass.restype = c_void_p -+ objc.objc_getClass.argtypes = [c_char_p] -+ objc.objc_msgSend.restype = c_void_p -+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p] -+ objc.sel_registerName.restype = c_void_p -+ objc.sel_registerName.argtypes = [c_char_p] -+ -+ UIDevice = c_void_p(objc.objc_getClass(b'UIDevice')) -+ SEL_currentDevice = c_void_p(objc.sel_registerName(b'currentDevice')) -+ device = c_void_p(objc.objc_msgSend(UIDevice, SEL_currentDevice)) -+ -+ SEL_systemVersion = c_void_p(objc.sel_registerName(b'systemVersion')) -+ systemVersion = c_void_p(objc.objc_msgSend(device, SEL_systemVersion)) -+ -+ SEL_systemName = c_void_p(objc.sel_registerName(b'systemName')) -+ systemName = c_void_p(objc.objc_msgSend(device, SEL_systemName)) -+ -+ # UTF8String returns a const char*; -+ SEL_UTF8String = c_void_p(objc.sel_registerName(b'UTF8String')) -+ objc.objc_msgSend.restype = c_char_p -+ -+ system = objc.objc_msgSend(systemName, SEL_UTF8String).decode() -+ release = objc.objc_msgSend(systemVersion, SEL_UTF8String).decode() ++ import _ios_support ++ return _ios_support.get_platform_ios() ++ ++def is_simulator(): ++ """Determine if the current platform is a device simulator. ++ Only useful when working with iOS, tvOS or watchOS, because ++ Apple provides simulator platforms for those devices. ++ If the platform is actual hardware, returns False. Will also ++ return False for device *emulators*, which are indistinguishable ++ from actual devices because they are reproducing actual device ++ properties. ++ """ ++ if sys.platform in ('ios', 'tvos', 'watchos'): ++ return sys.implementation._multiarch.endswith('simulator') + -+ return system, release ++ # All other platforms aren't simulators. ++ return False + + def _java_getprop(name, default): from java.lang import System -@@ -605,7 +646,7 @@ +@@ -605,7 +633,7 @@ default in case the command should fail. """ @@ -786,21 +812,47 @@ index e32f9c11cd..78afc8a9da 100755 # XXX Others too ? return default -@@ -744,6 +785,13 @@ +@@ -744,6 +772,24 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' -+ # iOS, tvOS and watchOS processor is the same as machine ++ # On iOS, tvOS and watchOS, os.uname returns the architecture ++ # as uname.machine. On device it doesn't; but there's only ++ # on CPU architecture on device + def get_ios(): -+ return '' ++ if sys.implementation._multiarch.endswith('simulator'): ++ return os.uname().machine ++ return 'arm64' + -+ get_tvos = get_ios -+ get_watchos = get_ios ++ def get_tvos(): ++ if sys.implementation._multiarch.endswith('simulator'): ++ return os.uname().machine ++ return 'arm64' ++ ++ def get_watchos(): ++ if sys.implementation._multiarch.endswith('simulator'): ++ return os.uname().machine ++ return 'arm64_32' + def from_subprocess(): """ Fall back to `uname -p` -@@ -1203,11 +1251,13 @@ +@@ -891,6 +937,14 @@ + system = 'Windows' + release = 'Vista' + ++ # Normalize responses on Apple mobile platforms ++ if sys.platform in ('ios', 'tvos'): ++ system, release, model = iOS_ver() ++ # Simulator devices report as "arm64" or "x86_64"; ++ # use the model as the basis for the normalized machine name. ++ if sys.implementation._multiarch.endswith('simulator'): ++ machine = f'{model} Simulator' ++ + vals = system, node, release, version, machine + # Replace 'unknown' values with the more portable '' + _uname_cache = uname_result(*map(_unknown_as_blank, vals)) +@@ -1203,11 +1257,13 @@ system, release, version = system_alias(system, release, version) if system == 'Darwin': @@ -810,7 +862,7 @@ index e32f9c11cd..78afc8a9da 100755 - system = 'macOS' - release = macos_release + if sys.platform in ('ios', 'tvos'): -+ system, release = iOS_ver() ++ system, release, model = iOS_ver() + else: + macos_release = mac_ver()[0] + if macos_release: @@ -834,10 +886,10 @@ index 939893eb5e..8c550ed95a 100644 return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' diff --git a/Lib/subprocess.py b/Lib/subprocess.py -index ccb46a6337..45fefd522d 100644 +index a414321b9d..6b073a2e85 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py -@@ -759,6 +759,9 @@ +@@ -762,6 +762,9 @@ pass_fds=(), *, user=None, group=None, extra_groups=None, encoding=None, errors=None, text=None, umask=-1, pipesize=-1): """Create new Popen instance.""" @@ -847,7 +899,7 @@ index ccb46a6337..45fefd522d 100644 _cleanup() # Held while anything is calling waitpid before returncode has been # updated to prevent clobbering returncode if wait() or poll() are -@@ -1855,7 +1858,7 @@ +@@ -1858,7 +1861,7 @@ else: self.returncode = waitstatus_to_exitcode(sts) @@ -856,7 +908,7 @@ index ccb46a6337..45fefd522d 100644 _WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD): """Check if child process has terminated. Returns returncode attribute. -@@ -1864,6 +1867,8 @@ +@@ -1867,6 +1870,8 @@ outside of the local scope (nor can any methods it calls). """ @@ -866,7 +918,7 @@ index ccb46a6337..45fefd522d 100644 if not self._waitpid_lock.acquire(False): # Something else is busy calling waitpid. Don't allow two diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py -index daf9f00006..327a5178ca 100644 +index daf9f00006..82d5af767c 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -56,6 +56,33 @@ @@ -923,6 +975,27 @@ index daf9f00006..327a5178ca 100644 return { 'prefix': 'posix_prefix', 'home': 'posix_home', +@@ -740,10 +774,16 @@ + if m: + release = m.group() + elif osname[:6] == "darwin": +- import _osx_support +- osname, release, machine = _osx_support.get_platform_osx( +- get_config_vars(), +- osname, release, machine) ++ if sys.platform in ("ios", "tvos", "watchos"): ++ import _ios_support ++ _, release, model = _ios_support.get_platform_ios() ++ osname = sys.platform ++ machine = sys.implementation._multiarch ++ else: ++ import _osx_support ++ osname, release, machine = _osx_support.get_platform_osx( ++ get_config_vars(), ++ osname, release, machine) + + return f"{osname}-{release}-{machine}" + diff --git a/Lib/test/support/script_helper.py b/Lib/test/support/script_helper.py index 6d699c8486..8a4085d4da 100644 --- a/Lib/test/support/script_helper.py @@ -1236,7 +1309,7 @@ index d299f766dc..ebeead6832 100644 code = """if 1: import os, sys diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py -index 33f0c93932..2808d02cc4 100644 +index a7318bf404..e98f27d2b2 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -225,7 +225,8 @@ @@ -1250,10 +1323,10 @@ index 33f0c93932..2808d02cc4 100644 "workers must be greater or equal to 0"): compileall.compile_dir(self.directory, workers=-1) diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py -index 50fa1f189b..0897e65ec8 100644 +index e174d5464d..a79fb48b4a 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py -@@ -160,6 +160,7 @@ +@@ -151,6 +151,7 @@ executor_type = futures.ThreadPoolExecutor @@ -1261,7 +1334,7 @@ index 50fa1f189b..0897e65ec8 100644 class ProcessPoolForkMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "fork" -@@ -174,6 +175,7 @@ +@@ -165,6 +166,7 @@ return super().get_context() @@ -1269,7 +1342,7 @@ index 50fa1f189b..0897e65ec8 100644 class ProcessPoolSpawnMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "spawn" -@@ -186,6 +188,7 @@ +@@ -177,6 +179,7 @@ return super().get_context() @@ -1278,10 +1351,10 @@ index 50fa1f189b..0897e65ec8 100644 executor_type = futures.ProcessPoolExecutor ctx = "forkserver" diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py -index 1098a7b9f4..1dad236b5b 100644 +index ebd4ad9192..ff3d467bf7 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py -@@ -2854,7 +2854,12 @@ +@@ -2875,7 +2875,12 @@ TestResults(failed=1, attempted=1) """ @@ -1395,7 +1468,7 @@ index 5f554897f8..18955cb173 100644 # Regex to parse: # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7 diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py -index 1cc020f635..5b477cacc4 100644 +index 8fdbab4ec0..93cafe1165 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -400,7 +400,7 @@ @@ -1407,7 +1480,7 @@ index 1cc020f635..5b477cacc4 100644 # On Mac OS the HFS+ filesystem replaces bytes that aren't valid # UTF-8 into a percent-encoded value. for name in os.listdir(self.tempdir): -@@ -610,6 +610,7 @@ +@@ -659,6 +659,7 @@ @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, "This test can't be run reliably as root (issue #13308).") @@ -1495,7 +1568,7 @@ index 1d7fca6efb..00bba81def 100644 args = sys.executable, '-m', 'json.tool' process = subprocess.run(args, input=self.data, capture_output=True, text=True, check=True) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py -index 8212cf7a9a..657a44a8ac 100644 +index af68f25820..15875b380e 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1776,9 +1776,21 @@ @@ -1641,10 +1714,10 @@ index 6558952308..a121e8c2dd 100644 from test import support diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py -index 7f7d14ef0a..5cd553ef96 100644 +index 1243b575dc..a05ed400c4 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py -@@ -992,6 +992,7 @@ +@@ -993,6 +993,7 @@ @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') @unittest.skipUnless(hasattr(os, 'popen'), "needs os.popen()") @@ -1652,7 +1725,7 @@ index 7f7d14ef0a..5cd553ef96 100644 def test_update2(self): os.environ.clear() os.environ.update(HELLO="World") -@@ -1002,6 +1003,7 @@ +@@ -1003,6 +1004,7 @@ @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') @unittest.skipUnless(hasattr(os, 'popen'), "needs os.popen()") @@ -1660,7 +1733,7 @@ index 7f7d14ef0a..5cd553ef96 100644 def test_os_popen_iter(self): with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" % unix_shell) as popen: -@@ -1953,6 +1955,7 @@ +@@ -1954,6 +1956,7 @@ @unittest.skipUnless(hasattr(os, 'execv'), "need os.execv()") @@ -1668,7 +1741,7 @@ index 7f7d14ef0a..5cd553ef96 100644 class ExecTests(unittest.TestCase): @unittest.skipIf(USING_LINUXTHREADS, "avoid triggering a linuxthreads bug: see issue #4970") -@@ -2277,6 +2280,7 @@ +@@ -2278,6 +2281,7 @@ self.assertRaises(OverflowError, os.setreuid, 0, self.UID_OVERFLOW) @unittest.skipUnless(hasattr(os, 'setreuid'), 'test needs os.setreuid()') @@ -1676,7 +1749,7 @@ index 7f7d14ef0a..5cd553ef96 100644 def test_setreuid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). -@@ -2285,6 +2289,7 @@ +@@ -2286,6 +2290,7 @@ 'import os,sys;os.setreuid(-1,-1);sys.exit(0)']) @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') @@ -1684,7 +1757,7 @@ index 7f7d14ef0a..5cd553ef96 100644 def test_setregid(self): if os.getuid() != 0 and not HAVE_WHEEL_GROUP: self.assertRaises(OSError, os.setregid, 0, 0) -@@ -2294,6 +2299,7 @@ +@@ -2295,6 +2300,7 @@ self.assertRaises(OverflowError, os.setregid, 0, self.GID_OVERFLOW) @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') @@ -1692,7 +1765,7 @@ index 7f7d14ef0a..5cd553ef96 100644 def test_setregid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). -@@ -2924,6 +2930,7 @@ +@@ -2967,6 +2973,7 @@ class PidTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'getppid'), "test needs os.getppid") @@ -1700,7 +1773,7 @@ index 7f7d14ef0a..5cd553ef96 100644 def test_getppid(self): p = subprocess.Popen([sys.executable, '-c', 'import os; print(os.getppid())'], -@@ -2950,6 +2957,9 @@ +@@ -2993,6 +3000,9 @@ self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) self.assertEqual(pid2, pid) @@ -1710,7 +1783,7 @@ index 7f7d14ef0a..5cd553ef96 100644 def test_waitpid(self): self.check_waitpid(code='pass', exitcode=0) -@@ -3619,6 +3629,7 @@ +@@ -3662,6 +3672,7 @@ self.assertGreaterEqual(size.columns, 0) self.assertGreaterEqual(size.lines, 0) @@ -1815,7 +1888,7 @@ index cac2f6177f..f24ca18099 100644 def _do_test_commandline(self, cmdline, expected): diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py -index 974edd766c..16b7f4d504 100644 +index 701543bb6a..8c5a6636aa 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -64,15 +64,21 @@ @@ -1988,10 +2061,10 @@ index cf32cf2f6a..1615b9ee68 100644 code = textwrap.dedent(''' import time diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py -index 7669b94ac3..b267fa8f88 100644 +index 62e9180375..0214670421 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py -@@ -1737,6 +1737,8 @@ +@@ -1756,6 +1756,8 @@ check_chown(dirname, uid, gid) @@ -2000,7 +2073,7 @@ index 7669b94ac3..b267fa8f88 100644 class TestWhich(BaseTest, unittest.TestCase): def setUp(self): -@@ -2604,6 +2606,7 @@ +@@ -2623,6 +2625,7 @@ self.assertGreaterEqual(size.lines, 0) @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty") @@ -2009,7 +2082,7 @@ index 7669b94ac3..b267fa8f88 100644 'need os.get_terminal_size()') def test_stty_match(self): diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py -index fa74e7770e..7314a9b9ef 100644 +index 93349ed8bb..430b1d7d95 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -210,6 +210,7 @@ @@ -2019,7 +2092,7 @@ index fa74e7770e..7314a9b9ef 100644 + @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') def test_s_option(self): # (ncoghlan) Change this to use script_helper... - usersite = site.USER_SITE + usersite = os.path.normpath(site.USER_SITE) @@ -495,6 +496,7 @@ class StartupImportTests(unittest.TestCase): @@ -2029,7 +2102,7 @@ index fa74e7770e..7314a9b9ef 100644 # Get sys.path in isolated mode (python3 -I) popen = subprocess.Popen([sys.executable, '-I', '-c', diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 5c15648b60..c7a43d1081 100755 +index 9c5f6d3dc9..4629b35f06 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1030,6 +1030,12 @@ @@ -2194,7 +2267,7 @@ index a0cb605c16..7aa460d560 100644 sub = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py -index 7bb0492969..872ddc4c17 100644 +index b91791a02a..9fb3d41249 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -50,6 +50,9 @@ @@ -2222,7 +2295,7 @@ index 007d68817c..b49f38fe2d 100644 import distutils.text_file import distutils.unixccompiler diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py -index 35da72c432..4c1e14a91b 100644 +index 3c362485d9..1c43514b74 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -110,6 +110,7 @@ @@ -2266,7 +2339,7 @@ index 35da72c432..4c1e14a91b 100644 # Force the POSIX locale env = os.environ.copy() diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py -index 9408657c91..093c29f8f0 100644 +index 5ee9839c04..cd8573ad51 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -263,12 +263,13 @@ @@ -2285,7 +2358,7 @@ index 9408657c91..093c29f8f0 100644 with PythonSymlink() as py: cmd = "-c", "import sysconfig; print(sysconfig.get_platform())" diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py -index 2b0ec46a10..edc24ef30a 100644 +index 1946b043d0..95da63a4d4 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -21,7 +21,7 @@ @@ -2362,10 +2435,10 @@ index 213b3cf252..874a572d56 100644 # Issue 4367 # Decoding \N escapes requires the unicodedata module. If it can't be diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py -index aa41811560..641394372b 100644 +index a7e7c9f0b9..cbe41898a0 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py -@@ -10,6 +10,10 @@ +@@ -11,6 +11,10 @@ import urllib.error import urllib.request import sys @@ -2376,7 +2449,7 @@ index aa41811560..641394372b 100644 support.requires("network") -@@ -193,6 +197,7 @@ +@@ -194,6 +198,7 @@ ## self._test_urls(urls, self._extra_handlers()+[bauth, dauth]) @@ -2384,7 +2457,7 @@ index aa41811560..641394372b 100644 def test_urlwithfrag(self): urlwith_frag = "http://www.pythontest.net/index.html#frag" with socket_helper.transient_internet(urlwith_frag): -@@ -201,6 +206,7 @@ +@@ -202,6 +207,7 @@ self.assertEqual(res.geturl(), "http://www.pythontest.net/index.html#frag") @@ -2429,10 +2502,10 @@ index d6a8333427..2064a563ac 100755 node = self.uuid._netstat_getnode() self.check_node(node, 'netstat') diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py -index 94d626598b..b53d3a97d0 100644 +index eca35ec4bf..ef494c479c 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py -@@ -178,6 +178,7 @@ +@@ -179,6 +179,7 @@ builder.upgrade_dependencies(fake_context) @requireVenvCreate @@ -2440,7 +2513,7 @@ index 94d626598b..b53d3a97d0 100644 def test_prefixes(self): """ Test that the prefix values are as expected. -@@ -315,6 +316,7 @@ +@@ -316,6 +317,7 @@ # point to the venv being used to run the test, and we lose the link # to the source build - so Python can't initialise properly. @requireVenvCreate @@ -2448,7 +2521,7 @@ index 94d626598b..b53d3a97d0 100644 def test_executable(self): """ Test that the sys.executable value is as expected. -@@ -328,6 +330,7 @@ +@@ -329,6 +331,7 @@ self.assertEqual(out.strip(), envpy.encode()) @unittest.skipUnless(can_symlink(), 'Needs symlinks') @@ -2456,7 +2529,7 @@ index 94d626598b..b53d3a97d0 100644 def test_executable_symlinks(self): """ Test that the sys.executable value is as expected. -@@ -413,6 +416,7 @@ +@@ -414,6 +417,7 @@ @requireVenvCreate class EnsurePipTest(BaseTest): """Test venv module installation of pip.""" @@ -2464,7 +2537,7 @@ index 94d626598b..b53d3a97d0 100644 def assert_pip_not_installed(self): envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe) -@@ -542,6 +546,7 @@ +@@ -557,6 +561,7 @@ # Issue #26610: pip/pep425tags.py requires ctypes @unittest.skipUnless(ctypes, 'pip requires ctypes') @@ -2488,7 +2561,7 @@ index 673cc995d3..76870d0111 100644 CMD_NAME = 'test' diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py -index 6e06ee6a60..bb0dd04a50 100644 +index e557d569a1..2be2a96b73 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -1162,6 +1162,7 @@ @@ -2581,8 +2654,21 @@ index ec3cece48c..8a79db0722 100755 # # Platform support for Windows +diff --git a/Makefile.pre.in b/Makefile.pre.in +index ee85f35b10..df7913ff5a 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -307,6 +307,8 @@ + ########################################################################## + + LIBFFI_INCLUDEDIR= @LIBFFI_INCLUDEDIR@ ++LIBFFI_LIBDIR= @LIBFFI_LIBDIR@ ++LIBFFI_LIB=@LIBFFI_LIB@ + + ########################################################################## + # Parser diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c -index a58159a277..0d6a26861d 100644 +index b852ad71d7..631ca5ebb8 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -599,11 +599,15 @@ @@ -2601,7 +2687,7 @@ index a58159a277..0d6a26861d 100644 if (errno != ENOENT && errno != ENOTDIR && saved_errno == 0) { saved_errno = errno; } -@@ -684,7 +688,11 @@ +@@ -690,7 +694,11 @@ } else #endif { @@ -2697,7 +2783,7 @@ index 4534176adc..0255bcfd72 100644 sin(pi*x), giving accurate results for all finite x (especially x integral or close to an integer). This is here for use in the diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c -index 03de470323..e211bd4742 100644 +index 3d74b22f72..63c8b58bd0 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -63,6 +63,8 @@ @@ -2825,7 +2911,7 @@ index 03de470323..e211bd4742 100644 return d; } -@@ -4859,6 +4890,9 @@ +@@ -4869,6 +4900,9 @@ /*[clinic end generated code: output=290fc437dd4f33a0 input=86a58554ba6094af]*/ { long result; @@ -2835,7 +2921,7 @@ index 03de470323..e211bd4742 100644 const char *bytes = PyBytes_AsString(command); if (PySys_Audit("os.system", "(O)", command) < 0) { -@@ -4868,6 +4902,7 @@ +@@ -4878,6 +4912,7 @@ Py_BEGIN_ALLOW_THREADS result = system(bytes); Py_END_ALLOW_THREADS @@ -2843,7 +2929,7 @@ index 03de470323..e211bd4742 100644 return result; } #endif -@@ -13591,6 +13626,7 @@ +@@ -13601,6 +13636,7 @@ int is_symlink; int need_stat; #endif @@ -2851,7 +2937,7 @@ index 03de470323..e211bd4742 100644 #ifdef MS_WINDOWS unsigned long dir_bits; #endif -@@ -13651,6 +13687,7 @@ +@@ -13661,6 +13697,7 @@ #endif return result; @@ -4635,10 +4721,172 @@ index 4125240606..317a576566 100644 + exit(ret); + return ret; +} +diff --git a/aclocal.m4 b/aclocal.m4 +index 2f1bd37528..a63e6654da 100644 +--- a/aclocal.m4 ++++ b/aclocal.m4 +@@ -1,6 +1,6 @@ +-# generated automatically by aclocal 1.16.3 -*- Autoconf -*- ++# generated automatically by aclocal 1.16.5 -*- Autoconf -*- + +-# Copyright (C) 1996-2020 Free Software Foundation, Inc. ++# Copyright (C) 1996-2021 Free Software Foundation, Inc. + + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -184,7 +184,7 @@ + # and this notice are preserved. This file is offered as-is, without any + # warranty. + +-#serial 10 ++#serial 11 + + AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL]) + AC_DEFUN([AX_CHECK_OPENSSL], [ +@@ -227,7 +227,7 @@ + if ! $found; then + OPENSSL_INCLUDES= + for ssldir in $ssldirs; do +- AC_MSG_CHECKING([for openssl/ssl.h in $ssldir]) ++ AC_MSG_CHECKING([for include/openssl/ssl.h in $ssldir]) + if test -f "$ssldir/include/openssl/ssl.h"; then + OPENSSL_INCLUDES="-I$ssldir/include" + OPENSSL_LDFLAGS="-L$ssldir/lib" +@@ -276,7 +276,7 @@ + ]) + + # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +-# serial 11 (pkg-config-0.29.1) ++# serial 12 (pkg-config-0.29.2) + + dnl Copyright © 2004 Scott James Remnant . + dnl Copyright © 2012-2015 Dan Nicholson +@@ -318,7 +318,7 @@ + dnl See the "Since" comment for each macro you use to see what version + dnl of the macros you require. + m4_defun([PKG_PREREQ], +-[m4_define([PKG_MACROS_VERSION], [0.29.1]) ++[m4_define([PKG_MACROS_VERSION], [0.29.2]) + m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, + [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) + ])dnl PKG_PREREQ +@@ -419,7 +419,7 @@ + AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + + pkg_failed=no +-AC_MSG_CHECKING([for $1]) ++AC_MSG_CHECKING([for $2]) + + _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) + _PKG_CONFIG([$1][_LIBS], [libs], [$2]) +@@ -429,11 +429,11 @@ + See the pkg-config man page for more details.]) + + if test $pkg_failed = yes; then +- AC_MSG_RESULT([no]) ++ AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` +- else ++ else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs +@@ -450,7 +450,7 @@ + _PKG_TEXT])[]dnl + ]) + elif test $pkg_failed = untried; then +- AC_MSG_RESULT([no]) ++ AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( + [The pkg-config script could not be found or is too old. Make sure it + is in your PATH or set the PKG_CONFIG environment variable to the full +@@ -551,71 +551,3 @@ + AS_VAR_IF([$1], [""], [$5], [$4])dnl + ])dnl PKG_CHECK_VAR + +-dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, +-dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], +-dnl [DESCRIPTION], [DEFAULT]) +-dnl ------------------------------------------ +-dnl +-dnl Prepare a "--with-" configure option using the lowercase +-dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and +-dnl PKG_CHECK_MODULES in a single macro. +-AC_DEFUN([PKG_WITH_MODULES], +-[ +-m4_pushdef([with_arg], m4_tolower([$1])) +- +-m4_pushdef([description], +- [m4_default([$5], [build with ]with_arg[ support])]) +- +-m4_pushdef([def_arg], [m4_default([$6], [auto])]) +-m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) +-m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) +- +-m4_case(def_arg, +- [yes],[m4_pushdef([with_without], [--without-]with_arg)], +- [m4_pushdef([with_without],[--with-]with_arg)]) +- +-AC_ARG_WITH(with_arg, +- AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, +- [AS_TR_SH([with_]with_arg)=def_arg]) +- +-AS_CASE([$AS_TR_SH([with_]with_arg)], +- [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], +- [auto],[PKG_CHECK_MODULES([$1],[$2], +- [m4_n([def_action_if_found]) $3], +- [m4_n([def_action_if_not_found]) $4])]) +- +-m4_popdef([with_arg]) +-m4_popdef([description]) +-m4_popdef([def_arg]) +- +-])dnl PKG_WITH_MODULES +- +-dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +-dnl [DESCRIPTION], [DEFAULT]) +-dnl ----------------------------------------------- +-dnl +-dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES +-dnl check._[VARIABLE-PREFIX] is exported as make variable. +-AC_DEFUN([PKG_HAVE_WITH_MODULES], +-[ +-PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) +- +-AM_CONDITIONAL([HAVE_][$1], +- [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) +-])dnl PKG_HAVE_WITH_MODULES +- +-dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +-dnl [DESCRIPTION], [DEFAULT]) +-dnl ------------------------------------------------------ +-dnl +-dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after +-dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make +-dnl and preprocessor variable. +-AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], +-[ +-PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) +- +-AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], +- [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) +-])dnl PKG_HAVE_DEFINE_WITH_MODULES +- diff --git a/config.sub b/config.sub -index d74fb6deac..249b391d71 100755 +index d74fb6deac..09ebc4287c 100755 --- a/config.sub +++ b/config.sub +@@ -1121,7 +1121,7 @@ + xscale-* | xscalee[bl]-*) + cpu=`echo "$cpu" | sed 's/^xscale/arm/'` + ;; +- arm64-*) ++ arm64-* | arm64_32-*) + cpu=aarch64 + ;; + @@ -1723,7 +1723,7 @@ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ @@ -4648,11 +4896,29 @@ index d74fb6deac..249b391d71 100755 | mpw* | magic* | mmixware* | mon960* | lnews* \ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ +@@ -1786,6 +1786,8 @@ + ;; + *-eabi* | *-gnueabi*) + ;; ++ ios*-simulator | tvos*-simulator | watchos*-simulator) ++ ;; + -*) + # Blank kernel with real OS is always fine. + ;; diff --git a/configure b/configure -index 19f1bd59ba..e201564956 100755 +index 19f1bd59ba..17d27b9c4d 100755 --- a/configure +++ b/configure -@@ -793,7 +793,6 @@ +@@ -660,6 +660,8 @@ + DTRACE + TCLTK_LIBS + TCLTK_INCLUDES ++LIBFFI_LIB ++LIBFFI_LIBDIR + LIBFFI_INCLUDEDIR + PKG_CONFIG_LIBDIR + PKG_CONFIG_PATH +@@ -793,7 +795,6 @@ docdir oldincludedir includedir @@ -4660,7 +4926,7 @@ index 19f1bd59ba..e201564956 100755 localstatedir sharedstatedir sysconfdir -@@ -917,7 +916,6 @@ +@@ -917,7 +918,6 @@ sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' @@ -4668,7 +4934,7 @@ index 19f1bd59ba..e201564956 100755 includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' -@@ -1170,15 +1168,6 @@ +@@ -1170,15 +1170,6 @@ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; @@ -4684,7 +4950,7 @@ index 19f1bd59ba..e201564956 100755 -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ -@@ -1316,7 +1305,7 @@ +@@ -1316,7 +1307,7 @@ for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ @@ -4693,7 +4959,7 @@ index 19f1bd59ba..e201564956 100755 do eval ac_val=\$$ac_var # Remove trailing slashes. -@@ -1469,7 +1458,6 @@ +@@ -1469,7 +1460,6 @@ --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] @@ -4701,23 +4967,23 @@ index 19f1bd59ba..e201564956 100755 --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] -@@ -3344,6 +3332,15 @@ +@@ -3344,6 +3334,15 @@ *-*-cygwin*) ac_sys_system=Cygwin ;; -+ *-apple-ios) ++ *-apple-ios*) + ac_sys_system=iOS + ;; -+ *-apple-tvos) ++ *-apple-tvos*) + ac_sys_system=tvOS + ;; -+ *-apple-watchos) ++ *-apple-watchos*) + ac_sys_system=watchOS + ;; *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -3394,6 +3391,15 @@ +@@ -3394,6 +3393,15 @@ *-*-cygwin*) _host_cpu= ;; @@ -4733,7 +4999,7 @@ index 19f1bd59ba..e201564956 100755 *-*-vxworks*) _host_cpu=$host_cpu ;; -@@ -3469,6 +3475,13 @@ +@@ -3469,6 +3477,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -4747,7 +5013,39 @@ index 19f1bd59ba..e201564956 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -5389,6 +5402,12 @@ +@@ -5365,7 +5380,30 @@ + #elif defined(__gnu_hurd__) + i386-gnu + #elif defined(__APPLE__) +- darwin ++# include "TargetConditionals.h" ++# if TARGET_OS_IOS ++# if TARGET_OS_SIMULATOR ++ iphonesimulator ++# else ++ iphoneos ++# endif ++# elif TARGET_OS_TV ++# if TARGET_OS_SIMULATOR ++ appletvsimulator ++# else ++ appletvos ++# endif ++# elif TARGET_OS_WATCH ++# if TARGET_OS_SIMULATOR ++ watchsimulator ++# else ++ watchos ++# endif ++# elif TARGET_OS_OSX ++ darwin ++# else ++# error unknown Apple platform ++# endif + #elif defined(__VXWORKS__) + vxworks + #else +@@ -5389,6 +5427,12 @@ case $ac_sys_system in #( Darwin*) : MULTIARCH="" ;; #( @@ -4760,7 +5058,7 @@ index 19f1bd59ba..e201564956 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -6249,11 +6268,17 @@ +@@ -6249,11 +6293,23 @@ fi if test "$cross_compiling" = yes; then @@ -4770,9 +5068,15 @@ index 19f1bd59ba..e201564956 100755 - ;; - esac + case "$host" in -+ *-apple-*os) ++ *-apple-ios*) + # readelf not required for iOS cross builds. + ;; ++ *-apple-tvos*) ++ # readelf not required for tvOS cross builds. ++ ;; ++ *-apple-watchos*) ++ # readelf not required for watchOS cross builds. ++ ;; + *) + case "$READELF" in + readelf|:) @@ -4783,7 +5087,7 @@ index 19f1bd59ba..e201564956 100755 fi -@@ -7051,8 +7076,6 @@ +@@ -7051,8 +7107,6 @@ # tweak BASECFLAGS based on compiler and platform case $GCC in yes) @@ -4792,18 +5096,71 @@ index 19f1bd59ba..e201564956 100755 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -Wextra" >&5 $as_echo_n "checking for -Wextra... " >&6; } ac_save_cc="$CC" -@@ -11911,6 +11934,10 @@ - then - case $ac_sys_system/$ac_sys_release in - hp*|HP*) DYNLOADFILE="dynload_hpux.o";; -+ # Disable dynamic loading on iOS -+ iOS/*) DYNLOADFILE="dynload_stub.o";; -+ tvOS/*) DYNLOADFILE="dynload_stub.o";; -+ watchOS/*) DYNLOADFILE="dynload_stub.o";; - *) - # use dynload_shlib.c and dlopen() if we have it; otherwise stub - # out any dynamic loading -@@ -19367,7 +19394,7 @@ +@@ -9787,6 +9841,10 @@ + BLDSHARED="$LDSHARED" + fi + ;; ++ iOS/*|tvOS/*|watchOS/*) ++ LDSHARED='$(CC) -bundle -undefined dynamic_lookup' ++ LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' ++ ;; + Linux*|GNU*|QNX*|VxWorks*) + LDSHARED='$(CC) -shared' + LDCXXSHARED='$(CXX) -shared';; +@@ -10811,23 +10869,35 @@ + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_system_ffi" >&5 + $as_echo "$with_system_ffi" >&6; } + else +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +-$as_echo "yes" >&6; } +- if test "$with_system_ffi" != "" ++ if test "$ac_sys_system" = "iOS" || test "$ac_sys_system" = "tvOS" || test "$ac_sys_system" = "watchOS" + then +- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with(out)-system-ffi is ignored on this platform" >&5 ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Using user-provided libffi configuration" >&5 ++$as_echo "$as_me: WARNING: Using user-provided libffi configuration" >&2;} ++ LIBFFI_LIBDIR="${LIBFFI_LIBDIR}" ++ LIBFFI_LIB="${LIBFFI_LIB}" ++ else ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 ++$as_echo "yes" >&6; } ++ if test "$with_system_ffi" != "" ++ then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with(out)-system-ffi is ignored on this platform" >&5 + $as_echo "$as_me: WARNING: --with(out)-system-ffi is ignored on this platform" >&2;} ++ fi ++ with_system_ffi="yes" + fi +- with_system_ffi="yes" + fi + + if test "$with_system_ffi" = "yes" && test -n "$PKG_CONFIG"; then + LIBFFI_INCLUDEDIR="`"$PKG_CONFIG" libffi --cflags-only-I 2>/dev/null | sed -e 's/^-I//;s/ *$//'`" + else +- LIBFFI_INCLUDEDIR="" ++ LIBFFI_INCLUDEDIR="${LIBFFI_INCLUDEDIR}" + fi + + ++ ++ + # Check for use of the system libmpdec library + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-system-libmpdec" >&5 + $as_echo_n "checking for --with-system-libmpdec... " >&6; } +@@ -17742,8 +17812,8 @@ + if ! $found; then + OPENSSL_INCLUDES= + for ssldir in $ssldirs; do +- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h in $ssldir" >&5 +-$as_echo_n "checking for openssl/ssl.h in $ssldir... " >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for include/openssl/ssl.h in $ssldir" >&5 ++$as_echo_n "checking for include/openssl/ssl.h in $ssldir... " >&6; } + if test -f "$ssldir/include/openssl/ssl.h"; then + OPENSSL_INCLUDES="-I$ssldir/include" + OPENSSL_LDFLAGS="-L$ssldir/lib" +@@ -19367,7 +19437,7 @@ echo "creating Modules/Setup.local" >&6 if test ! -f Modules/Setup.local then @@ -4813,20 +5170,20 @@ index 19f1bd59ba..e201564956 100755 echo "creating Makefile" >&6 diff --git a/configure.ac b/configure.ac -index 763fc697be..a4378560b3 100644 +index 763fc697be..36c28aaa28 100644 --- a/configure.ac +++ b/configure.ac @@ -400,6 +400,15 @@ *-*-cygwin*) ac_sys_system=Cygwin ;; -+ *-apple-ios) ++ *-apple-ios*) + ac_sys_system=iOS + ;; -+ *-apple-tvos) ++ *-apple-tvos*) + ac_sys_system=tvOS + ;; -+ *-apple-watchos) ++ *-apple-watchos*) + ac_sys_system=watchOS + ;; *-*-vxworks*) @@ -4862,7 +5219,39 @@ index 763fc697be..a4378560b3 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -875,6 +900,9 @@ +@@ -855,7 +880,30 @@ + #elif defined(__gnu_hurd__) + i386-gnu + #elif defined(__APPLE__) +- darwin ++# include "TargetConditionals.h" ++# if TARGET_OS_IOS ++# if TARGET_OS_SIMULATOR ++ iphonesimulator ++# else ++ iphoneos ++# endif ++# elif TARGET_OS_TV ++# if TARGET_OS_SIMULATOR ++ appletvsimulator ++# else ++ appletvos ++# endif ++# elif TARGET_OS_WATCH ++# if TARGET_OS_SIMULATOR ++ watchsimulator ++# else ++ watchos ++# endif ++# elif TARGET_OS_OSX ++ darwin ++# else ++# error unknown Apple platform ++# endif + #elif defined(__VXWORKS__) + vxworks + #else +@@ -875,6 +923,9 @@ AC_MSG_CHECKING([for multiarch]) AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], @@ -4872,7 +5261,7 @@ index 763fc697be..a4378560b3 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1210,11 +1238,17 @@ +@@ -1210,11 +1261,23 @@ AC_CHECK_TOOLS([READELF], [readelf], [:]) if test "$cross_compiling" = yes; then @@ -4882,9 +5271,15 @@ index 763fc697be..a4378560b3 100644 - ;; - esac + case "$host" in -+ *-apple-*os) ++ *-apple-ios*) + # readelf not required for iOS cross builds. + ;; ++ *-apple-tvos*) ++ # readelf not required for tvOS cross builds. ++ ;; ++ *-apple-watchos*) ++ # readelf not required for watchOS cross builds. ++ ;; + *) + case "$READELF" in + readelf|:) @@ -4895,7 +5290,7 @@ index 763fc697be..a4378560b3 100644 fi AC_SUBST(READELF) -@@ -1610,8 +1644,6 @@ +@@ -1610,8 +1673,6 @@ # tweak BASECFLAGS based on compiler and platform case $GCC in yes) @@ -4904,18 +5299,54 @@ index 763fc697be..a4378560b3 100644 AC_MSG_CHECKING(for -Wextra) ac_save_cc="$CC" CC="$CC -Wextra -Werror" -@@ -3672,6 +3704,10 @@ - then - case $ac_sys_system/$ac_sys_release in - hp*|HP*) DYNLOADFILE="dynload_hpux.o";; -+ # Disable dynamic loading on iOS -+ iOS/*) DYNLOADFILE="dynload_stub.o";; -+ tvOS/*) DYNLOADFILE="dynload_stub.o";; -+ watchOS/*) DYNLOADFILE="dynload_stub.o";; - *) - # use dynload_shlib.c and dlopen() if we have it; otherwise stub - # out any dynamic loading -@@ -6013,7 +6049,7 @@ +@@ -2712,6 +2773,10 @@ + BLDSHARED="$LDSHARED" + fi + ;; ++ iOS/*|tvOS/*|watchOS/*) ++ LDSHARED='$(CC) -bundle -undefined dynamic_lookup' ++ LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' ++ ;; + Linux*|GNU*|QNX*|VxWorks*) + LDSHARED='$(CC) -shared' + LDCXXSHARED='$(CXX) -shared';; +@@ -3118,20 +3183,30 @@ + esac + AC_MSG_RESULT($with_system_ffi) + else +- AC_MSG_RESULT(yes) +- if test "$with_system_ffi" != "" ++ if test "$ac_sys_system" = "iOS" || test "$ac_sys_system" = "tvOS" || test "$ac_sys_system" = "watchOS" + then +- AC_MSG_WARN([--with(out)-system-ffi is ignored on this platform]) ++ AC_MSG_RESULT(no) ++ AC_MSG_WARN([Using user-provided libffi configuration]) ++ LIBFFI_LIBDIR="${LIBFFI_LIBDIR}" ++ LIBFFI_LIB="${LIBFFI_LIB}" ++ else ++ AC_MSG_RESULT(yes) ++ if test "$with_system_ffi" != "" ++ then ++ AC_MSG_WARN([--with(out)-system-ffi is ignored on this platform]) ++ fi ++ with_system_ffi="yes" + fi +- with_system_ffi="yes" + fi + + if test "$with_system_ffi" = "yes" && test -n "$PKG_CONFIG"; then + LIBFFI_INCLUDEDIR="`"$PKG_CONFIG" libffi --cflags-only-I 2>/dev/null | sed -e 's/^-I//;s/ *$//'`" + else +- LIBFFI_INCLUDEDIR="" ++ LIBFFI_INCLUDEDIR="${LIBFFI_INCLUDEDIR}" + fi + AC_SUBST(LIBFFI_INCLUDEDIR) ++AC_SUBST(LIBFFI_LIBDIR) ++AC_SUBST(LIBFFI_LIB) + + # Check for use of the system libmpdec library + AC_MSG_CHECKING(for --with-system-libmpdec) +@@ -6013,7 +6088,7 @@ echo "creating Modules/Setup.local" >&AS_MESSAGE_FD if test ! -f Modules/Setup.local then @@ -5782,6 +6213,60 @@ index 763fc697be..a4378560b3 100644 + #import +#endif \ No newline at end of file +diff --git a/setup.py b/setup.py +index 85a2b26357..510a1ac62b 100644 +--- a/setup.py ++++ b/setup.py +@@ -84,6 +84,9 @@ + MS_WINDOWS = (HOST_PLATFORM == 'win32') + CYGWIN = (HOST_PLATFORM == 'cygwin') + MACOS = (HOST_PLATFORM == 'darwin') ++IOS = HOST_PLATFORM.startswith('ios-') ++TVOS = HOST_PLATFORM.startswith('tvos-') ++WATCHOS = HOST_PLATFORM.startswith('watchos-') + AIX = (HOST_PLATFORM.startswith('aix')) + VXWORKS = ('vxworks' in HOST_PLATFORM) + CC = os.environ.get("CC") +@@ -2243,6 +2246,11 @@ + extra_compile_args.append('-DMACOSX') + include_dirs.append('_ctypes/darwin') + ++ if IOS or TVOS or WATCHOS: ++ sources.append('_ctypes/malloc_closure.c') ++ extra_compile_args.append('-DUSING_MALLOC_CLOSURE_DOT_C=1') ++ include_dirs.append('_ctypes/darwin') ++ + elif HOST_PLATFORM == 'sunos5': + # XXX This shouldn't be necessary; it appears that some + # of the assembler code is non-PIC (i.e. it has relocations +@@ -2272,7 +2280,8 @@ + libraries=['m'])) + + ffi_inc = sysconfig.get_config_var("LIBFFI_INCLUDEDIR") +- ffi_lib = None ++ ffi_lib_dir = sysconfig.get_config_var("LIBFFI_LIBDIR") ++ ffi_lib = sysconfig.get_config_var("LIBFFI_LIB") + + ffi_inc_dirs = self.inc_dirs.copy() + if MACOS: +@@ -2301,6 +2310,7 @@ + for lib_name in ('ffi', 'ffi_pic'): + if (self.compiler.find_library_file(self.lib_dirs, lib_name)): + ffi_lib = lib_name ++ self.use_system_libffi = True + break + + if ffi_inc and ffi_lib: +@@ -2314,7 +2324,8 @@ + + ext.include_dirs.append(ffi_inc) + ext.libraries.append(ffi_lib) +- self.use_system_libffi = True ++ if ffi_lib_dir: ++ ext.library_dirs.append(ffi_lib_dir) + + if sysconfig.get_config_var('HAVE_LIBDL'): + # for dlopen, see bpo-32647 --- /dev/null +++ b/tvOS/Info.plist @@ -0,0 +1,20 @@ From 420576310826375895acebef592c458cc9e3c12e Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 7 Sep 2022 09:48:59 +0800 Subject: [PATCH 29/37] Updated Python patch to remove vestigial parts. --- patch/Python/Python.patch | 5088 ++----------------------------------- 1 file changed, 207 insertions(+), 4881 deletions(-) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 5bf60d06..37f75328 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -37,490 +37,6 @@ + model = objc.objc_msgSend(systemModel, SEL_UTF8String).decode() + + return system, release, model -diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py -index 9c39179d2a..459e0f476f 100644 ---- a/Lib/ctypes/test/test_as_parameter.py -+++ b/Lib/ctypes/test/test_as_parameter.py -@@ -1,9 +1,11 @@ -+import os - import unittest - from ctypes import * - from ctypes.test import need_symbol - import _ctypes_test - --dll = CDLL(_ctypes_test.__file__) -+ -+dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - try: - CALLBACK_FUNCTYPE = WINFUNCTYPE -diff --git a/Lib/ctypes/test/test_bitfields.py b/Lib/ctypes/test/test_bitfields.py -index 66acd62e68..95216920e6 100644 ---- a/Lib/ctypes/test/test_bitfields.py -+++ b/Lib/ctypes/test/test_bitfields.py -@@ -25,7 +25,7 @@ - ("R", c_short, 6), - ("S", c_short, 7)] - --func = CDLL(_ctypes_test.__file__).unpack_bitfields -+func = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])).unpack_bitfields - func.argtypes = POINTER(BITS), c_char - - ##for n in "ABCDEFGHIMNOPQRS": -diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py -index 8f95a24443..58a80e3dbb 100644 ---- a/Lib/ctypes/test/test_callbacks.py -+++ b/Lib/ctypes/test/test_callbacks.py -@@ -1,4 +1,5 @@ - import functools -+import os - import unittest - from test import support - -@@ -164,7 +165,7 @@ - - def test_integrate(self): - # Derived from some then non-working code, posted by David Foster -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - # The function prototype called by 'integrate': double func(double); - CALLBACK = CFUNCTYPE(c_double, c_double) -@@ -215,7 +216,7 @@ - def test_callback_register_int(self): - # Issue #8275: buggy handling of callback args under Win64 - # NOTE: should be run on release builds as well -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - CALLBACK = CFUNCTYPE(c_int, c_int, c_int, c_int, c_int, c_int) - # All this function does is call the callback with its args squared - func = dll._testfunc_cbk_reg_int -@@ -231,7 +232,7 @@ - def test_callback_register_double(self): - # Issue #8275: buggy handling of callback args under Win64 - # NOTE: should be run on release builds as well -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - CALLBACK = CFUNCTYPE(c_double, c_double, c_double, c_double, - c_double, c_double) - # All this function does is call the callback with its args squared -diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py -index 09b06840bf..613d02e1ef 100644 ---- a/Lib/ctypes/test/test_cfuncs.py -+++ b/Lib/ctypes/test/test_cfuncs.py -@@ -1,6 +1,7 @@ - # A lot of failures in these tests on Mac OS X. - # Byte order related? - -+import os - import unittest - from ctypes import * - from ctypes.test import need_symbol -@@ -8,7 +9,7 @@ - import _ctypes_test - - class CFunctions(unittest.TestCase): -- _dll = CDLL(_ctypes_test.__file__) -+ _dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - def S(self): - return c_longlong.in_dll(self._dll, "last_tf_arg_s").value -@@ -212,7 +213,7 @@ - - @need_symbol('WinDLL') - class stdcallCFunctions(CFunctions): -- _dll = stdcall_dll(_ctypes_test.__file__) -+ _dll = stdcall_dll(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - if __name__ == '__main__': - unittest.main() -diff --git a/Lib/ctypes/test/test_checkretval.py b/Lib/ctypes/test/test_checkretval.py -index e9567dc391..3c3fbf103c 100644 ---- a/Lib/ctypes/test/test_checkretval.py -+++ b/Lib/ctypes/test/test_checkretval.py -@@ -1,3 +1,4 @@ -+import os - import unittest - - from ctypes import * -@@ -14,7 +15,7 @@ - def test_checkretval(self): - - import _ctypes_test -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - self.assertEqual(42, dll._testfunc_p_p(42)) - - dll._testfunc_p_p.restype = CHECKED -diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py -index e0b9b54e97..1c05dcc22c 100644 ---- a/Lib/ctypes/test/test_funcptr.py -+++ b/Lib/ctypes/test/test_funcptr.py -@@ -1,3 +1,4 @@ -+import os - import unittest - from ctypes import * - -@@ -8,7 +9,10 @@ - WINFUNCTYPE = CFUNCTYPE - - import _ctypes_test --lib = CDLL(_ctypes_test.__file__) -+ -+ -+lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) -+ - - class CFuncPtrTestCase(unittest.TestCase): - def test_basic(self): -diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py -index fc571700ce..b3c7a1ead1 100644 ---- a/Lib/ctypes/test/test_functions.py -+++ b/Lib/ctypes/test/test_functions.py -@@ -7,6 +7,7 @@ - - from ctypes import * - from ctypes.test import need_symbol -+import os - import sys, unittest - - try: -@@ -16,7 +17,9 @@ - WINFUNCTYPE = CFUNCTYPE - - import _ctypes_test --dll = CDLL(_ctypes_test.__file__) -+ -+ -+dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - if sys.platform == "win32": - windll = WinDLL(_ctypes_test.__file__) - -diff --git a/Lib/ctypes/test/test_libc.py b/Lib/ctypes/test/test_libc.py -index 56285b5ff8..f78a152ade 100644 ---- a/Lib/ctypes/test/test_libc.py -+++ b/Lib/ctypes/test/test_libc.py -@@ -1,9 +1,12 @@ -+import os - import unittest - - from ctypes import * - import _ctypes_test - --lib = CDLL(_ctypes_test.__file__) -+ -+lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) -+ - - def three_way_cmp(x, y): - """Return -1 if x < y, 0 if x == y and 1 if x > y""" -diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py -index 38af7ac13d..872d3f364a 100644 ---- a/Lib/ctypes/test/test_parameters.py -+++ b/Lib/ctypes/test/test_parameters.py -@@ -1,3 +1,4 @@ -+import os - import unittest - from ctypes.test import need_symbol - import test.support -@@ -140,7 +141,7 @@ - import _ctypes_test - from ctypes import CDLL, c_void_p, ArgumentError - -- func = CDLL(_ctypes_test.__file__)._testfunc_p_p -+ func = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))._testfunc_p_p - func.restype = c_void_p - # TypeError: has no from_param method - self.assertRaises(TypeError, setattr, func, "argtypes", (object,)) -diff --git a/Lib/ctypes/test/test_pickling.py b/Lib/ctypes/test/test_pickling.py -index c4a79b9779..833608b629 100644 ---- a/Lib/ctypes/test/test_pickling.py -+++ b/Lib/ctypes/test/test_pickling.py -@@ -1,8 +1,11 @@ - import unittest -+import os - import pickle - from ctypes import * - import _ctypes_test --dll = CDLL(_ctypes_test.__file__) -+ -+ -+dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - class X(Structure): - _fields_ = [("a", c_int), ("b", c_double)] -diff --git a/Lib/ctypes/test/test_pointers.py b/Lib/ctypes/test/test_pointers.py -index e97515879f..d678be3800 100644 ---- a/Lib/ctypes/test/test_pointers.py -+++ b/Lib/ctypes/test/test_pointers.py -@@ -1,3 +1,4 @@ -+import os - import unittest, sys - - from ctypes import * -@@ -20,7 +21,7 @@ - self.assertRaises(TypeError, A, c_ulong(33)) - - def test_pass_pointers(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - func = dll._testfunc_p_p - if sizeof(c_longlong) == sizeof(c_void_p): - func.restype = c_longlong -@@ -38,7 +39,7 @@ - self.assertEqual(res[0], 12345678) - - def test_change_pointers(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - func = dll._testfunc_p_p - - i = c_int(87654) -@@ -77,7 +78,7 @@ - return 0 - callback = PROTOTYPE(func) - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - # This function expects a function pointer, - # and calls this with an integer pointer as parameter. - # The int pointer points to a table containing the numbers 1..10 -@@ -143,7 +144,7 @@ - - def test_charpp(self): - """Test that a character pointer-to-pointer is correctly passed""" -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - func = dll._testfunc_c_p_p - func.restype = c_char_p - argv = (c_char_p * 2)() -diff --git a/Lib/ctypes/test/test_prototypes.py b/Lib/ctypes/test/test_prototypes.py -index cd0c649de3..539351f798 100644 ---- a/Lib/ctypes/test/test_prototypes.py -+++ b/Lib/ctypes/test/test_prototypes.py -@@ -1,3 +1,4 @@ -+import os - from ctypes import * - from ctypes.test import need_symbol - import unittest -@@ -23,7 +24,9 @@ - # In this case, there would have to be an additional reference to the argument... - - import _ctypes_test --testdll = CDLL(_ctypes_test.__file__) -+ -+ -+testdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - # Return machine address `a` as a (possibly long) non-negative integer. - # Starting with Python 2.5, id(anything) is always non-negative, and -diff --git a/Lib/ctypes/test/test_refcounts.py b/Lib/ctypes/test/test_refcounts.py -index f2edfa6400..0e4dd7c126 100644 ---- a/Lib/ctypes/test/test_refcounts.py -+++ b/Lib/ctypes/test/test_refcounts.py -@@ -1,3 +1,4 @@ -+import os - import unittest - from test import support - import ctypes -@@ -7,7 +8,10 @@ - OtherCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_ulonglong) - - import _ctypes_test --dll = ctypes.CDLL(_ctypes_test.__file__) -+ -+ -+dll = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) -+ - - class RefcountTestCase(unittest.TestCase): - -diff --git a/Lib/ctypes/test/test_returnfuncptrs.py b/Lib/ctypes/test/test_returnfuncptrs.py -index 1974f40df6..7b76fae44c 100644 ---- a/Lib/ctypes/test/test_returnfuncptrs.py -+++ b/Lib/ctypes/test/test_returnfuncptrs.py -@@ -1,5 +1,6 @@ - import unittest - from ctypes import * -+import os - - import _ctypes_test - -@@ -8,7 +9,7 @@ - def test_with_prototype(self): - # The _ctypes_test shared lib/dll exports quite some functions for testing. - # The get_strchr function returns a *pointer* to the C strchr function. -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - get_strchr = dll.get_strchr - get_strchr.restype = CFUNCTYPE(c_char_p, c_char_p, c_char) - strchr = get_strchr() -@@ -20,7 +21,7 @@ - self.assertRaises(TypeError, strchr, b"abcdef") - - def test_without_prototype(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - get_strchr = dll.get_strchr - # the default 'c_int' would not work on systems where sizeof(int) != sizeof(void *) - get_strchr.restype = c_void_p -@@ -34,7 +35,7 @@ - self.assertRaises(TypeError, strchr, b"abcdef") - - def test_from_dll(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - # _CFuncPtr instances are now callable with a tuple argument - # which denotes a function name and a dll: - strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)(("my_strchr", dll)) -@@ -50,13 +51,13 @@ - if key == 0: - return "my_strchr" - if key == 1: -- return CDLL(_ctypes_test.__file__) -+ return CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - raise IndexError - - # _CFuncPtr instances are now callable with a tuple argument - # which denotes a function name and a dll: - strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)( -- BadSequence(("my_strchr", CDLL(_ctypes_test.__file__)))) -+ BadSequence(("my_strchr", CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))))) - self.assertTrue(strchr(b"abcdef", b"b"), "bcdef") - self.assertEqual(strchr(b"abcdef", b"x"), None) - self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0) -diff --git a/Lib/ctypes/test/test_slicing.py b/Lib/ctypes/test/test_slicing.py -index a3932f1767..6d7bfff8f2 100644 ---- a/Lib/ctypes/test/test_slicing.py -+++ b/Lib/ctypes/test/test_slicing.py -@@ -1,3 +1,4 @@ -+import os - import unittest - from ctypes import * - from ctypes.test import need_symbol -@@ -62,7 +63,7 @@ - def test_char_ptr(self): - s = b"abcdefghijklmnopqrstuvwxyz" - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - dll.my_strdup.restype = POINTER(c_char) - dll.my_free.restype = None - res = dll.my_strdup(s) -@@ -94,7 +95,7 @@ - dll.my_free(res) - - def test_char_ptr_with_free(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - s = b"abcdefghijklmnopqrstuvwxyz" - - class allocated_c_char_p(c_char_p): -@@ -130,7 +131,7 @@ - def test_wchar_ptr(self): - s = "abcdefghijklmnopqrstuvwxyz\0" - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - dll.my_wcsdup.restype = POINTER(c_wchar) - dll.my_wcsdup.argtypes = POINTER(c_wchar), - dll.my_free.restype = None -diff --git a/Lib/ctypes/test/test_stringptr.py b/Lib/ctypes/test/test_stringptr.py -index c20951f4ce..fde0eef1c7 100644 ---- a/Lib/ctypes/test/test_stringptr.py -+++ b/Lib/ctypes/test/test_stringptr.py -@@ -1,10 +1,12 @@ -+import os - import unittest - from test import support - from ctypes import * - - import _ctypes_test - --lib = CDLL(_ctypes_test.__file__) -+ -+lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - class StringPtrTestCase(unittest.TestCase): - -diff --git a/Lib/ctypes/test/test_unicode.py b/Lib/ctypes/test/test_unicode.py -index 60c75424b7..8c008b466b 100644 ---- a/Lib/ctypes/test/test_unicode.py -+++ b/Lib/ctypes/test/test_unicode.py -@@ -1,3 +1,4 @@ -+import os - import unittest - import ctypes - from ctypes.test import need_symbol -@@ -7,7 +8,7 @@ - @need_symbol('c_wchar') - class UnicodeTestCase(unittest.TestCase): - def test_wcslen(self): -- dll = ctypes.CDLL(_ctypes_test.__file__) -+ dll = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - wcslen = dll.my_wcslen - wcslen.argtypes = [ctypes.c_wchar_p] - -@@ -34,7 +35,7 @@ - t.unicode = "foo\0bar\0\0" - - --func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p -+func = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))._testfunc_p_p - - class StringTestCase(UnicodeTestCase): - def setUp(self): -diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py -index 435fdd22ea..a57f9cf054 100644 ---- a/Lib/ctypes/test/test_values.py -+++ b/Lib/ctypes/test/test_values.py -@@ -4,6 +4,7 @@ - - import _imp - import importlib.util -+import os - import unittest - import sys - from ctypes import * -@@ -16,7 +17,7 @@ - def test_an_integer(self): - # This test checks and changes an integer stored inside the - # _ctypes_test dll/shared lib. -- ctdll = CDLL(_ctypes_test.__file__) -+ ctdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - an_integer = c_int.in_dll(ctdll, "an_integer") - x = an_integer.value - self.assertEqual(x, ctdll.get_an_integer()) -@@ -28,7 +29,7 @@ - self.assertEqual(x, ctdll.get_an_integer()) - - def test_undefined(self): -- ctdll = CDLL(_ctypes_test.__file__) -+ ctdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") - - class PythonValuesTestCase(unittest.TestCase): -diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py -index e51bdc8ad6..78ba8e6786 100644 ---- a/Lib/ctypes/test/test_win32.py -+++ b/Lib/ctypes/test/test_win32.py -@@ -1,6 +1,7 @@ - # Windows specific tests - - from ctypes import * -+import os - import unittest, sys - from test import support - -@@ -102,7 +103,7 @@ - ("right", c_long), - ("bottom", c_long)] - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - pt = POINT(15, 25) - left = c_long.in_dll(dll, 'left') diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 0c2510e161..6c3c43f11d 100644 --- a/Lib/ctypes/util.py @@ -859,228 +375,230 @@ index 39b3530905..ef1fa18624 100644 def requires_subprocess(): """Used for subprocess, os.spawn calls, fd inheritance""" diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py -index 05d9107b28..76a6100a1e 100644 +index 05d9107b28..6df2bcf163 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py -@@ -542,6 +542,8 @@ +@@ -33,6 +33,7 @@ + from asyncio import selector_events + from test.test_asyncio import utils as test_utils + from test import support ++from test.support import is_apple_mobile + from test.support import socket_helper + from test.support import threading_helper + from test.support import ALWAYS_EQ, LARGEST, SMALLEST +@@ -542,6 +543,7 @@ self._basetest_create_connection(conn_fut) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_connection(self): # Issue #20682: On Mac OS X Tiger, getsockname() returns a # zero-length address for UNIX socket. -@@ -634,6 +636,8 @@ +@@ -634,6 +636,7 @@ self.assertEqual(cm.exception.reason, 'CERTIFICATE_VERIFY_FAILED') @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_ssl_connection(self): with test_utils.run_test_server(use_ssl=True) as httpd: create_connection = functools.partial( -@@ -645,6 +649,8 @@ +@@ -645,6 +648,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_ssl_unix_connection(self): # Issue #20682: On Mac OS X Tiger, getsockname() returns a # zero-length address for UNIX socket. -@@ -860,6 +866,8 @@ +@@ -860,6 +864,7 @@ return server, path @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server(self): proto = MyProto(loop=self.loop) server, path = self._make_unix_server(lambda: proto) -@@ -888,6 +896,8 @@ +@@ -888,6 +893,7 @@ server.close() @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_path_socket_error(self): proto = MyProto(loop=self.loop) sock = socket.socket() -@@ -953,6 +963,8 @@ +@@ -953,6 +959,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( -@@ -983,6 +995,8 @@ +@@ -983,6 +990,7 @@ server.close() @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) server, host, port = self._make_ssl_server( -@@ -1013,6 +1027,8 @@ +@@ -1013,6 +1021,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( -@@ -1073,6 +1089,8 @@ +@@ -1073,6 +1082,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl_verified(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py -index 098a0da344..1df5654356 100644 +index 098a0da344..65aa7abe7c 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py -@@ -60,6 +60,8 @@ +@@ -17,6 +17,7 @@ + + import asyncio + from test.test_asyncio import utils as test_utils ++from test.support import is_apple_mobile + + + def tearDownModule(): +@@ -60,6 +61,7 @@ self._basetest_open_connection(conn_fut) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection(self): with test_utils.run_test_unix_server() as httpd: conn_fut = asyncio.open_unix_connection(httpd.address) -@@ -91,6 +93,8 @@ +@@ -91,6 +93,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection_no_loop_ssl(self): with test_utils.run_test_unix_server(use_ssl=True) as httpd: conn_fut = asyncio.open_unix_connection( -@@ -119,6 +123,8 @@ +@@ -119,6 +122,7 @@ self._basetest_open_connection_error(conn_fut) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection_error(self): with test_utils.run_test_unix_server() as httpd: conn_fut = asyncio.open_unix_connection(httpd.address) -@@ -637,6 +643,8 @@ +@@ -637,6 +641,7 @@ self.assertEqual(messages, []) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_start_unix_server(self): class MyServer: diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py -index 1d922783ce..c974cfe00a 100644 +index 1d922783ce..81a5d2c699 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py -@@ -276,6 +276,8 @@ +@@ -13,6 +13,7 @@ + import threading + import unittest + from unittest import mock ++from test.support import is_apple_mobile + from test.support import os_helper + from test.support import socket_helper + +@@ -276,6 +277,7 @@ @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'UNIX Sockets are not supported') -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class SelectorEventLoopUnixSocketTests(test_utils.TestCase): def setUp(self): diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py -index fc8c39365f..00e988fdcc 100644 +index fc8c39365f..16235b8397 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py +@@ -6,7 +6,7 @@ + import sys + import unittest + from multiprocessing import Process +-from test.support import verbose, cpython_only ++from test.support import verbose, cpython_only, is_apple_mobile + from test.support.import_helper import import_module + from test.support.os_helper import TESTFN, unlink + @@ -25,7 +25,7 @@ start_len = "qq" if (sys.platform.startswith(('netbsd', 'freebsd', 'openbsd')) - or sys.platform == 'darwin'): -+ or sys.platform in ('darwin', 'ios', 'tvos', 'watchos')): ++ or sys.platform == 'darwin' or is_apple_mobile): if struct.calcsize('l') == 8: off_t = 'l' pid_t = 'i' diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py -index a937258069..bf011b0662 100644 +index a937258069..3829c29fe5 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py -@@ -401,7 +401,7 @@ +@@ -30,6 +30,7 @@ + + import unittest + from test import support ++from test.support import is_apple_mobile + from test.support import os_helper + from test.support import threading_helper + +@@ -401,7 +402,7 @@ 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': -+ if sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform == 'darwin' or is_apple_mobile: # On Mac OS the HFS+ filesystem replaces bytes that aren't valid # UTF-8 into a percent-encoded value. for name in os.listdir(self.tempdir): -diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py -index 1d5b6e7a5d..6fd6a543bf 100644 ---- a/Lib/test/test_importlib/extension/test_finder.py -+++ b/Lib/test/test_importlib/extension/test_finder.py -@@ -2,10 +2,13 @@ - - machinery = util.import_importlib('importlib.machinery') - -+import sys - import unittest - import sys - - -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ '%s does not support dynamic loading' % sys.platform) - class FinderTests(abc.FinderTests): - - """Test the finder for extension modules.""" -diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py -index 8570c6bc90..7de327ebf3 100644 ---- a/Lib/test/test_importlib/extension/test_loader.py -+++ b/Lib/test/test_importlib/extension/test_loader.py -@@ -13,6 +13,8 @@ - from test.support.script_helper import assert_python_failure - - -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ '%s does not support dynamic loading' % sys.platform) - class LoaderTests(abc.LoaderTests): - - """Test load_module() for extension modules.""" -@@ -94,6 +96,9 @@ - Source_LoaderTests - ) = util.test_both(LoaderTests, machinery=machinery) - -+ -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ '%s does not support dynamic loading' % sys.platform) - class MultiPhaseExtensionModuleTests(abc.LoaderTests): - # Test loading extension modules with multi-phase initialization (PEP 489). - diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py -index daccbae5b4..7158b41fef 100644 +index daccbae5b4..607e40eb66 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py -@@ -609,7 +609,7 @@ +@@ -40,6 +40,7 @@ + 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 is_apple_mobile + from test.support import os_helper + from test.support import threading_helper + from test.support import warnings_helper +@@ -609,7 +610,7 @@ # 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': -+ if sys.platform[:3] == 'win' or sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform[:3] == 'win' or sys.platform == 'darwin' or is_apple_mobile: support.requires( 'largefile', 'test requires %s bytes and a long time to run' % self.LARGE) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py -index 71926a5432..2918759262 100644 +index 71926a5432..0ef83b9f51 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py -@@ -1787,9 +1787,21 @@ +@@ -43,6 +43,7 @@ + import tempfile + from test.support.script_helper import assert_python_ok, assert_python_failure + from test import support ++from test.support import is_apple_mobile + from test.support import os_helper + from test.support import socket_helper + from test.support import threading_helper +@@ -1787,9 +1788,20 @@ # just need a name - file can't be present, or we'll get an # 'address already in use' error. os.remove(fn) @@ -1097,39 +615,43 @@ index 71926a5432..2918759262 100644 return fn @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixSocketHandlerTest(SocketHandlerTest): """Test for SocketHandler with unix sockets.""" -@@ -1873,6 +1885,8 @@ +@@ -1873,6 +1885,7 @@ self.assertEqual(self.log_output, "spam\neggs\n") @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixDatagramHandlerTest(DatagramHandlerTest): """Test for DatagramHandler using Unix sockets.""" -@@ -1967,6 +1981,8 @@ +@@ -1967,6 +1980,7 @@ self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00') @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixSysLogHandlerTest(SysLogHandlerTest): """Test for SysLogHandler with Unix sockets.""" diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py -index aae86cc257..bb48a85517 100644 +index aae86cc257..8be5af371b 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 os_helper, requires_debug_ranges, is_apple_mobile + from test.support.script_helper import assert_python_ok + import array + import io @@ -260,7 +260,10 @@ if os.name == 'nt': MAX_MARSHAL_STACK_DEPTH = 1000 else: - MAX_MARSHAL_STACK_DEPTH = 2000 -+ if sys.platform in ('ios', 'tvos', 'watchos'): ++ if is_apple_mobile: + MAX_MARSHAL_STACK_DEPTH = 1500 + else: + MAX_MARSHAL_STACK_DEPTH = 2000 @@ -1137,15 +659,22 @@ index aae86cc257..bb48a85517 100644 last.append([0]) last = last[-1] diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py -index 213a44d56f..9908e9a2c8 100644 +index 213a44d56f..87279579fe 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_mobile + ) + from test.support.import_helper import import_module + from test.support.os_helper import TESTFN, unlink @@ -245,7 +245,7 @@ with open(TESTFN, "r+b") as f: self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4) - if os.name == "posix": -+ if os.name == "posix" and sys.platform not in ('ios', 'tvos', 'watchos'): ++ if os.name == "posix" and not is_apple_mobile: # Try incompatible flags, prot and access parameters. with open(TESTFN, "r+b") as f: self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, @@ -1154,44 +683,60 @@ index 213a44d56f..9908e9a2c8 100644 def _make_test_file(self, num_zeroes, tail): - if sys.platform[:3] == 'win' or sys.platform == 'darwin': -+ if sys.platform[:3] == 'win' or sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform[:3] == 'win' or sys.platform == 'darwin' or is_apple_mobile: 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_platform.py b/Lib/test/test_platform.py -index 9b2cd201f3..dcd3a7a241 100644 +index 9b2cd201f3..e47de5d468 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py +@@ -8,7 +8,7 @@ + from unittest import mock + + from test import support +-from test.support import os_helper ++from test.support import os_helper, is_apple_mobile + + FEDORA_OS_RELEASE = """\ + NAME=Fedora @@ -315,7 +315,7 @@ def test_mac_ver(self): res = platform.mac_ver() - if platform.uname().system == 'Darwin': -+ if platform.uname().system == 'Darwin' and sys.platform not in ('ios', 'tvos', 'watchos'): ++ if platform.uname().system == 'Darwin' and not is_apple_mobile: # We are on a macOS system, check that the right version # information is returned output = subprocess.check_output(['sw_vers'], text=True) -@@ -347,6 +347,10 @@ +@@ -347,6 +347,9 @@ else: self.assertEqual(res[2], 'PowerPC') -+ @unittest.skipUnless(sys.platform in ('ios', 'tvos', 'watchos'), "iOS/tvOS/watchOS only test") ++ @unittest.skipUnless(is_apple_mobile, 'iOS/tvOS/watchOS only test') + def test_ios_ver(self): + res = platform.ios_ver() -+ @unittest.skipUnless(sys.platform == 'darwin', "OSX only test") def test_mac_ver_with_fork(self): diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py -index ae25ef5588..8f2b1ffa69 100644 +index ae25ef5588..2885d5fd52 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py -@@ -69,12 +69,19 @@ +@@ -2,6 +2,7 @@ + + from test import support + from test.support import import_helper ++from test.support import is_apple_mobile + from test.support import os_helper + from test.support import warnings_helper + from test.support.script_helper import assert_python_ok +@@ -69,12 +70,19 @@ "getpid", "getpgrp", "getppid", "getuid", "sync", ] + # getgroups can't be invoked on iOS/tvOS/watchOS. -+ if sys.platform not in ('ios', 'tvos', 'watchos'): ++ if is_apple_mobile: + NO_ARG_FUNCTIONS.append("getgroups") + for name in NO_ARG_FUNCTIONS: @@ -1208,43 +753,42 @@ index ae25ef5588..8f2b1ffa69 100644 @unittest.skipUnless(hasattr(posix, 'getresuid'), 'test needs posix.getresuid()') -@@ -776,9 +783,10 @@ +@@ -776,9 +784,10 @@ 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') and sys.platform not in ('ios', 'tvos', 'watchos'): ++ if hasattr(os, 'getgroups') and not is_apple_mobile: + 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) -@@ -1129,7 +1137,7 @@ +@@ -1129,7 +1138,7 @@ self.assertIsInstance(hi, int) self.assertGreaterEqual(hi, lo) # OSX evidently just returns 15 without checking the argument. - if sys.platform != "darwin": -+ if sys.platform not in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform != 'darwin' and not is_apple_mobile: self.assertRaises(OSError, posix.sched_get_priority_min, -23) self.assertRaises(OSError, posix.sched_get_priority_max, -23) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py -index a2c4ab5081..265b5618df 100644 +index a2c4ab5081..45927b3163 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py -@@ -1776,6 +1776,8 @@ +@@ -1776,6 +1776,7 @@ check_chown(dirname, uid, gid) -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't support other executable." % sys.platform) ++@unittest.skipIf(support.has_subprocess_support, 'Test requires support for subprocesses.') class TestWhich(BaseTest, unittest.TestCase): def setUp(self): -@@ -2642,6 +2644,7 @@ +@@ -2642,6 +2643,7 @@ self.assertGreaterEqual(size.lines, 0) @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty") @@ -1253,112 +797,109 @@ index a2c4ab5081..265b5618df 100644 'need os.get_terminal_size()') def test_stty_match(self): diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 72e3394df2..401620bae2 100644 +index 72e3394df2..832ee517f6 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py -@@ -1157,7 +1157,7 @@ +@@ -1,5 +1,6 @@ + import unittest + from test import support ++from test.support import is_apple_mobile + from test.support import os_helper + from test.support import socket_helper + from test.support import threading_helper +@@ -1157,7 +1158,7 @@ # 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')): -+ or sys.platform in ('linux', 'darwin', 'ios', 'tvos', 'watchos')): ++ or sys.platform in ('linux', 'darwin') or is_apple_mobile): # avoid the 'echo' service on this platform, as there is an # assumption breaking non-standard port/protocol entry services = ('daytime', 'qotd', 'domain') -@@ -3552,7 +3552,8 @@ +@@ -3552,7 +3553,7 @@ def _testFDPassCMSG_LEN(self): self.createAndSendFDs(1) - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparate(self): -@@ -3563,7 +3564,8 @@ +@@ -3563,7 +3564,7 @@ maxcmsgs=2) @testFDPassSeparate.client_skip - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparate(self): fd0, fd1 = self.newFDs(2) -@@ -3576,7 +3578,8 @@ +@@ -3576,7 +3577,7 @@ array.array("i", [fd1]))]), len(MSG)) - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparateMinSpace(self): -@@ -3590,7 +3593,8 @@ +@@ -3590,7 +3591,7 @@ maxcmsgs=2, ignoreflags=socket.MSG_CTRUNC) @testFDPassSeparateMinSpace.client_skip - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparateMinSpace(self): fd0, fd1 = self.newFDs(2) -@@ -3614,7 +3618,8 @@ +@@ -3614,7 +3615,7 @@ nbytes = self.sendmsgToServer([msg]) self.assertEqual(nbytes, len(msg)) - @unittest.skipIf(sys.platform == "darwin", "see issue #24725") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") def testFDPassEmpty(self): # Try to pass an empty FD array. Can receive either no array # or an empty array. -@@ -4434,28 +4439,38 @@ +@@ -4434,28 +4435,33 @@ pass @requireAttrs(socket.socket, "sendmsg") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class SendmsgUnixStreamTest(SendmsgStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "recvmsg") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class RecvmsgUnixStreamTest(RecvmsgTests, RecvmsgGenericStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "recvmsg_into") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class RecvmsgIntoUnixStreamTest(RecvmsgIntoTests, RecvmsgGenericStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "sendmsg", "recvmsg") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") class RecvmsgSCMRightsStreamTest(SCMRightsTest, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "sendmsg", "recvmsg_into") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") class RecvmsgIntoSCMRightsStreamTest(RecvmsgIntoMixin, SCMRightsTest, SendrecvmsgUnixStreamTestBase): diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py -index 2edb1e0c0e..67a3ecfcda 100644 +index 2edb1e0c0e..62307168f6 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py -@@ -8,6 +8,7 @@ +@@ -8,13 +8,14 @@ import select import signal import socket @@ -1366,20 +907,26 @@ index 2edb1e0c0e..67a3ecfcda 100644 import tempfile import threading import unittest -@@ -198,12 +199,16 @@ + import socketserver + + import test.support +-from test.support import reap_children, verbose ++from test.support import reap_children, verbose, is_apple_mobile + from test.support import os_helper + from test.support import socket_helper + from test.support import threading_helper +@@ -198,12 +199,14 @@ self.stream_examine) @requires_unix_sockets -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_UnixStreamServer(self): self.run_server(socketserver.UnixStreamServer, socketserver.StreamRequestHandler, self.stream_examine) @requires_unix_sockets -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_ThreadingUnixStreamServer(self): self.run_server(socketserver.ThreadingUnixStreamServer, socketserver.StreamRequestHandler, @@ -1418,24 +965,31 @@ index 578ac1db50..daf61113b2 100644 wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py -index 9c6561c099..f49aafcd9e 100644 +index 9c6561c099..ccccfb8b0b 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py -@@ -1171,6 +1171,8 @@ +@@ -3,7 +3,7 @@ + """ + + import test.support +-from test.support import threading_helper, requires_subprocess ++from test.support import threading_helper, requires_subprocess, is_apple_mobile + from test.support import verbose, cpython_only, os_helper + from test.support.import_helper import import_module + from test.support.script_helper import assert_python_ok, assert_python_failure +@@ -1171,6 +1171,7 @@ os.set_blocking(r, False) return (r, w) -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't have os.pipe" % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't have os.pipe" % sys.platform) def test_threads_join(self): # Non-daemon threads should be joined at subinterpreter shutdown # (issue #18808) -@@ -1199,6 +1201,8 @@ +@@ -1199,6 +1200,7 @@ # The thread was joined properly. self.assertEqual(os.read(r, 1), b"x") -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't have os.pipe" % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't have os.pipe" % sys.platform) def test_threads_join_2(self): # Same as above, but a delay gets introduced after the thread's # Python code returned but before the thread state is deleted. @@ -1708,53 +1262,6 @@ index 3026bb6a34..42f503abe6 100644 /* Using an alternative stack requires sigaltstack() and sigaction() SA_ONSTACK */ #if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) -diff --git a/Modules/makesetup b/Modules/makesetup -index 08303814c8..56d35ab407 100755 ---- a/Modules/makesetup -+++ b/Modules/makesetup -@@ -167,7 +167,7 @@ - esac - case $arg in - -framework) libs="$libs $arg"; skip=libs; -- # OSX/OSXS/Darwin framework link cmd -+ # OSX/OSXS/Darwin/iOS/tvOS/watchOS framework link cmd - ;; - -[IDUCfF]*) cpps="$cpps $arg";; - -Xcompiler) skip=cpps;; -@@ -187,6 +187,7 @@ - *.c++) srcs="$srcs $arg";; - *.cxx) srcs="$srcs $arg";; - *.cpp) srcs="$srcs $arg";; -+ *.S) srcs="$srcs $arg";; # Assembly src - \$\(*_CFLAGS\)) cpps="$cpps $arg";; - \$\(*_INCLUDES\)) cpps="$cpps $arg";; - \$\(*_LIBS\)) libs="$libs $arg";; -@@ -221,7 +222,16 @@ - done - case $doconfig in - yes) -- LIBS="$LIBS $libs" -+ # sed has a character limit for multiline substitutions on some -+ # installs. If you have a lot of customized module configurations, -+ # the LOCALMODLIBS definition can end up exceeding that line length. -+ # To ensure this doesn't happen, only include $libs if it adds -+ # something, and put each library definition on it's own line. This -+ # requires escaping for when this script runs, and escaping for when -+ # the output is run through sed. -+ if test "$libs"; then -+ LIBS="$LIBS \\\\\\$NL\t$libs" -+ fi - MODS="$MODS $mods" - BUILT="$BUILT $mods" - ;; -@@ -246,6 +256,7 @@ - *.C) obj=`basename $src .C`.o; cc='$(CXX)';; - *.cxx) obj=`basename $src .cxx`.o; cc='$(CXX)';; - *.cpp) obj=`basename $src .cpp`.o; cc='$(CXX)';; -+ *.S) obj=`basename $src .S`.o; cc='$(CC)';; # Assembly - *.m) obj=`basename $src .m`.o; cc='$(CC)';; # Obj-C - *) continue;; - esac diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index aa93e756c6..fcf3784c2f 100644 --- a/Modules/mathmodule.c @@ -2031,1621 +1538,6 @@ index 90a4405091..65dd71d868 100644 #endif #define TYPE_NULL '0' ---- /dev/null -+++ b/Tools/iOS-test/app/iOS-test/main.py -@@ -0,0 +1,12 @@ -+from datetime import datetime -+import platform -+from test import regrtest -+ -+regrtest.start = datetime.now() -+print("Testing on %s" % platform.machine()) -+print("START:", regrtest.start) -+regrtest.main_in_temp_cwd() -+regrtest.end = datetime.now() -+print("END:", regrtest.end) -+print("Duration:", regrtest.end - regrtest.start) -+ ---- /dev/null -+++ b/Tools/iOS-test/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test.xcodeproj/project.pbxproj -@@ -0,0 +1,369 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+ 60EAF0931C26F7310003B8F5 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */; }; -+ 60EAF0951C26F73D0003B8F5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 60EAF0941C26F73D0003B8F5 /* libz.tbd */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* iOS-test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS-test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* iOS-test-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iOS-test-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* iOS-test-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "iOS-test-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60DBD4B01B47DEF700068095 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; -+ 60EAF0941C26F73D0003B8F5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60EAF0951C26F73D0003B8F5 /* libz.tbd in Frameworks */, -+ 60EAF0931C26F7310003B8F5 /* libsqlite3.tbd in Frameworks */, -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* iOS-test */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* iOS-test.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60EAF0941C26F73D0003B8F5 /* libz.tbd */, -+ 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* iOS-test */ = { -+ isa = PBXGroup; -+ children = ( -+ 60DBD4B01B47DEF700068095 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = "iOS-test"; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* iOS-test-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* iOS-test-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* iOS-test */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "iOS-test" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = "iOS-test"; -+ productName = "iOS-test"; -+ productReference = 60796EE219190F4100A9926B /* iOS-test.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0720; -+ ORGANIZATIONNAME = "Python Software Foundation"; -+ TargetAttributes = { -+ 60796EE119190F4100A9926B = { -+ DevelopmentTeam = 383DLEZ2K4; -+ }; -+ }; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "iOS-test" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* iOS-test */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ ENABLE_BITCODE = NO; -+ ENABLE_TESTABILITY = YES; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_BITCODE = NO; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ CODE_SIGN_IDENTITY = "iPhone Developer"; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "iOS-test/iOS-test-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "iOS-test/iOS-test-Info.plist"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.$(PRODUCT_NAME:rfc1034identifier)"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ PROVISIONING_PROFILE = ""; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ CODE_SIGN_IDENTITY = "iPhone Developer"; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "iOS-test/iOS-test-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "iOS-test/iOS-test-Info.plist"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.$(PRODUCT_NAME:rfc1034identifier)"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ PROVISIONING_PROFILE = ""; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "iOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "iOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,58 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "3x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/iOS-test-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ $(PRODUCT_BUNDLE_IDENTIFIER) -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/iOS-test-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/main.m -@@ -0,0 +1,149 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *exe; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = Py_DecodeLocale([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ // Since iOS doesn't allow dynamic linking, we have to know -+ // the name of the executable so that we can find the ctypes -+ // test objects. However, sys.argv[0] will be updated to -+ // reflect the script name; the TEST_EXECUTABLE environment -+ // variable provides the mechanism for specifying the filename. -+ exe = [NSString stringWithFormat:@"TEST_EXECUTABLE=%s", argv[0], nil]; -+ putenv((char *)[exe UTF8String]); -+ -+ NSLog(@"Initializing Python runtime..."); -+ Py_Initialize(); -+ -+ /******************************************************* -+ To tell lldb not to stop on signals, use the following commands: -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ *******************************************************/ -+ -+ // Arguments to pass to test runner -+ char *test_args[] = { -+ "-j", "1", -+ "-u", "all,-audio,-curses,-largefile,-subprocess,-gui", -+// "-v", // Verbose test output -+ "-W", // Display test output on failure -+ -+ "-x", // Arguments are tests to *exclude* -+// Simulator failures -+// "test_coroutines", // docstring not being populated -+// "test_module", // docstring not being populated -+ -+// ARM64 failures -+// "test_coroutines", // docstring not being populated -+// "test_ctypes", // DL loading? -+// "test_module" // docstring not being populated -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+// "test_unicode", // encoding problem -+ -+// ARMv7 failures -+// "test_cmath", // math domain error -+// "test_ctypes", // DL loading? -+// "test_float", // rounding? -+// "test_math", // math domain error -+// "test_numeric_tower", // -+// "test_strtod", // -+// "test_importlib", // Thread locking problem -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+ -+// COMMON FAILURES -+ "test_bytes" // HARD CRASH ctypes related; PyBytes_FromFormat -+ -+ }; -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.iOS-test/app/iOS-test/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/iOS-test/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ int n_test_args = sizeof(test_args) / sizeof (*test_args) + 1; -+ -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * n_test_args); -+ python_argv[0] = Py_DecodeLocale(main_script, NULL); -+ for (i = 1; i < n_test_args; i++) { -+ python_argv[i] = Py_DecodeLocale(test_args[i-1], NULL); -+ } -+ -+ PySys_SetArgv(n_test_args, python_argv); -+ -+ // If other modules are using thread, we need to initialize them before. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/app/README -@@ -0,0 +1,3 @@ -+Your application code should be placed in this directory. -+ -+The native code will be looking for a tvOS-test/__main__.py file as the entry point. -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/app/tvOS-test/main.py -@@ -0,0 +1,14 @@ -+from __future__ import print_function -+ -+from datetime import datetime -+import platform -+from test import regrtest -+ -+regrtest.start = datetime.now() -+print("Testing on %s" % platform.machine()) -+print("START:", regrtest.start) -+regrtest.main_in_temp_cwd() -+regrtest.end = datetime.now() -+print("END:", regrtest.end) -+print("Duration:", regrtest.end - regrtest.start) -+ ---- /dev/null -+++ b/Tools/tvOS-test/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test.xcodeproj/project.pbxproj -@@ -0,0 +1,356 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 6023B2AE1C28BA7A006F2562 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6023B2AD1C28BA7A006F2562 /* main.m */; }; -+ 6023B2B71C28BA7A006F2562 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2B51C28BA7A006F2562 /* Main.storyboard */; }; -+ 6023B2B91C28BA7A006F2562 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2B81C28BA7A006F2562 /* Assets.xcassets */; }; -+ 6023B2C71C28BD44006F2562 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */; }; -+ 6023B2C81C28BD44006F2562 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */; }; -+ 6023B2C91C28BD44006F2562 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C31C28BD44006F2562 /* Foundation.framework */; }; -+ 6023B2CA1C28BD44006F2562 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */; }; -+ 6023B2CB1C28BD44006F2562 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C51C28BD44006F2562 /* libz.tbd */; }; -+ 6023B2CC1C28BD44006F2562 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C61C28BD44006F2562 /* UIKit.framework */; }; -+ 6023B2D01C28BDA3006F2562 /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2CE1C28BDA3006F2562 /* Python.framework */; }; -+ 6023B2D31C28BDB7006F2562 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2D11C28BDB7006F2562 /* app */; }; -+ 6023B2D41C28BDB7006F2562 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2D21C28BDB7006F2562 /* app_packages */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 6023B2A91C28BA7A006F2562 /* tvOS-test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "tvOS-test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 6023B2AD1C28BA7A006F2562 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 6023B2B61C28BA7A006F2562 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; -+ 6023B2B81C28BA7A006F2562 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; -+ 6023B2BA1C28BA7A006F2562 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -+ 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 6023B2C31C28BD44006F2562 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; -+ 6023B2C51C28BD44006F2562 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; -+ 6023B2C61C28BD44006F2562 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 6023B2CD1C28BDA3006F2562 /* OpenSSL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OpenSSL.framework; sourceTree = ""; }; -+ 6023B2CE1C28BDA3006F2562 /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 6023B2D11C28BDB7006F2562 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; name = app; path = ../app; sourceTree = ""; }; -+ 6023B2D21C28BDB7006F2562 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; name = app_packages; path = ../app_packages; sourceTree = ""; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 6023B2A61C28BA7A006F2562 /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 6023B2CB1C28BD44006F2562 /* libz.tbd in Frameworks */, -+ 6023B2CA1C28BD44006F2562 /* libsqlite3.tbd in Frameworks */, -+ 6023B2D01C28BDA3006F2562 /* Python.framework in Frameworks */, -+ 6023B2C71C28BD44006F2562 /* CoreFoundation.framework in Frameworks */, -+ 6023B2C81C28BD44006F2562 /* CoreGraphics.framework in Frameworks */, -+ 6023B2C91C28BD44006F2562 /* Foundation.framework in Frameworks */, -+ 6023B2CC1C28BD44006F2562 /* UIKit.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 6023B2A01C28BA7A006F2562 = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2AB1C28BA7A006F2562 /* tvOS-test */, -+ 6023B2C01C28BD23006F2562 /* Frameworks */, -+ 6023B2AA1C28BA7A006F2562 /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 6023B2AA1C28BA7A006F2562 /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2A91C28BA7A006F2562 /* tvOS-test.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 6023B2AB1C28BA7A006F2562 /* tvOS-test */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2D11C28BDB7006F2562 /* app */, -+ 6023B2D21C28BDB7006F2562 /* app_packages */, -+ 6023B2B81C28BA7A006F2562 /* Assets.xcassets */, -+ 6023B2AC1C28BA7A006F2562 /* Supporting Files */, -+ ); -+ path = "tvOS-test"; -+ sourceTree = ""; -+ }; -+ 6023B2AC1C28BA7A006F2562 /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2B51C28BA7A006F2562 /* Main.storyboard */, -+ 6023B2BA1C28BA7A006F2562 /* Info.plist */, -+ 6023B2AD1C28BA7A006F2562 /* main.m */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+ 6023B2C01C28BD23006F2562 /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */, -+ 6023B2C51C28BD44006F2562 /* libz.tbd */, -+ 6023B2CD1C28BDA3006F2562 /* OpenSSL.framework */, -+ 6023B2CE1C28BDA3006F2562 /* Python.framework */, -+ 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */, -+ 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */, -+ 6023B2C31C28BD44006F2562 /* Foundation.framework */, -+ 6023B2C61C28BD44006F2562 /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 6023B2A81C28BA7A006F2562 /* tvOS-test */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 6023B2BD1C28BA7A006F2562 /* Build configuration list for PBXNativeTarget "tvOS-test" */; -+ buildPhases = ( -+ 6023B2D61C28CB97006F2562 /* Refresh Python source */, -+ 6023B2A51C28BA7A006F2562 /* Sources */, -+ 6023B2A61C28BA7A006F2562 /* Frameworks */, -+ 6023B2A71C28BA7A006F2562 /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = "tvOS-test"; -+ productName = "tvOS-test"; -+ productReference = 6023B2A91C28BA7A006F2562 /* tvOS-test.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 6023B2A11C28BA7A006F2562 /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ LastUpgradeCheck = 0720; -+ ORGANIZATIONNAME = "Python Software Foundation"; -+ TargetAttributes = { -+ 6023B2A81C28BA7A006F2562 = { -+ CreatedOnToolsVersion = 7.2; -+ }; -+ }; -+ }; -+ buildConfigurationList = 6023B2A41C28BA7A006F2562 /* Build configuration list for PBXProject "tvOS-test" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ Base, -+ ); -+ mainGroup = 6023B2A01C28BA7A006F2562; -+ productRefGroup = 6023B2AA1C28BA7A006F2562 /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 6023B2A81C28BA7A006F2562 /* tvOS-test */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 6023B2A71C28BA7A006F2562 /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 6023B2D31C28BDB7006F2562 /* app in Resources */, -+ 6023B2B91C28BA7A006F2562 /* Assets.xcassets in Resources */, -+ 6023B2B71C28BA7A006F2562 /* Main.storyboard in Resources */, -+ 6023B2D41C28BDB7006F2562 /* app_packages in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 6023B2D61C28CB97006F2562 /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/org.python.tvOS-test\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/lib\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/include\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/org.python.tvOS-test\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app_packages/\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 6023B2A51C28BA7A006F2562 /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 6023B2AE1C28BA7A006F2562 /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 6023B2B51C28BA7A006F2562 /* Main.storyboard */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 6023B2B61C28BA7A006F2562 /* Base */, -+ ); -+ name = Main.storyboard; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 6023B2BB1C28BA7A006F2562 /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN_UNREACHABLE_CODE = YES; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ COPY_PHASE_STRIP = NO; -+ DEBUG_INFORMATION_FORMAT = dwarf; -+ ENABLE_STRICT_OBJC_MSGSEND = YES; -+ ENABLE_TESTABILITY = YES; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_NO_COMMON_BLOCKS = YES; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ MTL_ENABLE_DEBUG_INFO = YES; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = appletvos; -+ TARGETED_DEVICE_FAMILY = 3; -+ TVOS_DEPLOYMENT_TARGET = 9.1; -+ }; -+ name = Debug; -+ }; -+ 6023B2BC1C28BA7A006F2562 /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN_UNREACHABLE_CODE = YES; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ COPY_PHASE_STRIP = NO; -+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; -+ ENABLE_NS_ASSERTIONS = NO; -+ ENABLE_STRICT_OBJC_MSGSEND = YES; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_NO_COMMON_BLOCKS = YES; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ MTL_ENABLE_DEBUG_INFO = NO; -+ SDKROOT = appletvos; -+ TARGETED_DEVICE_FAMILY = 3; -+ TVOS_DEPLOYMENT_TARGET = 9.1; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 6023B2BE1C28BA7A006F2562 /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ INFOPLIST_FILE = "tvOS-test/Info.plist"; -+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.tvOS-test"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ }; -+ name = Debug; -+ }; -+ 6023B2BF1C28BA7A006F2562 /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ INFOPLIST_FILE = "tvOS-test/Info.plist"; -+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.tvOS-test"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 6023B2A41C28BA7A006F2562 /* Build configuration list for PBXProject "tvOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 6023B2BB1C28BA7A006F2562 /* Debug */, -+ 6023B2BC1C28BA7A006F2562 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 6023B2BD1C28BA7A006F2562 /* Build configuration list for PBXNativeTarget "tvOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 6023B2BE1C28BA7A006F2562 /* Debug */, -+ 6023B2BF1C28BA7A006F2562 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 6023B2A11C28BA7A006F2562 /* Project object */; -+} ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "large.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json -@@ -0,0 +1,17 @@ -+{ -+ "layers" : [ -+ { -+ "filename" : "Front.imagestacklayer" -+ }, -+ { -+ "filename" : "Middle.imagestacklayer" -+ }, -+ { -+ "filename" : "Back.imagestacklayer" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "large.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "large.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "small.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json -@@ -0,0 +1,17 @@ -+{ -+ "layers" : [ -+ { -+ "filename" : "Front.imagestacklayer" -+ }, -+ { -+ "filename" : "Middle.imagestacklayer" -+ }, -+ { -+ "filename" : "Back.imagestacklayer" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "small.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "small.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json -@@ -0,0 +1,26 @@ -+{ -+ "assets" : [ -+ { -+ "size" : "1280x768", -+ "idiom" : "tv", -+ "filename" : "App Icon - Large.imagestack", -+ "role" : "primary-app-icon" -+ }, -+ { -+ "size" : "400x240", -+ "idiom" : "tv", -+ "filename" : "App Icon - Small.imagestack", -+ "role" : "primary-app-icon" -+ }, -+ { -+ "size" : "1920x720", -+ "idiom" : "tv", -+ "filename" : "Top Shelf Image.imageset", -+ "role" : "top-shelf-image" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "shelf.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,16 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "landscape", -+ "idiom" : "tv", -+ "filename" : "launch.png", -+ "extent" : "full-screen", -+ "minimum-system-version" : "9.0", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Base.lproj/Main.storyboard -@@ -0,0 +1,25 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Info.plist -@@ -0,0 +1,32 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleExecutable -+ $(EXECUTABLE_NAME) -+ CFBundleIdentifier -+ $(PRODUCT_BUNDLE_IDENTIFIER) -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ $(PRODUCT_NAME) -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1 -+ LSRequiresIPhoneOS -+ -+ UIMainStoryboardFile -+ Main -+ UIRequiredDeviceCapabilities -+ -+ arm64 -+ -+ -+ ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/main.m -@@ -0,0 +1,149 @@ -+// -+// main.m -+// A main module for starting Python projects under tvOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *exe; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = Py_DecodeLocale([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // tvOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ // Since tvOS doesn't allow dynamic linking, we have to know -+ // the name of the executable so that we can find the ctypes -+ // test objects. However, sys.argv[0] will be updated to -+ // reflect the script name; the TEST_EXECUTABLE environment -+ // variable provides the mechanism for specifying the filename. -+ exe = [NSString stringWithFormat:@"TEST_EXECUTABLE=%s", argv[0], nil]; -+ putenv((char *)[exe UTF8String]); -+ -+ NSLog(@"Initializing Python runtime..."); -+ Py_Initialize(); -+ -+ /******************************************************* -+ To tell lldb not to stop on signals, use the following commands: -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ *******************************************************/ -+ -+ // Arguments to pass to test runner -+ char *test_args[] = { -+ "-j", "1", -+ "-u", "all,-audio,-curses,-largefile,-subprocess,-gui", -+// "-v", // Verbose test output -+ "-W", // Display test output on failure -+ -+ "-x", // Arguments are tests to *exclude* -+// Simulator failures -+// "test_coroutines", // docstring not being populated -+// "test_module", // docstring not being populated -+ -+// ARM64 failures -+// "test_coroutines", // docstring not being populated -+// "test_ctypes", // DL loading? -+// "test_module" // docstring not being populated -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+// "test_unicode", // encoding problem -+ -+// ARMv7 failures -+// "test_cmath", // math domain error -+// "test_ctypes", // DL loading? -+// "test_float", // rounding? -+// "test_math", // math domain error -+// "test_numeric_tower", // -+// "test_strtod", // -+// "test_importlib", // Thread locking problem -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+ -+// COMMON FAILURES -+ "test_bytes" // HARD CRASH ctypes related; PyBytes_FromFormat -+ -+ }; -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.tvOS-test/app/tvOS-test/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/tvOS-test/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ int n_test_args = sizeof(test_args) / sizeof (*test_args) + 1; -+ -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * n_test_args); -+ python_argv[0] = Py_DecodeLocale(main_script, NULL); -+ for (i = 1; i < n_test_args; i++) { -+ python_argv[i] = Py_DecodeLocale(test_args[i-1], NULL); -+ } -+ -+ PySys_SetArgv(n_test_args, python_argv); -+ -+ // If other modules are using thread, we need to initialize them before. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} diff --git a/aclocal.m4 b/aclocal.m4 index 6a33c0cc9d..09ae5d1aa8 100644 --- a/aclocal.m4 @@ -4646,864 +2538,6 @@ index 77fb609b74..72acc6e054 100644 [CYGWIN*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])], [QNX*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])], [FreeBSD*], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])], ---- /dev/null -+++ b/iOS/Info.plist -@@ -0,0 +1,20 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ English -+ CFBundleExecutable -+ Python -+ CFBundleIdentifier -+ org.python -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ FMWK -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ xxxVERSIONxxx -+ -+ ---- /dev/null -+++ b/iOS/README -@@ -0,0 +1,165 @@ -+==================== -+Python on iOS README -+==================== -+ -+:Authors: -+ Russell Keith-Magee (2015) -+ -+:Version: 3.5.2 -+ -+This document provides a overview of eccentricities of building and using -+Python on iOS. -+ -+Build instructions -+================== -+ -+The iOS build must be run on an Mac with XCode installed. To build the iOS -+framework, unpack the Python sources, move into the iOS subdirectory, and -+run ``make``. There are no configuration options to this build process - -+it will use XCode utilities to identify the location of compilers, -+resource directories, and so on. -+ -+The build process will configure and build Python 6 times, producing: -+ -+ * A "host" version of Python -+ * A version of Python compiled for the x86-64 iOS Simulator -+ * A version of Python compiled for the i386 iOS Simulator -+ * A version of Python compiled for ARM64 iOS devices -+ * A version of Python compiled for ARMv7s iOS devices -+ * A version of Python compiled for ARMv7 iOS devices -+ -+Build products will be "installed" into iOS/build. The built products will -+then be combined into a single "fat" ``Python.framework`` that can be added to -+an XCode project. The resulting framework will be located in the root -+directory of the Python source tree. -+ -+A ``make clean`` target also exists to clean out all build products; -+``make distclean`` will clean out all user-specific files from the test and -+sample projects. -+ -+Test instructions -+----------------- -+ -+The ``Tools`` directory contains an ``iOS-Test`` project that enables you to -+run the Python regression test suite. When you run ``make`` in the iOS -+directory, a copy of ``Python.framework`` will also be installed into this -+test project. -+ -+To run the test project, load the project into XCode, and run (either on a -+device or in the simulator). The test suite takes around 10 minutes to run on -+an iPhone6S. -+ -+.. note:: If you run the test project in debug mode, the XCode debugger will -+ stop whenever a signal is raised. The Python regression test suite checks -+ a number of signal handlers, and the test suite will stop mid-execution -+ when this occurs. -+ -+ To disable this signal handling, set a breakpoint at the start of -+ ``main.c``; when execution stops at the breakpoint, run the following -+ commands in the debugger (at the ``(lldb)`` prompt in the console log -+ window):: -+ -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ -+ Unfortunately, this has to be done every time the test suite is executed. -+ -+iOS-specific details -+==================== -+ -+* ``import sys; sys.platform`` will report as `'ios'`, regardless of whether you -+ are on a simulator or a real platform. -+ -+* ``import platform; platform.machine()`` will return the device identifier. -+ For example, an iPhone 5S will return `'iPhone6,2'` -+ -+* The following modules are not currently supported: -+ -+ - ``bsddb`` -+ - ``bz2`` -+ - ``curses`` -+ - ``dbm`` -+ - ``gdbm`` -+ - ``hotshot`` -+ - ``idlelib`` -+ - ``lzma`` -+ - ``nis`` -+ - ``ossaudiodev`` -+ - ``readline`` -+ - ``spwd`` -+ - ``sqlite3`` -+ - ``ssl`` -+ - ``tkinter`` -+ - ``turtledemo`` -+ - ``wsgiref`` -+ -+* Due to limitations in using dynamic loading on iOS, binary Python modules must be -+ statically-linked into the executable. The framework package produced by the iOS -+ ``make install`` statically links all the supported standard library modules. -+ If you have a third-party Python binary module, you'll need to incorporate the -+ source files for that module into the sources for your own app. -+ -+ If you want to add or remove a binary module from the set that is included in the -+ Python library, you can do so by providing module setup files for each platform. -+ There are three default module configuration files: -+ -+ - ``Modules/Setup.iOS-aarch64`` for ARM64 iOS builds -+ - ``Modules/Setup.iOS-arm`` for ARMv7 iOS builds -+ - ``Modules/Setup.iOS-x86_64`` for x86_64 iOS simulator builds -+ -+ If you copy these files to a ``.local`` version (e.g., -+ ``Modules/Setup.iOS-aarch64.local``), the local version will override the -+ default. You can then make modifications to the modules that will be included -+ in the iOS framework, and the flags passed to the compiler when compiling those -+ modules. -+ -+Adding Python to an iOS project -+=============================== -+ -+The iOS subdirectory contains a sample XCode 6.1 project to demonstrate how -+Python can be added to an iOS project. After building the Python iOS framework, -+copy it into the ``iOS/XCode-sample`` directory. You should end up with a directory -+structure that looks like this:: -+ -+ XCode-sample/ -+ Python.framework/ - Manually copied into the project -+ app/ -+ sample/ -+ __init__.py -+ main.py - The Python script to be executed -+ app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime -+ sample -+ Images.xcassets -+ en.lproj -+ main.c - The main() definition for the iOS application -+ sample-Info.plist -+ sample-Prefix.pch -+ sample.xcodeproj - The XCode project file -+ -+If you open the project file is project and run it, you should get output -+similar to the following:: -+ -+ 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app -+ 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime -+ 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py -+ Hello, World. -+ 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving -+ -+You can now modify the provide Python source code, import and use -+code from the Python standard library, and add third-party modules to -+app_packages. -+ -+The sample app is a console-only app, so it isn't of any real practical use. -+Python can be embedded into any Objective-C project using the normal Python -+APIs for embedding; but if you want to write a full iOS app in Python, or -+you want to access iOS services from within embedded code, you'll need to -+bridge between the Objective-C environment and the Python environment. -+This binding isn't something that Python does out of the box; you'll need -+to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. -+ -+.. _Rubicon ObjC: http://pybee.org/rubicon -+.. _Pyobjus: http://pyobjus.readthedocs.org/ -+.. _PyObjC: https://pythonhosted.org/pyobjc/ ---- /dev/null -+++ b/iOS/XCode-sample/app/sample/main.py -@@ -0,0 +1,3 @@ -+ -+if __name__ == '__main__': -+ print("Hello, World.") ---- /dev/null -+++ b/iOS/XCode-sample/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample.xcodeproj/project.pbxproj -@@ -0,0 +1,353 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* sample */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* sample.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */, -+ 60796F1819190FBB00A9926B /* libz.dylib */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* sample */ = { -+ isa = PBXGroup; -+ children = ( -+ 60F0BABD191FC83F006EC268 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = sample; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* sample-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* sample */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = sample; -+ productName = sample; -+ productReference = 60796EE219190F4100A9926B /* sample.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0630; -+ ORGANIZATIONNAME = "Example Corp"; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* sample */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/iOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,53 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/iOS/XCode-sample/sample/main.m -@@ -0,0 +1,111 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = _Py_char2wchar([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ NSLog(@"Initializing Python runtime"); -+ Py_Initialize(); -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/sample/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc); -+ -+ python_argv[0] = _Py_char2wchar(main_script, NULL); -+ for (i = 1; i < argc; i++) { -+ python_argv[i] = _Py_char2wchar(argv[i], NULL); -+ } -+ -+ PySys_SetArgv(argc, python_argv); -+ -+ // If other modules are using threads, we need to initialize them. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } else { -+ // In a normal iOS application, the following line is what -+ // actually runs the application. It requires that the -+ // Objective-C runtime environment has a class named -+ // "PythonAppDelegate". This project doesn't define -+ // one, because Objective-C bridging isn't something -+ // Python does out of the box. You'll need to use -+ // a library like Rubicon-ObjC [1], Pyobjus [2] or -+ // PyObjC [3] if you want to run an *actual* iOS app. -+ // [1] http://pybee.org/rubicon -+ // [2] http://pyobjus.readthedocs.org/ -+ // [3] https://pythonhosted.org/pyobjc/ -+ -+ UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/sample-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ com.example.${PRODUCT_NAME:rfc1034identifier} -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/sample-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file diff --git a/setup.py b/setup.py index 15d0d4576a..b59083aa47 100644 --- a/setup.py @@ -5558,1711 +2592,3 @@ index 15d0d4576a..b59083aa47 100644 if sysconfig.get_config_var('HAVE_LIBDL'): # for dlopen, see bpo-32647 ---- /dev/null -+++ b/tvOS/Info.plist -@@ -0,0 +1,20 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ English -+ CFBundleExecutable -+ Python -+ CFBundleIdentifier -+ org.python -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ FMWK -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ xxxVERSIONxxx -+ -+ ---- /dev/null -+++ b/tvOS/README -@@ -0,0 +1,161 @@ -+===================== -+Python on tvOS README -+===================== -+ -+:Authors: -+ Russell Keith-Magee (2015) -+ -+:Version: 3.5.2 -+ -+This document provides a overview of eccentricities of building and using -+Python on tvOS devices (i.e., AppleTV). -+ -+Build instructions -+================== -+ -+The tvOS build must be run on an Mac with XCode installed. To build the tvOS -+framework, unpack the Python sources, move into the tvOS subdirectory, and -+run ``make``. There are no configuration options to this build process - -+it will use XCode utilities to identify the location of compilers, -+resource directories, and so on. -+ -+The build process will configure and build Python 3 times, producing: -+ -+ * A "host" version of Python -+ * A version of Python compiled for the x86-64 tvOS Simulator -+ * A version of Python compiled for ARM64 tvOS devices -+ -+Build products will be "installed" into tvOS/build. The built products will -+then be combined into a single "fat" ``Python.framework`` that can be added to -+an XCode project. The resulting framework will be located in the root -+directory of the Python source tree. -+ -+A ``make clean`` target also exists to clean out all build products; -+``make distclean`` will clean out all user-specific files from the test and -+sample projects. -+ -+Test instructions -+----------------- -+ -+The ``Tools`` directory contains an ``tvOS-Test`` project that enables you to -+run the Python regression test suite. When you run ``make`` in the tvOS -+directory, a copy of ``Python.framework`` will also be installed into this -+test project. -+ -+To run the test project, load the project into XCode, and run (either on a -+device or in the simulator). The test suite takes around 10 minutes to run on -+an gen 4 AppleTV. -+ -+.. note:: If you run the test project in debug mode, the XCode debugger will -+ stop whenever a signal is raised. The Python regression test suite checks -+ a number of signal handlers, and the test suite will stop mid-execution -+ when this occurs. -+ -+ To disable this signal handling, set a breakpoint at the start of -+ ``main.c``; when execution stops at the breakpoint, run the following -+ commands in the debugger (at the ``(lldb)`` prompt in the console log -+ window):: -+ -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ -+ Unfortunately, this has to be done every time the test suite is executed. -+ -+tvOS-specific details -+==================== -+ -+* ``import sys; sys.platform`` will report as `'tvos'`, regardless of whether you -+ are on a simulator or a real platform. -+ -+* ``import platform; platform.machine()`` will return the device identifier. -+ For example, a Generation 4 Apple TV will return `'AppleTV5,3'` -+ -+* The following modules are not currently supported: -+ -+ - ``bsddb`` -+ - ``bz2`` -+ - ``curses`` -+ - ``dbm`` -+ - ``gdbm`` -+ - ``hotshot`` -+ - ``idlelib`` -+ - ``lzma`` -+ - ``nis`` -+ - ``ossaudiodev`` -+ - ``readline`` -+ - ``spwd`` -+ - ``sqlite3`` -+ - ``ssl`` -+ - ``tkinter`` -+ - ``turtledemo`` -+ - ``wsgiref`` -+ -+* Due to limitations in using dynamic loading on tvOS, binary Python modules must be -+ statically-linked into the executable. The framework package produced by the tvOS -+ ``make install`` statically links all the supported standard library modules. -+ If you have a third-party Python binary module, you'll need to incorporate the -+ source files for that module into the sources for your own app. -+ -+ If you want to add or remove a binary module from the set that is included in the -+ Python library, you can do so by providing module setup files for each platform. -+ There are two default module configuration files: -+ -+ - ``Modules/Setup.tvOS-aarch64`` for ARM64 tvOS builds -+ - ``Modules/Setup.tvOS-x86_64`` for x86_64 tvOS simulator builds -+ -+ If you copy these files to a ``.local`` version (e.g., -+ ``Modules/Setup.tvOS-aarch64.local``), the local version will override the -+ default. You can then make modifications to the modules that will be included -+ in the tvOS framework, and the flags passed to the compiler when compiling those -+ modules. -+ -+Adding Python to an tvOS project -+=============================== -+ -+The tvOS subdirectory contains a sample XCode 6.1 project to demonstrate how -+Python can be added to an tvOS project. After building the Python tvOS framework, -+copy it into the ``tvOS/XCode-sample`` directory. You should end up with a directory -+structure that looks like this:: -+ -+ XCode-sample/ -+ Python.framework/ - Manually copied into the project -+ app/ -+ sample/ -+ __init__.py -+ main.py - The Python script to be executed -+ app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime -+ sample -+ Images.xcassets -+ en.lproj -+ main.c - The main() definition for the tvOS application -+ sample-Info.plist -+ sample-Prefix.pch -+ sample.xcodeproj - The XCode project file -+ -+If you open the project file is project and run it, you should get output -+similar to the following:: -+ -+ 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app -+ 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime -+ 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py -+ Hello, World. -+ 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving -+ -+You can now modify the provide Python source code, import and use -+code from the Python standard library, and add third-party modules to -+app_packages. -+ -+The sample app is a console-only app, so it isn't of any real practical use. -+Python can be embedded into any Objective-C project using the normal Python -+APIs for embedding; but if you want to write a full tvOS app in Python, or -+you want to access tvOS services from within embedded code, you'll need to -+bridge between the Objective-C environment and the Python environment. -+This binding isn't something that Python does out of the box; you'll need -+to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. -+ -+.. _Rubicon ObjC: http://pybee.org/rubicon -+.. _Pyobjus: http://pyobjus.readthedocs.org/ -+.. _PyObjC: https://pythonhosted.org/pyobjc/ ---- /dev/null -+++ b/tvOS/XCode-sample/app/sample/main.py -@@ -0,0 +1,3 @@ -+ -+if __name__ == '__main__': -+ print("Hello, World.") ---- /dev/null -+++ b/tvOS/XCode-sample/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample.xcodeproj/project.pbxproj -@@ -0,0 +1,353 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* sample */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* sample.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */, -+ 60796F1819190FBB00A9926B /* libz.dylib */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* sample */ = { -+ isa = PBXGroup; -+ children = ( -+ 60F0BABD191FC83F006EC268 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = sample; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* sample-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* sample */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = sample; -+ productName = sample; -+ productReference = 60796EE219190F4100A9926B /* sample.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0630; -+ ORGANIZATIONNAME = "Example Corp"; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* sample */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/tvOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,53 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/tvOS/XCode-sample/sample/main.m -@@ -0,0 +1,111 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = _Py_char2wchar([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ NSLog(@"Initializing Python runtime"); -+ Py_Initialize(); -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/sample/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc); -+ -+ python_argv[0] = _Py_char2wchar(main_script, NULL); -+ for (i = 1; i < argc; i++) { -+ python_argv[i] = _Py_char2wchar(argv[i], NULL); -+ } -+ -+ PySys_SetArgv(argc, python_argv); -+ -+ // If other modules are using threads, we need to initialize them. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } else { -+ // In a normal iOS application, the following line is what -+ // actually runs the application. It requires that the -+ // Objective-C runtime environment has a class named -+ // "PythonAppDelegate". This project doesn't define -+ // one, because Objective-C bridging isn't something -+ // Python does out of the box. You'll need to use -+ // a library like Rubicon-ObjC [1], Pyobjus [2] or -+ // PyObjC [3] if you want to run an *actual* iOS app. -+ // [1] http://pybee.org/rubicon -+ // [2] http://pyobjus.readthedocs.org/ -+ // [3] https://pythonhosted.org/pyobjc/ -+ -+ UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/sample-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ com.example.${PRODUCT_NAME:rfc1034identifier} -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/sample-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file ---- /dev/null -+++ b/watchOS/Info.plist -@@ -0,0 +1,20 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ English -+ CFBundleExecutable -+ Python -+ CFBundleIdentifier -+ org.python -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ FMWK -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ xxxVERSIONxxx -+ -+ ---- /dev/null -+++ b/watchOS/README -@@ -0,0 +1,161 @@ -+======================== -+Python on watchOS README -+======================== -+ -+:Authors: -+ Russell Keith-Magee (2015) -+ -+:Version: 3.5.2 -+ -+This document provides a overview of eccentricities of building and using -+Python on watchOS devices (i.e., AppleTV). -+ -+Build instructions -+================== -+ -+The watchOS build must be run on an Mac with XCode installed. To build the watchOS -+framework, unpack the Python sources, move into the watchOS subdirectory, and -+run ``make``. There are no configuration options to this build process - -+it will use XCode utilities to identify the location of compilers, -+resource directories, and so on. -+ -+The build process will configure and build Python 3 times, producing: -+ -+ * A "host" version of Python -+ * A version of Python compiled for the i386 watchOS Simulator -+ * A version of Python compiled for ARMv7k watchOS devices -+ -+Build products will be "installed" into watchOS/build. The built products will -+then be combined into a single "fat" ``Python.framework`` that can be added to -+an XCode project. The resulting framework will be located in the root -+directory of the Python source tree. -+ -+A ``make clean`` target also exists to clean out all build products; -+``make distclean`` will clean out all user-specific files from the test and -+sample projects. -+ -+Test instructions -+----------------- -+ -+The ``Tools`` directory contains an ``watchOS-Test`` project that enables you to -+run the Python regression test suite. When you run ``make`` in the watchOS -+directory, a copy of ``Python.framework`` will also be installed into this -+test project. -+ -+To run the test project, load the project into XCode, and run (either on a -+device or in the simulator). The test suite takes around 10 minutes to run on -+an Apple Watch. -+ -+.. note:: If you run the test project in debug mode, the XCode debugger will -+ stop whenever a signal is raised. The Python regression test suite checks -+ a number of signal handlers, and the test suite will stop mid-execution -+ when this occurs. -+ -+ To disable this signal handling, set a breakpoint at the start of -+ ``main.c``; when execution stops at the breakpoint, run the following -+ commands in the debugger (at the ``(lldb)`` prompt in the console log -+ window):: -+ -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ -+ Unfortunately, this has to be done every time the test suite is executed. -+ -+watchOS-specific details -+==================== -+ -+* ``import sys; sys.platform`` will report as `'watchos'`, regardless of whether you -+ are on a simulator or a real platform. -+ -+* ``import platform; platform.machine()`` will return the device identifier. -+ For example, an origianl Apple Watch will return `'Watch1,1'` -+ -+* The following modules are not currently supported: -+ -+ - ``bsddb`` -+ - ``bz2`` -+ - ``curses`` -+ - ``dbm`` -+ - ``gdbm`` -+ - ``hotshot`` -+ - ``idlelib`` -+ - ``lzma`` -+ - ``nis`` -+ - ``ossaudiodev`` -+ - ``readline`` -+ - ``spwd`` -+ - ``sqlite3`` -+ - ``ssl`` -+ - ``tkinter`` -+ - ``turtledemo`` -+ - ``wsgiref`` -+ -+* Due to limitations in using dynamic loading on watchOS, binary Python modules must be -+ statically-linked into the executable. The framework package produced by the watchOS -+ ``make install`` statically links all the supported standard library modules. -+ If you have a third-party Python binary module, you'll need to incorporate the -+ source files for that module into the sources for your own app. -+ -+ If you want to add or remove a binary module from the set that is included in the -+ Python library, you can do so by providing module setup files for each platform. -+ There are two default module configuration files: -+ -+ - ``Modules/Setup.watchOS-aarch64`` for ARM64 watchOS builds -+ - ``Modules/Setup.watchOS-x86_64`` for x86_64 watchOS simulator builds -+ -+ If you copy these files to a ``.local`` version (e.g., -+ ``Modules/Setup.watchOS-aarch64.local``), the local version will override the -+ default. You can then make modifications to the modules that will be included -+ in the watchOS framework, and the flags passed to the compiler when compiling those -+ modules. -+ -+Adding Python to an watchOS project -+=============================== -+ -+The watchOS subdirectory contains a sample XCode 6.1 project to demonstrate how -+Python can be added to an watchOS project. After building the Python watchOS framework, -+copy it into the ``watchOS/XCode-sample`` directory. You should end up with a directory -+structure that looks like this:: -+ -+ XCode-sample/ -+ Python.framework/ - Manually copied into the project -+ app/ -+ sample/ -+ __init__.py -+ main.py - The Python script to be executed -+ app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime -+ sample -+ Images.xcassets -+ en.lproj -+ main.c - The main() definition for the watchOS application -+ sample-Info.plist -+ sample-Prefix.pch -+ sample.xcodeproj - The XCode project file -+ -+If you open the project file is project and run it, you should get output -+similar to the following:: -+ -+ 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app -+ 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime -+ 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py -+ Hello, World. -+ 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving -+ -+You can now modify the provide Python source code, import and use -+code from the Python standard library, and add third-party modules to -+app_packages. -+ -+The sample app is a console-only app, so it isn't of any real practical use. -+Python can be embedded into any Objective-C project using the normal Python -+APIs for embedding; but if you want to write a full watchOS app in Python, or -+you want to access watchOS services from within embedded code, you'll need to -+bridge between the Objective-C environment and the Python environment. -+This binding isn't something that Python does out of the box; you'll need -+to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. -+ -+.. _Rubicon ObjC: http://pybee.org/rubicon -+.. _Pyobjus: http://pyobjus.readthedocs.org/ -+.. _PyObjC: https://pythonhosted.org/pyobjc/ ---- /dev/null -+++ b/watchOS/XCode-sample/app/sample/main.py -@@ -0,0 +1,3 @@ -+ -+if __name__ == '__main__': -+ print("Hello, World.") ---- /dev/null -+++ b/watchOS/XCode-sample/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample.xcodeproj/project.pbxproj -@@ -0,0 +1,353 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* sample */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* sample.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */, -+ 60796F1819190FBB00A9926B /* libz.dylib */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* sample */ = { -+ isa = PBXGroup; -+ children = ( -+ 60F0BABD191FC83F006EC268 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = sample; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* sample-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* sample */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = sample; -+ productName = sample; -+ productReference = 60796EE219190F4100A9926B /* sample.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0630; -+ ORGANIZATIONNAME = "Example Corp"; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* sample */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/watchOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,53 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/watchOS/XCode-sample/sample/main.m -@@ -0,0 +1,111 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *python_home; -+ char *wpython_home; -+ const char* main_script; -+ char** python_argv; -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = strdup([python_home UTF8String]); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ NSLog(@"Initializing Python runtime"); -+ Py_Initialize(); -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/sample/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ python_argv = PyMem_Malloc(sizeof(char *) * argc); -+ -+ -+ python_argv[0] = strdup(main_script); -+ for (i = 1; i < argc; i++) { -+ python_argv[i] = argv[i]; -+ } -+ -+ PySys_SetArgv(argc, python_argv); -+ -+ // If other modules are using threads, we need to initialize them. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } else { -+ // In a normal iOS application, the following line is what -+ // actually runs the application. It requires that the -+ // Objective-C runtime environment has a class named -+ // "PythonAppDelegate". This project doesn't define -+ // one, because Objective-C bridging isn't something -+ // Python does out of the box. You'll need to use -+ // a library like Rubicon-ObjC [1], Pyobjus [2] or -+ // PyObjC [3] if you want to run an *actual* iOS app. -+ // [1] http://pybee.org/rubicon -+ // [2] http://pyobjus.readthedocs.org/ -+ // [3] https://pythonhosted.org/pyobjc/ -+ -+ UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_Free(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_Free(python_argv[i]); -+ } -+ PyMem_Free(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/sample-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ com.example.${PRODUCT_NAME:rfc1034identifier} -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/sample-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file From 857c819a6aa40612841be166c9c01c58a4472e5b Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 7 Sep 2022 11:14:39 +0800 Subject: [PATCH 30/37] Update patch for Python 3.9.13. --- Makefile | 2 +- README.rst | 2 +- patch/Python/Python.patch | 8912 +------------------------------------ 3 files changed, 27 insertions(+), 8889 deletions(-) diff --git a/Makefile b/Makefile index c87d9255..c70ce90a 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ BUILD_NUMBER=custom # PYTHON_VERSION is the full version number (e.g., 3.10.0b3) # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.9.12 +PYTHON_VERSION=3.9.13 PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) diff --git a/README.rst b/README.rst index 88dea946..2869a6dc 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ Python Apple Support ==================== -**This repository branch builds a packaged version of Python 3.9.12**. +**This repository branch builds a packaged version of Python 3.9.13**. Other Python versions are available by cloning other branches of the main repository. diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index f0eea8cd..2fdeaa9f 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,2609 +1,8 @@ -diff --git a/Doc/about.rst b/Doc/about.rst -index 3ea311fa62..f0b908487b 100644 ---- a/Doc/about.rst -+++ b/Doc/about.rst -@@ -23,9 +23,8 @@ - and writer of much of the content; - * the `Docutils `_ project for creating - reStructuredText and the Docutils suite; --* Fredrik Lundh for his `Alternative Python Reference -- `_ project from which Sphinx got many good -- ideas. -+* Fredrik Lundh for his Alternative Python Reference project from which Sphinx -+ got many good ideas. - - - Contributors to the Python Documentation -diff --git a/Doc/bugs.rst b/Doc/bugs.rst -index 192995ab06..6654a23c06 100644 ---- a/Doc/bugs.rst -+++ b/Doc/bugs.rst -@@ -35,6 +35,10 @@ - `Helping with Documentation `_ - Comprehensive guide for individuals that are interested in contributing to Python documentation. - -+ `Documentation Translations `_ -+ A list of GitHub pages for documentation translation and their primary contacts. -+ -+ - .. _using-the-tracker: - - Using the Python issue tracker -diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst -index 7a3188009c..6b60344a30 100644 ---- a/Doc/c-api/arg.rst -+++ b/Doc/c-api/arg.rst -@@ -288,7 +288,7 @@ - Convert a Python integer to a C :c:type:`unsigned long long` - without overflow checking. - --``n`` (:class:`int`) [Py_ssize_t] -+``n`` (:class:`int`) [:c:type:`Py_ssize_t`] - Convert a Python integer to a C :c:type:`Py_ssize_t`. - - ``c`` (:class:`bytes` or :class:`bytearray` of length 1) [char] -@@ -614,7 +614,7 @@ - ``K`` (:class:`int`) [unsigned long long] - Convert a C :c:type:`unsigned long long` to a Python integer object. - -- ``n`` (:class:`int`) [Py_ssize_t] -+ ``n`` (:class:`int`) [:c:type:`Py_ssize_t`] - Convert a C :c:type:`Py_ssize_t` to a Python integer. - - ``c`` (:class:`bytes` of length 1) [char] -diff --git a/Doc/c-api/bytearray.rst b/Doc/c-api/bytearray.rst -index 30bcfc7cf9..85a7d1373c 100644 ---- a/Doc/c-api/bytearray.rst -+++ b/Doc/c-api/bytearray.rst -@@ -42,8 +42,6 @@ - Return a new bytearray object from any object, *o*, that implements the - :ref:`buffer protocol `. - -- .. XXX expand about the buffer protocol, at least somewhere -- - - .. c:function:: PyObject* PyByteArray_FromStringAndSize(const char *string, Py_ssize_t len) - -diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst -index de65701037..ffbfb5a61b 100644 ---- a/Doc/c-api/bytes.rst -+++ b/Doc/c-api/bytes.rst -@@ -5,7 +5,7 @@ - Bytes Objects - ------------- - --These functions raise :exc:`TypeError` when expecting a bytes parameter and are -+These functions raise :exc:`TypeError` when expecting a bytes parameter and - called with a non-bytes parameter. - - .. index:: object: bytes -@@ -84,8 +84,8 @@ - | :attr:`%lu` | unsigned long | Equivalent to | - | | | ``printf("%lu")``. [1]_ | - +-------------------+---------------+--------------------------------+ -- | :attr:`%zd` | Py_ssize_t | Equivalent to | -- | | | ``printf("%zd")``. [1]_ | -+ | :attr:`%zd` | :c:type:`\ | Equivalent to | -+ | | Py_ssize_t` | ``printf("%zd")``. [1]_ | - +-------------------+---------------+--------------------------------+ - | :attr:`%zu` | size_t | Equivalent to | - | | | ``printf("%zu")``. [1]_ | -diff --git a/Doc/c-api/call.rst b/Doc/c-api/call.rst -index 739b5e97d1..cdf72bc1f4 100644 ---- a/Doc/c-api/call.rst -+++ b/Doc/c-api/call.rst -@@ -26,7 +26,7 @@ - :c:member:`~PyTypeObject.tp_new` and :c:member:`~PyTypeObject.tp_init` - also pass arguments this way. - --To call an object, use :c:func:`PyObject_Call` or other -+To call an object, use :c:func:`PyObject_Call` or another - :ref:`call API `. - - -diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst -index df38ba43ec..0e35590c67 100644 ---- a/Doc/c-api/exceptions.rst -+++ b/Doc/c-api/exceptions.rst -@@ -281,7 +281,7 @@ - - .. c:function:: void PyErr_SyntaxLocation(const char *filename, int lineno) - -- Like :c:func:`PyErr_SyntaxLocationEx`, but the col_offset parameter is -+ Like :c:func:`PyErr_SyntaxLocationEx`, but the *col_offset* parameter is - omitted. - - -@@ -333,7 +333,7 @@ - - Issue a warning message with explicit control over all warning attributes. This - is a straightforward wrapper around the Python function -- :func:`warnings.warn_explicit`, see there for more information. The *module* -+ :func:`warnings.warn_explicit`; see there for more information. The *module* - and *registry* arguments may be set to ``NULL`` to get the default effect - described there. - -@@ -441,7 +441,7 @@ - error indicator. - - --.. c:function:: void PyErr_NormalizeException(PyObject**exc, PyObject**val, PyObject**tb) -+.. c:function:: void PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) - - Under certain circumstances, the values returned by :c:func:`PyErr_Fetch` below - can be "unnormalized", meaning that ``*exc`` is a class object but ``*val`` is -@@ -845,11 +845,11 @@ - +-----------------------------------------+---------------------------------+----------+ - | C Name | Python Name | Notes | - +=========================================+=================================+==========+ --| :c:data:`PyExc_BaseException` | :exc:`BaseException` | \(1) | -+| :c:data:`PyExc_BaseException` | :exc:`BaseException` | [1]_ | - +-----------------------------------------+---------------------------------+----------+ --| :c:data:`PyExc_Exception` | :exc:`Exception` | \(1) | -+| :c:data:`PyExc_Exception` | :exc:`Exception` | [1]_ | - +-----------------------------------------+---------------------------------+----------+ --| :c:data:`PyExc_ArithmeticError` | :exc:`ArithmeticError` | \(1) | -+| :c:data:`PyExc_ArithmeticError` | :exc:`ArithmeticError` | [1]_ | - +-----------------------------------------+---------------------------------+----------+ - | :c:data:`PyExc_AssertionError` | :exc:`AssertionError` | | - +-----------------------------------------+---------------------------------+----------+ -@@ -895,7 +895,7 @@ - +-----------------------------------------+---------------------------------+----------+ - | :c:data:`PyExc_KeyboardInterrupt` | :exc:`KeyboardInterrupt` | | - +-----------------------------------------+---------------------------------+----------+ --| :c:data:`PyExc_LookupError` | :exc:`LookupError` | \(1) | -+| :c:data:`PyExc_LookupError` | :exc:`LookupError` | [1]_ | - +-----------------------------------------+---------------------------------+----------+ - | :c:data:`PyExc_MemoryError` | :exc:`MemoryError` | | - +-----------------------------------------+---------------------------------+----------+ -@@ -907,7 +907,7 @@ - +-----------------------------------------+---------------------------------+----------+ - | :c:data:`PyExc_NotImplementedError` | :exc:`NotImplementedError` | | - +-----------------------------------------+---------------------------------+----------+ --| :c:data:`PyExc_OSError` | :exc:`OSError` | \(1) | -+| :c:data:`PyExc_OSError` | :exc:`OSError` | [1]_ | - +-----------------------------------------+---------------------------------+----------+ - | :c:data:`PyExc_OverflowError` | :exc:`OverflowError` | | - +-----------------------------------------+---------------------------------+----------+ -@@ -917,7 +917,7 @@ - +-----------------------------------------+---------------------------------+----------+ - | :c:data:`PyExc_RecursionError` | :exc:`RecursionError` | | - +-----------------------------------------+---------------------------------+----------+ --| :c:data:`PyExc_ReferenceError` | :exc:`ReferenceError` | \(2) | -+| :c:data:`PyExc_ReferenceError` | :exc:`ReferenceError` | | - +-----------------------------------------+---------------------------------+----------+ - | :c:data:`PyExc_RuntimeError` | :exc:`RuntimeError` | | - +-----------------------------------------+---------------------------------+----------+ -@@ -982,7 +982,7 @@ - +-------------------------------------+----------+ - | :c:data:`PyExc_IOError` | | - +-------------------------------------+----------+ --| :c:data:`PyExc_WindowsError` | \(3) | -+| :c:data:`PyExc_WindowsError` | [2]_ | - +-------------------------------------+----------+ - - .. versionchanged:: 3.3 -@@ -990,10 +990,10 @@ - - Notes: - --(1) -+.. [1] - This is a base class for other standard exceptions. - --(2) -+.. [2] - Only defined on Windows; protect code that uses this by testing that the - preprocessor macro ``MS_WINDOWS`` is defined. - -@@ -1023,7 +1023,7 @@ - +------------------------------------------+---------------------------------+----------+ - | C Name | Python Name | Notes | - +==========================================+=================================+==========+ --| :c:data:`PyExc_Warning` | :exc:`Warning` | \(1) | -+| :c:data:`PyExc_Warning` | :exc:`Warning` | [3]_ | - +------------------------------------------+---------------------------------+----------+ - | :c:data:`PyExc_BytesWarning` | :exc:`BytesWarning` | | - +------------------------------------------+---------------------------------+----------+ -@@ -1051,5 +1051,5 @@ - - Notes: - --(1) -+.. [3] - This is a base class for other standard warning categories. -diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst -index 9d7876e055..06e886835d 100644 ---- a/Doc/c-api/init.rst -+++ b/Doc/c-api/init.rst -@@ -498,7 +498,7 @@ - .. index:: single: version (in module sys) - - The first word (up to the first space character) is the current Python version; -- the first three characters are the major and minor version separated by a -+ the first characters are the major and minor version separated by a - period. The returned string points into static storage; the caller should not - modify its value. The value is available to Python code as :data:`sys.version`. - -@@ -1688,7 +1688,7 @@ - argument is `NULL`. - - .. note:: -- A freed key becomes a dangling pointer, you should reset the key to -+ A freed key becomes a dangling pointer. You should reset the key to - `NULL`. - - -diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst -index 23009e4191..f44c7bb5cd 100644 ---- a/Doc/c-api/init_config.rst -+++ b/Doc/c-api/init_config.rst -@@ -823,7 +823,7 @@ - isolate Python from the system. For example, to embed Python into an - application. - --This configuration ignores global configuration variables, environments -+This configuration ignores global configuration variables, environment - variables, command line arguments (:c:member:`PyConfig.argv` is not parsed) - and user site directory. The C standard streams (ex: ``stdout``) and the - LC_CTYPE locale are left unchanged. Signal handlers are not installed. -@@ -1000,7 +1000,7 @@ - -------------------------------------------------- - - This section is a private provisional API introducing multi-phase --initialization, the core feature of the :pep:`432`: -+initialization, the core feature of :pep:`432`: - - * "Core" initialization phase, "bare minimum Python": - -diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst -index 7ca8693afa..ac1024cd5e 100644 ---- a/Doc/c-api/intro.rst -+++ b/Doc/c-api/intro.rst -@@ -502,6 +502,13 @@ - of a complex number. These will be discussed together with the functions that - use them. - -+.. c:type:: Py_ssize_t -+ -+ A signed integral type such that ``sizeof(Py_ssize_t) == sizeof(size_t)``. -+ C99 doesn't define such a thing directly (size_t is an unsigned integral type). -+ See :pep:`353` for details. ``PY_SSIZE_T_MAX`` is the largest positive value -+ of type :c:type:`Py_ssize_t`. -+ - - .. _api-exceptions: - -diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst -index 60e1791df9..95796e0fa5 100644 ---- a/Doc/c-api/long.rst -+++ b/Doc/c-api/long.rst -@@ -41,7 +41,7 @@ - Return a new :c:type:`PyLongObject` object from *v*, or ``NULL`` on failure. - - The current implementation keeps an array of integer objects for all integers -- between ``-5`` and ``256``, when you create an int in that range you actually -+ between ``-5`` and ``256``. When you create an int in that range you actually - just get back a reference to the existing object. - - -diff --git a/Doc/c-api/mapping.rst b/Doc/c-api/mapping.rst -index 682160d147..3c9d282c6d 100644 ---- a/Doc/c-api/mapping.rst -+++ b/Doc/c-api/mapping.rst -@@ -11,10 +11,10 @@ - - .. c:function:: int PyMapping_Check(PyObject *o) - -- Return ``1`` if the object provides mapping protocol or supports slicing, -+ Return ``1`` if the object provides the mapping protocol or supports slicing, - and ``0`` otherwise. Note that it returns ``1`` for Python classes with -- a :meth:`__getitem__` method since in general case it is impossible to -- determine what type of keys it supports. This function always succeeds. -+ a :meth:`__getitem__` method, since in general it is impossible to -+ determine what type of keys the class supports. This function always succeeds. - - - .. c:function:: Py_ssize_t PyMapping_Size(PyObject *o) -diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst -index 91b901d726..624bddcbb8 100644 ---- a/Doc/c-api/memory.rst -+++ b/Doc/c-api/memory.rst -@@ -362,7 +362,7 @@ - .. c:type:: PyMemAllocatorEx - - Structure used to describe a memory block allocator. The structure has -- four fields: -+ the following fields: - - +----------------------------------------------------------+---------------------------------------+ - | Field | Meaning | -diff --git a/Doc/c-api/method.rst b/Doc/c-api/method.rst -index 23852251df..6e7e1e21aa 100644 ---- a/Doc/c-api/method.rst -+++ b/Doc/c-api/method.rst -@@ -27,7 +27,7 @@ - - .. c:function:: PyObject* PyInstanceMethod_New(PyObject *func) - -- Return a new instance method object, with *func* being any callable object -+ Return a new instance method object, with *func* being any callable object. - *func* is the function that will be called when the instance method is - called. - -diff --git a/Doc/c-api/number.rst b/Doc/c-api/number.rst -index 620204ca8e..47033a3852 100644 ---- a/Doc/c-api/number.rst -+++ b/Doc/c-api/number.rst -@@ -44,7 +44,7 @@ - .. c:function:: PyObject* PyNumber_FloorDivide(PyObject *o1, PyObject *o2) - - Return the floor of *o1* divided by *o2*, or ``NULL`` on failure. This is -- equivalent to the "classic" division of integers. -+ the equivalent of the Python expression ``o1 // o2``. - - - .. c:function:: PyObject* PyNumber_TrueDivide(PyObject *o1, PyObject *o2) -@@ -53,7 +53,7 @@ - *o2*, or ``NULL`` on failure. The return value is "approximate" because binary - floating point numbers are approximate; it is not possible to represent all real - numbers in base two. This function can return a floating point value when -- passed two integers. -+ passed two integers. This is the equivalent of the Python expression ``o1 / o2``. - - - .. c:function:: PyObject* PyNumber_Remainder(PyObject *o1, PyObject *o2) -@@ -180,6 +180,7 @@ - floating point numbers are approximate; it is not possible to represent all real - numbers in base two. This function can return a floating point value when - passed two integers. The operation is done *in-place* when *o1* supports it. -+ This is the equivalent of the Python statement ``o1 /= o2``. - - - .. c:function:: PyObject* PyNumber_InPlaceRemainder(PyObject *o1, PyObject *o2) -@@ -268,11 +269,11 @@ - - .. c:function:: Py_ssize_t PyNumber_AsSsize_t(PyObject *o, PyObject *exc) - -- Returns *o* converted to a Py_ssize_t value if *o* can be interpreted as an -+ Returns *o* converted to a :c:type:`Py_ssize_t` value if *o* can be interpreted as an - integer. If the call fails, an exception is raised and ``-1`` is returned. - - If *o* can be converted to a Python int but the attempt to -- convert to a Py_ssize_t value would raise an :exc:`OverflowError`, then the -+ convert to a :c:type:`Py_ssize_t` value would raise an :exc:`OverflowError`, then the - *exc* argument is the type of exception that will be raised (usually - :exc:`IndexError` or :exc:`OverflowError`). If *exc* is ``NULL``, then the - exception is cleared and the value is clipped to ``PY_SSIZE_T_MIN`` for a negative -@@ -281,6 +282,6 @@ - - .. c:function:: int PyIndex_Check(PyObject *o) - -- Returns ``1`` if *o* is an index integer (has the nb_index slot of the -- tp_as_number structure filled in), and ``0`` otherwise. -+ Returns ``1`` if *o* is an index integer (has the ``nb_index`` slot of the -+ ``tp_as_number`` structure filled in), and ``0`` otherwise. - This function always succeeds. -diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst -index 9f874382e8..3e4f61ac02 100644 ---- a/Doc/c-api/object.rst -+++ b/Doc/c-api/object.rst -@@ -93,7 +93,7 @@ - return ``0`` on success. This is the equivalent of the Python statement - ``o.attr_name = v``. - -- If *v* is ``NULL``, the attribute is deleted, however this feature is -+ If *v* is ``NULL``, the attribute is deleted, but this feature is - deprecated in favour of using :c:func:`PyObject_DelAttrString`. - - -@@ -258,7 +258,7 @@ - - .. versionchanged:: 3.2 - The return type is now Py_hash_t. This is a signed integer the same size -- as Py_ssize_t. -+ as :c:type:`Py_ssize_t`. - - - .. c:function:: Py_hash_t PyObject_HashNotImplemented(PyObject *o) -@@ -291,7 +291,7 @@ - of object *o*. On failure, raises :exc:`SystemError` and returns ``NULL``. This - is equivalent to the Python expression ``type(o)``. This function increments the - reference count of the return value. There's really no reason to use this -- function instead of the common expression ``o->ob_type``, which returns a -+ function instead of the :c:func:`Py_TYPE()` function, which returns a - pointer of type :c:type:`PyTypeObject*`, except when the incremented reference - count is needed. - -diff --git a/Doc/c-api/sequence.rst b/Doc/c-api/sequence.rst -index 6581885904..c78d273f9f 100644 ---- a/Doc/c-api/sequence.rst -+++ b/Doc/c-api/sequence.rst -@@ -8,10 +8,10 @@ - - .. c:function:: int PySequence_Check(PyObject *o) - -- Return ``1`` if the object provides sequence protocol, and ``0`` otherwise. -+ Return ``1`` if the object provides the sequence protocol, and ``0`` otherwise. - Note that it returns ``1`` for Python classes with a :meth:`__getitem__` -- method unless they are :class:`dict` subclasses since in general case it -- is impossible to determine what the type of keys it supports. This -+ method, unless they are :class:`dict` subclasses, since in general it -+ is impossible to determine what type of keys the class supports. This - function always succeeds. - - -@@ -69,7 +69,7 @@ - is the equivalent of the Python statement ``o[i] = v``. This function *does - not* steal a reference to *v*. - -- If *v* is ``NULL``, the element is deleted, however this feature is -+ If *v* is ``NULL``, the element is deleted, but this feature is - deprecated in favour of using :c:func:`PySequence_DelItem`. - - -@@ -147,7 +147,7 @@ - - Returns the length of *o*, assuming that *o* was returned by - :c:func:`PySequence_Fast` and that *o* is not ``NULL``. The size can also be -- gotten by calling :c:func:`PySequence_Size` on *o*, but -+ retrieved by calling :c:func:`PySequence_Size` on *o*, but - :c:func:`PySequence_Fast_GET_SIZE` is faster because it can assume *o* is a - list or tuple. - -diff --git a/Doc/c-api/set.rst b/Doc/c-api/set.rst -index 84f34e7dae..9e3045e796 100644 ---- a/Doc/c-api/set.rst -+++ b/Doc/c-api/set.rst -@@ -13,7 +13,7 @@ - object: frozenset - - This section details the public API for :class:`set` and :class:`frozenset` --objects. Any functionality not listed below is best accessed using the either -+objects. Any functionality not listed below is best accessed using either - the abstract object protocol (including :c:func:`PyObject_CallMethod`, - :c:func:`PyObject_RichCompareBool`, :c:func:`PyObject_Hash`, - :c:func:`PyObject_Repr`, :c:func:`PyObject_IsTrue`, :c:func:`PyObject_Print`, and -@@ -31,7 +31,7 @@ - in that it is a fixed size for small sets (much like tuple storage) and will - point to a separate, variable sized block of memory for medium and large sized - sets (much like list storage). None of the fields of this structure should be -- considered public and are subject to change. All access should be done through -+ considered public and all are subject to change. All access should be done through - the documented API rather than by manipulating the values in the structure. - - -@@ -125,7 +125,7 @@ - .. c:function:: int PySet_Add(PyObject *set, PyObject *key) - - Add *key* to a :class:`set` instance. Also works with :class:`frozenset` -- instances (like :c:func:`PyTuple_SetItem` it can be used to fill-in the values -+ instances (like :c:func:`PyTuple_SetItem` it can be used to fill in the values - of brand new frozensets before they are exposed to other code). Return ``0`` on - success or ``-1`` on failure. Raise a :exc:`TypeError` if the *key* is - unhashable. Raise a :exc:`MemoryError` if there is no room to grow. Raise a -diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst -index 1e30d408ab..1599b88e4a 100644 ---- a/Doc/c-api/structures.rst -+++ b/Doc/c-api/structures.rst -@@ -461,7 +461,7 @@ - +=============+==================+===================================+ - | name | const char \* | attribute name | - +-------------+------------------+-----------------------------------+ -- | get | getter | C Function to get the attribute | -+ | get | getter | C function to get the attribute | - +-------------+------------------+-----------------------------------+ - | set | setter | optional C function to set or | - | | | delete the attribute, if omitted | -diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst -index 9ac9179097..0a5c8d7008 100644 ---- a/Doc/c-api/sys.rst -+++ b/Doc/c-api/sys.rst -@@ -185,7 +185,7 @@ - - Return a pointer to a newly allocated byte string, use :c:func:`PyMem_Free` - to free the memory. Return ``NULL`` on encoding error or memory allocation -- error -+ error. - - If error_pos is not ``NULL``, ``*error_pos`` is set to ``(size_t)-1`` on - success, or set to the index of the invalid character on encoding error. -@@ -205,7 +205,7 @@ - - .. versionchanged:: 3.8 - The function now uses the UTF-8 encoding on Windows if -- :c:data:`Py_LegacyWindowsFSEncodingFlag` is zero; -+ :c:data:`Py_LegacyWindowsFSEncodingFlag` is zero. - - - .. _systemfunctions: -@@ -321,7 +321,7 @@ - leaks.) - - Note that ``#`` format characters should always be treated as -- ``Py_ssize_t``, regardless of whether ``PY_SSIZE_T_CLEAN`` was defined. -+ :c:type:`Py_ssize_t`, regardless of whether ``PY_SSIZE_T_CLEAN`` was defined. - - :func:`sys.audit` performs the same function from Python code. - -@@ -329,14 +329,14 @@ - - .. versionchanged:: 3.8.2 - -- Require ``Py_ssize_t`` for ``#`` format characters. Previously, an -+ Require :c:type:`Py_ssize_t` for ``#`` format characters. Previously, an - unavoidable deprecation warning was raised. - - - .. c:function:: int PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData) - - Append the callable *hook* to the list of active auditing hooks. -- Return zero for success -+ Return zero on success - and non-zero on failure. If the runtime has been initialized, also set an - error on failure. Hooks added through this API are called for all - interpreters created by the runtime. -diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst -index cc2d8fdaa5..119a908171 100644 ---- a/Doc/c-api/type.rst -+++ b/Doc/c-api/type.rst -@@ -266,7 +266,7 @@ - - .. versionchanged:: 3.9 - -- Slots in :c:type:`PyBufferProcs` in may be set in the unlimited API. -+ Slots in :c:type:`PyBufferProcs` may be set in the unlimited API. - - .. c:member:: void *PyType_Slot.pfunc - -diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst -index d58a53b1d6..eedefd7cec 100644 ---- a/Doc/c-api/typeobj.rst -+++ b/Doc/c-api/typeobj.rst -@@ -43,13 +43,13 @@ - +================================================+===================================+===================+===+===+===+===+ - | :c:member:`~PyTypeObject.tp_name` | const char * | __name__ | X | X | | | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ -- | :c:member:`~PyTypeObject.tp_basicsize` | Py_ssize_t | | X | X | | X | -+ | :c:member:`~PyTypeObject.tp_basicsize` | :c:type:`Py_ssize_t` | | X | X | | X | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ -- | :c:member:`~PyTypeObject.tp_itemsize` | Py_ssize_t | | | X | | X | -+ | :c:member:`~PyTypeObject.tp_itemsize` | :c:type:`Py_ssize_t` | | | X | | X | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_dealloc` | :c:type:`destructor` | | X | X | | X | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ -- | :c:member:`~PyTypeObject.tp_vectorcall_offset` | Py_ssize_t | | | X | | X | -+ | :c:member:`~PyTypeObject.tp_vectorcall_offset` | :c:type:`Py_ssize_t` | | | X | | X | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | (:c:member:`~PyTypeObject.tp_getattr`) | :c:type:`getattrfunc` | __getattribute__, | | | | G | - | | | __getattr__ | | | | | -@@ -96,7 +96,7 @@ - | | | __gt__, | | | | | - | | | __ge__ | | | | | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ -- | :c:member:`~PyTypeObject.tp_weaklistoffset` | Py_ssize_t | | | X | | ? | -+ | :c:member:`~PyTypeObject.tp_weaklistoffset` | :c:type:`Py_ssize_t` | | | X | | ? | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_iter` | :c:type:`getiterfunc` | __iter__ | | | | X | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ -@@ -117,7 +117,7 @@ - | :c:member:`~PyTypeObject.tp_descr_set` | :c:type:`descrsetfunc` | __set__, | | | | X | - | | | __delete__ | | | | | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ -- | :c:member:`~PyTypeObject.tp_dictoffset` | Py_ssize_t | | | X | | ? | -+ | :c:member:`~PyTypeObject.tp_dictoffset` | :c:type:`Py_ssize_t` | | | X | | ? | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_init` | :c:type:`initproc` | __init__ | X | X | | X | - +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ -@@ -331,7 +331,7 @@ - | :c:type:`allocfunc` | .. line-block:: | :c:type:`PyObject` * | - | | | | - | | :c:type:`PyTypeObject` * | | --| | Py_ssize_t | | -+| | :c:type:`Py_ssize_t` | | - +-----------------------------+-----------------------------+----------------------+ - | :c:type:`destructor` | void * | void | - +-----------------------------+-----------------------------+----------------------+ -@@ -403,7 +403,7 @@ - +-----------------------------+-----------------------------+----------------------+ - | :c:type:`iternextfunc` | :c:type:`PyObject` * | :c:type:`PyObject` * | - +-----------------------------+-----------------------------+----------------------+ --| :c:type:`lenfunc` | :c:type:`PyObject` * | Py_ssize_t | -+| :c:type:`lenfunc` | :c:type:`PyObject` * | :c:type:`Py_ssize_t` | - +-----------------------------+-----------------------------+----------------------+ - | :c:type:`getbufferproc` | .. line-block:: | int | - | | | | -@@ -436,12 +436,12 @@ - | :c:type:`ssizeargfunc` | .. line-block:: | :c:type:`PyObject` * | - | | | | - | | :c:type:`PyObject` * | | --| | Py_ssize_t | | -+| | :c:type:`Py_ssize_t` | | - +-----------------------------+-----------------------------+----------------------+ - | :c:type:`ssizeobjargproc` | .. line-block:: | int | - | | | | - | | :c:type:`PyObject` * | | --| | Py_ssize_t | | -+| | :c:type:`Py_ssize_t` | | - +-----------------------------+-----------------------------+----------------------+ - | :c:type:`objobjproc` | .. line-block:: | int | - | | | | -@@ -2482,7 +2482,7 @@ - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mymod.MyObject", - .tp_basicsize = sizeof(MyObject), -- .tp_doc = "My objects", -+ .tp_doc = PyDoc_STR("My objects"), - .tp_new = myobj_new, - .tp_dealloc = (destructor)myobj_dealloc, - .tp_repr = (reprfunc)myobj_repr, -@@ -2512,7 +2512,7 @@ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - 0, /* tp_flags */ -- "My objects", /* tp_doc */ -+ PyDoc_STR("My objects"), /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -2545,7 +2545,7 @@ - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mymod.MyObject", - .tp_basicsize = sizeof(MyObject), -- .tp_doc = "My objects", -+ .tp_doc = PyDoc_STR("My objects"), - .tp_weaklistoffset = offsetof(MyObject, weakreflist), - .tp_dictoffset = offsetof(MyObject, inst_dict), - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, -@@ -2572,7 +2572,7 @@ - .tp_name = "mymod.MyStr", - .tp_basicsize = sizeof(MyStr), - .tp_base = NULL, // set to &PyUnicode_Type in module init -- .tp_doc = "my custom str", -+ .tp_doc = PyDoc_STR("my custom str"), - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_new = NULL, - .tp_repr = (reprfunc)myobj_repr, -diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst -index a6ae8ba136..4129c614a1 100644 ---- a/Doc/c-api/unicode.rst -+++ b/Doc/c-api/unicode.rst -@@ -490,11 +490,11 @@ - | :attr:`%llu` | unsigned long long | Equivalent to | - | | | ``printf("%llu")``. [1]_ | - +-------------------+---------------------+----------------------------------+ -- | :attr:`%zd` | Py_ssize_t | Equivalent to | -- | | | ``printf("%zd")``. [1]_ | -+ | :attr:`%zd` | :c:type:`\ | Equivalent to | -+ | | Py_ssize_t` | ``printf("%zd")``. [1]_ | - +-------------------+---------------------+----------------------------------+ -- | :attr:`%zi` | Py_ssize_t | Equivalent to | -- | | | ``printf("%zi")``. [1]_ | -+ | :attr:`%zi` | :c:type:`\ | Equivalent to | -+ | | Py_ssize_t` | ``printf("%zi")``. [1]_ | - +-------------------+---------------------+----------------------------------+ - | :attr:`%zu` | size_t | Equivalent to | - | | | ``printf("%zu")``. [1]_ | -@@ -1110,7 +1110,8 @@ - - This caches the UTF-8 representation of the string in the Unicode object, and - subsequent calls will return a pointer to the same buffer. The caller is not -- responsible for deallocating the buffer. -+ responsible for deallocating the buffer. The buffer is deallocated and -+ pointers to it become invalid when the Unicode object is garbage collected. - - .. versionadded:: 3.3 - -diff --git a/Doc/extending/newtypes_tutorial.rst b/Doc/extending/newtypes_tutorial.rst -index 4da77e797d..7771e20520 100644 ---- a/Doc/extending/newtypes_tutorial.rst -+++ b/Doc/extending/newtypes_tutorial.rst -@@ -67,8 +67,8 @@ - This is what a Custom object will contain. ``PyObject_HEAD`` is mandatory - at the start of each object struct and defines a field called ``ob_base`` - of type :c:type:`PyObject`, containing a pointer to a type object and a --reference count (these can be accessed using the macros :c:macro:`Py_REFCNT` --and :c:macro:`Py_TYPE` respectively). The reason for the macro is to -+reference count (these can be accessed using the macros :c:macro:`Py_TYPE` -+and :c:macro:`Py_REFCNT` respectively). The reason for the macro is to - abstract away the layout and to enable additional fields in debug builds. - - .. note:: -@@ -89,7 +89,7 @@ - static PyTypeObject CustomType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "custom.Custom", -- .tp_doc = "Custom objects", -+ .tp_doc = PyDoc_STR("Custom objects"), - .tp_basicsize = sizeof(CustomObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT, -@@ -160,7 +160,7 @@ - - We provide a doc string for the type in :c:member:`~PyTypeObject.tp_doc`. :: - -- .tp_doc = "Custom objects", -+ .tp_doc = PyDoc_STR("Custom objects"), - - To enable object creation, we have to provide a :c:member:`~PyTypeObject.tp_new` - handler. This is the equivalent of the Python method :meth:`__new__`, but -diff --git a/Doc/extending/windows.rst b/Doc/extending/windows.rst -index c7b92c6ea2..28d0350f6f 100644 ---- a/Doc/extending/windows.rst -+++ b/Doc/extending/windows.rst -@@ -106,8 +106,7 @@ - - - Windows Python is built in Microsoft Visual C++; using other compilers may or --may not work (though Borland seems to). The rest of this section is MSVC++ --specific. -+may not work. The rest of this section is MSVC++ specific. - - When creating DLLs in Windows, you must pass :file:`pythonXY.lib` to the linker. - To build two DLLs, spam and ni (which uses C functions found in spam), you could -@@ -134,4 +133,3 @@ - need, adding about 100K to your executable. To get rid of them, use the Project - Settings dialog, Link tab, to specify *ignore default libraries*. Add the - correct :file:`msvcrtxx.lib` to the list of libraries. -- -diff --git a/Doc/faq/library.rst b/Doc/faq/library.rst -index 7703d48a21..96525ddca3 100644 ---- a/Doc/faq/library.rst -+++ b/Doc/faq/library.rst -@@ -106,9 +106,6 @@ - operating systems that only have BSD curses, but there don't seem to be any - currently maintained OSes that fall into this category. - --For Windows: use `the consolelib module --`_. -- - - Is there an equivalent to C's onexit() in Python? - ------------------------------------------------- -diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst -index 3cf041e1a4..c2656382aa 100644 ---- a/Doc/faq/programming.rst -+++ b/Doc/faq/programming.rst -@@ -78,7 +78,7 @@ - Python binary to produce a single executable. - - One is to use the freeze tool, which is included in the Python source tree as --``Tools/freeze``. It converts Python byte code to C arrays; a C compiler you can -+``Tools/freeze``. It converts Python byte code to C arrays; with a C compiler you can - embed all your modules into a new program, which is then linked with the - standard Python modules. - -diff --git a/Doc/faq/windows.rst b/Doc/faq/windows.rst -index 0153a4f316..4f50b3f1b9 100644 ---- a/Doc/faq/windows.rst -+++ b/Doc/faq/windows.rst -@@ -26,8 +26,8 @@ - obvious; otherwise, you might need a little more guidance. - - Unless you use some sort of integrated development environment, you will end up --*typing* Windows commands into what is variously referred to as a "DOS window" --or "Command prompt window". Usually you can create such a window from your -+*typing* Windows commands into what is referred to as a -+"Command prompt window". Usually you can create such a window from your - search bar by searching for ``cmd``. You should be able to recognize - when you have started such a window because you will see a Windows "command - prompt", which usually looks like this: -@@ -186,9 +186,6 @@ - by the Windows ``GetProcAddress()`` routine. Macros can make using these - pointers transparent to any C code that calls routines in Python's C API. - -- Borland note: convert :file:`python{NN}.lib` to OMF format using Coff2Omf.exe -- first. -- - .. XXX what about static linking? - - 2. If you use SWIG, it is easy to create a Python "extension module" that will -@@ -279,4 +276,3 @@ - Use the :mod:`msvcrt` module. This is a standard Windows-specific extension module. - It defines a function ``kbhit()`` which checks whether a keyboard hit is - present, and ``getch()`` which gets one character without echoing it. -- -diff --git a/Doc/glossary.rst b/Doc/glossary.rst -index f759c3fb05..b08e395bab 100644 ---- a/Doc/glossary.rst -+++ b/Doc/glossary.rst -@@ -1072,7 +1072,16 @@ - as :keyword:`if`, :keyword:`while` or :keyword:`for`. - - text encoding -- A codec which encodes Unicode strings to bytes. -+ A string in Python is a sequence of Unicode code points (in range -+ ``U+0000``--``U+10FFFF``). To store or transfer a string, it needs to be -+ serialized as a sequence of bytes. -+ -+ Serializing a string into a sequence of bytes is known as "encoding", and -+ recreating the string from the sequence of bytes is known as "decoding". -+ -+ There are a variety of different text serialization -+ :ref:`codecs `, which are collectively referred to as -+ "text encodings". - - text file - A :term:`file object` able to read and write :class:`str` objects. -diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst -index 50041829b8..145febe093 100644 ---- a/Doc/howto/clinic.rst -+++ b/Doc/howto/clinic.rst -@@ -1288,7 +1288,7 @@ - /*[python end generated code: output=da39a3ee5e6b4b0d input=35521e4e733823c7]*/ - - This block adds a converter to Argument Clinic named ``ssize_t``. Parameters --declared as ``ssize_t`` will be declared as type ``Py_ssize_t``, and will -+declared as ``ssize_t`` will be declared as type :c:type:`Py_ssize_t`, and will - be parsed by the ``'O&'`` format unit, which will call the - ``ssize_t_converter`` converter function. ``ssize_t`` variables - automatically support default values. -diff --git a/Doc/howto/curses.rst b/Doc/howto/curses.rst -index cc4b4785b1..c0149ffff3 100644 ---- a/Doc/howto/curses.rst -+++ b/Doc/howto/curses.rst -@@ -55,11 +55,7 @@ - - The Windows version of Python doesn't include the :mod:`curses` - module. A ported version called `UniCurses --`_ is available. You could --also try `the Console module `_ --written by Fredrik Lundh, which doesn't --use the same API as curses but provides cursor-addressable text output --and full support for mouse and keyboard input. -+`_ is available. - - - The Python curses module -diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst -index c7f8bc8f17..695b9b31a7 100644 ---- a/Doc/howto/functional.rst -+++ b/Doc/howto/functional.rst -@@ -589,7 +589,7 @@ - In addition to :meth:`~generator.send`, there are two other methods on - generators: - --* :meth:`throw(type, value=None, traceback=None) ` is used to -+* :meth:`throw(value) ` is used to - raise an exception inside the generator; the exception is raised by the - ``yield`` expression where the generator's execution is paused. - -diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst -index d574c3736b..c4ebbd311e 100644 ---- a/Doc/howto/regex.rst -+++ b/Doc/howto/regex.rst -@@ -89,7 +89,7 @@ - characters. If you wanted to match only lowercase letters, your RE would be - ``[a-z]``. - --Metacharacters are not active inside classes. For example, ``[akm$]`` will -+Metacharacters (except ``\``) are not active inside classes. For example, ``[akm$]`` will - match any of the characters ``'a'``, ``'k'``, ``'m'``, or ``'$'``; ``'$'`` is - usually a metacharacter, but inside a character class it's stripped of its - special nature. -diff --git a/Doc/howto/sorting.rst b/Doc/howto/sorting.rst -index a8efe65353..be017cfba3 100644 ---- a/Doc/howto/sorting.rst -+++ b/Doc/howto/sorting.rst -@@ -287,7 +287,7 @@ - >>> standard_way - [('red', 1), ('red', 2), ('blue', 1), ('blue', 2)] - --* The sort routines are guaranteed to use :meth:`__lt__` when making comparisons -+* The sort routines use ``<`` when making comparisons - between two objects. So, it is easy to add a standard sort order to a class by - defining an :meth:`__lt__` method:: - -@@ -295,6 +295,9 @@ - >>> sorted(student_objects) - [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] - -+ However, note that ``<`` can fall back to using :meth:`__gt__` if -+ :meth:`__lt__` is not implemented (see :func:`object.__lt__`). -+ - * Key functions need not depend directly on the objects being sorted. A key - function can also access external resources. For instance, if the student grades - are stored in a dictionary, they can be used to sort a separate list of student -diff --git a/Doc/includes/custom.c b/Doc/includes/custom.c -index f361baf830..26ca754964 100644 ---- a/Doc/includes/custom.c -+++ b/Doc/includes/custom.c -@@ -9,7 +9,7 @@ - static PyTypeObject CustomType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "custom.Custom", -- .tp_doc = "Custom objects", -+ .tp_doc = PyDoc_STR("Custom objects"), - .tp_basicsize = sizeof(CustomObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT, -diff --git a/Doc/includes/custom2.c b/Doc/includes/custom2.c -index 5bacab7a2a..2a3c59f8f0 100644 ---- a/Doc/includes/custom2.c -+++ b/Doc/includes/custom2.c -@@ -98,7 +98,7 @@ - static PyTypeObject CustomType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "custom2.Custom", -- .tp_doc = "Custom objects", -+ .tp_doc = PyDoc_STR("Custom objects"), - .tp_basicsize = sizeof(CustomObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, -diff --git a/Doc/includes/custom3.c b/Doc/includes/custom3.c -index 2b7a99ecf9..5a47530f0a 100644 ---- a/Doc/includes/custom3.c -+++ b/Doc/includes/custom3.c -@@ -148,7 +148,7 @@ - static PyTypeObject CustomType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "custom3.Custom", -- .tp_doc = "Custom objects", -+ .tp_doc = PyDoc_STR("Custom objects"), - .tp_basicsize = sizeof(CustomObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, -diff --git a/Doc/includes/custom4.c b/Doc/includes/custom4.c -index 584992fc5f..c7ee555784 100644 ---- a/Doc/includes/custom4.c -+++ b/Doc/includes/custom4.c -@@ -160,7 +160,7 @@ - static PyTypeObject CustomType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "custom4.Custom", -- .tp_doc = "Custom objects", -+ .tp_doc = PyDoc_STR("Custom objects"), - .tp_basicsize = sizeof(CustomObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, -diff --git a/Doc/includes/sublist.c b/Doc/includes/sublist.c -index b2c26e73eb..b36dadf07e 100644 ---- a/Doc/includes/sublist.c -+++ b/Doc/includes/sublist.c -@@ -31,7 +31,7 @@ - static PyTypeObject SubListType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "sublist.SubList", -- .tp_doc = "SubList objects", -+ .tp_doc = PyDoc_STR("SubList objects"), - .tp_basicsize = sizeof(SubListObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, -diff --git a/Doc/library/__future__.rst b/Doc/library/__future__.rst -index 41399942d3..8bd23daee7 100644 ---- a/Doc/library/__future__.rst -+++ b/Doc/library/__future__.rst -@@ -90,12 +90,20 @@ - | generator_stop | 3.5.0b1 | 3.7 | :pep:`479`: | - | | | | *StopIteration handling inside generators* | - +------------------+-------------+--------------+---------------------------------------------+ --| annotations | 3.7.0b1 | 3.10 | :pep:`563`: | -+| annotations | 3.7.0b1 | TBD [1]_ | :pep:`563`: | - | | | | *Postponed evaluation of annotations* | - +------------------+-------------+--------------+---------------------------------------------+ - - .. XXX Adding a new entry? Remember to update simple_stmts.rst, too. - -+.. [1] -+ ``from __future__ import annotations`` was previously scheduled to -+ become mandatory in Python 3.10, but the Python Steering Council -+ twice decided to delay the change -+ (`announcement for Python 3.10 `__; -+ `announcement for Python 3.11 `__). -+ No final decision has been made yet. See also :pep:`563` and :pep:`649`. -+ - - .. seealso:: - -diff --git a/Doc/library/aifc.rst b/Doc/library/aifc.rst -index edb4bf86e5..fa27785757 100644 ---- a/Doc/library/aifc.rst -+++ b/Doc/library/aifc.rst -@@ -14,7 +14,8 @@ - - - .. deprecated:: 3.11 -- The :mod:`aifc` module is deprecated (see :pep:`594` for details). -+ The :mod:`aifc` module is deprecated -+ (see :pep:`PEP 594 <594#aifc>` for details). - - -------------- - -diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst -index fa731fec3a..8989d1cef1 100644 ---- a/Doc/library/argparse.rst -+++ b/Doc/library/argparse.rst -@@ -501,7 +501,7 @@ - fromfile_prefix_chars - ^^^^^^^^^^^^^^^^^^^^^ - --Sometimes, for example when dealing with a particularly long argument lists, it -+Sometimes, for example when dealing with a particularly long argument list, it - may make sense to keep the list of arguments in a file rather than typing it out - at the command line. If the ``fromfile_prefix_chars=`` argument is given to the - :class:`ArgumentParser` constructor, then arguments that start with any of the -diff --git a/Doc/library/asynchat.rst b/Doc/library/asynchat.rst -index 4354444a1d..7cc9d99779 100644 ---- a/Doc/library/asynchat.rst -+++ b/Doc/library/asynchat.rst -@@ -11,7 +11,8 @@ - **Source code:** :source:`Lib/asynchat.py` - - .. deprecated:: 3.6 -- :mod:`asynchat` will be removed in Python 3.12 (:pep:`594`). -+ :mod:`asynchat` will be removed in Python 3.12 -+ (see :pep:`PEP 594 <594#asynchat>` for details). - Please use :mod:`asyncio` instead. - - -------------- -diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst -index 6ba24249f2..fbe68f78a2 100644 ---- a/Doc/library/asyncio-subprocess.rst -+++ b/Doc/library/asyncio-subprocess.rst -@@ -278,7 +278,7 @@ - Use the :meth:`communicate` method rather than - :attr:`process.stdin.write() `, - :attr:`await process.stdout.read() ` or -- :attr:`await process.stderr.read `. -+ :attr:`await process.stderr.read() `. - This avoids deadlocks due to streams pausing reading or writing - and blocking the child process. - -diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst -index 38b12e44b6..7c12e2eda1 100644 ---- a/Doc/library/asyncio-task.rst -+++ b/Doc/library/asyncio-task.rst -@@ -20,7 +20,7 @@ - - :term:`Coroutines ` declared with the async/await syntax is the - preferred way of writing asyncio applications. For example, the following --snippet of code (requires Python 3.7+) prints "hello", waits 1 second, -+snippet of code prints "hello", waits 1 second, - and then prints "world":: - - >>> import asyncio -@@ -259,21 +259,6 @@ - :exc:`RuntimeError` is raised if there is no running loop in - current thread. - -- This function has been **added in Python 3.7**. Prior to -- Python 3.7, the low-level :func:`asyncio.ensure_future` function -- can be used instead:: -- -- async def coro(): -- ... -- -- # In Python 3.7+ -- task = asyncio.create_task(coro()) -- ... -- -- # This works in all Python versions but is less readable -- task = asyncio.ensure_future(coro()) -- ... -- - .. important:: - - Save a reference to the result of this function, to avoid -diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst -index 94a853259d..a642939438 100644 ---- a/Doc/library/asyncio.rst -+++ b/Doc/library/asyncio.rst -@@ -17,7 +17,6 @@ - await asyncio.sleep(1) - print('... World!') - -- # Python 3.7+ - asyncio.run(main()) - - asyncio is a library to write **concurrent** code using -diff --git a/Doc/library/asyncore.rst b/Doc/library/asyncore.rst -index e481e13db7..a732fd7ba4 100644 ---- a/Doc/library/asyncore.rst -+++ b/Doc/library/asyncore.rst -@@ -14,7 +14,8 @@ - **Source code:** :source:`Lib/asyncore.py` - - .. deprecated:: 3.6 -- :mod:`asyncore` will be removed in Python 3.12 (:pep:`594`). -+ :mod:`asyncore` will be removed in Python 3.12 -+ (see :pep:`PEP 594 <594#asyncore>` for details). - Please use :mod:`asyncio` instead. - - -------------- -diff --git a/Doc/library/audioop.rst b/Doc/library/audioop.rst -index eae206084f..649c99e796 100644 ---- a/Doc/library/audioop.rst -+++ b/Doc/library/audioop.rst -@@ -6,7 +6,8 @@ - :deprecated: - - .. deprecated:: 3.11 -- The :mod:`audioop` module is deprecated (see :pep:`594` for details). -+ The :mod:`audioop` module is deprecated -+ (see :pep:`PEP 594 <594#audioop>` for details). - - -------------- - -diff --git a/Doc/library/cgi.rst b/Doc/library/cgi.rst -index 80c4d80331..5744ee548c 100644 ---- a/Doc/library/cgi.rst -+++ b/Doc/library/cgi.rst -@@ -16,7 +16,8 @@ - single: Common Gateway Interface - - .. deprecated:: 3.11 -- The :mod:`cgi` module is deprecated (see :pep:`594` for details). -+ The :mod:`cgi` module is deprecated -+ (see :pep:`PEP 594 <594#cgi>` for details and alternatives). - - -------------- - -diff --git a/Doc/library/cgitb.rst b/Doc/library/cgitb.rst -index 349414610b..3b0b106aba 100644 ---- a/Doc/library/cgitb.rst -+++ b/Doc/library/cgitb.rst -@@ -17,7 +17,8 @@ - single: tracebacks; in CGI scripts - - .. deprecated:: 3.11 -- The :mod:`cgitb` module is deprecated (see :pep:`594` for details). -+ The :mod:`cgitb` module is deprecated -+ (see :pep:`PEP 594 <594#cgitb>` for details). - - -------------- - -diff --git a/Doc/library/chunk.rst b/Doc/library/chunk.rst -index 7999420f53..5a84c8904f 100644 ---- a/Doc/library/chunk.rst -+++ b/Doc/library/chunk.rst -@@ -18,7 +18,8 @@ - single: RMFF - - .. deprecated:: 3.11 -- The :mod:`chunk` module is deprecated (see :pep:`594` for details). -+ The :mod:`chunk` module is deprecated -+ (see :pep:`PEP 594 <594#chunk>` for details). - - -------------- - -diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst -index 3386208d5e..d4ea2b34b6 100644 ---- a/Doc/library/codecs.rst -+++ b/Doc/library/codecs.rst -@@ -23,11 +23,11 @@ - This module defines base classes for standard Python codecs (encoders and - decoders) and provides access to the internal Python codec registry, which - manages the codec and error handling lookup process. Most standard codecs --are :term:`text encodings `, which encode text to bytes, --but there are also codecs provided that encode text to text, and bytes to --bytes. Custom codecs may encode and decode between arbitrary types, but some --module features are restricted to use specifically with --:term:`text encodings `, or with codecs that encode to -+are :term:`text encodings `, which encode text to bytes (and -+decode bytes to text), but there are also codecs provided that encode text to -+text, and bytes to bytes. Custom codecs may encode and decode between arbitrary -+types, but some module features are restricted to be used specifically with -+:term:`text encodings ` or with codecs that encode to - :class:`bytes`. - - The module defines the following functions for encoding and decoding with -@@ -294,58 +294,56 @@ - Error Handlers - ^^^^^^^^^^^^^^ - --To simplify and standardize error handling, --codecs may implement different error handling schemes by --accepting the *errors* string argument. The following string values are --defined and implemented by all standard Python codecs: -+To simplify and standardize error handling, codecs may implement different -+error handling schemes by accepting the *errors* string argument: - --.. tabularcolumns:: |l|L| -- --+-------------------------+-----------------------------------------------+ --| Value | Meaning | --+=========================+===============================================+ --| ``'strict'`` | Raise :exc:`UnicodeError` (or a subclass); | --| | this is the default. Implemented in | --| | :func:`strict_errors`. | --+-------------------------+-----------------------------------------------+ --| ``'ignore'`` | Ignore the malformed data and continue | --| | without further notice. Implemented in | --| | :func:`ignore_errors`. | --+-------------------------+-----------------------------------------------+ -- --The following error handlers are only applicable to --:term:`text encodings `: -+ >>> 'German ß, ♬'.encode(encoding='ascii', errors='backslashreplace') -+ b'German \\xdf, \\u266c' -+ >>> 'German ß, ♬'.encode(encoding='ascii', errors='xmlcharrefreplace') -+ b'German ß, ♬' - - .. index:: -+ pair: strict; error handler's name -+ pair: ignore; error handler's name -+ pair: replace; error handler's name -+ pair: backslashreplace; error handler's name -+ pair: surrogateescape; error handler's name - single: ? (question mark); replacement character - single: \ (backslash); escape sequence - single: \x; escape sequence - single: \u; escape sequence - single: \U; escape sequence -- single: \N; escape sequence -+ -+The following error handlers can be used with all Python -+:ref:`standard-encodings` codecs: -+ -+.. tabularcolumns:: |l|L| - - +-------------------------+-----------------------------------------------+ - | Value | Meaning | - +=========================+===============================================+ --| ``'replace'`` | Replace with a suitable replacement | --| | marker; Python will use the official | --| | ``U+FFFD`` REPLACEMENT CHARACTER for the | --| | built-in codecs on decoding, and '?' on | --| | encoding. Implemented in | --| | :func:`replace_errors`. | -+| ``'strict'`` | Raise :exc:`UnicodeError` (or a subclass), | -+| | this is the default. Implemented in | -+| | :func:`strict_errors`. | - +-------------------------+-----------------------------------------------+ --| ``'xmlcharrefreplace'`` | Replace with the appropriate XML character | --| | reference (only for encoding). Implemented | --| | in :func:`xmlcharrefreplace_errors`. | -+| ``'ignore'`` | Ignore the malformed data and continue without| -+| | further notice. Implemented in | -+| | :func:`ignore_errors`. | -++-------------------------+-----------------------------------------------+ -+| ``'replace'`` | Replace with a replacement marker. On | -+| | encoding, use ``?`` (ASCII character). On | -+| | decoding, use ``�`` (U+FFFD, the official | -+| | REPLACEMENT CHARACTER). Implemented in | -+| | :func:`replace_errors`. | - +-------------------------+-----------------------------------------------+ - | ``'backslashreplace'`` | Replace with backslashed escape sequences. | -+| | On encoding, use hexadecimal form of Unicode | -+| | code point with formats ``\xhh`` ``\uxxxx`` | -+| | ``\Uxxxxxxxx``. On decoding, use hexadecimal | -+| | form of byte value with format ``\xhh``. | - | | Implemented in | - | | :func:`backslashreplace_errors`. | - +-------------------------+-----------------------------------------------+ --| ``'namereplace'`` | Replace with ``\N{...}`` escape sequences | --| | (only for encoding). Implemented in | --| | :func:`namereplace_errors`. | --+-------------------------+-----------------------------------------------+ - | ``'surrogateescape'`` | On decoding, replace byte with individual | - | | surrogate code ranging from ``U+DC80`` to | - | | ``U+DCFF``. This code will then be turned | -@@ -355,27 +353,55 @@ - | | more.) | - +-------------------------+-----------------------------------------------+ - -+.. index:: -+ pair: xmlcharrefreplace; error handler's name -+ pair: namereplace; error handler's name -+ single: \N; escape sequence -+ -+The following error handlers are only applicable to encoding (within -+:term:`text encodings `): -+ -++-------------------------+-----------------------------------------------+ -+| Value | Meaning | -++=========================+===============================================+ -+| ``'xmlcharrefreplace'`` | Replace with XML/HTML numeric character | -+| | reference, which is a decimal form of Unicode | -+| | code point with format ``&#num;`` Implemented | -+| | in :func:`xmlcharrefreplace_errors`. | -++-------------------------+-----------------------------------------------+ -+| ``'namereplace'`` | Replace with ``\N{...}`` escape sequences, | -+| | what appears in the braces is the Name | -+| | property from Unicode Character Database. | -+| | Implemented in :func:`namereplace_errors`. | -++-------------------------+-----------------------------------------------+ -+ -+.. index:: -+ pair: surrogatepass; error handler's name -+ - In addition, the following error handler is specific to the given codecs: - - +-------------------+------------------------+-------------------------------------------+ - | Value | Codecs | Meaning | - +===================+========================+===========================================+ --|``'surrogatepass'``| utf-8, utf-16, utf-32, | Allow encoding and decoding of surrogate | --| | utf-16-be, utf-16-le, | codes. These codecs normally treat the | --| | utf-32-be, utf-32-le | presence of surrogates as an error. | -+|``'surrogatepass'``| utf-8, utf-16, utf-32, | Allow encoding and decoding surrogate code| -+| | utf-16-be, utf-16-le, | point (``U+D800`` - ``U+DFFF``) as normal | -+| | utf-32-be, utf-32-le | code point. Otherwise these codecs treat | -+| | | the presence of surrogate code point in | -+| | | :class:`str` as an error. | - +-------------------+------------------------+-------------------------------------------+ - - .. versionadded:: 3.1 - The ``'surrogateescape'`` and ``'surrogatepass'`` error handlers. - - .. versionchanged:: 3.4 -- The ``'surrogatepass'`` error handlers now works with utf-16\* and utf-32\* codecs. -+ The ``'surrogatepass'`` error handler now works with utf-16\* and utf-32\* -+ codecs. - - .. versionadded:: 3.5 - The ``'namereplace'`` error handler. - - .. versionchanged:: 3.5 -- The ``'backslashreplace'`` error handlers now works with decoding and -+ The ``'backslashreplace'`` error handler now works with decoding and - translating. - - The set of allowed values can be extended by registering a new named error -@@ -418,42 +444,59 @@ - - .. function:: strict_errors(exception) - -- Implements the ``'strict'`` error handling: each encoding or -- decoding error raises a :exc:`UnicodeError`. -+ Implements the ``'strict'`` error handling. - -+ Each encoding or decoding error raises a :exc:`UnicodeError`. - --.. function:: replace_errors(exception) - -- Implements the ``'replace'`` error handling (for :term:`text encodings -- ` only): substitutes ``'?'`` for encoding errors -- (to be encoded by the codec), and ``'\ufffd'`` (the Unicode replacement -- character) for decoding errors. -+.. function:: ignore_errors(exception) - -+ Implements the ``'ignore'`` error handling. - --.. function:: ignore_errors(exception) -+ Malformed data is ignored; encoding or decoding is continued without -+ further notice. - -- Implements the ``'ignore'`` error handling: malformed data is ignored and -- encoding or decoding is continued without further notice. - -+.. function:: replace_errors(exception) - --.. function:: xmlcharrefreplace_errors(exception) -+ Implements the ``'replace'`` error handling. - -- Implements the ``'xmlcharrefreplace'`` error handling (for encoding with -- :term:`text encodings ` only): the -- unencodable character is replaced by an appropriate XML character reference. -+ Substitutes ``?`` (ASCII character) for encoding errors or ``�`` (U+FFFD, -+ the official REPLACEMENT CHARACTER) for decoding errors. - - - .. function:: backslashreplace_errors(exception) - -- Implements the ``'backslashreplace'`` error handling (for -- :term:`text encodings ` only): malformed data is -- replaced by a backslashed escape sequence. -+ Implements the ``'backslashreplace'`` error handling. -+ -+ Malformed data is replaced by a backslashed escape sequence. -+ On encoding, use the hexadecimal form of Unicode code point with formats -+ ``\xhh`` ``\uxxxx`` ``\Uxxxxxxxx``. On decoding, use the hexadecimal form of -+ byte value with format ``\xhh``. -+ -+ .. versionchanged:: 3.5 -+ Works with decoding and translating. -+ -+ -+.. function:: xmlcharrefreplace_errors(exception) -+ -+ Implements the ``'xmlcharrefreplace'`` error handling (for encoding within -+ :term:`text encoding` only). -+ -+ The unencodable character is replaced by an appropriate XML/HTML numeric -+ character reference, which is a decimal form of Unicode code point with -+ format ``&#num;`` . -+ - - .. function:: namereplace_errors(exception) - -- Implements the ``'namereplace'`` error handling (for encoding with -- :term:`text encodings ` only): the -- unencodable character is replaced by a ``\N{...}`` escape sequence. -+ Implements the ``'namereplace'`` error handling (for encoding within -+ :term:`text encoding` only). -+ -+ The unencodable character is replaced by a ``\N{...}`` escape sequence. The -+ set of characters that appear in the braces is the Name property from -+ Unicode Character Database. For example, the German lowercase letter ``'ß'`` -+ will be converted to byte sequence ``\N{LATIN SMALL LETTER SHARP S}`` . - - .. versionadded:: 3.5 - -@@ -467,7 +510,7 @@ - function interfaces of the stateless encoder and decoder: - - --.. method:: Codec.encode(input[, errors]) -+.. method:: Codec.encode(input, errors='strict') - - Encodes the object *input* and returns a tuple (output object, length consumed). - For instance, :term:`text encoding` converts -@@ -485,7 +528,7 @@ - of the output object type in this situation. - - --.. method:: Codec.decode(input[, errors]) -+.. method:: Codec.decode(input, errors='strict') - - Decodes the object *input* and returns a tuple (output object, length - consumed). For instance, for a :term:`text encoding`, decoding converts -@@ -552,7 +595,7 @@ - object. - - -- .. method:: encode(object[, final]) -+ .. method:: encode(object, final=False) - - Encodes *object* (taking the current state of the encoder into account) - and returns the resulting encoded object. If this is the last call to -@@ -609,7 +652,7 @@ - object. - - -- .. method:: decode(object[, final]) -+ .. method:: decode(object, final=False) - - Decodes *object* (taking the current state of the decoder into account) - and returns the resulting decoded object. If this is the last call to -@@ -743,7 +786,7 @@ - :func:`register_error`. - - -- .. method:: read([size[, chars, [firstline]]]) -+ .. method:: read(size=-1, chars=-1, firstline=False) - - Decodes data from the stream and returns the resulting object. - -@@ -769,7 +812,7 @@ - available on the stream, these should be read too. - - -- .. method:: readline([size[, keepends]]) -+ .. method:: readline(size=None, keepends=True) - - Read one line from the input stream and return the decoded data. - -@@ -780,7 +823,7 @@ - returned. - - -- .. method:: readlines([sizehint[, keepends]]) -+ .. method:: readlines(sizehint=None, keepends=True) - - Read all lines available on the input stream and return them as a list of - lines. -@@ -871,7 +914,7 @@ - --------------------- - - Strings are stored internally as sequences of code points in --range ``0x0``--``0x10FFFF``. (See :pep:`393` for -+range ``U+0000``--``U+10FFFF``. (See :pep:`393` for - more details about the implementation.) - Once a string object is used outside of CPU and memory, endianness - and how these arrays are stored as bytes become an issue. As with other -@@ -909,7 +952,7 @@ - ``U+FEFF``. This character can be prepended to every ``UTF-16`` or ``UTF-32`` - byte sequence. The byte swapped version of this character (``0xFFFE``) is an - illegal character that may not appear in a Unicode text. So when the --first character in an ``UTF-16`` or ``UTF-32`` byte sequence -+first character in a ``UTF-16`` or ``UTF-32`` byte sequence - appears to be a ``U+FFFE`` the bytes have to be swapped on decoding. - Unfortunately the character ``U+FEFF`` had a second purpose as - a ``ZERO WIDTH NO-BREAK SPACE``: a character that has no width and doesn't allow -@@ -952,7 +995,7 @@ - decode any random byte sequence. However that's not possible with UTF-8, as - UTF-8 byte sequences have a structure that doesn't allow arbitrary byte - sequences. To increase the reliability with which a UTF-8 encoding can be --detected, Microsoft invented a variant of UTF-8 (that Python 2.5 calls -+detected, Microsoft invented a variant of UTF-8 (that Python calls - ``"utf-8-sig"``) for its Notepad program: Before any of the Unicode characters - is written to the file, a UTF-8 encoded BOM (which looks like this as a byte - sequence: ``0xef``, ``0xbb``, ``0xbf``) is written. As it's rather improbable -@@ -1420,7 +1463,7 @@ - and :mod:`stringprep`. - - If you need the IDNA 2008 standard from :rfc:`5891` and :rfc:`5895`, use the --third-party `idna module _`. -+third-party `idna module `_. - - These RFCs together define a protocol to support non-ASCII characters in domain - names. A domain name containing non-ASCII characters (such as -diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst -index 66a0f17a92..d471c8ce52 100644 ---- a/Doc/library/contextlib.rst -+++ b/Doc/library/contextlib.rst -@@ -368,6 +368,9 @@ - # the with statement, even if attempts to open files later - # in the list raise an exception - -+ The :meth:`__enter__` method returns the :class:`ExitStack` instance, and -+ performs no additional operations. -+ - Each instance maintains a stack of registered callbacks that are called in - reverse order when the instance is closed (either explicitly or implicitly - at the end of a :keyword:`with` statement). Note that callbacks are *not* -diff --git a/Doc/library/crypt.rst b/Doc/library/crypt.rst -index 73df87ca0d..e795f10f50 100644 ---- a/Doc/library/crypt.rst -+++ b/Doc/library/crypt.rst -@@ -17,7 +17,9 @@ - pair: cipher; DES - - .. deprecated:: 3.11 -- The :mod:`crypt` module is deprecated (see :pep:`594` for details). -+ The :mod:`crypt` module is deprecated -+ (see :pep:`PEP 594 <594#crypt>` for details and alternatives). -+ The :mod:`hashlib` module is a potential replacement for certain use cases. - - -------------- - -@@ -96,8 +98,7 @@ - :func:`mksalt`, one of the ``crypt.METHOD_*`` values (though not all - may be available on all platforms), or a full encrypted password - including salt, as returned by this function. If *salt* is not -- provided, the strongest method will be used (as returned by -- :func:`methods`). -+ provided, the strongest method available in :attr:`methods` will be used. - - Checking a password is usually done by passing the plain-text password - as *word* and the full results of a previous :func:`crypt` call, -@@ -125,8 +126,8 @@ - .. function:: mksalt(method=None, *, rounds=None) - - Return a randomly generated salt of the specified method. If no -- *method* is given, the strongest method available as returned by -- :func:`methods` is used. -+ *method* is given, the strongest method available in :attr:`methods` is -+ used. - - The return value is a string suitable for passing as the *salt* argument - to :func:`crypt`. -diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst -index c10e54f153..19ce19b1d7 100644 ---- a/Doc/library/ctypes.rst -+++ b/Doc/library/ctypes.rst -@@ -1361,10 +1361,6 @@ - functions in these libraries use the ``stdcall`` calling convention, and are - assumed to return :c:type:`int` by default. - -- On Windows CE only the standard calling convention is used, for convenience the -- :class:`WinDLL` and :class:`OleDLL` use the standard calling convention on this -- platform. -- - The Python :term:`global interpreter lock` is released before calling any - function exported by these libraries, and reacquired afterwards. - -@@ -1665,8 +1661,7 @@ - .. function:: WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) - - Windows only: The returned function prototype creates functions that use the -- ``stdcall`` calling convention, except on Windows CE where -- :func:`WINFUNCTYPE` is the same as :func:`CFUNCTYPE`. The function will -+ ``stdcall`` calling convention. The function will - release the GIL during the call. *use_errno* and *use_last_error* have the - same meaning as above. - -@@ -2513,7 +2508,7 @@ - Abstract base class for arrays. - - The recommended way to create concrete array types is by multiplying any -- :mod:`ctypes` data type with a positive integer. Alternatively, you can subclass -+ :mod:`ctypes` data type with a non-negative integer. Alternatively, you can subclass - this type and define :attr:`_length_` and :attr:`_type_` class variables. - Array elements can be read and written using standard - subscript and slice accesses; for slice reads, the resulting object is -diff --git a/Doc/library/email.charset.rst b/Doc/library/email.charset.rst -index 38fda23bd8..adbe6c1c7d 100644 ---- a/Doc/library/email.charset.rst -+++ b/Doc/library/email.charset.rst -@@ -58,9 +58,9 @@ - .. attribute:: header_encoding - - If the character set must be encoded before it can be used in an email -- header, this attribute will be set to ``Charset.QP`` (for -- quoted-printable), ``Charset.BASE64`` (for base64 encoding), or -- ``Charset.SHORTEST`` for the shortest of QP or BASE64 encoding. Otherwise, -+ header, this attribute will be set to ``charset.QP`` (for -+ quoted-printable), ``charset.BASE64`` (for base64 encoding), or -+ ``charset.SHORTEST`` for the shortest of QP or BASE64 encoding. Otherwise, - it will be ``None``. - - -@@ -68,7 +68,7 @@ - - Same as *header_encoding*, but describes the encoding for the mail - message's body, which indeed may be different than the header encoding. -- ``Charset.SHORTEST`` is not allowed for *body_encoding*. -+ ``charset.SHORTEST`` is not allowed for *body_encoding*. - - - .. attribute:: output_charset -@@ -175,9 +175,9 @@ - *charset* is the input character set, and must be the canonical name of a - character set. - -- Optional *header_enc* and *body_enc* is either ``Charset.QP`` for -- quoted-printable, ``Charset.BASE64`` for base64 encoding, -- ``Charset.SHORTEST`` for the shortest of quoted-printable or base64 encoding, -+ Optional *header_enc* and *body_enc* is either ``charset.QP`` for -+ quoted-printable, ``charset.BASE64`` for base64 encoding, -+ ``charset.SHORTEST`` for the shortest of quoted-printable or base64 encoding, - or ``None`` for no encoding. ``SHORTEST`` is only valid for - *header_enc*. The default is ``None`` for no encoding. - -diff --git a/Doc/library/errno.rst b/Doc/library/errno.rst -index 1cbd51c582..c87da091bf 100644 ---- a/Doc/library/errno.rst -+++ b/Doc/library/errno.rst -@@ -8,7 +8,7 @@ - - This module makes available standard ``errno`` system symbols. The value of each - symbol is the corresponding integer value. The names and descriptions are --borrowed from :file:`linux/include/errno.h`, which should be pretty -+borrowed from :file:`linux/include/errno.h`, which should be - all-inclusive. - - -@@ -27,25 +27,26 @@ - - .. data:: EPERM - -- Operation not permitted -+ Operation not permitted. This error is mapped to the exception -+ :exc:`PermissionError`. - - - .. data:: ENOENT - -- No such file or directory -+ No such file or directory. This error is mapped to the exception -+ :exc:`FileNotFoundError`. - - - .. data:: ESRCH - -- No such process -+ No such process. This error is mapped to the exception -+ :exc:`ProcessLookupError`. - - - .. data:: EINTR - -- Interrupted system call. -- -- .. seealso:: -- This error is mapped to the exception :exc:`InterruptedError`. -+ Interrupted system call. This error is mapped to the exception -+ :exc:`InterruptedError`. - - - .. data:: EIO -@@ -75,12 +76,13 @@ - - .. data:: ECHILD - -- No child processes -+ No child processes. This error is mapped to the exception -+ :exc:`ChildProcessError`. - - - .. data:: EAGAIN - -- Try again -+ Try again. This error is mapped to the exception :exc:`BlockingIOError`. - - - .. data:: ENOMEM -@@ -90,7 +92,8 @@ - - .. data:: EACCES - -- Permission denied -+ Permission denied. This error is mapped to the exception -+ :exc:`PermissionError`. - - - .. data:: EFAULT -@@ -110,7 +113,8 @@ - - .. data:: EEXIST - -- File exists -+ File exists. This error is mapped to the exception -+ :exc:`FileExistsError`. - - - .. data:: EXDEV -@@ -125,12 +129,14 @@ - - .. data:: ENOTDIR - -- Not a directory -+ Not a directory. This error is mapped to the exception -+ :exc:`NotADirectoryError`. - - - .. data:: EISDIR - -- Is a directory -+ Is a directory. This error is mapped to the exception -+ :exc:`IsADirectoryError`. - - - .. data:: EINVAL -@@ -185,7 +191,8 @@ - - .. data:: EPIPE - -- Broken pipe -+ Broken pipe. This error is mapped to the exception -+ :exc:`BrokenPipeError`. - - - .. data:: EDOM -@@ -230,7 +237,8 @@ - - .. data:: EWOULDBLOCK - -- Operation would block -+ Operation would block. This error is mapped to the exception -+ :exc:`BlockingIOError`. - - - .. data:: ENOMSG -@@ -540,12 +548,14 @@ - - .. data:: ECONNABORTED - -- Software caused connection abort -+ Software caused connection abort. This error is mapped to the -+ exception :exc:`ConnectionAbortedError`. - - - .. data:: ECONNRESET - -- Connection reset by peer -+ Connection reset by peer. This error is mapped to the exception -+ :exc:`ConnectionResetError`. - - - .. data:: ENOBUFS -@@ -565,7 +575,8 @@ - - .. data:: ESHUTDOWN - -- Cannot send after transport endpoint shutdown -+ Cannot send after transport endpoint shutdown. This error is mapped -+ to the exception :exc:`BrokenPipeError`. - - - .. data:: ETOOMANYREFS -@@ -575,12 +586,14 @@ - - .. data:: ETIMEDOUT - -- Connection timed out -+ Connection timed out. This error is mapped to the exception -+ :exc:`TimeoutError`. - - - .. data:: ECONNREFUSED - -- Connection refused -+ Connection refused. This error is mapped to the exception -+ :exc:`ConnectionRefusedError`. - - - .. data:: EHOSTDOWN -@@ -595,12 +608,14 @@ - - .. data:: EALREADY - -- Operation already in progress -+ Operation already in progress. This error is mapped to the -+ exception :exc:`BlockingIOError`. - - - .. data:: EINPROGRESS - -- Operation now in progress -+ Operation now in progress. This error is mapped to the exception -+ :exc:`BlockingIOError`. - - - .. data:: ESTALE -diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst -index c494567947..b6a5df8775 100644 ---- a/Doc/library/exceptions.rst -+++ b/Doc/library/exceptions.rst -@@ -234,6 +234,15 @@ - accidentally caught by code that catches :exc:`Exception` and thus prevent - the interpreter from exiting. - -+ .. note:: -+ -+ Catching a :exc:`KeyboardInterrupt` requires special consideration. -+ Because it can be raised at unpredictable points, it may, in some -+ circumstances, leave the running program in an inconsistent state. It is -+ generally best to allow :exc:`KeyboardInterrupt` to end the program as -+ quickly as possible or avoid raising it entirely. (See -+ :ref:`handlers-and-exceptions`.) -+ - - .. exception:: MemoryError - -@@ -604,8 +613,8 @@ - - Raised when an operation would block on an object (e.g. socket) set - for non-blocking operation. -- Corresponds to :c:data:`errno` ``EAGAIN``, ``EALREADY``, -- ``EWOULDBLOCK`` and ``EINPROGRESS``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.EAGAIN`, :py:data:`~errno.EALREADY`, -+ :py:data:`~errno.EWOULDBLOCK` and :py:data:`~errno.EINPROGRESS`. - - In addition to those of :exc:`OSError`, :exc:`BlockingIOError` can have - one more attribute: -@@ -619,7 +628,7 @@ - .. exception:: ChildProcessError - - Raised when an operation on a child process failed. -- Corresponds to :c:data:`errno` ``ECHILD``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.ECHILD`. - - .. exception:: ConnectionError - -@@ -633,35 +642,35 @@ - A subclass of :exc:`ConnectionError`, raised when trying to write on a - pipe while the other end has been closed, or trying to write on a socket - which has been shutdown for writing. -- Corresponds to :c:data:`errno` ``EPIPE`` and ``ESHUTDOWN``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.EPIPE` and :py:data:`~errno.ESHUTDOWN`. - - .. exception:: ConnectionAbortedError - - A subclass of :exc:`ConnectionError`, raised when a connection attempt - is aborted by the peer. -- Corresponds to :c:data:`errno` ``ECONNABORTED``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.ECONNABORTED`. - - .. exception:: ConnectionRefusedError - - A subclass of :exc:`ConnectionError`, raised when a connection attempt - is refused by the peer. -- Corresponds to :c:data:`errno` ``ECONNREFUSED``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.ECONNREFUSED`. - - .. exception:: ConnectionResetError - - A subclass of :exc:`ConnectionError`, raised when a connection is - reset by the peer. -- Corresponds to :c:data:`errno` ``ECONNRESET``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.ECONNRESET`. - - .. exception:: FileExistsError - - Raised when trying to create a file or directory which already exists. -- Corresponds to :c:data:`errno` ``EEXIST``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.EEXIST`. - - .. exception:: FileNotFoundError - - Raised when a file or directory is requested but doesn't exist. -- Corresponds to :c:data:`errno` ``ENOENT``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.ENOENT`. - - .. exception:: InterruptedError - -@@ -677,7 +686,7 @@ - - Raised when a file operation (such as :func:`os.remove`) is requested - on a directory. -- Corresponds to :c:data:`errno` ``EISDIR``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.EISDIR`. - - .. exception:: NotADirectoryError - -@@ -685,23 +694,23 @@ - something which is not a directory. On most POSIX platforms, it may also be - raised if an operation attempts to open or traverse a non-directory file as if - it were a directory. -- Corresponds to :c:data:`errno` ``ENOTDIR``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.ENOTDIR`. - - .. exception:: PermissionError - - Raised when trying to run an operation without the adequate access - rights - for example filesystem permissions. -- Corresponds to :c:data:`errno` ``EACCES`` and ``EPERM``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.EACCES` and :py:data:`~errno.EPERM`. - - .. exception:: ProcessLookupError - - Raised when a given process doesn't exist. -- Corresponds to :c:data:`errno` ``ESRCH``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.ESRCH`. - - .. exception:: TimeoutError - - Raised when a system function timed out at the system level. -- Corresponds to :c:data:`errno` ``ETIMEDOUT``. -+ Corresponds to :c:data:`errno` :py:data:`~errno.ETIMEDOUT`. - - .. versionadded:: 3.3 - All the above :exc:`OSError` subclasses were added. -diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst -index 07a15d2721..f1c071ab99 100644 ---- a/Doc/library/fcntl.rst -+++ b/Doc/library/fcntl.rst -@@ -37,7 +37,8 @@ - On macOS, the fcntl module exposes the ``F_GETPATH`` constant, which obtains - the path of a file from a file descriptor. - On Linux(>=3.15), the fcntl module exposes the ``F_OFD_GETLK``, ``F_OFD_SETLK`` -- and ``F_OFD_SETLKW`` constants, which working with open file description locks. -+ and ``F_OFD_SETLKW`` constants, which are used when working with open file -+ description locks. - - The module defines the following functions: - -diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst -index 9a9c707dc1..13d7d6e5b0 100644 ---- a/Doc/library/functions.rst -+++ b/Doc/library/functions.rst -@@ -862,7 +862,8 @@ - Return ``True`` if *class* is a subclass (direct, indirect or :term:`virtual - `) of *classinfo*. A - class is considered a subclass of itself. *classinfo* may be a tuple of class -- objects, in which case return ``True`` if *class* is a subclass of any entry -+ objects (or recursively, other such tuples), -+ in which case return ``True`` if *class* is a subclass of any entry - in *classinfo*. In any other case, a :exc:`TypeError` exception is raised. - - -@@ -1113,7 +1114,11 @@ - *buffering* is an optional integer used to set the buffering policy. Pass 0 - to switch buffering off (only allowed in binary mode), 1 to select line - buffering (only usable in text mode), and an integer > 1 to indicate the size -- in bytes of a fixed-size chunk buffer. When no *buffering* argument is -+ in bytes of a fixed-size chunk buffer. Note that specifying a buffer size this -+ way applies for binary buffered I/O, but ``TextIOWrapper`` (i.e., files opened -+ with ``mode='r+'``) would have another buffering. To disable buffering in -+ ``TextIOWrapper``, consider using the ``write_through`` flag for -+ :func:`io.TextIOWrapper.reconfigure`. When no *buffering* argument is - given, the default buffering policy works as follows: - - * Binary files are buffered in fixed-size chunks; the size of the buffer is -diff --git a/Doc/library/glob.rst b/Doc/library/glob.rst -index 3c468ebf73..ced84c7fd8 100644 ---- a/Doc/library/glob.rst -+++ b/Doc/library/glob.rst -@@ -23,8 +23,11 @@ - arbitrary order. No tilde expansion is done, but ``*``, ``?``, and character - ranges expressed with ``[]`` will be correctly matched. This is done by using - the :func:`os.scandir` and :func:`fnmatch.fnmatch` functions in concert, and --not by actually invoking a subshell. Note that unlike :func:`fnmatch.fnmatch`, --:mod:`glob` treats filenames beginning with a dot (``.``) as special cases. -+not by actually invoking a subshell. -+ -+Note that files beginning with a dot (``.``) can only be matched by -+patterns that also start with a dot, -+unlike :func:`fnmatch.fnmatch` or :func:`pathlib.Path.glob`. - (For tilde and shell variable expansion, use :func:`os.path.expanduser` and - :func:`os.path.expandvars`.) - -diff --git a/Doc/library/grp.rst b/Doc/library/grp.rst -index 74de3f9520..fbfb922d3e 100644 ---- a/Doc/library/grp.rst -+++ b/Doc/library/grp.rst -@@ -12,7 +12,7 @@ - - Group database entries are reported as a tuple-like object, whose attributes - correspond to the members of the ``group`` structure (Attribute field below, see --````): -+````): - - +-------+-----------+---------------------------------+ - | Index | Attribute | Meaning | -diff --git a/Doc/library/imghdr.rst b/Doc/library/imghdr.rst -index f63a0fa56f..186480be3e 100644 ---- a/Doc/library/imghdr.rst -+++ b/Doc/library/imghdr.rst -@@ -8,7 +8,8 @@ - **Source code:** :source:`Lib/imghdr.py` - - .. deprecated:: 3.11 -- The :mod:`imghdr` module is deprecated (see :pep:`594` for details). -+ The :mod:`imghdr` module is deprecated -+ (see :pep:`PEP 594 <594#imghdr>` for details and alternatives). - - -------------- - -diff --git a/Doc/library/index.rst b/Doc/library/index.rst -index 0fd6e4c0d0..5007bacbab 100644 ---- a/Doc/library/index.rst -+++ b/Doc/library/index.rst -@@ -75,5 +75,4 @@ - windows.rst - unix.rst - superseded.rst -- undoc.rst - security_warnings.rst -diff --git a/Doc/library/io.rst b/Doc/library/io.rst -index 9f62dd21af..ea57aae71a 100644 ---- a/Doc/library/io.rst -+++ b/Doc/library/io.rst -@@ -299,7 +299,7 @@ - Return ``True`` if the stream can be read from. If ``False``, :meth:`read` - will raise :exc:`OSError`. - -- .. method:: readline(size=-1) -+ .. method:: readline(size=-1, /) - - Read and return one line from the stream. If *size* is specified, at - most *size* bytes will be read. -@@ -308,7 +308,7 @@ - the *newline* argument to :func:`open` can be used to select the line - terminator(s) recognized. - -- .. method:: readlines(hint=-1) -+ .. method:: readlines(hint=-1, /) - - Read and return a list of lines from the stream. *hint* can be specified - to control the number of lines read: no more lines will be read if the -@@ -320,7 +320,7 @@ - Note that it's already possible to iterate on file objects using ``for - line in file: ...`` without calling ``file.readlines()``. - -- .. method:: seek(offset, whence=SEEK_SET) -+ .. method:: seek(offset, whence=SEEK_SET, /) - - Change the stream position to the given byte *offset*. *offset* is - interpreted relative to the position indicated by *whence*. The default -@@ -352,7 +352,7 @@ - - Return the current stream position. - -- .. method:: truncate(size=None) -+ .. method:: truncate(size=None, /) - - Resize the stream to the given *size* in bytes (or the current position - if *size* is not specified). The current stream position isn't changed. -@@ -369,7 +369,7 @@ - Return ``True`` if the stream supports writing. If ``False``, - :meth:`write` and :meth:`truncate` will raise :exc:`OSError`. - -- .. method:: writelines(lines) -+ .. method:: writelines(lines, /) - - Write a list of lines to the stream. Line separators are not added, so it - is usual for each of the lines provided to have a line separator at the -@@ -394,7 +394,7 @@ - :class:`RawIOBase` provides these methods in addition to those from - :class:`IOBase`: - -- .. method:: read(size=-1) -+ .. method:: read(size=-1, /) - - Read up to *size* bytes from the object and return them. As a convenience, - if *size* is unspecified or -1, all bytes until EOF are returned. -@@ -413,7 +413,7 @@ - Read and return all the bytes from the stream until EOF, using multiple - calls to the stream if necessary. - -- .. method:: readinto(b) -+ .. method:: readinto(b, /) - - Read bytes into a pre-allocated, writable - :term:`bytes-like object` *b*, and return the -@@ -421,7 +421,7 @@ - If the object is in non-blocking mode and no bytes - are available, ``None`` is returned. - -- .. method:: write(b) -+ .. method:: write(b, /) - - Write the given :term:`bytes-like object`, *b*, to the - underlying raw stream, and return the number of -@@ -478,7 +478,7 @@ - - .. versionadded:: 3.1 - -- .. method:: read(size=-1) -+ .. method:: read(size=-1, /) - - Read and return up to *size* bytes. If the argument is omitted, ``None``, - or negative, data is read and returned until EOF is reached. An empty -@@ -493,7 +493,7 @@ - A :exc:`BlockingIOError` is raised if the underlying raw stream is in - non blocking-mode, and has no data available at the moment. - -- .. method:: read1([size]) -+ .. method:: read1(size=-1, /) - - Read and return up to *size* bytes, with at most one call to the - underlying raw stream's :meth:`~RawIOBase.read` (or -@@ -504,7 +504,7 @@ - If *size* is ``-1`` (the default), an arbitrary number of bytes are - returned (more than zero unless EOF is reached). - -- .. method:: readinto(b) -+ .. method:: readinto(b, /) - - Read bytes into a pre-allocated, writable - :term:`bytes-like object` *b* and return the number of bytes read. -@@ -516,7 +516,7 @@ - A :exc:`BlockingIOError` is raised if the underlying raw stream is in non - blocking-mode, and has no data available at the moment. - -- .. method:: readinto1(b) -+ .. method:: readinto1(b, /) - - Read bytes into a pre-allocated, writable - :term:`bytes-like object` *b*, using at most one call to -@@ -528,7 +528,7 @@ - - .. versionadded:: 3.5 - -- .. method:: write(b) -+ .. method:: write(b, /) - - Write the given :term:`bytes-like object`, *b*, and return the number - of bytes written (always equal to the length of *b* in bytes, since if -@@ -611,7 +611,7 @@ - Buffered I/O streams provide a higher-level interface to an I/O device - than raw I/O does. - --.. class:: BytesIO([initial_bytes]) -+.. class:: BytesIO(initial_bytes=b'') - - A binary stream using an in-memory bytes buffer. It inherits - :class:`BufferedIOBase`. The buffer is discarded when the -@@ -646,14 +646,14 @@ - Return :class:`bytes` containing the entire contents of the buffer. - - -- .. method:: read1([size]) -+ .. method:: read1(size=-1, /) - - In :class:`BytesIO`, this is the same as :meth:`~BufferedIOBase.read`. - - .. versionchanged:: 3.7 - The *size* argument is now optional. - -- .. method:: readinto1(b) -+ .. method:: readinto1(b, /) - - In :class:`BytesIO`, this is the same as :meth:`~BufferedIOBase.readinto`. - -@@ -676,18 +676,18 @@ - :class:`BufferedReader` provides or overrides these methods in addition to - those from :class:`BufferedIOBase` and :class:`IOBase`: - -- .. method:: peek([size]) -+ .. method:: peek(size=0, /) - - Return bytes from the stream without advancing the position. At most one - single read on the raw stream is done to satisfy the call. The number of - bytes returned may be less or more than requested. - -- .. method:: read([size]) -+ .. method:: read(size=-1, /) - - Read and return *size* bytes, or if *size* is not given or negative, until - EOF or if the read call would block in non-blocking mode. - -- .. method:: read1([size]) -+ .. method:: read1(size=-1, /) - - Read and return up to *size* bytes with only one call on the raw stream. - If at least one byte is buffered, only buffered bytes are returned. -@@ -724,7 +724,7 @@ - Force bytes held in the buffer into the raw stream. A - :exc:`BlockingIOError` should be raised if the raw stream blocks. - -- .. method:: write(b) -+ .. method:: write(b, /) - - Write the :term:`bytes-like object`, *b*, and return the - number of bytes written. When in non-blocking mode, a -@@ -747,7 +747,7 @@ - are guaranteed to be implemented. - - --.. class:: BufferedRWPair(reader, writer, buffer_size=DEFAULT_BUFFER_SIZE) -+.. class:: BufferedRWPair(reader, writer, buffer_size=DEFAULT_BUFFER_SIZE, /) - - A buffered binary stream providing higher-level access to two non seekable - :class:`RawIOBase` raw binary streams---one readable, the other writeable. -@@ -814,19 +814,19 @@ - - .. versionadded:: 3.1 - -- .. method:: read(size=-1) -+ .. method:: read(size=-1, /) - - Read and return at most *size* characters from the stream as a single - :class:`str`. If *size* is negative or ``None``, reads until EOF. - -- .. method:: readline(size=-1) -+ .. method:: readline(size=-1, /) - - Read until newline or EOF and return a single ``str``. If the stream is - already at EOF, an empty string is returned. - - If *size* is specified, at most *size* characters will be read. - -- .. method:: seek(offset, whence=SEEK_SET) -+ .. method:: seek(offset, whence=SEEK_SET, /) - - Change the stream position to the given *offset*. Behaviour depends on - the *whence* parameter. The default value for *whence* is -@@ -853,7 +853,7 @@ - does not usually represent a number of bytes in the underlying - binary storage. - -- .. method:: write(s) -+ .. method:: write(s, /) - - Write the string *s* to the stream and return the number of characters - written. -diff --git a/Doc/library/json.rst b/Doc/library/json.rst -index 1810e04cc8..608e70df5b 100644 ---- a/Doc/library/json.rst -+++ b/Doc/library/json.rst -@@ -125,13 +125,6 @@ - This module's encoders and decoders preserve input and output order by - default. Order is only lost if the underlying containers are unordered. - -- Prior to Python 3.7, :class:`dict` was not guaranteed to be ordered, so -- inputs and outputs were typically scrambled unless -- :class:`collections.OrderedDict` was specifically requested. Starting -- with Python 3.7, the regular :class:`dict` became order preserving, so -- it is no longer necessary to specify :class:`collections.OrderedDict` for -- JSON generation and parsing. -- - - Basic Usage - ----------- -diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst -index bf57a08355..5b5a0c7552 100644 ---- a/Doc/library/locale.rst -+++ b/Doc/library/locale.rst -@@ -423,10 +423,10 @@ - .. versionadded:: 3.5 - - --.. function:: atof(string) -+.. function:: atof(string, func=float) - -- Converts a string to a floating point number, following the :const:`LC_NUMERIC` -- settings. -+ Converts a string to a number, following the :const:`LC_NUMERIC` settings, -+ by calling *func* on the result of calling :func:`delocalize` on *string*. - - - .. function:: atoi(string) -diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst -index 9d6167cca5..f85c691f5d 100644 ---- a/Doc/library/logging.rst -+++ b/Doc/library/logging.rst -@@ -1073,16 +1073,6 @@ - Logs a message with level *level* on the root logger. The other arguments are - interpreted as for :func:`debug`. - -- .. note:: The above module-level convenience functions, which delegate to the -- root logger, call :func:`basicConfig` to ensure that at least one handler -- is available. Because of this, they should *not* be used in threads, -- in versions of Python earlier than 2.7.1 and 3.2, unless at least one -- handler has been added to the root logger *before* the threads are -- started. In earlier versions of Python, due to a thread safety shortcoming -- in :func:`basicConfig`, this can (under rare circumstances) lead to -- handlers being added multiple times to the root logger, which can in turn -- lead to multiple messages for the same event. -- - .. function:: disable(level=CRITICAL) - - Provides an overriding level *level* for all loggers which takes precedence over -diff --git a/Doc/library/mailcap.rst b/Doc/library/mailcap.rst -index bf9639bdac..a22b5b9c9e 100644 ---- a/Doc/library/mailcap.rst -+++ b/Doc/library/mailcap.rst -@@ -3,9 +3,15 @@ - - .. module:: mailcap - :synopsis: Mailcap file handling. -+ :deprecated: - - **Source code:** :source:`Lib/mailcap.py` - -+.. deprecated:: 3.11 -+ The :mod:`mailcap` module is deprecated -+ (see :pep:`PEP 594 <594#mailcap>` for details). -+ The :mod:`mimetypes` module provides an alternative. -+ - -------------- - - Mailcap files are used to configure how MIME-aware applications such as mail -diff --git a/Doc/library/math.rst b/Doc/library/math.rst -index b20e557b5c..ad9116b63e 100644 ---- a/Doc/library/math.rst -+++ b/Doc/library/math.rst -@@ -32,8 +32,8 @@ - .. function:: ceil(x) - - Return the ceiling of *x*, the smallest integer greater than or equal to *x*. -- If *x* is not a float, delegates to ``x.__ceil__()``, which should return an -- :class:`~numbers.Integral` value. -+ If *x* is not a float, delegates to :meth:`x.__ceil__ `, -+ which should return an :class:`~numbers.Integral` value. - - - .. function:: comb(n, k) -@@ -77,9 +77,9 @@ - - .. function:: floor(x) - -- Return the floor of *x*, the largest integer less than or equal to *x*. -- If *x* is not a float, delegates to ``x.__floor__()``, which should return an -- :class:`~numbers.Integral` value. -+ Return the floor of *x*, the largest integer less than or equal to *x*. If -+ *x* is not a float, delegates to :meth:`x.__floor__ `, which -+ should return an :class:`~numbers.Integral` value. - - - .. function:: fmod(x, y) -@@ -298,9 +298,11 @@ - - .. function:: trunc(x) - -- Return the :class:`~numbers.Real` value *x* truncated to an -- :class:`~numbers.Integral` (usually an integer). Delegates to -- :meth:`x.__trunc__() `. -+ Return *x* with the fractional part -+ removed, leaving the integer part. This rounds toward 0: ``trunc()`` is -+ equivalent to :func:`floor` for positive *x*, and equivalent to :func:`ceil` -+ for negative *x*. If *x* is not a float, delegates to :meth:`x.__trunc__ -+ `, which should return an :class:`~numbers.Integral` value. - - .. function:: ulp(x) - -@@ -622,8 +624,23 @@ - - .. data:: nan - -- A floating-point "not a number" (NaN) value. Equivalent to the output of -- ``float('nan')``. -+ A floating-point "not a number" (NaN) value. Equivalent to the output of -+ ``float('nan')``. Due to the requirements of the `IEEE-754 standard -+ `_, ``math.nan`` and ``float('nan')`` are -+ not considered to equal to any other numeric value, including themselves. To check -+ whether a number is a NaN, use the :func:`isnan` function to test -+ for NaNs instead of ``is`` or ``==``. -+ Example:: -+ -+ >>> import math -+ >>> math.nan == math.nan -+ False -+ >>> float('nan') == float('nan') -+ False -+ >>> math.isnan(math.nan) -+ True -+ >>> math.isnan(float('nan')) -+ True - - .. versionadded:: 3.5 - -diff --git a/Doc/library/msilib.rst b/Doc/library/msilib.rst -index 5ce18a1f75..0eba2275c8 100644 ---- a/Doc/library/msilib.rst -+++ b/Doc/library/msilib.rst -@@ -14,7 +14,8 @@ - .. index:: single: msi - - .. deprecated:: 3.11 -- The :mod:`msilib` module is deprecated (see :pep:`594` for details). -+ The :mod:`msilib` module is deprecated -+ (see :pep:`PEP 594 <594#msilib>` for details). - - -------------- - -diff --git a/Doc/library/netdata.rst b/Doc/library/netdata.rst -index 8a3ad68753..2520036c2f 100644 ---- a/Doc/library/netdata.rst -+++ b/Doc/library/netdata.rst -@@ -13,7 +13,6 @@ - - email.rst - json.rst -- mailcap.rst - mailbox.rst - mimetypes.rst - base64.rst -diff --git a/Doc/library/nis.rst b/Doc/library/nis.rst -index f6b6ea8394..49fe62954c 100644 ---- a/Doc/library/nis.rst -+++ b/Doc/library/nis.rst -@@ -11,7 +11,8 @@ - .. sectionauthor:: Moshe Zadka - - .. deprecated:: 3.11 -- The :mod:`nis` module is deprecated (see :pep:`594` for details). -+ The :mod:`nis` module is deprecated -+ (see :pep:`PEP 594 <594#nis>` for details). - - -------------- - -diff --git a/Doc/library/optparse.rst b/Doc/library/optparse.rst -index b1094198f4..0d686b1036 100644 ---- a/Doc/library/optparse.rst -+++ b/Doc/library/optparse.rst -@@ -131,7 +131,7 @@ - These option syntaxes are not supported by :mod:`optparse`, and they never - will be. This is deliberate: the first three are non-standard on any - environment, and the last only makes sense if you're exclusively targeting -- VMS, MS-DOS, and/or Windows. -+ Windows or certain legacy platforms (e.g. VMS, MS-DOS). - - option argument - an argument that follows an option, is closely associated with that option, -diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst -index 2fb6ea9da8..97bb684a24 100644 ---- a/Doc/library/os.path.rst -+++ b/Doc/library/os.path.rst -@@ -5,22 +5,16 @@ - :synopsis: Operations on pathnames. - - **Source code:** :source:`Lib/posixpath.py` (for POSIX) and --:source:`Lib/ntpath.py` (for Windows NT). -+:source:`Lib/ntpath.py` (for Windows). - - .. index:: single: path; operations - - -------------- - --This module implements some useful functions on pathnames. To read or --write files see :func:`open`, and for accessing the filesystem see the --:mod:`os` module. The path parameters can be passed as either strings, --or bytes. Applications are encouraged to represent file names as --(Unicode) character strings. Unfortunately, some file names may not be --representable as strings on Unix, so applications that need to support --arbitrary file names on Unix should use bytes objects to represent --path names. Vice versa, using bytes objects cannot represent all file --names on Windows (in the standard ``mbcs`` encoding), hence Windows --applications should use string objects to access all files. -+This module implements some useful functions on pathnames. To read or write -+files see :func:`open`, and for accessing the filesystem see the :mod:`os` -+module. The path parameters can be passed as strings, or bytes, or any object -+implementing the :class:`os.PathLike` protocol. - - Unlike a unix shell, Python does not do any *automatic* path expansions. - Functions such as :func:`expanduser` and :func:`expandvars` can be invoked -@@ -38,7 +32,6 @@ - their parameters. The result is an object of the same type, if a path or - file name is returned. - -- - .. note:: - - Since different operating systems have different path name conventions, there diff --git a/Doc/library/os.rst b/Doc/library/os.rst -index 2f7b37019e..3da4f3daa9 100644 +index 35a7e1e96d..3da4f3daa9 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst -@@ -109,8 +109,8 @@ - - This mapping is captured the first time the :mod:`os` module is imported, - typically during Python startup as part of processing :file:`site.py`. Changes -- to the environment made after this time are not reflected in ``os.environ``, -- except for changes made by modifying ``os.environ`` directly. -+ to the environment made after this time are not reflected in :data:`os.environ`, -+ except for changes made by modifying :data:`os.environ` directly. - - This mapping may be used to modify the environment as well as query the - environment. :func:`putenv` will be called automatically when the mapping -@@ -122,8 +122,8 @@ - - .. note:: - -- Calling :func:`putenv` directly does not change ``os.environ``, so it's better -- to modify ``os.environ``. -+ Calling :func:`putenv` directly does not change :data:`os.environ`, so it's better -+ to modify :data:`os.environ`. - - .. note:: - -@@ -133,7 +133,7 @@ - - You can delete items in this mapping to unset environment variables. - :func:`unsetenv` will be called automatically when an item is deleted from -- ``os.environ``, and when one of the :meth:`pop` or :meth:`clear` methods is -+ :data:`os.environ`, and when one of the :meth:`pop` or :meth:`clear` methods is - called. - - .. versionchanged:: 3.9 -@@ -224,7 +224,10 @@ - .. function:: getenv(key, default=None) - - Return the value of the environment variable *key* if it exists, or -- *default* if it doesn't. *key*, *default* and the result are str. -+ *default* if it doesn't. *key*, *default* and the result are str. Note that -+ since :func:`getenv` uses :data:`os.environ`, the mapping of :func:`getenv` is -+ similarly also captured on import, and the function may not reflect -+ future environment changes. - - On Unix, keys and values are decoded with :func:`sys.getfilesystemencoding` - and ``'surrogateescape'`` error handler. Use :func:`os.getenvb` if you -@@ -236,7 +239,11 @@ - .. function:: getenvb(key, default=None) - - Return the value of the environment variable *key* if it exists, or -- *default* if it doesn't. *key*, *default* and the result are bytes. -+ *default* if it doesn't. *key*, *default* and the result are bytes. Note that -+ since :func:`getenvb` uses :data:`os.environb`, the mapping of :func:`getenvb` is -+ similarly also captured on import, and the function may not reflect -+ future environment changes. -+ - - :func:`getenvb` is only available if :data:`supports_bytes_environ` - is ``True``. -@@ -287,7 +294,8 @@ - - Return list of group ids that *user* belongs to. If *group* is not in the - list, it is included; typically, *group* is specified as the group ID -- field from the password record for *user*. -+ field from the password record for *user*, because that group ID will -+ otherwise be potentially omitted. - - .. availability:: Unix. - -@@ -442,10 +450,11 @@ - changes to the environment affect subprocesses started with :func:`os.system`, - :func:`popen` or :func:`fork` and :func:`execv`. - -- Assignments to items in ``os.environ`` are automatically translated into -+ Assignments to items in :data:`os.environ` are automatically translated into - corresponding calls to :func:`putenv`; however, calls to :func:`putenv` -- don't update ``os.environ``, so it is actually preferable to assign to items -- of ``os.environ``. -+ don't update :data:`os.environ`, so it is actually preferable to assign to items -+ of :data:`os.environ`. This also applies to :func:`getenv` and :func:`getenvb`, which -+ respectively use :data:`os.environ` and :data:`os.environb` in their implementations. - - .. note:: - -@@ -644,10 +653,10 @@ - environment affect subprocesses started with :func:`os.system`, :func:`popen` or - :func:`fork` and :func:`execv`. - -- Deletion of items in ``os.environ`` is automatically translated into a -+ Deletion of items in :data:`os.environ` is automatically translated into a - corresponding call to :func:`unsetenv`; however, calls to :func:`unsetenv` -- don't update ``os.environ``, so it is actually preferable to delete items of -- ``os.environ``. -+ don't update :data:`os.environ`, so it is actually preferable to delete items of -+ :data:`os.environ`. - - .. audit-event:: os.unsetenv key os.unsetenv - -@@ -2206,7 +2215,7 @@ - - .. function:: replace(src, dst, *, src_dir_fd=None, dst_dir_fd=None) - -- Rename the file or directory *src* to *dst*. If *dst* is a directory, -+ Rename the file or directory *src* to *dst*. If *dst* is a non-empty directory, - :exc:`OSError` will be raised. If *dst* exists and is a file, it will - be replaced silently if the user has permission. The operation may fail - if *src* and *dst* are on different filesystems. If successful, -@@ -3163,6 +3172,13 @@ +@@ -3172,6 +3172,13 @@ .. versionadded:: 3.8 @@ -2617,749 +16,8 @@ index 2f7b37019e..3da4f3daa9 100644 .. data:: MFD_CLOEXEC MFD_ALLOW_SEALING -@@ -3318,8 +3334,8 @@ - Add a path to the DLL search path. - - This search path is used when resolving dependencies for imported -- extension modules (the module itself is resolved through sys.path), -- and also by :mod:`ctypes`. -+ extension modules (the module itself is resolved through -+ :data:`sys.path`), and also by :mod:`ctypes`. - - Remove the directory by calling **close()** on the returned object - or using it in a :keyword:`with` statement. -diff --git a/Doc/library/ossaudiodev.rst b/Doc/library/ossaudiodev.rst -index e0f0a6b825..728ee30360 100644 ---- a/Doc/library/ossaudiodev.rst -+++ b/Doc/library/ossaudiodev.rst -@@ -7,7 +7,8 @@ - :deprecated: - - .. deprecated:: 3.11 -- The :mod:`ossaudiodev` module is deprecated (see :pep:`594` for details). -+ The :mod:`ossaudiodev` module is deprecated -+ (see :pep:`PEP 594 <594#ossaudiodev>` for details). - - -------------- - -diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst -index 3a41b5454b..30611abcb9 100644 ---- a/Doc/library/pathlib.rst -+++ b/Doc/library/pathlib.rst -@@ -1028,7 +1028,7 @@ - - Rename this file or directory to the given *target*, and return a new Path - instance pointing to *target*. If *target* points to an existing file or -- directory, it will be unconditionally replaced. -+ empty directory, it will be unconditionally replaced. - - The target path may be absolute or relative. Relative paths are interpreted - relative to the current working directory, *not* the directory of the Path -diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst -index 13e1a19936..dcd509de56 100644 ---- a/Doc/library/pdb.rst -+++ b/Doc/library/pdb.rst -@@ -233,7 +233,8 @@ - single ``;`` is not used as it is the separator for multiple commands in a line - that is passed to the Python parser.) No intelligence is applied to separating - the commands; the input is split at the first ``;;`` pair, even if it is in the --middle of a quoted string. -+middle of a quoted string. A workaround for strings with double semicolons -+is to use implicit string concatenation ``';'';'`` or ``";"";"``. - - .. index:: - pair: .pdbrc; file -diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst -index be48561ed1..f7db0e8415 100644 ---- a/Doc/library/pickle.rst -+++ b/Doc/library/pickle.rst -@@ -147,7 +147,7 @@ - earlier versions of Python. - - * Protocol version 2 was introduced in Python 2.3. It provides much more -- efficient pickling of :term:`new-style class`\es. Refer to :pep:`307` for -+ efficient pickling of :term:`new-style classes `. Refer to :pep:`307` for - information about improvements brought by protocol 2. - - * Protocol version 3 was added in Python 3.0. It has explicit support for -@@ -261,7 +261,7 @@ - protocol argument is needed. Bytes past the pickled representation - of the object are ignored. - -- Arguments *file*, *fix_imports*, *encoding*, *errors*, *strict* and *buffers* -+ Arguments *fix_imports*, *encoding*, *errors*, *strict* and *buffers* - have the same meaning as in the :class:`Unpickler` constructor. - - .. versionchanged:: 3.8 -@@ -368,7 +368,7 @@ - - .. versionadded:: 3.3 - -- .. method:: reducer_override(self, obj) -+ .. method:: reducer_override(obj) - - Special reducer that can be defined in :class:`Pickler` subclasses. This - method has priority over any reducer in the :attr:`dispatch_table`. It -@@ -494,20 +494,18 @@ - - The following types can be pickled: - --* ``None``, ``True``, and ``False`` -- --* integers, floating point numbers, complex numbers -+* ``None``, ``True``, and ``False``; - --* strings, bytes, bytearrays -+* integers, floating-point numbers, complex numbers; - --* tuples, lists, sets, and dictionaries containing only picklable objects -+* strings, bytes, bytearrays; - --* functions defined at the top level of a module (using :keyword:`def`, not -- :keyword:`lambda`) -+* tuples, lists, sets, and dictionaries containing only picklable objects; - --* built-in functions defined at the top level of a module -+* functions (built-in and user-defined) defined at the top level of a module -+ (using :keyword:`def`, not :keyword:`lambda`); - --* classes that are defined at the top level of a module -+* classes defined at the top level of a module; - - * instances of such classes whose :attr:`~object.__dict__` or the result of - calling :meth:`__getstate__` is picklable (see section :ref:`pickle-inst` for -@@ -520,14 +518,14 @@ - raised in this case. You can carefully raise this limit with - :func:`sys.setrecursionlimit`. - --Note that functions (built-in and user-defined) are pickled by "fully qualified" --name reference, not by value. [#]_ This means that only the function name is -+Note that functions (built-in and user-defined) are pickled by fully qualified -+name, not by value. [#]_ This means that only the function name is - pickled, along with the name of the module the function is defined in. Neither - the function's code, nor any of its function attributes are pickled. Thus the - defining module must be importable in the unpickling environment, and the module - must contain the named object, otherwise an exception will be raised. [#]_ - --Similarly, classes are pickled by named reference, so the same restrictions in -+Similarly, classes are pickled by fully qualified name, so the same restrictions in - the unpickling environment apply. Note that none of the class's code or data is - pickled, so in the following example the class attribute ``attr`` is not - restored in the unpickling environment:: -@@ -537,7 +535,7 @@ - - picklestring = pickle.dumps(Foo) - --These restrictions are why picklable functions and classes must be defined in -+These restrictions are why picklable functions and classes must be defined at - the top level of a module. - - Similarly, when class instances are pickled, their class's code and data are not -@@ -569,7 +567,7 @@ - def save(obj): - return (obj.__class__, obj.__dict__) - -- def load(cls, attributes): -+ def restore(cls, attributes): - obj = cls.__new__(cls) - obj.__dict__.update(attributes) - return obj -@@ -788,14 +786,15 @@ - f = io.BytesIO() - p = MyPickler(f) - --does the same, but all instances of ``MyPickler`` will by default --share the same dispatch table. The equivalent code using the --:mod:`copyreg` module is :: -+does the same but all instances of ``MyPickler`` will by default -+share the private dispatch table. On the other hand, the code :: - - copyreg.pickle(SomeClass, reduce_SomeClass) - f = io.BytesIO() - p = pickle.Pickler(f) - -+modifies the global dispatch table shared by all users of the :mod:`copyreg` module. -+ - .. _pickle-state: - - Handling Stateful Objects -@@ -1098,7 +1097,7 @@ - """Helper function analogous to pickle.loads().""" - return RestrictedUnpickler(io.BytesIO(s)).load() - --A sample usage of our unpickler working has intended:: -+A sample usage of our unpickler working as intended:: - - >>> restricted_loads(pickle.dumps([1, 2, range(15)])) - [1, 2, range(0, 15)] -@@ -1142,7 +1141,7 @@ - - # An arbitrary collection of objects supported by pickle. - data = { -- 'a': [1, 2.0, 3, 4+6j], -+ 'a': [1, 2.0, 3+4j], - 'b': ("character string", b"byte string"), - 'c': {None, True, False} - } -@@ -1198,6 +1197,6 @@ - operations. - - .. [#] The limitation on alphanumeric characters is due to the fact -- the persistent IDs, in protocol 0, are delimited by the newline -+ that persistent IDs in protocol 0 are delimited by the newline - character. Therefore if any kind of newline characters occurs in -- persistent IDs, the resulting pickle will become unreadable. -+ persistent IDs, the resulting pickled data will become unreadable. -diff --git a/Doc/library/pipes.rst b/Doc/library/pipes.rst -index 25f808566e..bc21c097f7 100644 ---- a/Doc/library/pipes.rst -+++ b/Doc/library/pipes.rst -@@ -11,7 +11,9 @@ - **Source code:** :source:`Lib/pipes.py` - - .. deprecated:: 3.11 -- The :mod:`pipes` module is deprecated (see :pep:`594` for details). -+ The :mod:`pipes` module is deprecated -+ (see :pep:`PEP 594 <594#pipes>` for details). -+ Please use the :mod:`subprocess` module instead. - - -------------- - -diff --git a/Doc/library/pkgutil.rst b/Doc/library/pkgutil.rst -index 3b17b9a621..788a02dcb8 100644 ---- a/Doc/library/pkgutil.rst -+++ b/Doc/library/pkgutil.rst -@@ -26,7 +26,7 @@ - __path__ = extend_path(__path__, __name__) - - This will add to the package's ``__path__`` all subdirectories of directories -- on ``sys.path`` named after the package. This is useful if one wants to -+ on :data:`sys.path` named after the package. This is useful if one wants to - distribute different parts of a single logical package as multiple - directories. - -@@ -128,9 +128,9 @@ - - Yield :term:`finder` objects for the given module name. - -- If fullname contains a '.', the finders will be for the package -+ If fullname contains a ``'.'``, the finders will be for the package - containing fullname, otherwise they will be all registered top level -- finders (i.e. those on both sys.meta_path and sys.path_hooks). -+ finders (i.e. those on both :data:`sys.meta_path` and :data:`sys.path_hooks`). - - If the named module is in a package, that package is imported as a side - effect of invoking this function. -@@ -145,7 +145,7 @@ - .. function:: iter_modules(path=None, prefix='') - - Yields :class:`ModuleInfo` for all submodules on *path*, or, if -- *path* is ``None``, all top-level modules on ``sys.path``. -+ *path* is ``None``, all top-level modules on :data:`sys.path`. - - *path* should be either ``None`` or a list of paths to look for modules in. - -diff --git a/Doc/library/platform.rst b/Doc/library/platform.rst -index d87205c3d4..722a5b59e3 100644 ---- a/Doc/library/platform.rst -+++ b/Doc/library/platform.rst -@@ -139,7 +139,7 @@ - - .. function:: release() - -- Returns the system's release, e.g. ``'2.2.0'`` or ``'NT'`` An empty string is -+ Returns the system's release, e.g. ``'2.2.0'`` or ``'NT'``. An empty string is - returned if the value cannot be determined. - - -@@ -176,7 +176,7 @@ - Entries which cannot be determined are set to ``''``. - - .. versionchanged:: 3.3 -- Result changed from a tuple to a namedtuple. -+ Result changed from a tuple to a :func:`~collections.namedtuple`. - - - Java Platform -@@ -201,7 +201,9 @@ - - Get additional version information from the Windows Registry and return a tuple - ``(release, version, csd, ptype)`` referring to OS release, version number, -- CSD level (service pack) and OS type (multi/single processor). -+ CSD level (service pack) and OS type (multi/single processor). Values which -+ cannot be determined are set to the defaults given as parameters (which all -+ default to an empty string). - - As a hint: *ptype* is ``'Uniprocessor Free'`` on single processor NT machines - and ``'Multiprocessor Free'`` on multi processor machines. The *'Free'* refers -@@ -211,9 +213,9 @@ - - .. function:: win32_edition() - -- Returns a string representing the current Windows edition. Possible -- values include but are not limited to ``'Enterprise'``, ``'IoTUAP'``, -- ``'ServerStandard'``, and ``'nanoserver'``. -+ Returns a string representing the current Windows edition, or ``None`` if the -+ value cannot be determined. Possible values include but are not limited to -+ ``'Enterprise'``, ``'IoTUAP'``, ``'ServerStandard'``, and ``'nanoserver'``. - - .. versionadded:: 3.8 - -diff --git a/Doc/library/queue.rst b/Doc/library/queue.rst -index 0ec5900bef..def39eae8a 100644 ---- a/Doc/library/queue.rst -+++ b/Doc/library/queue.rst -@@ -138,7 +138,7 @@ - - .. method:: Queue.put_nowait(item) - -- Equivalent to ``put(item, False)``. -+ Equivalent to ``put(item, block=False)``. - - - .. method:: Queue.get(block=True, timeout=None) -@@ -249,7 +249,7 @@ - - .. method:: SimpleQueue.put_nowait(item) - -- Equivalent to ``put(item)``, provided for compatibility with -+ Equivalent to ``put(item, block=False)``, provided for compatibility with - :meth:`Queue.put_nowait`. - - -diff --git a/Doc/library/runpy.rst b/Doc/library/runpy.rst -index af35e81a2d..26a4f14352 100644 ---- a/Doc/library/runpy.rst -+++ b/Doc/library/runpy.rst -@@ -93,7 +93,7 @@ - run this way, as well as ensuring the real module name is always - accessible as ``__spec__.name``. - --.. function:: run_path(file_path, init_globals=None, run_name=None) -+.. function:: run_path(path_name, init_globals=None, run_name=None) - - .. index:: - module: __main__ -@@ -140,7 +140,7 @@ - - A number of alterations are also made to the :mod:`sys` module. Firstly, - ``sys.path`` may be altered as described above. ``sys.argv[0]`` is updated -- with the value of ``file_path`` and ``sys.modules[__name__]`` is updated -+ with the value of ``path_name`` and ``sys.modules[__name__]`` is updated - with a temporary module object for the module being executed. All - modifications to items in :mod:`sys` are reverted before the function - returns. -diff --git a/Doc/library/secrets.rst b/Doc/library/secrets.rst -index afa8e2d385..c22da727b5 100644 ---- a/Doc/library/secrets.rst -+++ b/Doc/library/secrets.rst -@@ -193,7 +193,7 @@ - .. testcode:: - - import secrets -- url = 'https://mydomain.com/reset=' + secrets.token_urlsafe() -+ url = 'https://example.com/reset=' + secrets.token_urlsafe() - - - -diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst -index 11c6707492..193c010061 100644 ---- a/Doc/library/shutil.rst -+++ b/Doc/library/shutil.rst -@@ -160,7 +160,8 @@ - Copies the file *src* to the file or directory *dst*. *src* and *dst* - should be :term:`path-like objects ` or strings. If - *dst* specifies a directory, the file will be copied into *dst* using the -- base filename from *src*. Returns the path to the newly created file. -+ base filename from *src*. If *dst* specifies a file that already exists, -+ it will be replaced. Returns the path to the newly created file. - - If *follow_symlinks* is false, and *src* is a symbolic link, - *dst* will be created as a symbolic link. If *follow_symlinks* -@@ -230,9 +231,8 @@ - dirs_exist_ok=False) - - Recursively copy an entire directory tree rooted at *src* to a directory -- named *dst* and return the destination directory. *dirs_exist_ok* dictates -- whether to raise an exception in case *dst* or any missing parent directory -- already exists. -+ named *dst* and return the destination directory. All intermediate -+ directories needed to contain *dst* will also be created by default. - - Permissions and times of directories are copied with :func:`copystat`, - individual files are copied using :func:`~shutil.copy2`. -@@ -263,8 +263,14 @@ - - If *copy_function* is given, it must be a callable that will be used to copy - each file. It will be called with the source path and the destination path --   as arguments. By default, :func:`~shutil.copy2` is used, but any function --   that supports the same signature (like :func:`~shutil.copy`) can be used. -+ as arguments. By default, :func:`~shutil.copy2` is used, but any function -+ that supports the same signature (like :func:`~shutil.copy`) can be used. -+ -+ If *dirs_exist_ok* is false (the default) and *dst* already exists, a -+ :exc:`FileExistsError` is raised. If *dirs_exist_ok* is true, the copying -+ operation will continue if it encounters existing directories, and files -+ within the *dst* tree will be overwritten by corresponding files from the -+ *src* tree. - - .. audit-event:: shutil.copytree src,dst shutil.copytree - -@@ -275,7 +281,7 @@ - .. versionchanged:: 3.2 - Added the *copy_function* argument to be able to provide a custom copy - function. -- Added the *ignore_dangling_symlinks* argument to silent dangling symlinks -+ Added the *ignore_dangling_symlinks* argument to silence dangling symlinks - errors when *symlinks* is false. - - .. versionchanged:: 3.8 -@@ -470,42 +476,7 @@ - copytree example - ~~~~~~~~~~~~~~~~ - --This example is the implementation of the :func:`copytree` function, described --above, with the docstring omitted. It demonstrates many of the other functions --provided by this module. :: -- -- def copytree(src, dst, symlinks=False): -- names = os.listdir(src) -- os.makedirs(dst) -- errors = [] -- for name in names: -- srcname = os.path.join(src, name) -- dstname = os.path.join(dst, name) -- try: -- if symlinks and os.path.islink(srcname): -- linkto = os.readlink(srcname) -- os.symlink(linkto, dstname) -- elif os.path.isdir(srcname): -- copytree(srcname, dstname, symlinks) -- else: -- copy2(srcname, dstname) -- # XXX What about devices, sockets etc.? -- except OSError as why: -- errors.append((srcname, dstname, str(why))) -- # catch the Error from the recursive copytree so that we can -- # continue with other files -- except Error as err: -- errors.extend(err.args[0]) -- try: -- copystat(src, dst) -- except OSError as why: -- # can't copy file access times on Windows -- if why.winerror is None: -- errors.extend((src, dst, str(why))) -- if errors: -- raise Error(errors) -- --Another example that uses the :func:`ignore_patterns` helper:: -+An example that uses the :func:`ignore_patterns` helper:: - - from shutil import copytree, ignore_patterns - -@@ -659,10 +630,16 @@ - - .. audit-event:: shutil.unpack_archive filename,extract_dir,format shutil.unpack_archive - -+ .. warning:: -+ -+ Never extract archives from untrusted sources without prior inspection. -+ It is possible that files are created outside of the path specified in -+ the *extract_dir* argument, e.g. members that have absolute filenames -+ starting with "/" or filenames with two dots "..". -+ - .. versionchanged:: 3.7 - Accepts a :term:`path-like object` for *filename* and *extract_dir*. - -- - .. function:: register_unpack_format(name, extensions, function[, extra_args[, description]]) - - Registers an unpack format. *name* is the name of the format and -diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst -index e1daeff48a..1a419f42d0 100644 ---- a/Doc/library/signal.rst -+++ b/Doc/library/signal.rst -@@ -46,6 +46,9 @@ - arbitrary amount of time, regardless of any signals received. The Python - signal handlers will be called when the calculation finishes. - -+* If the handler raises an exception, it will be raised "out of thin air" in -+ the main thread. See the :ref:`note below ` for a -+ discussion. - - .. _signals-and-threads: - -@@ -673,7 +676,75 @@ - if __name__ == '__main__': - main() - --Do not set :const:`SIGPIPE`'s disposition to :const:`SIG_DFL` --in order to avoid :exc:`BrokenPipeError`. Doing that would cause --your program to exit unexpectedly also whenever any socket connection --is interrupted while your program is still writing to it. -+Do not set :const:`SIGPIPE`'s disposition to :const:`SIG_DFL` in -+order to avoid :exc:`BrokenPipeError`. Doing that would cause -+your program to exit unexpectedly whenever any socket -+connection is interrupted while your program is still writing to -+it. -+ -+.. _handlers-and-exceptions: -+ -+Note on Signal Handlers and Exceptions -+-------------------------------------- -+ -+If a signal handler raises an exception, the exception will be propagated to -+the main thread and may be raised after any :term:`bytecode` instruction. Most -+notably, a :exc:`KeyboardInterrupt` may appear at any point during execution. -+Most Python code, including the standard library, cannot be made robust against -+this, and so a :exc:`KeyboardInterrupt` (or any other exception resulting from -+a signal handler) may on rare occasions put the program in an unexpected state. -+ -+To illustrate this issue, consider the following code:: -+ -+ class SpamContext: -+ def __init__(self): -+ self.lock = threading.Lock() -+ -+ def __enter__(self): -+ # If KeyboardInterrupt occurs here, everything is fine -+ self.lock.acquire() -+ # If KeyboardInterrupt occcurs here, __exit__ will not be called -+ ... -+ # KeyboardInterrupt could occur just before the function returns -+ -+ def __exit__(self, exc_type, exc_val, exc_tb): -+ ... -+ self.lock.release() -+ -+For many programs, especially those that merely want to exit on -+:exc:`KeyboardInterrupt`, this is not a problem, but applications that are -+complex or require high reliability should avoid raising exceptions from signal -+handlers. They should also avoid catching :exc:`KeyboardInterrupt` as a means -+of gracefully shutting down. Instead, they should install their own -+:const:`SIGINT` handler. Below is an example of an HTTP server that avoids -+:exc:`KeyboardInterrupt`:: -+ -+ import signal -+ import socket -+ from selectors import DefaultSelector, EVENT_READ -+ from http.server import HTTPServer, SimpleHTTPRequestHandler -+ -+ interrupt_read, interrupt_write = socket.socketpair() -+ -+ def handler(signum, frame): -+ print('Signal handler called with signal', signum) -+ interrupt_write.send(b'\0') -+ signal.signal(signal.SIGINT, handler) -+ -+ def serve_forever(httpd): -+ sel = DefaultSelector() -+ sel.register(interrupt_read, EVENT_READ) -+ sel.register(httpd, EVENT_READ) -+ -+ while True: -+ for key, _ in sel.select(): -+ if key.fileobj == interrupt_read: -+ interrupt_read.recv(1) -+ return -+ if key.fileobj == httpd: -+ httpd.handle_request() -+ -+ print("Serving on port 8000") -+ httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler) -+ serve_forever(httpd) -+ print("Shutdown...") -diff --git a/Doc/library/smtpd.rst b/Doc/library/smtpd.rst -index a39bc02404..8fd009e5b2 100644 ---- a/Doc/library/smtpd.rst -+++ b/Doc/library/smtpd.rst -@@ -15,7 +15,8 @@ - This module offers several classes to implement SMTP (email) servers. - - .. deprecated:: 3.6 -- :mod:`smtpd` will be removed in Python 3.12 (:pep:`594`). -+ :mod:`smtpd` will be removed in Python 3.12 -+ (see :pep:`PEP 594 <594#smtpd>` for details). - The `aiosmtpd `_ package is a recommended - replacement for this module. It is based on :mod:`asyncio` and provides a - more straightforward API. -diff --git a/Doc/library/sndhdr.rst b/Doc/library/sndhdr.rst -index 41bce18b9c..3ca36f270d 100644 ---- a/Doc/library/sndhdr.rst -+++ b/Doc/library/sndhdr.rst -@@ -15,7 +15,8 @@ - single: u-LAW - - .. deprecated:: 3.11 -- The :mod:`sndhdr` module is deprecated (see :pep:`594` for details). -+ The :mod:`sndhdr` module is deprecated -+ (see :pep:`PEP 594 <594#sndhdr>` for details and alternatives). - - -------------- - -diff --git a/Doc/library/spwd.rst b/Doc/library/spwd.rst -index cb31a10a52..40f50de07b 100644 ---- a/Doc/library/spwd.rst -+++ b/Doc/library/spwd.rst -@@ -7,7 +7,8 @@ - :deprecated: - - .. deprecated:: 3.11 -- The :mod:`spwd` module is deprecated (see :pep:`594` for details). -+ The :mod:`spwd` module is deprecated -+ (see :pep:`PEP 594 <594#spwd>` for details and alternatives). - - -------------- - -diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst -index 9e6950652e..d4ba3e2627 100644 ---- a/Doc/library/sqlite3.rst -+++ b/Doc/library/sqlite3.rst -@@ -373,24 +373,21 @@ - - .. method:: execute(sql[, parameters]) - -- This is a nonstandard shortcut that creates a cursor object by calling -- the :meth:`~Connection.cursor` method, calls the cursor's -- :meth:`~Cursor.execute` method with the *parameters* given, and returns -- the cursor. -+ Create a new :class:`Cursor` object and call -+ :meth:`~Cursor.execute` on it with the given *sql* and *parameters*. -+ Return the new cursor object. - - .. method:: executemany(sql[, parameters]) - -- This is a nonstandard shortcut that creates a cursor object by -- calling the :meth:`~Connection.cursor` method, calls the cursor's -- :meth:`~Cursor.executemany` method with the *parameters* given, and -- returns the cursor. -+ Create a new :class:`Cursor` object and call -+ :meth:`~Cursor.executemany` on it with the given *sql* and *parameters*. -+ Return the new cursor object. - - .. method:: executescript(sql_script) - -- This is a nonstandard shortcut that creates a cursor object by -- calling the :meth:`~Connection.cursor` method, calls the cursor's -- :meth:`~Cursor.executescript` method with the given *sql_script*, and -- returns the cursor. -+ Create a new :class:`Cursor` object and call -+ :meth:`~Cursor.executescript` on it with the given *sql_script*. -+ Return the new cursor object. - - .. method:: create_function(name, num_params, func, *, deterministic=False) - -diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst -index c3acf61d2b..af9bf798da 100644 ---- a/Doc/library/ssl.rst -+++ b/Doc/library/ssl.rst -@@ -1357,7 +1357,7 @@ - .. method:: SSLSocket.version() - - Return the actual SSL protocol version negotiated by the connection -- as a string, or ``None`` is no secure connection is established. -+ as a string, or ``None`` if no secure connection is established. - As of this writing, possible return values include ``"SSLv2"``, - ``"SSLv3"``, ``"TLSv1"``, ``"TLSv1.1"`` and ``"TLSv1.2"``. - Recent OpenSSL versions may define more return values. -@@ -1494,7 +1494,7 @@ - string must be the path to a single file in PEM format containing the - certificate as well as any number of CA certificates needed to establish - the certificate's authenticity. The *keyfile* string, if present, must -- point to a file containing the private key in. Otherwise the private -+ point to a file containing the private key. Otherwise the private - key will be taken from *certfile* as well. See the discussion of - :ref:`ssl-certificates` for more information on how the certificate - is stored in the *certfile*. -@@ -2345,7 +2345,7 @@ - context.load_cert_chain(certfile="mycertfile", keyfile="mykeyfile") - - bindsocket = socket.socket() -- bindsocket.bind(('myaddr.mydomain.com', 10023)) -+ bindsocket.bind(('myaddr.example.com', 10023)) - bindsocket.listen(5) - - When a client connects, you'll call :meth:`accept` on the socket to get the -diff --git a/Doc/library/stat.rst b/Doc/library/stat.rst -index 98219eaee9..083dc5e3bc 100644 ---- a/Doc/library/stat.rst -+++ b/Doc/library/stat.rst -@@ -109,7 +109,7 @@ - - for f in os.listdir(top): - pathname = os.path.join(top, f) -- mode = os.stat(pathname).st_mode -+ mode = os.lstat(pathname).st_mode - if S_ISDIR(mode): - # It's a directory, recurse into it - walktree(pathname, callback) -diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst -index b10909d81a..2892486757 100644 ---- a/Doc/library/stdtypes.rst -+++ b/Doc/library/stdtypes.rst -@@ -1448,7 +1448,8 @@ - depends on whether *encoding* or *errors* is given, as follows. - - If neither *encoding* nor *errors* is given, ``str(object)`` returns -- :meth:`object.__str__() `, which is the "informal" or nicely -+ :meth:`type(object).__str__(object) `, -+ which is the "informal" or nicely - printable string representation of *object*. For string objects, this is - the string itself. If *object* does not have a :meth:`~object.__str__` - method, then :func:`str` falls back to returning -@@ -2117,7 +2118,11 @@ - >>> "they're bill's friends from the UK".title() - "They'Re Bill'S Friends From The Uk" - -- A workaround for apostrophes can be constructed using regular expressions:: -+ The :func:`string.capwords` function does not have this problem, as it -+ splits words on spaces only. -+ -+ Alternatively, a workaround for apostrophes can be constructed using regular -+ expressions:: - - >>> import re - >>> def titlecase(s): -@@ -2497,16 +2502,6 @@ - since it is often more useful than e.g. ``bytes([46, 46, 46])``. You can - always convert a bytes object into a list of integers using ``list(b)``. - --.. note:: -- For Python 2.x users: In the Python 2.x series, a variety of implicit -- conversions between 8-bit strings (the closest thing 2.x offers to a -- built-in binary data type) and Unicode strings were permitted. This was a -- backwards compatibility workaround to account for the fact that Python -- originally only supported 8-bit text, and Unicode text was a later -- addition. In Python 3.x, those implicit conversions are gone - conversions -- between 8-bit binary data and Unicode text must be explicit, and bytes and -- string objects will always compare unequal. -- - - .. _typebytearray: - -@@ -3545,7 +3540,7 @@ - | | be used for Python2/3 code bases. | | - +------------+-----------------------------------------------------+-------+ - | ``'a'`` | Bytes (converts any Python object using | \(5) | --| | ``repr(obj).encode('ascii','backslashreplace)``). | | -+| | ``repr(obj).encode('ascii', 'backslashreplace')``). | | - +------------+-----------------------------------------------------+-------+ - | ``'r'`` | ``'r'`` is an alias for ``'a'`` and should only | \(7) | - | | be used for Python2/3 code bases. | | -@@ -4306,10 +4301,6 @@ - however, that since computers store floating-point numbers as approximations it - is usually unwise to use them as dictionary keys.) - --Dictionaries can be created by placing a comma-separated list of ``key: value`` --pairs within braces, for example: ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098: --'jack', 4127: 'sjoerd'}``, or by the :class:`dict` constructor. -- - .. class:: dict(**kwargs) - dict(mapping, **kwargs) - dict(iterable, **kwargs) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst -index f9e9f63f21..9af070c61f 100644 +index 45eff7ffcc..9af070c61f 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -25,6 +25,11 @@ @@ -3374,1217 +32,6 @@ index f9e9f63f21..9af070c61f 100644 Using the :mod:`subprocess` Module ---------------------------------- -@@ -214,7 +219,9 @@ - .. exception:: CalledProcessError - - Subclass of :exc:`SubprocessError`, raised when a process run by -- :func:`check_call` or :func:`check_output` returns a non-zero exit status. -+ :func:`check_call`, :func:`check_output`, or :func:`run` (with ``check=True``) -+ returns a non-zero exit status. -+ - - .. attribute:: returncode - -diff --git a/Doc/library/sunau.rst b/Doc/library/sunau.rst -index cfb1257f58..b4d996e67e 100644 ---- a/Doc/library/sunau.rst -+++ b/Doc/library/sunau.rst -@@ -10,7 +10,8 @@ - **Source code:** :source:`Lib/sunau.py` - - .. deprecated:: 3.11 -- The :mod:`sunau` module is deprecated (see :pep:`594` for details). -+ The :mod:`sunau` module is deprecated -+ (see :pep:`PEP 594 <594#sunau>` for details). - - -------------- - -diff --git a/Doc/library/superseded.rst b/Doc/library/superseded.rst -index e3f9b0d37f..b38f16691f 100644 ---- a/Doc/library/superseded.rst -+++ b/Doc/library/superseded.rst -@@ -20,9 +20,10 @@ - crypt.rst - imghdr.rst - imp.rst -+ mailcap.rst - msilib.rst -- nntplib.rst - nis.rst -+ nntplib.rst - optparse.rst - ossaudiodev.rst - pipes.rst -diff --git a/Doc/library/telnetlib.rst b/Doc/library/telnetlib.rst -index 97b0a713e4..48a927c8ac 100644 ---- a/Doc/library/telnetlib.rst -+++ b/Doc/library/telnetlib.rst -@@ -12,7 +12,8 @@ - .. index:: single: protocol; Telnet - - .. deprecated:: 3.11 -- The :mod:`telnetlib` module is deprecated (see :pep:`594` for details). -+ The :mod:`telnetlib` module is deprecated -+ (see :pep:`PEP 594 <594#telnetlib>` for details and alternatives). - - -------------- - -diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst -index 6bd7d003f5..fe47a4f061 100644 ---- a/Doc/library/tempfile.rst -+++ b/Doc/library/tempfile.rst -@@ -84,7 +84,7 @@ - file-like object. Whether the name can be - used to open the file a second time, while the named temporary file is - still open, varies across platforms (it can be so used on Unix; it cannot -- on Windows NT or later). If *delete* is true (the default), the file is -+ on Windows). If *delete* is true (the default), the file is - deleted as soon as it is closed. - The returned object is always a file-like object whose :attr:`!file` - attribute is the underlying true file object. This file-like object can -diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst -index 999852a58c..401887f216 100644 ---- a/Doc/library/tkinter.rst -+++ b/Doc/library/tkinter.rst -@@ -63,16 +63,72 @@ - from tkinter import ttk - - --.. class:: Tk(screenName=None, baseName=None, className='Tk', useTk=1) -- -- The :class:`Tk` class is instantiated without arguments. This creates a toplevel -- widget of Tk which usually is the main window of an application. Each instance -- has its own associated Tcl interpreter. -- -- .. FIXME: The following keyword arguments are currently recognized: -- -- --.. function:: Tcl(screenName=None, baseName=None, className='Tk', useTk=0) -+.. class:: Tk(screenName=None, baseName=None, className='Tk', useTk=True, sync=False, use=None) -+ -+ Construct a toplevel Tk widget, which is usually the main window of an -+ application, and initialize a Tcl interpreter for this widget. Each -+ instance has its own associated Tcl interpreter. -+ -+ The :class:`Tk` class is typically instantiated using all default values. -+ However, the following keyword arguments are currently recognized: -+ -+ *screenName* -+ When given (as a string), sets the :envvar:`DISPLAY` environment -+ variable. (X11 only) -+ *baseName* -+ Name of the profile file. By default, *baseName* is derived from the -+ program name (``sys.argv[0]``). -+ *className* -+ Name of the widget class. Used as a profile file and also as the name -+ with which Tcl is invoked (*argv0* in *interp*). -+ *useTk* -+ If ``True``, initialize the Tk subsystem. The :func:`tkinter.Tcl() ` -+ function sets this to ``False``. -+ *sync* -+ If ``True``, execute all X server commands synchronously, so that errors -+ are reported immediately. Can be used for debugging. (X11 only) -+ *use* -+ Specifies the *id* of the window in which to embed the application, -+ instead of it being created as an independent toplevel window. *id* must -+ be specified in the same way as the value for the -use option for -+ toplevel widgets (that is, it has a form like that returned by -+ :meth:`winfo_id`). -+ -+ Note that on some platforms this will only work correctly if *id* refers -+ to a Tk frame or toplevel that has its -container option enabled. -+ -+ :class:`Tk` reads and interprets profile files, named -+ :file:`.{className}.tcl` and :file:`.{baseName}.tcl`, into the Tcl -+ interpreter and calls :func:`exec` on the contents of -+ :file:`.{className}.py` and :file:`.{baseName}.py`. The path for the -+ profile files is the :envvar:`HOME` environment variable or, if that -+ isn't defined, then :attr:`os.curdir`. -+ -+ .. attribute:: tk -+ -+ The Tk application object created by instantiating :class:`Tk`. This -+ provides access to the Tcl interpreter. Each widget that is attached -+ the same instance of :class:`Tk` has the same value for its :attr:`tk` -+ attribute. -+ -+ .. attribute:: master -+ -+ The widget object that contains this widget. For :class:`Tk`, the -+ *master* is :const:`None` because it is the main window. The terms -+ *master* and *parent* are similar and sometimes used interchangeably -+ as argument names; however, calling :meth:`winfo_parent` returns a -+ string of the widget name whereas :attr:`master` returns the object. -+ *parent*/*child* reflects the tree-like relationship while -+ *master*/*slave* reflects the container structure. -+ -+ .. attribute:: children -+ -+ The immediate descendants of this widget as a :class:`dict` with the -+ child widget names as the keys and the child instance objects as the -+ values. -+ -+ -+.. function:: Tcl(screenName=None, baseName=None, className='Tk', useTk=False) - - The :func:`Tcl` function is a factory function which creates an object much like - that created by the :class:`Tk` class, except that it does not initialize the Tk -diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst -index 2689c7f293..f3c6a2e074 100644 ---- a/Doc/library/typing.rst -+++ b/Doc/library/typing.rst -@@ -103,7 +103,7 @@ - NewType - ======= - --Use the :func:`NewType` helper function to create distinct types:: -+Use the :func:`NewType` helper to create distinct types:: - - from typing import NewType - -@@ -132,7 +132,7 @@ - - Note that these checks are enforced only by the static type checker. At runtime, - the statement ``Derived = NewType('Derived', Base)`` will make ``Derived`` a --function that immediately returns whatever parameter you pass it. That means -+callable that immediately returns whatever parameter you pass it. That means - the expression ``Derived(some_value)`` does not create a new class or introduce - any overhead beyond that of a regular function call. - -@@ -194,6 +194,10 @@ - on_error: Callable[[int, Exception], None]) -> None: - # Body - -+ async def on_update(value: str) -> None: -+ # Body -+ callback: Callable[[str], Awaitable[None]] = on_update -+ - It is possible to declare the return type of a callable without specifying - the call signature by substituting a literal ellipsis - for the list of arguments in the type hint: ``Callable[..., ReturnType]``. -@@ -431,7 +435,7 @@ - Nominal vs structural subtyping - =============================== - --Initially :pep:`484` defined Python static type system as using -+Initially :pep:`484` defined the Python static type system as using - *nominal subtyping*. This means that a class ``A`` is allowed where - a class ``B`` is expected if and only if ``A`` is a subclass of ``B``. - -@@ -749,7 +753,7 @@ - ``no_type_check`` functionality that currently exists in the ``typing`` - module which completely disables typechecking annotations on a function - or a class, the ``Annotated`` type allows for both static typechecking -- of ``T`` (e.g., via mypy or Pyre, which can safely ignore ``x``) -+ of ``T`` (which can safely ignore ``x``) - together with runtime access to ``x`` within a specific application. - - Ultimately, the responsibility of how to interpret the annotations (if -@@ -930,7 +934,7 @@ - self.radius = radius - - # Use a type variable to show that the return type -- # will always be an instance of whatever `cls` is -+ # will always be an instance of whatever ``cls`` is - @classmethod - def with_circumference(cls: type[C], circumference: float) -> C: - """Create a circle with the specified circumference""" -@@ -1060,7 +1064,7 @@ - The resulting class has an extra attribute ``__annotations__`` giving a - dict that maps the field names to the field types. (The field names are in - the ``_fields`` attribute and the default values are in the -- ``_field_defaults`` attribute both of which are part of the namedtuple -+ ``_field_defaults`` attribute, both of which are part of the :func:`~collections.namedtuple` - API.) - - ``NamedTuple`` subclasses can also have docstrings and methods:: -@@ -1136,7 +1140,7 @@ - Point2D = TypedDict('Point2D', x=int, y=int, label=str) - - The functional syntax should also be used when any of the keys are not valid -- :ref:`identifiers`, for example because they are keywords or contain hyphens. -+ :ref:`identifiers `, for example because they are keywords or contain hyphens. - Example:: - - # raises SyntaxError -@@ -1178,7 +1182,7 @@ - y: int - z: int - -- A ``TypedDict`` cannot inherit from a non-TypedDict class, -+ A ``TypedDict`` cannot inherit from a non-\ ``TypedDict`` class, - notably including :class:`Generic`. For example:: - - class X(TypedDict): -@@ -1586,7 +1590,7 @@ - - .. class:: Hashable - -- An alias to :class:`collections.abc.Hashable` -+ An alias to :class:`collections.abc.Hashable`. - - .. class:: Reversible(Iterable[T_co]) - -@@ -1598,7 +1602,7 @@ - - .. class:: Sized - -- An alias to :class:`collections.abc.Sized` -+ An alias to :class:`collections.abc.Sized`. - - Asynchronous programming - """""""""""""""""""""""" -@@ -1803,7 +1807,7 @@ - ... - class Sub(Base): - def done(self) -> None: # Error reported by type checker -- ... -+ ... - - @final - class Leaf: -@@ -1940,10 +1944,10 @@ - - .. note:: - -- If ``from __future__ import annotations`` is used in Python 3.7 or later, -+ If ``from __future__ import annotations`` is used, - annotations are not evaluated at function definition time. -- Instead, they are stored as strings in ``__annotations__``, -- This makes it unnecessary to use quotes around the annotation. -+ Instead, they are stored as strings in ``__annotations__``. -+ This makes it unnecessary to use quotes around the annotation - (see :pep:`563`). - - .. versionadded:: 3.5.2 -diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst -index 3fbb1b6b83..537fbcf4e0 100644 ---- a/Doc/library/unittest.mock.rst -+++ b/Doc/library/unittest.mock.rst -@@ -2549,7 +2549,7 @@ - - >>> mock = Mock(name='Thing', return_value=None) - >>> mock(1, 2, 3) -- >>> mock.assret_called_once_with(4, 5, 6) -+ >>> mock.assret_called_once_with(4, 5, 6) # Intentional typo! - - Your tests can pass silently and incorrectly because of the typo. - -@@ -2569,7 +2569,7 @@ - - >>> from urllib import request - >>> mock = Mock(spec=request.Request) -- >>> mock.assret_called_with -+ >>> mock.assret_called_with # Intentional typo! - Traceback (most recent call last): - ... - AttributeError: Mock object has no attribute 'assret_called_with' -@@ -2581,7 +2581,7 @@ - - >>> mock.has_data() - -- >>> mock.has_data.assret_called_with() -+ >>> mock.has_data.assret_called_with() # Intentional typo! - - Auto-speccing solves this problem. You can either pass ``autospec=True`` to - :func:`patch` / :func:`patch.object` or use the :func:`create_autospec` function to create a -@@ -2624,7 +2624,7 @@ - - >>> req.add_header('spam', 'eggs') - -- >>> req.add_header.assret_called_with -+ >>> req.add_header.assret_called_with # Intentional typo! - Traceback (most recent call last): - ... - AttributeError: Mock object has no attribute 'assret_called_with' -diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst -index 9080757926..a000428c8b 100644 ---- a/Doc/library/unittest.rst -+++ b/Doc/library/unittest.rst -@@ -2418,7 +2418,7 @@ - after :func:`setUpModule` if :func:`setUpModule` raises an exception. - - It is responsible for calling all the cleanup functions added by -- :func:`addCleanupModule`. If you need cleanup functions to be called -+ :func:`addModuleCleanup`. If you need cleanup functions to be called - *prior* to :func:`tearDownModule` then you can call - :func:`doModuleCleanups` yourself. - -diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst -index bfb994e315..ae9abe7d48 100644 ---- a/Doc/library/urllib.request.rst -+++ b/Doc/library/urllib.request.rst -@@ -213,6 +213,7 @@ - (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11"``, while - :mod:`urllib`'s default user agent string is - ``"Python-urllib/2.6"`` (on Python 2.6). -+ All header keys are sent in camel case. - - An appropriate ``Content-Type`` header should be included if the *data* - argument is present. If this header has not been provided and *data* -@@ -541,7 +542,8 @@ - name, and later calls will overwrite previous calls in case the *key* collides. - Currently, this is no loss of HTTP functionality, since all headers which have - meaning when used more than once have a (header-specific) way of gaining the -- same functionality using only one header. -+ same functionality using only one header. Note that headers added using -+ this method are also added to redirected requests. - - - .. method:: Request.add_unredirected_header(key, header) -@@ -732,7 +734,7 @@ - - This method, if implemented, will be called by the parent - :class:`OpenerDirector`. It should return a file-like object as described in -- the return value of the :meth:`open` of :class:`OpenerDirector`, or ``None``. -+ the return value of the :meth:`~OpenerDirector.open` method of :class:`OpenerDirector`, or ``None``. - It should raise :exc:`~urllib.error.URLError`, unless a truly exceptional - thing happens (for example, :exc:`MemoryError` should not be mapped to - :exc:`URLError`). -diff --git a/Doc/library/uu.rst b/Doc/library/uu.rst -index c341bc83dc..026ec415c9 100644 ---- a/Doc/library/uu.rst -+++ b/Doc/library/uu.rst -@@ -10,7 +10,9 @@ - **Source code:** :source:`Lib/uu.py` - - .. deprecated:: 3.11 -- The :mod:`uu` module is deprecated (see :pep:`594` for details). -+ The :mod:`uu` module is deprecated -+ (see :pep:`PEP 594 <594#uu-and-the-uu-encoding>` for details). -+ :mod:`base64` is a modern alternative. - - -------------- - -diff --git a/Doc/library/xdrlib.rst b/Doc/library/xdrlib.rst -index 060b2e2c60..a3124a9865 100644 ---- a/Doc/library/xdrlib.rst -+++ b/Doc/library/xdrlib.rst -@@ -12,7 +12,8 @@ - single: External Data Representation - - .. deprecated:: 3.11 -- The :mod:`xdrlib` module is deprecated (see :pep:`594` for details). -+ The :mod:`xdrlib` module is deprecated -+ (see :pep:`PEP 594 <594#xdrlib>` for details). - - -------------- - -diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst -index f4bccf6609..92248e7516 100644 ---- a/Doc/library/xml.etree.elementtree.rst -+++ b/Doc/library/xml.etree.elementtree.rst -@@ -363,13 +363,6 @@ - |--> Commander Clement - - --Additional resources --^^^^^^^^^^^^^^^^^^^^ -- --See http://effbot.org/zone/element-index.htm for tutorials and links to other --docs. -- -- - .. _elementtree-xpath: - - XPath support -diff --git a/Doc/library/xml.sax.handler.rst b/Doc/library/xml.sax.handler.rst -index ae0877ca90..c7831ce3d7 100644 ---- a/Doc/library/xml.sax.handler.rst -+++ b/Doc/library/xml.sax.handler.rst -@@ -141,7 +141,7 @@ - .. data:: property_xml_string - - | value: ``"http://xml.org/sax/properties/xml-string"`` -- | data type: String -+ | data type: Bytes - | description: The literal string of characters that was the source for the - current event. - | access: read-only -diff --git a/Doc/library/xmlrpc.client.rst b/Doc/library/xmlrpc.client.rst -index 3240381953..30c5a710e0 100644 ---- a/Doc/library/xmlrpc.client.rst -+++ b/Doc/library/xmlrpc.client.rst -@@ -169,12 +169,6 @@ - `XML-RPC Specification `_ - The official specification. - -- `Unofficial XML-RPC Errata `_ -- Fredrik Lundh's "unofficial errata, intended to clarify certain -- details in the XML-RPC specification, as well as hint at -- 'best practices' to use when designing your own XML-RPC -- implementations." -- - .. _serverproxy-objects: - - ServerProxy Objects -diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst -index 3d1e8d8c46..de508cadcc 100644 ---- a/Doc/library/zipfile.rst -+++ b/Doc/library/zipfile.rst -@@ -289,7 +289,7 @@ - compressed text files in :term:`universal newlines` mode. - - .. versionchanged:: 3.6 -- :meth:`open` can now be used to write files into the archive with the -+ :meth:`ZipFile.open` can now be used to write files into the archive with the - ``mode='w'`` option. - - .. versionchanged:: 3.6 -diff --git a/Doc/make.bat b/Doc/make.bat -index 7fde063642..ac66b56e9a 100644 ---- a/Doc/make.bat -+++ b/Doc/make.bat -@@ -13,7 +13,7 @@ - %PYTHON% -c "import sphinx" > nul 2> nul - if errorlevel 1 ( - echo Installing sphinx with %PYTHON% -- %PYTHON% -m pip install sphinx==2.2.0 -+ %PYTHON% -m pip install -r requirements.txt - if errorlevel 1 exit /B - ) - set SPHINXBUILD=%PYTHON% -c "import sphinx.cmd.build, sys; sys.exit(sphinx.cmd.build.main())" -@@ -30,6 +30,7 @@ - %PYTHON% -c "import blurb" > nul 2> nul - if errorlevel 1 ( - echo Installing blurb with %PYTHON% -+ rem Should have been installed with Sphinx earlier - %PYTHON% -m pip install blurb - if errorlevel 1 exit /B - ) -diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst -index 84320f41f6..033d65fd7c 100644 ---- a/Doc/reference/datamodel.rst -+++ b/Doc/reference/datamodel.rst -@@ -2854,7 +2854,8 @@ - :exc:`StopIteration`, or other exception) is the same as when - iterating over the :meth:`__await__` return value, described above. - --.. method:: coroutine.throw(type[, value[, traceback]]) -+.. method:: coroutine.throw(value) -+ coroutine.throw(type[, value[, traceback]]) - - Raises the specified exception in the coroutine. This method delegates - to the :meth:`~generator.throw` method of the iterator that caused -diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst -index 4ffb651210..502c022501 100644 ---- a/Doc/reference/expressions.rst -+++ b/Doc/reference/expressions.rst -@@ -557,14 +557,27 @@ - could receive the value. - - --.. method:: generator.throw(type[, value[, traceback]]) -+.. method:: generator.throw(value) -+ generator.throw(type[, value[, traceback]]) - -- Raises an exception of type ``type`` at the point where the generator was paused, -+ Raises an exception at the point where the generator was paused, - and returns the next value yielded by the generator function. If the generator - exits without yielding another value, a :exc:`StopIteration` exception is - raised. If the generator function does not catch the passed-in exception, or - raises a different exception, then that exception propagates to the caller. - -+ In typical use, this is called with a single exception instance similar to the -+ way the :keyword:`raise` keyword is used. -+ -+ For backwards compatability, however, the second signature is -+ supported, following a convention from older versions of Python. -+ The *type* argument should be an exception class, and *value* -+ should be an exception instance. If the *value* is not provided, the -+ *type* constructor is called to get an instance. If *traceback* -+ is provided, it is set on the exception, otherwise any existing -+ :attr:`~BaseException.__traceback__` attribute stored in *value* may -+ be cleared. -+ - .. index:: exception: GeneratorExit - - -diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst -index 2c84a97a26..2d9802b0f3 100644 ---- a/Doc/reference/import.rst -+++ b/Doc/reference/import.rst -@@ -483,21 +483,19 @@ - spam/ - __init__.py - foo.py -- bar.py - --and ``spam/__init__.py`` has the following lines in it:: -+and ``spam/__init__.py`` has the following line in it:: - - from .foo import Foo -- from .bar import Bar - --then executing the following puts a name binding to ``foo`` and ``bar`` in the -+then executing the following puts name bindings for ``foo`` and ``Foo`` in the - ``spam`` module:: - - >>> import spam - >>> spam.foo - -- >>> spam.bar -- -+ >>> spam.Foo -+ - - Given Python's familiar name binding rules this might seem surprising, but - it's actually a fundamental feature of the import system. The invariant -diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst -index 77e0578f5d..b8f9ca21c0 100644 ---- a/Doc/reference/lexical_analysis.rst -+++ b/Doc/reference/lexical_analysis.rst -@@ -101,12 +101,11 @@ - (``b'\xef\xbb\xbf'``), the declared file encoding is UTF-8 (this is supported, - among others, by Microsoft's :program:`notepad`). - --If an encoding is declared, the encoding name must be recognized by Python. The -+If an encoding is declared, the encoding name must be recognized by Python -+(see :ref:`standard-encodings`). The - encoding is used for all lexical analysis, including string literals, comments - and identifiers. - --.. XXX there should be a list of supported encodings. -- - - .. _explicit-joining: - -@@ -448,9 +447,11 @@ - In plain English: Both types of literals can be enclosed in matching single quotes - (``'``) or double quotes (``"``). They can also be enclosed in matching groups - of three single or double quotes (these are generally referred to as --*triple-quoted strings*). The backslash (``\``) character is used to escape --characters that otherwise have a special meaning, such as newline, backslash --itself, or the quote character. -+*triple-quoted strings*). The backslash (``\``) character is used to give special -+meaning to otherwise ordinary characters like ``n``, which means 'newline' when -+escaped (``\n``). It can also be used to escape characters that otherwise have a -+special meaning, such as newline, backslash itself, or the quote character. -+See :ref:`escape sequences ` below for examples. - - .. index:: - single: b'; bytes literal -@@ -509,6 +510,8 @@ - single: \u; escape sequence - single: \U; escape sequence - -+.. _escape-sequences: -+ - Unless an ``'r'`` or ``'R'`` prefix is present, escape sequences in string and - bytes literals are interpreted according to rules similar to those used by - Standard C. The recognized escape sequences are: -diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst -index 79f4011307..2c2c2453e4 100644 ---- a/Doc/reference/simple_stmts.rst -+++ b/Doc/reference/simple_stmts.rst -@@ -124,9 +124,7 @@ - * If the target list is a single target with no trailing comma, - optionally in parentheses, the object is assigned to that target. - --* Else: The object must be an iterable with the same number of -- items as there are targets in the target list, and the items are assigned, -- from left to right, to the corresponding targets. -+* Else: - - * If the target list contains one target prefixed with an asterisk, called a - "starred" target: The object must be an iterable with at least as many items -@@ -797,9 +795,9 @@ - Examples:: - - import foo # foo imported and bound locally -- import foo.bar.baz # foo.bar.baz imported, foo bound locally -- import foo.bar.baz as fbb # foo.bar.baz imported and bound as fbb -- from foo.bar import baz # foo.bar.baz imported and bound as baz -+ import foo.bar.baz # foo, foo.bar, and foo.bar.baz imported, foo bound locally -+ import foo.bar.baz as fbb # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as fbb -+ from foo.bar import baz # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as baz - from foo import attr # foo imported and foo.attr bound as attr - - .. index:: single: * (asterisk); import statement -diff --git a/Doc/requirements.txt b/Doc/requirements.txt -index 1b75aed035..cf659a0fba 100644 ---- a/Doc/requirements.txt -+++ b/Doc/requirements.txt -@@ -8,6 +8,8 @@ - # version 2.4.4. It can be removed after bumping Sphinx version to at - # least 3.5.4. - docutils==0.17.1 -+# Jinja version is pinned to a version compatible with Sphinx version 2.4.4. -+jinja2==3.0.3 - - blurb - -diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py -index 74de6ffa75..4575300f53 100644 ---- a/Doc/tools/extensions/pyspecific.py -+++ b/Doc/tools/extensions/pyspecific.py -@@ -43,7 +43,8 @@ - import suspicious - - --ISSUE_URI = 'https://bugs.python.org/issue%s' -+ISSUE_URI = 'https://bugs.python.org/issue?@action=redirect&bpo=%s' -+GH_ISSUE_URI = 'https://github.com/python/cpython/issues/%s' - SOURCE_URI = 'https://github.com/python/cpython/tree/3.9/%s' - - # monkey-patch reST parser to disable alphabetic and roman enumerated lists -@@ -58,11 +59,33 @@ - - def issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): - issue = utils.unescape(text) -+ # sanity check: there are no bpo issues within these two values -+ if 47261 < int(issue) < 400000: -+ msg = inliner.reporter.error(f'The BPO ID {text!r} seems too high -- ' -+ 'use :gh:`...` for GitHub IDs', line=lineno) -+ prb = inliner.problematic(rawtext, rawtext, msg) -+ return [prb], [msg] - text = 'bpo-' + issue - refnode = nodes.reference(text, text, refuri=ISSUE_URI % issue) - return [refnode], [] - - -+# Support for marking up and linking to GitHub issues -+ -+def gh_issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): -+ issue = utils.unescape(text) -+ # sanity check: all GitHub issues have ID >= 32426 -+ # even though some of them are also valid BPO IDs -+ if int(issue) < 32426: -+ msg = inliner.reporter.error(f'The GitHub ID {text!r} seems too low -- ' -+ 'use :issue:`...` for BPO IDs', line=lineno) -+ prb = inliner.problematic(rawtext, rawtext, msg) -+ return [prb], [msg] -+ text = 'gh-' + issue -+ refnode = nodes.reference(text, text, refuri=GH_ISSUE_URI % issue) -+ return [refnode], [] -+ -+ - # Support for linking to Python source files easily - - def source_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): -@@ -368,7 +391,8 @@ - - # Support for including Misc/NEWS - --issue_re = re.compile('(?:[Ii]ssue #|bpo-)([0-9]+)') -+issue_re = re.compile('(?:[Ii]ssue #|bpo-)([0-9]+)', re.I) -+gh_issue_re = re.compile('(?:gh-issue-|gh-)([0-9]+)', re.I) - whatsnew_re = re.compile(r"(?im)^what's new in (.*?)\??$") - - -@@ -395,8 +419,9 @@ - text = 'The NEWS file is not available.' - node = nodes.strong(text, text) - return [node] -- content = issue_re.sub(r'`bpo-\1 `__', -- content) -+ content = issue_re.sub(r':issue:`\1`', content) -+ # Fallback handling for the GitHub issue -+ content = gh_issue_re.sub(r':gh:`\1`', content) - content = whatsnew_re.sub(r'\1', content) - # remove first 3 lines as they are the main heading - lines = ['.. default-role:: obj', ''] + content.splitlines()[3:] -@@ -576,6 +601,7 @@ - - def setup(app): - app.add_role('issue', issue_role) -+ app.add_role('gh', gh_issue_role) - app.add_role('source', source_role) - app.add_directive('impl-detail', ImplementationDetail) - app.add_directive('availability', Availability) -diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv -index 67e449316e..3eb3d7954f 100644 ---- a/Doc/tools/susp-ignored.csv -+++ b/Doc/tools/susp-ignored.csv -@@ -370,4 +370,4 @@ - library/importlib.metadata,,:main,"EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')" - library/importlib.metadata,,`,loading the metadata for packages for the indicated ``context``. - library/re,,`,"`" --library/typing,,`, # will always be an instance of whatever `cls` is -+library/typing,,`, # will always be an instance of whatever ``cls`` is -diff --git a/Doc/tutorial/inputoutput.rst b/Doc/tutorial/inputoutput.rst -index 7f83c4d461..b50063654e 100644 ---- a/Doc/tutorial/inputoutput.rst -+++ b/Doc/tutorial/inputoutput.rst -@@ -279,11 +279,12 @@ - object: file - - :func:`open` returns a :term:`file object`, and is most commonly used with --two arguments: ``open(filename, mode)``. -+two positional arguments and one keyword argument: -+``open(filename, mode, encoding=None)`` - - :: - -- >>> f = open('workfile', 'w') -+ >>> f = open('workfile', 'w', encoding="utf-8") - - .. XXX str(f) is - -@@ -300,11 +301,14 @@ - omitted. - - Normally, files are opened in :dfn:`text mode`, that means, you read and write --strings from and to the file, which are encoded in a specific encoding. If --encoding is not specified, the default is platform dependent (see --:func:`open`). ``'b'`` appended to the mode opens the file in --:dfn:`binary mode`: now the data is read and written in the form of bytes --objects. This mode should be used for all files that don't contain text. -+strings from and to the file, which are encoded in a specific *encoding*. -+If *encoding* is not specified, the default is platform dependent -+(see :func:`open`). -+Because UTF-8 is the modern de-facto standard, ``encoding="utf-8"`` is -+recommended unless you know that you need to use a different encoding. -+Appending a ``'b'`` to the mode opens the file in :dfn:`binary mode`. -+Binary mode data is read and written as :class:`bytes` objects. -+You can not specify *encoding* when opening file in binary mode. - - In text mode, the default when reading is to convert platform-specific line - endings (``\n`` on Unix, ``\r\n`` on Windows) to just ``\n``. When writing in -@@ -320,7 +324,7 @@ - point. Using :keyword:`!with` is also much shorter than writing - equivalent :keyword:`try`\ -\ :keyword:`finally` blocks:: - -- >>> with open('workfile') as f: -+ >>> with open('workfile', encoding="utf-8") as f: - ... read_data = f.read() - - >>> # We can check that the file has been automatically closed. -@@ -490,11 +494,15 @@ - - json.dump(x, f) - --To decode the object again, if ``f`` is a :term:`text file` object which has --been opened for reading:: -+To decode the object again, if ``f`` is a :term:`binary file` or -+:term:`text file` object which has been opened for reading:: - - x = json.load(f) - -+.. note:: -+ JSON files must be encoded in UTF-8. Use ``encoding="utf-8"`` when opening -+ JSON file as a :term:`text file` for both of reading and writing. -+ - This simple serialization technique can handle lists and dictionaries, but - serializing arbitrary class instances in JSON requires a bit of extra effort. - The reference for the :mod:`json` module contains an explanation of this. -diff --git a/Doc/tutorial/modules.rst b/Doc/tutorial/modules.rst -index f6250291c8..c5cca91791 100644 ---- a/Doc/tutorial/modules.rst -+++ b/Doc/tutorial/modules.rst -@@ -183,7 +183,8 @@ - .. index:: triple: module; search; path - - When a module named :mod:`spam` is imported, the interpreter first searches for --a built-in module with that name. If not found, it then searches for a file -+a built-in module with that name. These module names are listed in -+:data:`sys.builtin_module_names`. If not found, it then searches for a file - named :file:`spam.py` in a list of directories given by the variable - :data:`sys.path`. :data:`sys.path` is initialized from these locations: - -@@ -504,7 +505,7 @@ - __all__ = ["echo", "surround", "reverse"] - - This would mean that ``from sound.effects import *`` would import the three --named submodules of the :mod:`sound` package. -+named submodules of the :mod:`sound.effects` package. - - If ``__all__`` is not defined, the statement ``from sound.effects import *`` - does *not* import all submodules from the package :mod:`sound.effects` into the -diff --git a/Doc/tutorial/stdlib.rst b/Doc/tutorial/stdlib.rst -index ab64ca6d40..32dda97c79 100644 ---- a/Doc/tutorial/stdlib.rst -+++ b/Doc/tutorial/stdlib.rst -@@ -78,8 +78,9 @@ - - import argparse - -- parser = argparse.ArgumentParser(prog = 'top', -- description = 'Show top lines from each file') -+ parser = argparse.ArgumentParser( -+ prog='top', -+ description='Show top lines from each file') - parser.add_argument('filenames', nargs='+') - parser.add_argument('-l', '--lines', type=int, default=10) - args = parser.parse_args() -@@ -326,7 +327,7 @@ - sophisticated and robust capabilities of its larger packages. For example: - - * The :mod:`xmlrpc.client` and :mod:`xmlrpc.server` modules make implementing -- remote procedure calls into an almost trivial task. Despite the modules -+ remote procedure calls into an almost trivial task. Despite the modules' - names, no direct knowledge or handling of XML is needed. - - * The :mod:`email` package is a library for managing email messages, including -diff --git a/Doc/using/mac.rst b/Doc/using/mac.rst -index 2f132a96be..f7db038430 100644 ---- a/Doc/using/mac.rst -+++ b/Doc/using/mac.rst -@@ -160,7 +160,7 @@ - - The standard tool for deploying standalone Python applications on the Mac is - :program:`py2app`. More information on installing and using py2app can be found --at http://undefined.org/python/#py2app. -+at https://pypi.org/project/py2app/. - - - Other Resources -diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst -index be21b55fe8..abb74637fd 100644 ---- a/Doc/using/windows.rst -+++ b/Doc/using/windows.rst -@@ -374,7 +374,9 @@ - subdirectory. By default, the subdirectory is named the same as the package, - and without the ``-ExcludeVersion`` option this name will include the specific - version installed. Inside the subdirectory is a ``tools`` directory that --contains the Python installation:: -+contains the Python installation: -+ -+.. code-block:: doscon - - # Without -ExcludeVersion - > .\python.3.5.2\tools\python.exe -V -@@ -421,7 +423,7 @@ - .. note:: - - The embedded distribution does not include the `Microsoft C Runtime -- `_ and it is -+ `_ and it is - the responsibility of the application installer to provide this. The - runtime may have already been installed on a user's system previously or - automatically via Windows Update, and can be detected by finding -@@ -555,27 +557,22 @@ - Windows will concatenate User variables *after* System variables, which may - cause unexpected results when modifying :envvar:`PATH`. - -- The :envvar:`PYTHONPATH` variable is used by all versions of Python 2 and -- Python 3, so you should not permanently configure this variable unless it -- only includes code that is compatible with all of your installed Python -+ The :envvar:`PYTHONPATH` variable is used by all versions of Python, -+ so you should not permanently configure it unless the listed paths -+ only include code that is compatible with all of your installed Python - versions. - - .. seealso:: - -- https://www.microsoft.com/en-us/wdsi/help/folder-variables -- Environment variables in Windows NT -+ https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables -+ Overview of environment variables on Windows - -- https://technet.microsoft.com/en-us/library/cc754250.aspx -- The SET command, for temporarily modifying environment variables -+ https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/set_1 -+ The ``set`` command, for temporarily modifying environment variables - -- https://technet.microsoft.com/en-us/library/cc755104.aspx -- The SETX command, for permanently modifying environment variables -+ https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/setx -+ The ``setx`` command, for permanently modifying environment variables - -- https://support.microsoft.com/en-us/help/310519/how-to-manage-environment-variables-in-windows-xp -- How To Manage Environment Variables in Windows XP -- -- https://www.chem.gla.ac.uk/~louis/software/faq/q1.html -- Setting Environment variables, Louis J. Farrugia - - .. _windows-path-mod: - -@@ -679,9 +676,7 @@ - System-wide installations of Python 3.3 and later will put the launcher on your - :envvar:`PATH`. The launcher is compatible with all available versions of - Python, so it does not matter which version is installed. To check that the --launcher is available, execute the following command in Command Prompt: -- --:: -+launcher is available, execute the following command in Command Prompt:: - - py - -@@ -689,26 +684,20 @@ - started - it can be exited as normal, and any additional command-line - arguments specified will be sent directly to Python. - --If you have multiple versions of Python installed (e.g., 2.7 and |version|) you --will have noticed that Python |version| was started - to launch Python 2.7, try --the command: -- --:: -+If you have multiple versions of Python installed (e.g., 3.7 and |version|) you -+will have noticed that Python |version| was started - to launch Python 3.7, try -+the command:: - -- py -2.7 -+ py -3.7 - --If you want the latest version of Python 2.x you have installed, try the --command: -- --:: -+If you want the latest version of Python 2 you have installed, try the -+command:: - - py -2 - --You should find the latest version of Python 2.x starts. -- --If you see the following error, you do not have the launcher installed: -+You should find the latest version of Python 3.x starts. - --:: -+If you see the following error, you do not have the launcher installed:: - - 'py' is not recognized as an internal or external command, - operable program or batch file. -@@ -716,6 +705,12 @@ - Per-user installations of Python do not add the launcher to :envvar:`PATH` - unless the option was selected on installation. - -+The command:: -+ -+ py --list -+ -+displays the currently installed version(s) of Python. -+ - Virtual environments - ^^^^^^^^^^^^^^^^^^^^ - -@@ -740,9 +735,7 @@ - import sys - sys.stdout.write("hello from Python %s\n" % (sys.version,)) - --From the directory in which hello.py lives, execute the command: -- --:: -+From the directory in which hello.py lives, execute the command:: - - py hello.py - -@@ -755,9 +748,9 @@ - - Re-executing the command should now print the latest Python 3.x information. - As with the above command-line examples, you can specify a more explicit --version qualifier. Assuming you have Python 2.6 installed, try changing the --first line to ``#! python2.6`` and you should find the 2.6 version --information printed. -+version qualifier. Assuming you have Python 3.7 installed, try changing -+the first line to ``#! python3.7`` and you should find the |version| -+version information printed. - - Note that unlike interactive use, a bare "python" will use the latest - version of Python 2.x that you have installed. This is for backward -@@ -810,8 +803,8 @@ - Any of the above virtual commands can be suffixed with an explicit version - (either just the major version, or the major and minor version). - Furthermore the 32-bit version can be requested by adding "-32" after the --minor version. I.e. ``/usr/bin/python2.7-32`` will request usage of the --32-bit python 2.7. -+minor version. I.e. ``/usr/bin/python3.7-32`` will request usage of the -+32-bit python 3.7. - - .. versionadded:: 3.7 - -@@ -897,19 +890,19 @@ - ``python2`` will use the latest Python 2.x version installed and - the command ``python3`` will use the latest Python 3.x installed. - --* The commands ``python3.1`` and ``python2.7`` will not consult any -+* The command ``python3.7`` will not consult any - options at all as the versions are fully specified. - - * If ``PY_PYTHON=3``, the commands ``python`` and ``python3`` will both use - the latest installed Python 3 version. - --* If ``PY_PYTHON=3.1-32``, the command ``python`` will use the 32-bit -- implementation of 3.1 whereas the command ``python3`` will use the latest -+* If ``PY_PYTHON=3.7-32``, the command ``python`` will use the 32-bit -+ implementation of 3.7 whereas the command ``python3`` will use the latest - installed Python (PY_PYTHON was not considered at all as a major - version was specified.) - --* If ``PY_PYTHON=3`` and ``PY_PYTHON3=3.1``, the commands -- ``python`` and ``python3`` will both use specifically 3.1 -+* If ``PY_PYTHON=3`` and ``PY_PYTHON3=3.7``, the commands -+ ``python`` and ``python3`` will both use specifically 3.7 - - In addition to environment variables, the same settings can be configured - in the .INI file used by the launcher. The section in the INI file is -@@ -920,21 +913,21 @@ - - For example: - --* Setting ``PY_PYTHON=3.1`` is equivalent to the INI file containing: -+* Setting ``PY_PYTHON=3.7`` is equivalent to the INI file containing: - - .. code-block:: ini - - [defaults] -- python=3.1 -+ python=3.7 - --* Setting ``PY_PYTHON=3`` and ``PY_PYTHON3=3.1`` is equivalent to the INI file -+* Setting ``PY_PYTHON=3`` and ``PY_PYTHON3=3.7`` is equivalent to the INI file - containing: - - .. code-block:: ini - - [defaults] - python=3 -- python3=3.1 -+ python3=3.7 - - Diagnostics - ----------- -@@ -1088,13 +1081,14 @@ - utilities for: - - * `Component Object Model -- `_ -+ `_ - (COM) - * Win32 API calls - * Registry - * Event log --* `Microsoft Foundation Classes `_ (MFC) -- user interfaces -+* `Microsoft Foundation Classes -+ `_ -+ (MFC) user interfaces - - `PythonWin `_ is a sample MFC application -@@ -1105,7 +1099,7 @@ - `Win32 How Do I...? `_ - by Tim Golden - -- `Python and COM `_ -+ `Python and COM `_ - by David and Paul Boddie - - -@@ -1119,18 +1113,6 @@ - Python. - - --WConio -------- -- --Since Python's advanced terminal handling layer, :mod:`curses`, is restricted to --Unix-like systems, there is a library exclusive to Windows as well: Windows --Console I/O for Python. -- --`WConio `_ is a wrapper for --Turbo-C's :file:`CONIO.H`, used to create text user interfaces. -- -- -- - Compiling Python on Windows - =========================== - -@@ -1140,21 +1122,13 @@ - `_. - - The source tree contains a build solution and project files for Microsoft --Visual Studio 2015, which is the compiler used to build the official Python -+Visual Studio, which is the compiler used to build the official Python - releases. These files are in the :file:`PCbuild` directory. - - Check :file:`PCbuild/readme.txt` for general information on the build process. - -- - For extension modules, consult :ref:`building-on-windows`. - --.. seealso:: -- -- `Python + Windows + distutils + SWIG + gcc MinGW `_ -- or "Creating Python extensions in C/C++ with SWIG and compiling them with -- MinGW gcc under Windows" or "Installing Python extension with distutils -- and without Microsoft Visual C++" by Sébastien Sauvage, 2003 -- - - Other Platforms - =============== -@@ -1163,12 +1137,12 @@ - earlier are no longer supported (due to the lack of users or developers). - Check :pep:`11` for details on all unsupported platforms. - --* `Windows CE `_ is still supported. --* The `Cygwin `_ installer offers to install the Python -- interpreter as well (cf. `Cygwin package source -- `_, `Maintainer releases -- `_) -+* `Windows CE `_ is -+ `no longer supported `__ -+ since Python 3 (if it ever was). -+* The `Cygwin `_ installer offers to install the -+ `Python interpreter `__ -+ as well - - See `Python for Windows `_ - for detailed information about platforms with pre-compiled installers. -diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst -index 4e85abaea7..5816380e10 100644 ---- a/Doc/whatsnew/2.5.rst -+++ b/Doc/whatsnew/2.5.rst -@@ -1767,7 +1767,7 @@ - - The rest of this section will provide a brief overview of using ElementTree. - Full documentation for ElementTree is available at --http://effbot.org/zone/element-index.htm. -+https://web.archive.org/web/20201124024954/http://effbot.org/zone/element-index.htm. - - ElementTree represents an XML document as a tree of element nodes. The text - content of the document is stored as the :attr:`text` and :attr:`tail` -@@ -1865,7 +1865,7 @@ - - .. seealso:: - -- http://effbot.org/zone/element-index.htm -+ https://web.archive.org/web/20201124024954/http://effbot.org/zone/element-index.htm - Official documentation for ElementTree. - - .. ====================================================================== -diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst -index abb65222dd..6d704586cd 100644 ---- a/Doc/whatsnew/2.7.rst -+++ b/Doc/whatsnew/2.7.rst -@@ -2089,7 +2089,7 @@ - - Fredrik Lundh develops ElementTree and produced the 1.3 version; - you can read his article describing 1.3 at --http://effbot.org/zone/elementtree-13-intro.htm. -+https://web.archive.org/web/20200703234532/http://effbot.org/zone/elementtree-13-intro.htm. - Florent Xicluna updated the version included with - Python, after discussions on python-dev and in :issue:`6472`.) - -diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst -index 06bee9966c..828d7ca115 100644 ---- a/Doc/whatsnew/3.2.rst -+++ b/Doc/whatsnew/3.2.rst -@@ -744,7 +744,8 @@ - * :meth:`xml.etree.ElementTree.getiterator` use ``Element.iter`` instead. - - For details of the update, see `Introducing ElementTree --`_ on Fredrik Lundh's website. -+`_ -+on Fredrik Lundh's website. - - (Contributed by Florent Xicluna and Fredrik Lundh, :issue:`6472`.) - -diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst -index 1b3bb152ee..92bdb45555 100644 ---- a/Doc/whatsnew/3.8.rst -+++ b/Doc/whatsnew/3.8.rst -@@ -1489,7 +1489,7 @@ - first introduced in Python 3.4. It offers better performance and smaller - size compared to Protocol 3 available since Python 3.0. - --* Removed one ``Py_ssize_t`` member from ``PyGC_Head``. All GC tracked -+* Removed one :c:type:`Py_ssize_t` member from ``PyGC_Head``. All GC tracked - objects (e.g. tuple, list, dict) size is reduced 4 or 8 bytes. - (Contributed by Inada Naoki in :issue:`33597`.) - diff --git a/Include/datetime.h b/Include/datetime.h index 5d9f2558f9..fc66471923 100644 --- a/Include/datetime.h @@ -4597,25 +44,6 @@ index 5d9f2558f9..fc66471923 100644 /* This block is only used as part of the public API and should not be * included in _datetimemodule.c, which does not use the C API capsule. * See bpo-35081 for more details. -diff --git a/Include/patchlevel.h b/Include/patchlevel.h -index a4418b0a6c..cf3529473e 100644 ---- a/Include/patchlevel.h -+++ b/Include/patchlevel.h -@@ -18,12 +18,12 @@ - /*--start constants--*/ - #define PY_MAJOR_VERSION 3 - #define PY_MINOR_VERSION 9 --#define PY_MICRO_VERSION 12 -+#define PY_MICRO_VERSION 13 - #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL - #define PY_RELEASE_SERIAL 0 - - /* Version as a string */ --#define PY_VERSION "3.9.12" -+#define PY_VERSION "3.9.13" - /*--end constants--*/ - - /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. --- /dev/null +++ b/Lib/_ios_support.py @@ -0,0 +1,36 @@ @@ -4655,154 +83,6 @@ index a4418b0a6c..cf3529473e 100644 + model = objc.objc_msgSend(systemModel, SEL_UTF8String).decode() + + return system, release, model -diff --git a/Lib/_markupbase.py b/Lib/_markupbase.py -index 2af5f1c23b..7091eb635b 100644 ---- a/Lib/_markupbase.py -+++ b/Lib/_markupbase.py -@@ -157,6 +157,7 @@ - match= _msmarkedsectionclose.search(rawdata, i+3) - else: - self.error('unknown status keyword %r in marked section' % rawdata[i+3:j]) -+ match = None - if not match: - return -1 - if report: -diff --git a/Lib/argparse.py b/Lib/argparse.py -index c46bb30271..b9c22051f6 100644 ---- a/Lib/argparse.py -+++ b/Lib/argparse.py -@@ -848,6 +848,7 @@ - 'default', - 'type', - 'choices', -+ 'required', - 'help', - 'metavar', - ] -diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py -index 7a14e5e139..4356bfae01 100644 ---- a/Lib/asyncio/base_events.py -+++ b/Lib/asyncio/base_events.py -@@ -885,7 +885,7 @@ - # non-mmap files even if sendfile is supported by OS - raise exceptions.SendfileNotAvailableError( - f"syscall sendfile is not available for socket {sock!r} " -- "and file {file!r} combination") -+ f"and file {file!r} combination") - - async def _sock_sendfile_fallback(self, sock, file, offset, count): - if offset: -diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py -index 71080b8ad1..572d4a8ce1 100644 ---- a/Lib/asyncio/selector_events.py -+++ b/Lib/asyncio/selector_events.py -@@ -487,7 +487,8 @@ - if self._debug and sock.gettimeout() != 0: - raise ValueError("the socket must be non-blocking") - -- if not hasattr(socket, 'AF_UNIX') or sock.family != socket.AF_UNIX: -+ if sock.family == socket.AF_INET or ( -+ base_events._HAS_IPv6 and sock.family == socket.AF_INET6): - resolved = await self._ensure_resolved( - address, family=sock.family, type=sock.type, proto=sock.proto, - loop=self, -diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py -index 5c00f2edbe..cf119ac643 100644 ---- a/Lib/concurrent/futures/_base.py -+++ b/Lib/concurrent/futures/_base.py -@@ -381,7 +381,7 @@ - return self._state == RUNNING - - def done(self): -- """Return True of the future was cancelled or finished executing.""" -+ """Return True if the future was cancelled or finished executing.""" - with self._condition: - return self._state in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED] - -diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py -index a29e5247ab..f223b86d1f 100644 ---- a/Lib/concurrent/futures/process.py -+++ b/Lib/concurrent/futures/process.py -@@ -126,6 +126,9 @@ - tb = traceback.format_exception(type(exc), exc, tb) - tb = ''.join(tb) - self.exc = exc -+ # Traceback object needs to be garbage-collected as its frames -+ # contain references to all the objects in the exception scope -+ self.exc.__traceback__ = None - self.tb = '\n"""\n%s"""' % tb - def __reduce__(self): - return _rebuild_exc, (self.exc, self.tb) -@@ -604,6 +607,10 @@ - mp_context = mp.get_context() - self._mp_context = mp_context - -+ # https://github.com/python/cpython/issues/90622 -+ self._safe_to_dynamically_spawn_children = ( -+ self._mp_context.get_start_method(allow_none=False) != "fork") -+ - if initializer is not None and not callable(initializer): - raise TypeError("initializer must be a callable") - self._initializer = initializer -@@ -654,6 +661,8 @@ - def _start_executor_manager_thread(self): - if self._executor_manager_thread is None: - # Start the processes so that their sentinels are known. -+ if not self._safe_to_dynamically_spawn_children: # ie, using fork. -+ self._launch_processes() - self._executor_manager_thread = _ExecutorManagerThread(self) - self._executor_manager_thread.start() - _threads_wakeups[self._executor_manager_thread] = \ -@@ -666,14 +675,31 @@ - - process_count = len(self._processes) - if process_count < self._max_workers: -- p = self._mp_context.Process( -- target=_process_worker, -- args=(self._call_queue, -- self._result_queue, -- self._initializer, -- self._initargs)) -- p.start() -- self._processes[p.pid] = p -+ # Assertion disabled as this codepath is also used to replace a -+ # worker that unexpectedly dies, even when using the 'fork' start -+ # method. That means there is still a potential deadlock bug. If a -+ # 'fork' mp_context worker dies, we'll be forking a new one when -+ # we know a thread is running (self._executor_manager_thread). -+ #assert self._safe_to_dynamically_spawn_children or not self._executor_manager_thread, 'https://github.com/python/cpython/issues/90622' -+ self._spawn_process() -+ -+ def _launch_processes(self): -+ # https://github.com/python/cpython/issues/90622 -+ assert not self._executor_manager_thread, ( -+ 'Processes cannot be fork()ed after the thread has started, ' -+ 'deadlock in the child processes could result.') -+ for _ in range(len(self._processes), self._max_workers): -+ self._spawn_process() -+ -+ def _spawn_process(self): -+ p = self._mp_context.Process( -+ target=_process_worker, -+ args=(self._call_queue, -+ self._result_queue, -+ self._initializer, -+ self._initargs)) -+ p.start() -+ self._processes[p.pid] = p - - def submit(self, fn, /, *args, **kwargs): - with self._shutdown_lock: -@@ -694,7 +720,8 @@ - # Wake up queue management thread - self._executor_manager_thread_wakeup.wakeup() - -- self._adjust_process_count() -+ if self._safe_to_dynamically_spawn_children: -+ self._adjust_process_count() - self._start_executor_manager_thread() - return f - submit.__doc__ = _base.Executor.submit.__doc__ diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py index f9d27cb89d..da172b345a 100644 --- a/Lib/ctypes/test/test_as_parameter.py @@ -5290,28 +570,6 @@ index 0c2510e161..6c3c43f11d 100644 from ctypes.macholib.dyld import dyld_find as _dyld_find def find_library(name): possible = ['lib%s.dylib' % name, -diff --git a/Lib/datetime.py b/Lib/datetime.py -index 23d2bf0918..51c704994b 100644 ---- a/Lib/datetime.py -+++ b/Lib/datetime.py -@@ -1683,7 +1683,7 @@ - y, m, d, hh, mm, ss, weekday, jday, dst = converter(t) - ss = min(ss, 59) # clamp out leap seconds if the platform has them - result = cls(y, m, d, hh, mm, ss, us, tz) -- if tz is None: -+ if tz is None and not utc: - # As of version 2015f max fold in IANA database is - # 23 hours at 1969-09-30 13:00:00 in Kwajalein. - # Let's probe 24 hours in the past to detect a transition: -@@ -1704,7 +1704,7 @@ - probe2 = cls(y, m, d, hh, mm, ss, us, tz) - if probe2 == result: - result._fold = 1 -- else: -+ elif tz is not None: - result = tz.fromutc(result) - return result - diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py index 808c0dc287..b4813efe0c 100644 --- a/Lib/distutils/tests/test_build_ext.py @@ -5466,88 +724,6 @@ index bf0d4333f9..09ba51bcea 100644 def test_check_environ(self): util._environ_checked = 0 os.environ.pop('HOME', None) -diff --git a/Lib/email/_encoded_words.py b/Lib/email/_encoded_words.py -index 295ae7eb21..6795a606de 100644 ---- a/Lib/email/_encoded_words.py -+++ b/Lib/email/_encoded_words.py -@@ -179,15 +179,15 @@ - # Turn the CTE decoded bytes into unicode. - try: - string = bstring.decode(charset) -- except UnicodeError: -+ except UnicodeDecodeError: - defects.append(errors.UndecodableBytesDefect("Encoded word " -- "contains bytes not decodable using {} charset".format(charset))) -+ f"contains bytes not decodable using {charset!r} charset")) - string = bstring.decode(charset, 'surrogateescape') -- except LookupError: -+ except (LookupError, UnicodeEncodeError): - string = bstring.decode('ascii', 'surrogateescape') - if charset.lower() != 'unknown-8bit': -- defects.append(errors.CharsetError("Unknown charset {} " -- "in encoded word; decoded as unknown bytes".format(charset))) -+ defects.append(errors.CharsetError(f"Unknown charset {charset!r} " -+ f"in encoded word; decoded as unknown bytes")) - return string, charset, lang, defects - - -diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py -index 51d355fbb0..8a8fb8bc42 100644 ---- a/Lib/email/_header_value_parser.py -+++ b/Lib/email/_header_value_parser.py -@@ -781,7 +781,7 @@ - else: - try: - value = value.decode(charset, 'surrogateescape') -- except LookupError: -+ except (LookupError, UnicodeEncodeError): - # XXX: there should really be a custom defect for - # unknown character set to make it easy to find, - # because otherwise unknown charset is a silent -diff --git a/Lib/email/charset.py b/Lib/email/charset.py -index d3d759ad91..791b6584b2 100644 ---- a/Lib/email/charset.py -+++ b/Lib/email/charset.py -@@ -112,8 +112,8 @@ - charset is the input character set, and must be the canonical name of a - character set. - -- Optional header_enc and body_enc is either Charset.QP for -- quoted-printable, Charset.BASE64 for base64 encoding, Charset.SHORTEST for -+ Optional header_enc and body_enc is either charset.QP for -+ quoted-printable, charset.BASE64 for base64 encoding, charset.SHORTEST for - the shortest of qp or base64 encoding, or None for no encoding. SHORTEST - is only valid for header_enc. It describes how message headers and - message bodies in the input charset are to be encoded. Default is no -@@ -185,13 +185,13 @@ - - header_encoding: If the character set must be encoded before it can be - used in an email header, this attribute will be set to -- Charset.QP (for quoted-printable), Charset.BASE64 (for -- base64 encoding), or Charset.SHORTEST for the shortest of -+ charset.QP (for quoted-printable), charset.BASE64 (for -+ base64 encoding), or charset.SHORTEST for the shortest of - QP or BASE64 encoding. Otherwise, it will be None. - - body_encoding: Same as header_encoding, but describes the encoding for the - mail message's body, which indeed may be different than the -- header encoding. Charset.SHORTEST is not allowed for -+ header encoding. charset.SHORTEST is not allowed for - body_encoding. - - output_charset: Some character sets must be converted before they can be -diff --git a/Lib/html/entities.py b/Lib/html/entities.py -index 91ea5da2af..dc508631ac 100644 ---- a/Lib/html/entities.py -+++ b/Lib/html/entities.py -@@ -4,6 +4,7 @@ - - - # maps the HTML entity name to the Unicode code point -+# from https://html.spec.whatwg.org/multipage/named-characters.html - name2codepoint = { - 'AElig': 0x00c6, # latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 - 'Aacute': 0x00c1, # latin capital letter A with acute, U+00C1 ISOlat1 diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index f3828b10e1..7e86539bfa 100644 --- a/Lib/importlib/_bootstrap_external.py @@ -5561,148 +737,6 @@ index f3828b10e1..7e86539bfa 100644 _CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) -diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py -index 6cb92ed552..25f373a06a 100644 ---- a/Lib/ipaddress.py -+++ b/Lib/ipaddress.py -@@ -50,8 +50,7 @@ - except (AddressValueError, NetmaskValueError): - pass - -- raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % -- address) -+ raise ValueError(f'{address!r} does not appear to be an IPv4 or IPv6 address') - - - def ip_network(address, strict=True): -@@ -80,8 +79,7 @@ - except (AddressValueError, NetmaskValueError): - pass - -- raise ValueError('%r does not appear to be an IPv4 or IPv6 network' % -- address) -+ raise ValueError(f'{address!r} does not appear to be an IPv4 or IPv6 network') - - - def ip_interface(address): -@@ -115,8 +113,7 @@ - except (AddressValueError, NetmaskValueError): - pass - -- raise ValueError('%r does not appear to be an IPv4 or IPv6 interface' % -- address) -+ raise ValueError(f'{address!r} does not appear to be an IPv4 or IPv6 interface') - - - def v4_int_to_packed(address): -@@ -159,7 +156,7 @@ - """Helper to split the netmask and raise AddressValueError if needed""" - addr = str(address).split('/') - if len(addr) > 2: -- raise AddressValueError("Only one '/' permitted in %r" % address) -+ raise AddressValueError(f"Only one '/' permitted in {address!r}") - return addr - - -@@ -1303,7 +1300,7 @@ - # which converts into a formatted IP string. - addr_str = str(address) - if '/' in addr_str: -- raise AddressValueError("Unexpected '/' in %r" % address) -+ raise AddressValueError(f"Unexpected '/' in {address!r}") - self._ip = self._ip_int_from_string(addr_str) - - @property -@@ -1912,7 +1909,7 @@ - # which converts into a formatted IP string. - addr_str = str(address) - if '/' in addr_str: -- raise AddressValueError("Unexpected '/' in %r" % address) -+ raise AddressValueError(f"Unexpected '/' in {address!r}") - addr_str, self._scope_id = self._split_scope_id(addr_str) - - self._ip = self._ip_int_from_string(addr_str) -diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py -index dfa566c6fc..1f9295619f 100644 ---- a/Lib/multiprocessing/managers.py -+++ b/Lib/multiprocessing/managers.py -@@ -669,7 +669,7 @@ - if hasattr(process, 'terminate'): - util.info('trying to `terminate()` manager process') - process.terminate() -- process.join(timeout=0.1) -+ process.join(timeout=1.0) - if process.is_alive(): - util.info('manager still alive after terminate') - -diff --git a/Lib/multiprocessing/queues.py b/Lib/multiprocessing/queues.py -index a290181487..f37f114a96 100644 ---- a/Lib/multiprocessing/queues.py -+++ b/Lib/multiprocessing/queues.py -@@ -139,13 +139,10 @@ - - def close(self): - self._closed = True -- try: -- self._reader.close() -- finally: -- close = self._close -- if close: -- self._close = None -- close() -+ close = self._close -+ if close: -+ self._close = None -+ close() - - def join_thread(self): - debug('Queue.join_thread()') -@@ -169,8 +166,9 @@ - self._thread = threading.Thread( - target=Queue._feed, - args=(self._buffer, self._notempty, self._send_bytes, -- self._wlock, self._writer.close, self._ignore_epipe, -- self._on_queue_feeder_error, self._sem), -+ self._wlock, self._reader.close, self._writer.close, -+ self._ignore_epipe, self._on_queue_feeder_error, -+ self._sem), - name='QueueFeederThread' - ) - self._thread.daemon = True -@@ -211,8 +209,8 @@ - notempty.notify() - - @staticmethod -- def _feed(buffer, notempty, send_bytes, writelock, close, ignore_epipe, -- onerror, queue_sem): -+ def _feed(buffer, notempty, send_bytes, writelock, reader_close, -+ writer_close, ignore_epipe, onerror, queue_sem): - debug('starting thread to feed data to pipe') - nacquire = notempty.acquire - nrelease = notempty.release -@@ -238,7 +236,8 @@ - obj = bpopleft() - if obj is sentinel: - debug('feeder thread got sentinel -- exiting') -- close() -+ reader_close() -+ writer_close() - return - - # serialize the data before acquiring the lock -diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py -index 21f2a7ebe2..697b35a0eb 100644 ---- a/Lib/multiprocessing/util.py -+++ b/Lib/multiprocessing/util.py -@@ -120,7 +120,7 @@ - return address[0] == 0 - elif isinstance(address, str): - return address[0] == "\0" -- raise TypeError('address type of {address!r} unrecognized') -+ raise TypeError(f'address type of {address!r} unrecognized') - - - abstract_sockets_supported = _platform_supports_abstract_sockets() diff --git a/Lib/os.py b/Lib/os.py index b794159f86..78aab1ea2a 100644 --- a/Lib/os.py @@ -5835,212 +869,8 @@ index d6412e169b..3229a13a39 100755 + system = 'macOS' + release = macos_release - if system == 'Windows': - # MS platforms -diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py -index 2546eb933b..318e85660d 100644 ---- a/Lib/pydoc_data/topics.py -+++ b/Lib/pydoc_data/topics.py -@@ -1,5 +1,5 @@ - # -*- coding: utf-8 -*- --# Autogenerated by Sphinx on Wed Mar 23 22:08:02 2022 -+# Autogenerated by Sphinx on Tue May 17 12:57:36 2022 - topics = {'assert': 'The "assert" statement\n' - '**********************\n' - '\n' -@@ -93,11 +93,7 @@ - ' optionally in parentheses, the object is assigned to that ' - 'target.\n' - '\n' -- '* Else: The object must be an iterable with the same number of ' -- 'items\n' -- ' as there are targets in the target list, and the items are ' -- 'assigned,\n' -- ' from left to right, to the corresponding targets.\n' -+ '* Else:\n' - '\n' - ' * If the target list contains one target prefixed with an ' - 'asterisk,\n' -@@ -3980,7 +3976,10 @@ - 'is\n' - 'applied to separating the commands; the input is split at the ' - 'first\n' -- '";;" pair, even if it is in the middle of a quoted string.\n' -+ '";;" pair, even if it is in the middle of a quoted string. A\n' -+ 'workaround for strings with double semicolons is to use ' -+ 'implicit\n' -+ 'string concatenation "\';\'\';\'" or "";"";"".\n' - '\n' - 'If a file ".pdbrc" exists in the user’s home directory or in ' - 'the\n' -@@ -6402,12 +6401,12 @@ - 'Examples:\n' - '\n' - ' import foo # foo imported and bound locally\n' -- ' import foo.bar.baz # foo.bar.baz imported, foo bound ' -- 'locally\n' -- ' import foo.bar.baz as fbb # foo.bar.baz imported and bound as ' -- 'fbb\n' -- ' from foo.bar import baz # foo.bar.baz imported and bound as ' -- 'baz\n' -+ ' import foo.bar.baz # foo, foo.bar, and foo.bar.baz ' -+ 'imported, foo bound locally\n' -+ ' import foo.bar.baz as fbb # foo, foo.bar, and foo.bar.baz ' -+ 'imported, foo.bar.baz bound as fbb\n' -+ ' from foo.bar import baz # foo, foo.bar, and foo.bar.baz ' -+ 'imported, foo.bar.baz bound as baz\n' - ' from foo import attr # foo imported and foo.attr bound as ' - 'attr\n' - '\n' -@@ -11091,9 +11090,13 @@ - ' >>> "they\'re bill\'s friends from the UK".title()\n' - ' "They\'Re Bill\'S Friends From The Uk"\n' - '\n' -- ' A workaround for apostrophes can be constructed using ' -- 'regular\n' -- ' expressions:\n' -+ ' The "string.capwords()" function does not have this ' -+ 'problem, as it\n' -+ ' splits words on spaces only.\n' -+ '\n' -+ ' Alternatively, a workaround for apostrophes can be ' -+ 'constructed\n' -+ ' using regular expressions:\n' - '\n' - ' >>> import re\n' - ' >>> def titlecase(s):\n' -@@ -11215,12 +11218,15 @@ - 'single quotes ("\'") or double quotes ("""). They can also be ' - 'enclosed\n' - 'in matching groups of three single or double quotes (these are\n' -- 'generally referred to as *triple-quoted strings*). The ' -- 'backslash\n' -- '("\\") character is used to escape characters that otherwise have ' -- 'a\n' -- 'special meaning, such as newline, backslash itself, or the quote\n' -+ 'generally referred to as *triple-quoted strings*). The backslash ' -+ '("\\")\n' -+ 'character is used to give special meaning to otherwise ordinary\n' -+ 'characters like "n", which means ‘newline’ when escaped ("\\n"). ' -+ 'It can\n' -+ 'also be used to escape characters that otherwise have a special\n' -+ 'meaning, such as newline, backslash itself, or the quote ' - 'character.\n' -+ 'See escape sequences below for examples.\n' - '\n' - 'Bytes literals are always prefixed with "\'b\'" or "\'B\'"; they ' - 'produce\n' -@@ -12788,14 +12794,6 @@ - 'unwise to use\n' - 'them as dictionary keys.)\n' - '\n' -- 'Dictionaries can be created by placing a comma-separated ' -- 'list of "key:\n' -- 'value" pairs within braces, for example: "{\'jack\': 4098, ' -- "'sjoerd':\n" -- '4127}" or "{4098: \'jack\', 4127: \'sjoerd\'}", or by the ' -- '"dict"\n' -- 'constructor.\n' -- '\n' - 'class dict(**kwargs)\n' - 'class dict(mapping, **kwargs)\n' - 'class dict(iterable, **kwargs)\n' -diff --git a/Lib/queue.py b/Lib/queue.py -index 10dbcbc18e..55f5008846 100644 ---- a/Lib/queue.py -+++ b/Lib/queue.py -@@ -298,7 +298,7 @@ - def put_nowait(self, item): - '''Put an item into the queue without blocking. - -- This is exactly equivalent to `put(item)` and is only provided -+ This is exactly equivalent to `put(item, block=False)` and is only provided - for compatibility with the Queue class. - ''' - return self.put(item, block=False) -diff --git a/Lib/runpy.py b/Lib/runpy.py -index 7e1e1ac5dd..c602bbe3f7 100644 ---- a/Lib/runpy.py -+++ b/Lib/runpy.py -@@ -199,9 +199,24 @@ - - def run_module(mod_name, init_globals=None, - run_name=None, alter_sys=False): -- """Execute a module's code without importing it -+ """Execute a module's code without importing it. - -- Returns the resulting top level namespace dictionary -+ mod_name -- an absolute module name or package name. -+ -+ Optional arguments: -+ init_globals -- dictionary used to pre-populate the module’s -+ globals dictionary before the code is executed. -+ -+ run_name -- if not None, this will be used for setting __name__; -+ otherwise, __name__ will be set to mod_name + '__main__' if the -+ named module is a package and to just mod_name otherwise. -+ -+ alter_sys -- if True, sys.argv[0] is updated with the value of -+ __file__ and sys.modules[__name__] is updated with a temporary -+ module object for the module being executed. Both are -+ restored to their original values before the function returns. -+ -+ Returns the resulting module globals dictionary. - """ - mod_name, mod_spec, code = _get_module_details(mod_name) - if run_name is None: -@@ -243,14 +258,19 @@ - return code, fname - - def run_path(path_name, init_globals=None, run_name=None): -- """Execute code located at the specified filesystem location -+ """Execute code located at the specified filesystem location. -+ -+ path_name -- filesystem location of a Python script, zipfile, -+ or directory containing a top level __main__.py script. -+ -+ Optional arguments: -+ init_globals -- dictionary used to pre-populate the module’s -+ globals dictionary before the code is executed. - -- Returns the resulting top level namespace dictionary -+ run_name -- if not None, this will be used to set __name__; -+ otherwise, '' will be used for __name__. - -- The file path may refer directly to a Python script (i.e. -- one that could be directly executed with execfile) or else -- it may refer to a zipfile or directory containing a top -- level __main__.py script. -+ Returns the resulting module globals dictionary. - """ - if run_name is None: - run_name = "" -diff --git a/Lib/shutil.py b/Lib/shutil.py -index c048cdf9b2..48a60c0d28 100644 ---- a/Lib/shutil.py -+++ b/Lib/shutil.py -@@ -526,9 +526,6 @@ - ignore_dangling_symlinks=False, dirs_exist_ok=False): - """Recursively copy a directory tree and return the destination directory. - -- dirs_exist_ok dictates whether to raise an exception in case dst or any -- missing parent directory already exists. -- - If exception(s) occur, an Error is raised with a list of reasons. - - If the optional symlinks flag is true, symbolic links in the -@@ -559,6 +556,11 @@ - destination path as arguments. By default, copy2() is used, but any - function that supports the same signature (like copy()) can be used. - -+ If dirs_exist_ok is false (the default) and `dst` already exists, a -+ `FileExistsError` is raised. If `dirs_exist_ok` is true, the copying -+ operation will continue if it encounters existing directories, and files -+ within the `dst` tree will be overwritten by corresponding files from the -+ `src` tree. - """ - sys.audit("shutil.copytree", src, dst) - with os.scandir(src) as itr: + if system == 'Windows': + # MS platforms diff --git a/Lib/site.py b/Lib/site.py index 9e617afb00..41305298d3 100644 --- a/Lib/site.py @@ -6055,218 +885,6 @@ index 9e617afb00..41305298d3 100644 return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' -diff --git a/Lib/sqlite3/__init__.py b/Lib/sqlite3/__init__.py -index 1e717450c2..34a9c047dd 100644 ---- a/Lib/sqlite3/__init__.py -+++ b/Lib/sqlite3/__init__.py -@@ -21,7 +21,7 @@ - # 3. This notice may not be removed or altered from any source distribution. - - """ --The sqlite3 extension module provides a DB-API 2.0 (PEP 249) compilant -+The sqlite3 extension module provides a DB-API 2.0 (PEP 249) compliant - interface to the SQLite library, and requires SQLite 3.7.15 or newer. - - To use the module, start by creating a database Connection object: -diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py -index 6ba3356479..4d5b7b7ab3 100644 ---- a/Lib/sqlite3/test/regression.py -+++ b/Lib/sqlite3/test/regression.py -@@ -28,6 +28,9 @@ - import functools - from test import support - -+from unittest.mock import patch -+ -+ - class RegressionTests(unittest.TestCase): - def setUp(self): - self.con = sqlite.connect(":memory:") -@@ -413,10 +416,50 @@ - - - -+class RecursiveUseOfCursors(unittest.TestCase): -+ # GH-80254: sqlite3 should not segfault for recursive use of cursors. -+ msg = "Recursive use of cursors not allowed" -+ -+ def setUp(self): -+ self.con = sqlite.connect(":memory:", -+ detect_types=sqlite.PARSE_COLNAMES) -+ self.cur = self.con.cursor() -+ self.cur.execute("create table test(x foo)") -+ self.cur.executemany("insert into test(x) values (?)", -+ [("foo",), ("bar",)]) -+ -+ def tearDown(self): -+ self.cur.close() -+ self.con.close() -+ del self.cur -+ del self.con -+ -+ def test_recursive_cursor_init(self): -+ conv = lambda x: self.cur.__init__(self.con) -+ with patch.dict(sqlite.converters, {"INIT": conv}): -+ with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg): -+ self.cur.execute(f'select x as "x [INIT]", x from test') -+ -+ def test_recursive_cursor_close(self): -+ conv = lambda x: self.cur.close() -+ with patch.dict(sqlite.converters, {"CLOSE": conv}): -+ with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg): -+ self.cur.execute(f'select x as "x [CLOSE]", x from test') -+ -+ def test_recursive_cursor_fetch(self): -+ conv = lambda x, l=[]: self.cur.fetchone() if l else l.append(None) -+ with patch.dict(sqlite.converters, {"ITER": conv}): -+ self.cur.execute(f'select x as "x [ITER]", x from test') -+ with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg): -+ self.cur.fetchall() -+ -+ - def suite(): - regression_suite = unittest.makeSuite(RegressionTests, "Check") -+ recursive_cursor = unittest.makeSuite(RecursiveUseOfCursors) - return unittest.TestSuite(( - regression_suite, -+ recursive_cursor, - )) - - def test(): -diff --git a/Lib/sre_compile.py b/Lib/sre_compile.py -index c6398bfb83..aed752d11d 100644 ---- a/Lib/sre_compile.py -+++ b/Lib/sre_compile.py -@@ -52,6 +52,22 @@ - (0x3c2, 0x3c3), # ςσ - # GREEK SMALL LETTER PHI, GREEK PHI SYMBOL - (0x3c6, 0x3d5), # φϕ -+ # CYRILLIC SMALL LETTER VE, CYRILLIC SMALL LETTER ROUNDED VE -+ (0x432, 0x1c80), # вᲀ -+ # CYRILLIC SMALL LETTER DE, CYRILLIC SMALL LETTER LONG-LEGGED DE -+ (0x434, 0x1c81), # дᲠ-+ # CYRILLIC SMALL LETTER O, CYRILLIC SMALL LETTER NARROW O -+ (0x43e, 0x1c82), # оᲂ -+ # CYRILLIC SMALL LETTER ES, CYRILLIC SMALL LETTER WIDE ES -+ (0x441, 0x1c83), # Ñᲃ -+ # CYRILLIC SMALL LETTER TE, CYRILLIC SMALL LETTER TALL TE, CYRILLIC SMALL LETTER THREE-LEGGED TE -+ (0x442, 0x1c84, 0x1c85), # тᲄᲅ -+ # CYRILLIC SMALL LETTER HARD SIGN, CYRILLIC SMALL LETTER TALL HARD SIGN -+ (0x44a, 0x1c86), # ъᲆ -+ # CYRILLIC SMALL LETTER YAT, CYRILLIC SMALL LETTER TALL YAT -+ (0x463, 0x1c87), # ѣᲇ -+ # CYRILLIC SMALL LETTER UNBLENDED UK, CYRILLIC SMALL LETTER MONOGRAPH UK -+ (0x1c88, 0xa64b), # ᲈꙋ - # LATIN SMALL LETTER S WITH DOT ABOVE, LATIN SMALL LETTER LONG S WITH DOT ABOVE - (0x1e61, 0x1e9b), # ṡẛ - # LATIN SMALL LIGATURE LONG S T, LATIN SMALL LIGATURE ST -@@ -320,11 +336,19 @@ - charmap += b'\0' * 0xff00 - continue - # Character set contains non-BMP character codes. -+ # For range, all BMP characters in the range are already -+ # proceeded. - if fixup: - hascased = True -- # There are only two ranges of cased non-BMP characters: -- # 10400-1044F (Deseret) and 118A0-118DF (Warang Citi), -- # and for both ranges RANGE_UNI_IGNORE works. -+ # For now, IN_UNI_IGNORE+LITERAL and -+ # IN_UNI_IGNORE+RANGE_UNI_IGNORE work for all non-BMP -+ # characters, because two characters (at least one of -+ # which is not in the BMP) match case-insensitively -+ # if and only if: -+ # 1) c1.lower() == c2.lower() -+ # 2) c1.lower() == c2 or c1.lower().upper() == c2 -+ # Also, both c.lower() and c.lower().upper() are single -+ # characters for every non-BMP character. - if op is RANGE: - op = RANGE_UNI_IGNORE - tail.append((op, av)) -diff --git a/Lib/sre_constants.py b/Lib/sre_constants.py -index 8e613cb3fa..db3ca51e83 100644 ---- a/Lib/sre_constants.py -+++ b/Lib/sre_constants.py -@@ -62,6 +62,8 @@ - def __repr__(self): - return self.name - -+ __reduce__ = None -+ - MAXREPEAT = _NamedIntConstant(MAXREPEAT, 'MAXREPEAT') - - def _makecodes(names): -diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py -index 53706676e9..20a6025011 100644 ---- a/Lib/sre_parse.py -+++ b/Lib/sre_parse.py -@@ -78,6 +78,7 @@ - self.groupdict = {} - self.groupwidths = [None] # group 0 - self.lookbehindgroups = None -+ self.grouprefpos = {} - @property - def groups(self): - return len(self.groupwidths) -@@ -330,7 +331,7 @@ - charname = source.getuntil('}', 'character name') - try: - c = ord(unicodedata.lookup(charname)) -- except KeyError: -+ except (KeyError, TypeError): - raise source.error("undefined character name %r" % charname, - len(charname) + len(r'\N{}')) - return LITERAL, c -@@ -390,7 +391,7 @@ - charname = source.getuntil('}', 'character name') - try: - c = ord(unicodedata.lookup(charname)) -- except KeyError: -+ except (KeyError, TypeError): - raise source.error("undefined character name %r" % charname, - len(charname) + len(r'\N{}')) - return LITERAL, c -@@ -786,6 +787,10 @@ - if condgroup >= MAXGROUPS: - msg = "invalid group reference %d" % condgroup - raise source.error(msg, len(condname) + 1) -+ if condgroup not in state.grouprefpos: -+ state.grouprefpos[condgroup] = ( -+ source.tell() - len(condname) - 1 -+ ) - state.checklookbehindgroup(condgroup, source) - item_yes = _parse(source, state, verbose, nested + 1) - if source.match("|"): -@@ -963,6 +968,11 @@ - assert source.next == ")" - raise source.error("unbalanced parenthesis") - -+ for g in p.state.grouprefpos: -+ if g >= p.state.groups: -+ msg = "invalid group reference %d" % g -+ raise error(msg, str, p.state.grouprefpos[g]) -+ - if flags & SRE_FLAG_DEBUG: - p.dump() - -diff --git a/Lib/ssl.py b/Lib/ssl.py -index 30f4e5934f..0e3606b835 100644 ---- a/Lib/ssl.py -+++ b/Lib/ssl.py -@@ -18,9 +18,10 @@ - seconds past the Epoch (the time values - returned from time.time()) - -- fetch_server_certificate (HOST, PORT) -- fetch the certificate provided -- by the server running on HOST at port PORT. No -- validation of the certificate is performed. -+ get_server_certificate (addr, ssl_version, ca_certs, timeout) -- Retrieve the -+ certificate from the server at the specified -+ address and return it as a PEM-encoded string -+ - - Integer constants: - diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 4effc1d8b3..b0e4a5acac 100644 --- a/Lib/subprocess.py @@ -6379,148 +997,6 @@ index e3f79bfde5..8c0d7908a9 100644 return "%s-%s-%s" % (osname, release, machine) -diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py -index 361d5e9ba7..aa5fe82afb 100644 ---- a/Lib/test/datetimetester.py -+++ b/Lib/test/datetimetester.py -@@ -2467,45 +2467,101 @@ - self.assertEqual(t.microsecond, 7812) - - def test_timestamp_limits(self): -- # minimum timestamp -- min_dt = self.theclass.min.replace(tzinfo=timezone.utc) -+ with self.subTest("minimum UTC"): -+ min_dt = self.theclass.min.replace(tzinfo=timezone.utc) -+ min_ts = min_dt.timestamp() -+ -+ # This test assumes that datetime.min == 0000-01-01T00:00:00.00 -+ # If that assumption changes, this value can change as well -+ self.assertEqual(min_ts, -62135596800) -+ -+ with self.subTest("maximum UTC"): -+ # Zero out microseconds to avoid rounding issues -+ max_dt = self.theclass.max.replace(tzinfo=timezone.utc, -+ microsecond=0) -+ max_ts = max_dt.timestamp() -+ -+ # This test assumes that datetime.max == 9999-12-31T23:59:59.999999 -+ # If that assumption changes, this value can change as well -+ self.assertEqual(max_ts, 253402300799.0) -+ -+ def test_fromtimestamp_limits(self): -+ try: -+ self.theclass.fromtimestamp(-2**32 - 1) -+ except (OSError, OverflowError): -+ self.skipTest("Test not valid on this platform") -+ -+ # XXX: Replace these with datetime.{min,max}.timestamp() when we solve -+ # the issue with gh-91012 -+ min_dt = self.theclass.min + timedelta(days=1) - min_ts = min_dt.timestamp() -+ -+ max_dt = self.theclass.max.replace(microsecond=0) -+ max_ts = ((self.theclass.max - timedelta(hours=23)).timestamp() + -+ timedelta(hours=22, minutes=59, seconds=59).total_seconds()) -+ -+ for (test_name, ts, expected) in [ -+ ("minimum", min_ts, min_dt), -+ ("maximum", max_ts, max_dt), -+ ]: -+ with self.subTest(test_name, ts=ts, expected=expected): -+ actual = self.theclass.fromtimestamp(ts) -+ -+ self.assertEqual(actual, expected) -+ -+ # Test error conditions -+ test_cases = [ -+ ("Too small by a little", min_ts - timedelta(days=1, hours=12).total_seconds()), -+ ("Too small by a lot", min_ts - timedelta(days=400).total_seconds()), -+ ("Too big by a little", max_ts + timedelta(days=1).total_seconds()), -+ ("Too big by a lot", max_ts + timedelta(days=400).total_seconds()), -+ ] -+ -+ for test_name, ts in test_cases: -+ with self.subTest(test_name, ts=ts): -+ with self.assertRaises((ValueError, OverflowError)): -+ # converting a Python int to C time_t can raise a -+ # OverflowError, especially on 32-bit platforms. -+ self.theclass.fromtimestamp(ts) -+ -+ def test_utcfromtimestamp_limits(self): - try: -- # date 0001-01-01 00:00:00+00:00: timestamp=-62135596800 -- self.assertEqual(self.theclass.fromtimestamp(min_ts, tz=timezone.utc), -- min_dt) -- except (OverflowError, OSError) as exc: -- # the date 0001-01-01 doesn't fit into 32-bit time_t, -- # or platform doesn't support such very old date -- self.skipTest(str(exc)) -- -- # maximum timestamp: set seconds to zero to avoid rounding issues -- max_dt = self.theclass.max.replace(tzinfo=timezone.utc, -- second=0, microsecond=0) -+ self.theclass.utcfromtimestamp(-2**32 - 1) -+ except (OSError, OverflowError): -+ self.skipTest("Test not valid on this platform") -+ -+ min_dt = self.theclass.min.replace(tzinfo=timezone.utc) -+ min_ts = min_dt.timestamp() -+ -+ max_dt = self.theclass.max.replace(microsecond=0, tzinfo=timezone.utc) - max_ts = max_dt.timestamp() -- # date 9999-12-31 23:59:00+00:00: timestamp 253402300740 -- self.assertEqual(self.theclass.fromtimestamp(max_ts, tz=timezone.utc), -- max_dt) -- -- # number of seconds greater than 1 year: make sure that the new date -- # is not valid in datetime.datetime limits -- delta = 3600 * 24 * 400 -- -- # too small -- ts = min_ts - delta -- # converting a Python int to C time_t can raise a OverflowError, -- # especially on 32-bit platforms. -- with self.assertRaises((ValueError, OverflowError)): -- self.theclass.fromtimestamp(ts) -- with self.assertRaises((ValueError, OverflowError)): -- self.theclass.utcfromtimestamp(ts) -- -- # too big -- ts = max_dt.timestamp() + delta -- with self.assertRaises((ValueError, OverflowError)): -- self.theclass.fromtimestamp(ts) -- with self.assertRaises((ValueError, OverflowError)): -- self.theclass.utcfromtimestamp(ts) -+ -+ for (test_name, ts, expected) in [ -+ ("minimum", min_ts, min_dt.replace(tzinfo=None)), -+ ("maximum", max_ts, max_dt.replace(tzinfo=None)), -+ ]: -+ with self.subTest(test_name, ts=ts, expected=expected): -+ try: -+ actual = self.theclass.utcfromtimestamp(ts) -+ except (OSError, OverflowError) as exc: -+ self.skipTest(str(exc)) -+ -+ self.assertEqual(actual, expected) -+ -+ # Test error conditions -+ test_cases = [ -+ ("Too small by a little", min_ts - 1), -+ ("Too small by a lot", min_ts - timedelta(days=400).total_seconds()), -+ ("Too big by a little", max_ts + 1), -+ ("Too big by a lot", max_ts + timedelta(days=400).total_seconds()), -+ ] -+ -+ for test_name, ts in test_cases: -+ with self.subTest(test_name, ts=ts): -+ with self.assertRaises((ValueError, OverflowError)): -+ # converting a Python int to C time_t can raise a -+ # OverflowError, especially on 32-bit platforms. -+ self.theclass.utcfromtimestamp(ts) - - def test_insane_fromtimestamp(self): - # It's possible that some platform maps time_t to double, diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 86ac8f0966..0777ccc56d 100644 --- a/Lib/test/support/__init__.py @@ -6585,40 +1061,6 @@ index 37e576d4a7..1d7176bf13 100644 def spawn_python(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw): """Run a Python subprocess with the given arguments. -diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py -index dcc392faf2..2d1cec13c2 100644 ---- a/Lib/test/test_argparse.py -+++ b/Lib/test/test_argparse.py -@@ -4873,12 +4873,13 @@ - nargs='+', - default=42, - choices=[1, 2, 3], -+ required=False, - help='HELP', - metavar='METAVAR') - string = ( - "Action(option_strings=['--foo', '-a', '-b'], dest='b', " - "nargs='+', const=None, default=42, type='int', " -- "choices=[1, 2, 3], help='HELP', metavar='METAVAR')") -+ "choices=[1, 2, 3], required=False, help='HELP', metavar='METAVAR')") - self.assertStringEqual(option, string) - - def test_argument(self): -@@ -4889,12 +4890,13 @@ - nargs='?', - default=2.5, - choices=[0.5, 1.5, 2.5], -+ required=True, - help='H HH H', - metavar='MV MV MV') - string = ( - "Action(option_strings=[], dest='x', nargs='?', " - "const=None, default=2.5, type=%r, choices=[0.5, 1.5, 2.5], " -- "help='H HH H', metavar='MV MV MV')" % float) -+ "required=True, help='H HH H', metavar='MV MV MV')" % float) - self.assertStringEqual(argument, string) - - def test_namespace(self): diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 72189bfd38..84940caf8a 100644 --- a/Lib/test/test_asyncio/test_events.py @@ -6814,25 +1256,6 @@ index 1dbeac41dc..73e63d1c31 100644 class TestMain(unittest.TestCase): def tearDown(self): if os.path.exists(support.TESTFN): -diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py -index 6241d114d3..d6170c7e81 100644 ---- a/Lib/test/test_calendar.py -+++ b/Lib/test/test_calendar.py -@@ -595,6 +595,14 @@ - self.assertEqual(days[0][1], firstweekday) - self.assertEqual(days[-1][1], (firstweekday - 1) % 7) - -+ def test_iterweekdays(self): -+ week0 = list(range(7)) -+ for firstweekday in range(7): -+ cal = calendar.Calendar(firstweekday) -+ week = list(cal.iterweekdays()) -+ expected = week0[firstweekday:] + week0[:firstweekday] -+ self.assertEqual(week, expected) -+ - - class MonthCalendarTestCase(unittest.TestCase): - def setUp(self): diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 39b1b64651..f9c0614003 100644 --- a/Lib/test/test_capi.py @@ -6913,221 +1336,8 @@ index 4b3e33c4fd..3f15078807 100644 def _test_no_stdio(self, streams): code = """if 1: import os, sys -diff --git a/Lib/test/test_codeccallbacks.py b/Lib/test/test_codeccallbacks.py -index 243f002c4e..4991330489 100644 ---- a/Lib/test/test_codeccallbacks.py -+++ b/Lib/test/test_codeccallbacks.py -@@ -1,5 +1,6 @@ - import codecs - import html.entities -+import itertools - import sys - import unicodedata - import unittest -@@ -22,6 +23,18 @@ - self.pos = len(exc.object) - return ("", oldpos) - -+class RepeatedPosReturn: -+ def __init__(self, repl=""): -+ self.repl = repl -+ self.pos = 0 -+ self.count = 0 -+ -+ def handle(self, exc): -+ if self.count > 0: -+ self.count -= 1 -+ return (self.repl, self.pos) -+ return (self.repl, exc.end) -+ - # A UnicodeEncodeError object with a bad start attribute - class BadStartUnicodeEncodeError(UnicodeEncodeError): - def __init__(self): -@@ -783,20 +796,104 @@ - codecs.lookup_error("namereplace") - ) - -- def test_unencodablereplacement(self): -+ def test_encode_nonascii_replacement(self): -+ def handle(exc): -+ if isinstance(exc, UnicodeEncodeError): -+ return (repl, exc.end) -+ raise TypeError("don't know how to handle %r" % exc) -+ codecs.register_error("test.replacing", handle) -+ -+ for enc, input, repl in ( -+ ("ascii", "[¤]", "abc"), -+ ("iso-8859-1", "[€]", "½¾"), -+ ("iso-8859-15", "[¤]", "œŸ"), -+ ): -+ res = input.encode(enc, "test.replacing") -+ self.assertEqual(res, ("[" + repl + "]").encode(enc)) -+ -+ for enc, input, repl in ( -+ ("utf-8", "[\udc80]", "\U0001f40d"), -+ ("utf-16", "[\udc80]", "\U0001f40d"), -+ ("utf-32", "[\udc80]", "\U0001f40d"), -+ ): -+ with self.subTest(encoding=enc): -+ with self.assertRaises(UnicodeEncodeError) as cm: -+ input.encode(enc, "test.replacing") -+ exc = cm.exception -+ self.assertEqual(exc.start, 1) -+ self.assertEqual(exc.end, 2) -+ self.assertEqual(exc.object, input) -+ -+ def test_encode_unencodable_replacement(self): - def unencrepl(exc): - if isinstance(exc, UnicodeEncodeError): -- return ("\u4242", exc.end) -+ return (repl, exc.end) - else: - raise TypeError("don't know how to handle %r" % exc) - codecs.register_error("test.unencreplhandler", unencrepl) -- for enc in ("ascii", "iso-8859-1", "iso-8859-15"): -- self.assertRaises( -- UnicodeEncodeError, -- "\u4242".encode, -- enc, -- "test.unencreplhandler" -- ) -+ -+ for enc, input, repl in ( -+ ("ascii", "[¤]", "½"), -+ ("iso-8859-1", "[€]", "Å“"), -+ ("iso-8859-15", "[¤]", "½"), -+ ("utf-8", "[\udc80]", "\udcff"), -+ ("utf-16", "[\udc80]", "\udcff"), -+ ("utf-32", "[\udc80]", "\udcff"), -+ ): -+ with self.subTest(encoding=enc): -+ with self.assertRaises(UnicodeEncodeError) as cm: -+ input.encode(enc, "test.unencreplhandler") -+ exc = cm.exception -+ self.assertEqual(exc.start, 1) -+ self.assertEqual(exc.end, 2) -+ self.assertEqual(exc.object, input) -+ -+ def test_encode_bytes_replacement(self): -+ def handle(exc): -+ if isinstance(exc, UnicodeEncodeError): -+ return (repl, exc.end) -+ raise TypeError("don't know how to handle %r" % exc) -+ codecs.register_error("test.replacing", handle) -+ -+ # It works even if the bytes sequence is not decodable. -+ for enc, input, repl in ( -+ ("ascii", "[¤]", b"\xbd\xbe"), -+ ("iso-8859-1", "[€]", b"\xbd\xbe"), -+ ("iso-8859-15", "[¤]", b"\xbd\xbe"), -+ ("utf-8", "[\udc80]", b"\xbd\xbe"), -+ ("utf-16le", "[\udc80]", b"\xbd\xbe"), -+ ("utf-16be", "[\udc80]", b"\xbd\xbe"), -+ ("utf-32le", "[\udc80]", b"\xbc\xbd\xbe\xbf"), -+ ("utf-32be", "[\udc80]", b"\xbc\xbd\xbe\xbf"), -+ ): -+ with self.subTest(encoding=enc): -+ res = input.encode(enc, "test.replacing") -+ self.assertEqual(res, "[".encode(enc) + repl + "]".encode(enc)) -+ -+ def test_encode_odd_bytes_replacement(self): -+ def handle(exc): -+ if isinstance(exc, UnicodeEncodeError): -+ return (repl, exc.end) -+ raise TypeError("don't know how to handle %r" % exc) -+ codecs.register_error("test.replacing", handle) -+ -+ input = "[\udc80]" -+ # Tests in which the replacement bytestring contains not whole number -+ # of code units. -+ for enc, repl in ( -+ *itertools.product(("utf-16le", "utf-16be"), -+ [b"a", b"abc"]), -+ *itertools.product(("utf-32le", "utf-32be"), -+ [b"a", b"ab", b"abc", b"abcde"]), -+ ): -+ with self.subTest(encoding=enc, repl=repl): -+ with self.assertRaises(UnicodeEncodeError) as cm: -+ input.encode(enc, "test.replacing") -+ exc = cm.exception -+ self.assertEqual(exc.start, 1) -+ self.assertEqual(exc.end, 2) -+ self.assertEqual(exc.object, input) -+ self.assertEqual(exc.reason, "surrogates not allowed") - - def test_badregistercall(self): - # enhance coverage of: -@@ -940,6 +1037,68 @@ - self.assertRaises(ValueError, codecs.charmap_encode, "\xff", err, D()) - self.assertRaises(TypeError, codecs.charmap_encode, "\xff", err, {0xff: 300}) - -+ def test_decodehelper_bug36819(self): -+ handler = RepeatedPosReturn("x") -+ codecs.register_error("test.bug36819", handler.handle) -+ -+ testcases = [ -+ ("ascii", b"\xff"), -+ ("utf-8", b"\xff"), -+ ("utf-16be", b'\xdc\x80'), -+ ("utf-32be", b'\x00\x00\xdc\x80'), -+ ("iso-8859-6", b"\xff"), -+ ] -+ for enc, bad in testcases: -+ input = "abcd".encode(enc) + bad -+ with self.subTest(encoding=enc): -+ handler.count = 50 -+ decoded = input.decode(enc, "test.bug36819") -+ self.assertEqual(decoded, 'abcdx' * 51) -+ -+ def test_encodehelper_bug36819(self): -+ handler = RepeatedPosReturn() -+ codecs.register_error("test.bug36819", handler.handle) -+ -+ input = "abcd\udc80" -+ encodings = ["ascii", "latin1", "utf-8", "utf-16", "utf-32"] # built-in -+ encodings += ["iso-8859-15"] # charmap codec -+ if sys.platform == 'win32': -+ encodings = ["mbcs", "oem"] # code page codecs -+ -+ handler.repl = "\udcff" -+ for enc in encodings: -+ with self.subTest(encoding=enc): -+ handler.count = 50 -+ with self.assertRaises(UnicodeEncodeError) as cm: -+ input.encode(enc, "test.bug36819") -+ exc = cm.exception -+ self.assertEqual(exc.start, 4) -+ self.assertEqual(exc.end, 5) -+ self.assertEqual(exc.object, input) -+ if sys.platform == "win32": -+ handler.count = 50 -+ with self.assertRaises(UnicodeEncodeError) as cm: -+ codecs.code_page_encode(437, input, "test.bug36819") -+ exc = cm.exception -+ self.assertEqual(exc.start, 4) -+ self.assertEqual(exc.end, 5) -+ self.assertEqual(exc.object, input) -+ -+ handler.repl = "x" -+ for enc in encodings: -+ with self.subTest(encoding=enc): -+ # The interpreter should segfault after a handful of attempts. -+ # 50 was chosen to try to ensure a segfault without a fix, -+ # but not OOM a machine with one. -+ handler.count = 50 -+ encoded = input.encode(enc, "test.bug36819") -+ self.assertEqual(encoded.decode(enc), "abcdx" * 51) -+ if sys.platform == "win32": -+ handler.count = 50 -+ encoded = codecs.code_page_encode(437, input, "test.bug36819") -+ self.assertEqual(encoded[0].decode(), "abcdx" * 51) -+ self.assertEqual(encoded[1], len(input)) -+ - def test_translatehelper(self): - # enhance coverage of: - # Objects/unicodeobject.c::unicode_encode_call_errorhandler() diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py -index a904f426b1..9de34a3efd 100644 +index ab647d66f0..9de34a3efd 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -219,7 +219,8 @@ @@ -7140,166 +1350,11 @@ index a904f426b1..9de34a3efd 100644 with self.assertRaisesRegex(ValueError, "workers must be greater or equal to 0"): compileall.compile_dir(self.directory, workers=-1) -@@ -449,32 +450,29 @@ - class CommandLineTestsBase: - """Test compileall's CLI.""" - -- @classmethod -- def setUpClass(cls): -- for path in filter(os.path.isdir, sys.path): -- directory_created = False -- directory = pathlib.Path(path) / '__pycache__' -- path = directory / 'test.try' -- try: -- if not directory.is_dir(): -- directory.mkdir() -- directory_created = True -- with path.open('w') as file: -- file.write('# for test_compileall') -- except OSError: -- sys_path_writable = False -- break -- finally: -- support.unlink(str(path)) -- if directory_created: -- directory.rmdir() -- else: -- sys_path_writable = True -- cls._sys_path_writable = sys_path_writable -- -- def _skip_if_sys_path_not_writable(self): -- if not self._sys_path_writable: -- raise unittest.SkipTest('not all entries on sys.path are writable') -+ def setUp(self): -+ self.directory = tempfile.mkdtemp() -+ self.addCleanup(shutil.rmtree, self.directory) -+ self.pkgdir = os.path.join(self.directory, 'foo') -+ os.mkdir(self.pkgdir) -+ self.pkgdir_cachedir = os.path.join(self.pkgdir, '__pycache__') -+ # Create the __init__.py and a package module. -+ self.initfn = script_helper.make_script(self.pkgdir, '__init__', '') -+ self.barfn = script_helper.make_script(self.pkgdir, 'bar', '') -+ -+ @contextlib.contextmanager -+ def temporary_pycache_prefix(self): -+ """Adjust and restore sys.pycache_prefix.""" -+ old_prefix = sys.pycache_prefix -+ new_prefix = os.path.join(self.directory, '__testcache__') -+ try: -+ sys.pycache_prefix = new_prefix -+ yield { -+ 'PYTHONPATH': self.directory, -+ 'PYTHONPYCACHEPREFIX': new_prefix, -+ } -+ finally: -+ sys.pycache_prefix = old_prefix - - def _get_run_args(self, args): - return [*support.optim_args_from_interpreter_flags(), -@@ -502,49 +500,39 @@ - path = importlib.util.cache_from_source(fn) - self.assertFalse(os.path.exists(path)) - -- def setUp(self): -- self.directory = tempfile.mkdtemp() -- self.addCleanup(support.rmtree, self.directory) -- self.pkgdir = os.path.join(self.directory, 'foo') -- os.mkdir(self.pkgdir) -- self.pkgdir_cachedir = os.path.join(self.pkgdir, '__pycache__') -- # Create the __init__.py and a package module. -- self.initfn = script_helper.make_script(self.pkgdir, '__init__', '') -- self.barfn = script_helper.make_script(self.pkgdir, 'bar', '') -- - def test_no_args_compiles_path(self): - # Note that -l is implied for the no args case. -- self._skip_if_sys_path_not_writable() - bazfn = script_helper.make_script(self.directory, 'baz', '') -- self.assertRunOK(PYTHONPATH=self.directory) -- self.assertCompiled(bazfn) -- self.assertNotCompiled(self.initfn) -- self.assertNotCompiled(self.barfn) -+ with self.temporary_pycache_prefix() as env: -+ self.assertRunOK(**env) -+ self.assertCompiled(bazfn) -+ self.assertNotCompiled(self.initfn) -+ self.assertNotCompiled(self.barfn) - - @without_source_date_epoch # timestamp invalidation test - def test_no_args_respects_force_flag(self): -- self._skip_if_sys_path_not_writable() - bazfn = script_helper.make_script(self.directory, 'baz', '') -- self.assertRunOK(PYTHONPATH=self.directory) -- pycpath = importlib.util.cache_from_source(bazfn) -+ with self.temporary_pycache_prefix() as env: -+ self.assertRunOK(**env) -+ pycpath = importlib.util.cache_from_source(bazfn) - # Set atime/mtime backward to avoid file timestamp resolution issues - os.utime(pycpath, (time.time()-60,)*2) - mtime = os.stat(pycpath).st_mtime - # Without force, no recompilation -- self.assertRunOK(PYTHONPATH=self.directory) -+ self.assertRunOK(**env) - mtime2 = os.stat(pycpath).st_mtime - self.assertEqual(mtime, mtime2) - # Now force it. -- self.assertRunOK('-f', PYTHONPATH=self.directory) -+ self.assertRunOK('-f', **env) - mtime2 = os.stat(pycpath).st_mtime - self.assertNotEqual(mtime, mtime2) - - def test_no_args_respects_quiet_flag(self): -- self._skip_if_sys_path_not_writable() - script_helper.make_script(self.directory, 'baz', '') -- noisy = self.assertRunOK(PYTHONPATH=self.directory) -+ with self.temporary_pycache_prefix() as env: -+ noisy = self.assertRunOK(**env) - self.assertIn(b'Listing ', noisy) -- quiet = self.assertRunOK('-q', PYTHONPATH=self.directory) -+ quiet = self.assertRunOK('-q', **env) - self.assertNotIn(b'Listing ', quiet) - - # Ensure that the default behavior of compileall's CLI is to create diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py -index d421b02754..7916bafb21 100644 +index 2d27a294b1..7916bafb21 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py -@@ -26,10 +26,10 @@ - PENDING, RUNNING, CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED, Future, - BrokenExecutor) - from concurrent.futures.process import BrokenProcessPool --from multiprocessing import get_context - - import multiprocessing.process - import multiprocessing.util -+import multiprocessing as mp - - - if support.check_sanitizer(address=True, memory=True): -@@ -131,7 +131,6 @@ - self.executor = self.executor_type( - max_workers=self.worker_count, - **self.executor_kwargs) -- self._prime_executor() - - def tearDown(self): - self.executor.shutdown(wait=True) -@@ -145,21 +144,14 @@ - super().tearDown() - - def get_context(self): -- return get_context(self.ctx) -- -- def _prime_executor(self): -- # Make sure that the executor is ready to do work before running the -- # tests. This should reduce the probability of timeouts in the tests. -- futures = [self.executor.submit(time.sleep, 0.1) -- for _ in range(self.worker_count)] -- for f in futures: -- f.result() -+ return mp.get_context(self.ctx) - - - class ThreadPoolMixin(ExecutorMixin): +@@ -151,6 +151,7 @@ executor_type = futures.ThreadPoolExecutor @@ -7307,7 +1362,7 @@ index d421b02754..7916bafb21 100644 class ProcessPoolForkMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "fork" -@@ -170,11 +162,13 @@ +@@ -161,11 +162,13 @@ return super().get_context() @@ -7321,282 +1376,6 @@ index d421b02754..7916bafb21 100644 class ProcessPoolForkserverMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "forkserver" -@@ -261,9 +255,6 @@ - with self.assertRaises(BrokenExecutor): - self.executor.submit(get_init_status) - -- def _prime_executor(self): -- pass -- - @contextlib.contextmanager - def _assert_logged(self, msg): - if self.log_queue is not None: -@@ -350,14 +341,14 @@ - f.result() - - def test_cancel_futures(self): -- executor = self.executor_type(max_workers=3) -- fs = [executor.submit(time.sleep, .1) for _ in range(50)] -- executor.shutdown(cancel_futures=True) -+ assert self.worker_count <= 5, "test needs few workers" -+ fs = [self.executor.submit(time.sleep, .1) for _ in range(50)] -+ self.executor.shutdown(cancel_futures=True) - # We can't guarantee the exact number of cancellations, but we can -- # guarantee that *some* were cancelled. With setting max_workers to 3, -- # most of the submitted futures should have been cancelled. -+ # guarantee that *some* were cancelled. With few workers, many of -+ # the submitted futures should have been cancelled. - cancelled = [fut for fut in fs if fut.cancelled()] -- self.assertTrue(len(cancelled) >= 35, msg=f"{len(cancelled)=}") -+ self.assertGreater(len(cancelled), 20) - - # Ensure the other futures were able to finish. - # Use "not fut.cancelled()" instead of "fut.done()" to include futures -@@ -370,33 +361,32 @@ - # Similar to the number of cancelled futures, we can't guarantee the - # exact number that completed. But, we can guarantee that at least - # one finished. -- self.assertTrue(len(others) > 0, msg=f"{len(others)=}") -+ self.assertGreater(len(others), 0) - -- def test_hang_issue39205(self): -+ def test_hang_gh83386(self): - """shutdown(wait=False) doesn't hang at exit with running futures. - -- See https://bugs.python.org/issue39205. -+ See https://github.com/python/cpython/issues/83386. - """ - if self.executor_type == futures.ProcessPoolExecutor: - raise unittest.SkipTest( -- "Hangs due to https://bugs.python.org/issue39205") -+ "Hangs, see https://github.com/python/cpython/issues/83386") - - rc, out, err = assert_python_ok('-c', """if True: - from concurrent.futures import {executor_type} - from test.test_concurrent_futures import sleep_and_print - if __name__ == "__main__": -+ if {context!r}: multiprocessing.set_start_method({context!r}) - t = {executor_type}(max_workers=3) - t.submit(sleep_and_print, 1.0, "apple") - t.shutdown(wait=False) -- """.format(executor_type=self.executor_type.__name__)) -+ """.format(executor_type=self.executor_type.__name__, -+ context=getattr(self, 'ctx', None))) - self.assertFalse(err) - self.assertEqual(out.strip(), b"apple") - - - class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest, BaseTestCase): -- def _prime_executor(self): -- pass -- - def test_threads_terminate(self): - def acquire_lock(lock): - lock.acquire() -@@ -491,18 +481,21 @@ - - - class ProcessPoolShutdownTest(ExecutorShutdownTest): -- def _prime_executor(self): -- pass -- - def test_processes_terminate(self): - def acquire_lock(lock): - lock.acquire() - -- mp_context = get_context() -+ mp_context = self.get_context() -+ if mp_context.get_start_method(allow_none=False) == "fork": -+ # fork pre-spawns, not on demand. -+ expected_num_processes = self.worker_count -+ else: -+ expected_num_processes = 3 -+ - sem = mp_context.Semaphore(0) - for _ in range(3): - self.executor.submit(acquire_lock, sem) -- self.assertEqual(len(self.executor._processes), 3) -+ self.assertEqual(len(self.executor._processes), expected_num_processes) - for _ in range(3): - sem.release() - processes = self.executor._processes -@@ -512,7 +505,8 @@ - p.join() - - def test_context_manager_shutdown(self): -- with futures.ProcessPoolExecutor(max_workers=5) as e: -+ with futures.ProcessPoolExecutor( -+ max_workers=5, mp_context=self.get_context()) as e: - processes = e._processes - self.assertEqual(list(e.map(abs, range(-5, 5))), - [5, 4, 3, 2, 1, 0, 1, 2, 3, 4]) -@@ -521,7 +515,8 @@ - p.join() - - def test_del_shutdown(self): -- executor = futures.ProcessPoolExecutor(max_workers=5) -+ executor = futures.ProcessPoolExecutor( -+ max_workers=5, mp_context=self.get_context()) - res = executor.map(abs, range(-5, 5)) - executor_manager_thread = executor._executor_manager_thread - processes = executor._processes -@@ -544,7 +539,8 @@ - def test_shutdown_no_wait(self): - # Ensure that the executor cleans up the processes when calling - # shutdown with wait=False -- executor = futures.ProcessPoolExecutor(max_workers=5) -+ executor = futures.ProcessPoolExecutor( -+ max_workers=5, mp_context=self.get_context()) - res = executor.map(abs, range(-5, 5)) - processes = executor._processes - call_queue = executor._call_queue -@@ -921,7 +917,7 @@ - pool.submit(submit, pool) - - for _ in range(50): -- with futures.ProcessPoolExecutor(1, mp_context=get_context('fork')) as workers: -+ with futures.ProcessPoolExecutor(1, mp_context=mp.get_context('fork')) as workers: - workers.submit(tuple) - - -@@ -991,7 +987,7 @@ - def test_ressources_gced_in_workers(self): - # Ensure that argument for a job are correctly gc-ed after the job - # is finished -- mgr = get_context(self.ctx).Manager() -+ mgr = self.get_context().Manager() - obj = EventfulGCObj(mgr) - future = self.executor.submit(id, obj) - future.result() -@@ -1007,36 +1003,41 @@ - mgr.join() - - def test_saturation(self): -- executor = self.executor_type(4) -- mp_context = get_context() -+ executor = self.executor -+ mp_context = self.get_context() - sem = mp_context.Semaphore(0) - job_count = 15 * executor._max_workers -- try: -- for _ in range(job_count): -- executor.submit(sem.acquire) -- self.assertEqual(len(executor._processes), executor._max_workers) -- for _ in range(job_count): -- sem.release() -- finally: -- executor.shutdown() -+ for _ in range(job_count): -+ executor.submit(sem.acquire) -+ self.assertEqual(len(executor._processes), executor._max_workers) -+ for _ in range(job_count): -+ sem.release() - - def test_idle_process_reuse_one(self): -- executor = self.executor_type(4) -+ executor = self.executor -+ assert executor._max_workers >= 4 -+ if self.get_context().get_start_method(allow_none=False) == "fork": -+ raise unittest.SkipTest("Incompatible with the fork start method.") - executor.submit(mul, 21, 2).result() - executor.submit(mul, 6, 7).result() - executor.submit(mul, 3, 14).result() - self.assertEqual(len(executor._processes), 1) -- executor.shutdown() - - def test_idle_process_reuse_multiple(self): -- executor = self.executor_type(4) -+ executor = self.executor -+ assert executor._max_workers <= 5 -+ if self.get_context().get_start_method(allow_none=False) == "fork": -+ raise unittest.SkipTest("Incompatible with the fork start method.") - executor.submit(mul, 12, 7).result() - executor.submit(mul, 33, 25) - executor.submit(mul, 25, 26).result() - executor.submit(mul, 18, 29) -- self.assertLessEqual(len(executor._processes), 2) -+ executor.submit(mul, 1, 2).result() -+ executor.submit(mul, 0, 9) -+ self.assertLessEqual(len(executor._processes), 3) - executor.shutdown() - -+ - create_executor_tests(ProcessPoolExecutorTest, - executor_mixins=(ProcessPoolForkMixin, - ProcessPoolForkserverMixin, -@@ -1138,7 +1139,7 @@ - self.executor.shutdown(wait=True) - - executor = self.executor_type( -- max_workers=2, mp_context=get_context(self.ctx)) -+ max_workers=2, mp_context=self.get_context()) - res = executor.submit(func, *args) - - if ignore_stderr: -@@ -1217,7 +1218,7 @@ - # if a worker fails after the shutdown call. - self.executor.shutdown(wait=True) - with self.executor_type(max_workers=2, -- mp_context=get_context(self.ctx)) as executor: -+ mp_context=self.get_context()) as executor: - self.executor = executor # Allow clean up in fail_on_deadlock - f = executor.submit(_crash, delay=.1) - executor.shutdown(wait=True) -@@ -1230,7 +1231,7 @@ - # Reported in bpo-39104. - self.executor.shutdown(wait=True) - with self.executor_type(max_workers=2, -- mp_context=get_context(self.ctx)) as executor: -+ mp_context=self.get_context()) as executor: - self.executor = executor # Allow clean up in fail_on_deadlock - - # Start the executor and get the executor_manager_thread to collect -diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py -index 4bdc2379fd..48b1d53f80 100644 ---- a/Lib/test/test_curses.py -+++ b/Lib/test/test_curses.py -@@ -266,7 +266,12 @@ - stdscr.echochar(b'A') - stdscr.echochar(65) - with self.assertRaises((UnicodeEncodeError, OverflowError)): -- stdscr.echochar('\u20ac') -+ # Unicode is not fully supported yet, but at least it does -+ # not crash. -+ # It is supposed to fail because either the character is -+ # not encodable with the current encoding, or it is encoded to -+ # a multibyte sequence. -+ stdscr.echochar('\u0114') - stdscr.echochar('A', curses.A_BOLD) - self.assertIs(stdscr.is_wintouched(), False) - -diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py -index e5e9f49396..a8fe3ef6cd 100644 ---- a/Lib/test/test_descr.py -+++ b/Lib/test/test_descr.py -@@ -5686,6 +5686,23 @@ - class A(metaclass=M): - pass - -+ def test_disappearing_custom_mro(self): -+ """ -+ gh-92112: A custom mro() returning a result conflicting with -+ __bases__ and deleting itself caused a double free. -+ """ -+ class B: -+ pass -+ -+ class M(DebugHelperMeta): -+ def mro(cls): -+ del M.mro -+ return (B,) -+ -+ with self.assertRaises(TypeError): -+ class A(metaclass=M): -+ pass -+ - - if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index dba19103e8..68e0d5cff9 100644 --- a/Lib/test/test_doctest.py @@ -7627,97 +1406,6 @@ index a5f8f6465e..00287fea2b 100644 class EINTRTests(unittest.TestCase): @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") -diff --git a/Lib/test/test_email/test__encoded_words.py b/Lib/test/test_email/test__encoded_words.py -index 0b8b1de335..1713962f94 100644 ---- a/Lib/test/test_email/test__encoded_words.py -+++ b/Lib/test/test_email/test__encoded_words.py -@@ -130,6 +130,13 @@ - # XXX Should this be a new Defect instead? - defects = [errors.CharsetError]) - -+ def test_invalid_character_in_charset(self): -+ self._test('=?utf-8\udce2\udc80\udc9d?q?foo=ACbar?=', -+ b'foo\xacbar'.decode('ascii', 'surrogateescape'), -+ charset = 'utf-8\udce2\udc80\udc9d', -+ # XXX Should this be a new Defect instead? -+ defects = [errors.CharsetError]) -+ - def test_q_nonascii(self): - self._test('=?utf-8?q?=C3=89ric?=', - 'Éric', -diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py -index 489cd05be4..761ea90b78 100644 ---- a/Lib/test/test_email/test_email.py -+++ b/Lib/test/test_email/test_email.py -@@ -5323,6 +5323,15 @@ - Content-Transfer-Encoding: 8bit - Content-Disposition: inline; filename*=X-UNKNOWN''myfile.txt - -+""" -+ msg = email.message_from_string(m) -+ self.assertEqual(msg.get_filename(), 'myfile.txt') -+ -+ def test_rfc2231_bad_character_in_encoding(self): -+ m = """\ -+Content-Transfer-Encoding: 8bit -+Content-Disposition: inline; filename*=utf-8\udce2\udc80\udc9d''myfile.txt -+ - """ - msg = email.message_from_string(m) - self.assertEqual(msg.get_filename(), 'myfile.txt') -diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py -index 68bbc9561c..9a512fdb9d 100644 ---- a/Lib/test/test_email/test_headerregistry.py -+++ b/Lib/test/test_email/test_headerregistry.py -@@ -698,6 +698,18 @@ - " charset*=unknown-8bit''utf-8%E2%80%9D\n", - ), - -+ 'rfc2231_nonascii_in_charset_of_charset_parameter_value': ( -+ "text/plain; charset*=utf-8â€''utf-8%E2%80%9D", -+ 'text/plain', -+ 'text', -+ 'plain', -+ {'charset': 'utf-8â€'}, -+ [], -+ 'text/plain; charset="utf-8â€"', -+ "Content-Type: text/plain;" -+ " charset*=utf-8''utf-8%E2%80%9D\n", -+ ), -+ - 'rfc2231_encoded_then_unencoded_segments': ( - ('application/x-foo;' - '\tname*0*="us-ascii\'en-us\'My";' -diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py -index 11c8f01289..8e5cfa4824 100644 ---- a/Lib/test/test_embed.py -+++ b/Lib/test/test_embed.py -@@ -1145,20 +1145,11 @@ - - if MS_WINDOWS: - # Copy pythonXY.dll (or pythonXY_d.dll) -- ver = sys.version_info -- dll = f'python{ver.major}{ver.minor}' -- dll3 = f'python{ver.major}' -- if debug_build(sys.executable): -- dll += '_d' -- dll3 += '_d' -- dll += '.dll' -- dll3 += '.dll' -- dll = os.path.join(os.path.dirname(self.test_exe), dll) -- dll3 = os.path.join(os.path.dirname(self.test_exe), dll3) -- dll_copy = os.path.join(tmpdir, os.path.basename(dll)) -- dll3_copy = os.path.join(tmpdir, os.path.basename(dll3)) -- shutil.copyfile(dll, dll_copy) -- shutil.copyfile(dll3, dll3_copy) -+ import fnmatch -+ exedir = os.path.dirname(self.test_exe) -+ for f in os.listdir(exedir): -+ if fnmatch.fnmatch(f, '*.dll'): -+ shutil.copyfile(os.path.join(exedir, f), os.path.join(tmpdir, f)) - - # Copy Python program - exec_copy = os.path.join(tmpdir, os.path.basename(self.test_exe)) diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 2f8f5eec47..9629ef5226 100644 --- a/Lib/test/test_faulthandler.py @@ -7805,38 +1493,6 @@ index b3aa855a5b..3304cb9606 100644 # Regex to parse: # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7 -diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py -index 12917755a5..44a76a445d 100644 ---- a/Lib/test/test_htmlparser.py -+++ b/Lib/test/test_htmlparser.py -@@ -787,5 +787,27 @@ - ('starttag', 'form', - [('action', 'bogus|&#()value')])]) - -+ def test_invalid_keyword_error_exception(self): -+ # bpo-34480: check that subclasses that define an -+ # error method that raises an exception work -+ class InvalidMarkupException(Exception): -+ pass -+ class MyHTMLParser(html.parser.HTMLParser): -+ def error(self, message): -+ raise InvalidMarkupException(message) -+ parser = MyHTMLParser() -+ with self.assertRaises(InvalidMarkupException): -+ parser.feed('') -+ -+ def test_invalid_keyword_error_pass(self): -+ # bpo-34480: check that subclasses that define an -+ # error method that doesn't raise an exception work -+ class MyHTMLParser(html.parser.HTMLParser): -+ def error(self, message): -+ pass -+ parser = MyHTMLParser() -+ self.assertEqual(parser.feed(''), None) -+ -+ - if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index c1494d29ca..e42244ccfe 100644 --- a/Lib/test/test_httpservers.py @@ -7925,62 +1581,6 @@ index feee861830..401b7ca493 100644 support.requires( 'largefile', 'test requires %s bytes and a long time to run' % self.LARGE) -diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py -index cdd9880c3c..90897f6bed 100644 ---- a/Lib/test/test_ipaddress.py -+++ b/Lib/test/test_ipaddress.py -@@ -580,6 +580,10 @@ - assertBadAddress("1.2.3.256", re.escape("256 (> 255)")) - - def test_valid_netmask(self): -+ self.assertEqual(str(self.factory(('192.0.2.0', 24))), '192.0.2.0/24') -+ self.assertEqual(str(self.factory(('192.0.2.0', '24'))), '192.0.2.0/24') -+ self.assertEqual(str(self.factory(('192.0.2.0', '255.255.255.0'))), -+ '192.0.2.0/24') - self.assertEqual(str(self.factory('192.0.2.0/255.255.255.0')), - '192.0.2.0/24') - for i in range(0, 33): -@@ -740,6 +744,10 @@ - def test_valid_netmask(self): - # We only support CIDR for IPv6, because expanded netmasks are not - # standard notation. -+ self.assertEqual(str(self.factory(('2001:db8::', 32))), -+ '2001:db8::/32') -+ self.assertEqual(str(self.factory(('2001:db8::', '32'))), -+ '2001:db8::/32') - self.assertEqual(str(self.factory('2001:db8::/32')), '2001:db8::/32') - for i in range(0, 129): - # Generate and re-parse the CIDR format (trivial). -@@ -1133,6 +1141,14 @@ - self.assertEqual(ipaddress.IPv4Interface((3221225985, 24)), - ipaddress.IPv4Interface('192.0.2.1/24')) - -+ # Invalid netmask -+ with self.assertRaises(ValueError): -+ ipaddress.IPv4Network(('192.0.2.1', '255.255.255.255.0')) -+ -+ # Invalid netmask using factory -+ with self.assertRaises(ValueError): -+ ipaddress.ip_network(('192.0.2.1', '255.255.255.255.0')) -+ - # issue #16531: constructing IPv6Network from an (address, mask) tuple - def testIPv6Tuple(self): - # /128 -@@ -1192,6 +1208,14 @@ - ipaddress.IPv6Network((ip_scoped, 96)) - # strict=False and host bits set - -+ # Invalid netmask -+ with self.assertRaises(ValueError): -+ ipaddress.IPv6Network(('2001:db8::1', '255.255.255.0')) -+ -+ # Invalid netmask using factory -+ with self.assertRaises(ValueError): -+ ipaddress.ip_network(('2001:db8::1', '255.255.255.0')) -+ - # issue57 - def testAddressIntMath(self): - self.assertEqual(ipaddress.IPv4Address('1.1.1.1') + 255, diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index d441bb15a7..c3c7a052ba 100644 --- a/Lib/test/test_json/test_tool.py @@ -8140,18 +1740,10 @@ index 6558952308..a121e8c2dd 100644 from test import support diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py -index e48157a3de..71561f03c8 100644 +index 78dd3151b3..71561f03c8 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py -@@ -23,6 +23,7 @@ - import sys - import sysconfig - import tempfile -+import textwrap - import threading - import time - import types -@@ -851,6 +852,7 @@ +@@ -852,6 +852,7 @@ # Bug 1110478 @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') @@ -8159,7 +1751,7 @@ index e48157a3de..71561f03c8 100644 def test_update2(self): os.environ.clear() os.environ.update(HELLO="World") -@@ -860,6 +862,7 @@ +@@ -861,6 +862,7 @@ @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') @@ -8167,7 +1759,7 @@ index e48157a3de..71561f03c8 100644 def test_os_popen_iter(self): with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" % unix_shell) as popen: -@@ -1811,6 +1814,7 @@ +@@ -1812,6 +1814,7 @@ @unittest.skipUnless(hasattr(os, 'execv'), "need os.execv()") @@ -8175,7 +1767,7 @@ index e48157a3de..71561f03c8 100644 class ExecTests(unittest.TestCase): @unittest.skipIf(USING_LINUXTHREADS, "avoid triggering a linuxthreads bug: see issue #4970") -@@ -2132,6 +2136,7 @@ +@@ -2133,6 +2136,7 @@ self.assertRaises(OverflowError, os.setreuid, 0, self.UID_OVERFLOW) @unittest.skipUnless(hasattr(os, 'setreuid'), 'test needs os.setreuid()') @@ -8183,7 +1775,7 @@ index e48157a3de..71561f03c8 100644 def test_setreuid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). -@@ -2140,6 +2145,7 @@ +@@ -2141,6 +2145,7 @@ 'import os,sys;os.setreuid(-1,-1);sys.exit(0)']) @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') @@ -8191,7 +1783,7 @@ index e48157a3de..71561f03c8 100644 def test_setregid(self): if os.getuid() != 0 and not HAVE_WHEEL_GROUP: self.assertRaises(OSError, os.setregid, 0, 0) -@@ -2149,6 +2155,7 @@ +@@ -2150,6 +2155,7 @@ self.assertRaises(OverflowError, os.setregid, 0, self.GID_OVERFLOW) @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') @@ -8199,56 +1791,7 @@ index e48157a3de..71561f03c8 100644 def test_setregid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). -@@ -2714,6 +2721,48 @@ - - self.assertEqual(0, handle_delta) - -+ def test_stat_unlink_race(self): -+ # bpo-46785: the implementation of os.stat() falls back to reading -+ # the parent directory if CreateFileW() fails with a permission -+ # error. If reading the parent directory fails because the file or -+ # directory are subsequently unlinked, or because the volume or -+ # share are no longer available, then the original permission error -+ # should not be restored. -+ filename = support.TESTFN -+ self.addCleanup(support.unlink, filename) -+ deadline = time.time() + 5 -+ command = textwrap.dedent("""\ -+ import os -+ import sys -+ import time -+ -+ filename = sys.argv[1] -+ deadline = float(sys.argv[2]) -+ -+ while time.time() < deadline: -+ try: -+ with open(filename, "w") as f: -+ pass -+ except OSError: -+ pass -+ try: -+ os.remove(filename) -+ except OSError: -+ pass -+ """) -+ -+ with subprocess.Popen([sys.executable, '-c', command, filename, str(deadline)]) as proc: -+ while time.time() < deadline: -+ try: -+ os.stat(filename) -+ except FileNotFoundError as e: -+ assert e.winerror == 2 # ERROR_FILE_NOT_FOUND -+ try: -+ proc.wait(1) -+ except subprocess.TimeoutExpired: -+ proc.terminate() -+ -+ - @support.skip_unless_symlink - class NonLocalSymlinkTests(unittest.TestCase): - -@@ -2779,6 +2828,7 @@ +@@ -2822,6 +2828,7 @@ class PidTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'getppid'), "test needs os.getppid") @@ -8256,7 +1799,7 @@ index e48157a3de..71561f03c8 100644 def test_getppid(self): p = subprocess.Popen([sys.executable, '-c', 'import os; print(os.getppid())'], -@@ -2805,6 +2855,9 @@ +@@ -2848,6 +2855,9 @@ self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) self.assertEqual(pid2, pid) @@ -8266,7 +1809,7 @@ index e48157a3de..71561f03c8 100644 def test_waitpid(self): self.check_waitpid(code='pass', exitcode=0) -@@ -3474,6 +3527,7 @@ +@@ -3517,6 +3527,7 @@ self.assertGreaterEqual(size.columns, 0) self.assertGreaterEqual(size.lines, 0) @@ -8369,7 +1912,7 @@ index ab1bc77655..60be660d25 100644 def _do_test_commandline(self, cmdline, expected): diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py -index a8db30679a..30f0088291 100644 +index 6ba14547bd..30f0088291 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -61,15 +61,21 @@ @@ -8438,28 +1981,6 @@ index a8db30679a..30f0088291 100644 self.assertRaises(OSError, posix.sched_get_priority_min, -23) self.assertRaises(OSError, posix.sched_get_priority_max, -23) -@@ -1153,7 +1161,9 @@ - mask = posix.sched_getaffinity(0) - self.assertIsInstance(mask, set) - self.assertGreaterEqual(len(mask), 1) -- self.assertRaises(OSError, posix.sched_getaffinity, -1) -+ if not sys.platform.startswith("freebsd"): -+ # bpo-47205: does not raise OSError on FreeBSD -+ self.assertRaises(OSError, posix.sched_getaffinity, -1) - for cpu in mask: - self.assertIsInstance(cpu, int) - self.assertGreaterEqual(cpu, 0) -@@ -1171,7 +1181,9 @@ - self.assertRaises(ValueError, posix.sched_setaffinity, 0, [-10]) - self.assertRaises(ValueError, posix.sched_setaffinity, 0, map(int, "0X")) - self.assertRaises(OverflowError, posix.sched_setaffinity, 0, [1<<128]) -- self.assertRaises(OSError, posix.sched_setaffinity, -1, mask) -+ if not sys.platform.startswith("freebsd"): -+ # bpo-47205: does not raise OSError on FreeBSD -+ self.assertRaises(OSError, posix.sched_setaffinity, -1, mask) - - def test_rtld_constants(self): - # check presence of major RTLD_* constants diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index 9c32467cbb..ce273f0b4d 100644 --- a/Lib/test/test_pty.py @@ -8492,280 +2013,6 @@ index 715544c8a9..6e66178816 100644 def test_scriptdecode(self): (p, e) = self.STRINGS[-1] process = subprocess.Popen([sys.executable, "-mquopri", "-d"], -diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py -index 48a609b5a0..c779057bc1 100644 ---- a/Lib/test/test_re.py -+++ b/Lib/test/test_re.py -@@ -217,6 +217,16 @@ - re.compile(r'(?Px)(?P=a)(?(a)y)') - re.compile(r'(?Px)(?P=a1)(?(a1)y)') - re.compile(r'(?Px)\1(?(1)y)') -+ re.compile(b'(?Px)(?P=a1)(?(a1)y)') -+ # New valid identifiers in Python 3 -+ re.compile('(?P<µ>x)(?P=µ)(?(µ)y)') -+ re.compile('(?P<ð”˜ð”«ð”¦ð” ð”¬ð”¡ð”¢>x)(?P=ð”˜ð”«ð”¦ð” ð”¬ð”¡ð”¢)(?(ð”˜ð”«ð”¦ð” ð”¬ð”¡ð”¢)y)') -+ # Support > 100 groups. -+ pat = '|'.join('x(?P%x)y' % (i, i) for i in range(1, 200 + 1)) -+ pat = '(?:%s)(?(200)z|t)' % pat -+ self.assertEqual(re.match(pat, 'xc8yz').span(), (0, 5)) -+ -+ def test_symbolic_groups_errors(self): - self.checkPatternError(r'(?P)(?P)', - "redefinition of group name 'a' as group 2; " - "was group 1") -@@ -242,16 +252,22 @@ - self.checkPatternError(r'(?(-1))', "bad character in group name '-1'", 3) - self.checkPatternError(r'(?(1a))', "bad character in group name '1a'", 3) - self.checkPatternError(r'(?(a.))', "bad character in group name 'a.'", 3) -- # New valid/invalid identifiers in Python 3 -- re.compile('(?P<µ>x)(?P=µ)(?(µ)y)') -- re.compile('(?P<ð”˜ð”«ð”¦ð” ð”¬ð”¡ð”¢>x)(?P=ð”˜ð”«ð”¦ð” ð”¬ð”¡ð”¢)(?(ð”˜ð”«ð”¦ð” ð”¬ð”¡ð”¢)y)') - self.checkPatternError('(?P<©>x)', "bad character in group name '©'", 4) -+ self.checkPatternError('(?P=©)', "bad character in group name '©'", 4) -+ self.checkPatternError('(?(©)y)', "bad character in group name '©'", 3) -+ -+ def test_symbolic_refs(self): -+ self.assertEqual(re.sub('(?Px)|(?Py)', r'\g', 'xx'), '') -+ self.assertEqual(re.sub('(?Px)|(?Py)', r'\2', 'xx'), '') -+ self.assertEqual(re.sub(b'(?Px)', br'\g', b'xx'), b'xx') -+ # New valid identifiers in Python 3 -+ self.assertEqual(re.sub('(?P<µ>x)', r'\g<µ>', 'xx'), 'xx') -+ self.assertEqual(re.sub('(?P<ð”˜ð”«ð”¦ð” ð”¬ð”¡ð”¢>x)', r'\g<ð”˜ð”«ð”¦ð” ð”¬ð”¡ð”¢>', 'xx'), 'xx') - # Support > 100 groups. - pat = '|'.join('x(?P%x)y' % (i, i) for i in range(1, 200 + 1)) -- pat = '(?:%s)(?(200)z|t)' % pat -- self.assertEqual(re.match(pat, 'xc8yz').span(), (0, 5)) -+ self.assertEqual(re.sub(pat, r'\g<200>', 'xc8yzxc8y'), 'c8zc8') - -- def test_symbolic_refs(self): -+ def test_symbolic_refs_errors(self): - self.checkTemplateError('(?Px)', r'\g, unterminated name', 3) - self.checkTemplateError('(?Px)', r'\g<', 'xx', -@@ -269,18 +285,14 @@ - 'invalid group reference 2', 1) - with self.assertRaisesRegex(IndexError, "unknown group name 'ab'"): - re.sub('(?Px)', r'\g', 'xx') -- self.assertEqual(re.sub('(?Px)|(?Py)', r'\g', 'xx'), '') -- self.assertEqual(re.sub('(?Px)|(?Py)', r'\2', 'xx'), '') - self.checkTemplateError('(?Px)', r'\g<-1>', 'xx', - "bad character in group name '-1'", 3) -- # New valid/invalid identifiers in Python 3 -- self.assertEqual(re.sub('(?P<µ>x)', r'\g<µ>', 'xx'), 'xx') -- self.assertEqual(re.sub('(?P<ð”˜ð”«ð”¦ð” ð”¬ð”¡ð”¢>x)', r'\g<ð”˜ð”«ð”¦ð” ð”¬ð”¡ð”¢>', 'xx'), 'xx') - self.checkTemplateError('(?Px)', r'\g<©>', 'xx', - "bad character in group name '©'", 3) -- # Support > 100 groups. -- pat = '|'.join('x(?P%x)y' % (i, i) for i in range(1, 200 + 1)) -- self.assertEqual(re.sub(pat, r'\g<200>', 'xc8yzxc8y'), 'c8zc8') -+ self.checkTemplateError('(?Px)', r'\g<㊀>', 'xx', -+ "bad character in group name '㊀'", 3) -+ self.checkTemplateError('(?Px)', r'\g<¹>', 'xx', -+ "bad character in group name '¹'", 3) - - def test_re_subn(self): - self.assertEqual(re.subn("(?i)b+", "x", "bbbb BBBB"), ('x x', 2)) -@@ -542,12 +554,28 @@ - pat = '(?:%s)(?(200)z)' % pat - self.assertEqual(re.match(pat, 'xc8yz').span(), (0, 5)) - -- self.checkPatternError(r'(?P)(?(0))', 'bad group number', 10) -+ def test_re_groupref_exists_errors(self): -+ self.checkPatternError(r'(?P)(?(0)a|b)', 'bad group number', 10) -+ self.checkPatternError(r'()(?(-1)a|b)', -+ "bad character in group name '-1'", 5) -+ self.checkPatternError(r'()(?(㊀)a|b)', -+ "bad character in group name '㊀'", 5) -+ self.checkPatternError(r'()(?(¹)a|b)', -+ "bad character in group name '¹'", 5) -+ self.checkPatternError(r'()(?(1', -+ "missing ), unterminated name", 5) -+ self.checkPatternError(r'()(?(1)a', -+ "missing ), unterminated subpattern", 2) - self.checkPatternError(r'()(?(1)a|b', - 'missing ), unterminated subpattern', 2) -+ self.checkPatternError(r'()(?(1)a|b|c', -+ 'conditional backref with more than ' -+ 'two branches', 10) - self.checkPatternError(r'()(?(1)a|b|c)', - 'conditional backref with more than ' - 'two branches', 10) -+ self.checkPatternError(r'()(?(2)a)', -+ "invalid group reference 2", 5) - - def test_re_groupref_overflow(self): - from sre_constants import MAXGROUPS -@@ -727,6 +755,10 @@ - "undefined character name 'SPAM'", 0) - self.checkPatternError(r'[\N{SPAM}]', - "undefined character name 'SPAM'", 1) -+ self.checkPatternError(r'\N{KEYCAP NUMBER SIGN}', -+ "undefined character name 'KEYCAP NUMBER SIGN'", 0) -+ self.checkPatternError(r'[\N{KEYCAP NUMBER SIGN}]', -+ "undefined character name 'KEYCAP NUMBER SIGN'", 1) - self.checkPatternError(br'\N{LESS-THAN SIGN}', r'bad escape \N', 0) - self.checkPatternError(br'[\N{LESS-THAN SIGN}]', r'bad escape \N', 1) - -@@ -840,16 +872,30 @@ - self.assertEqual(re.match(r"((a)\s(abc|a))", "a a", re.I).group(1), "a a") - self.assertEqual(re.match(r"((a)\s(abc|a)*)", "a aa", re.I).group(1), "a aa") - -- assert '\u212a'.lower() == 'k' # 'K' -+ # Two different characters have the same lowercase. -+ assert 'K'.lower() == '\u212a'.lower() == 'k' # 'K' - self.assertTrue(re.match(r'K', '\u212a', re.I)) - self.assertTrue(re.match(r'k', '\u212a', re.I)) - self.assertTrue(re.match(r'\u212a', 'K', re.I)) - self.assertTrue(re.match(r'\u212a', 'k', re.I)) -- assert '\u017f'.upper() == 'S' # 'Å¿' -+ -+ # Two different characters have the same uppercase. -+ assert 's'.upper() == '\u017f'.upper() == 'S' # 'Å¿' - self.assertTrue(re.match(r'S', '\u017f', re.I)) - self.assertTrue(re.match(r's', '\u017f', re.I)) - self.assertTrue(re.match(r'\u017f', 'S', re.I)) - self.assertTrue(re.match(r'\u017f', 's', re.I)) -+ -+ # Two different characters have the same uppercase. Unicode 9.0+. -+ assert '\u0432'.upper() == '\u1c80'.upper() == '\u0412' # 'в', 'á²€', 'Ð’' -+ self.assertTrue(re.match(r'\u0412', '\u0432', re.I)) -+ self.assertTrue(re.match(r'\u0412', '\u1c80', re.I)) -+ self.assertTrue(re.match(r'\u0432', '\u0412', re.I)) -+ self.assertTrue(re.match(r'\u0432', '\u1c80', re.I)) -+ self.assertTrue(re.match(r'\u1c80', '\u0412', re.I)) -+ self.assertTrue(re.match(r'\u1c80', '\u0432', re.I)) -+ -+ # Two different characters have the same multicharacter uppercase. - assert '\ufb05'.upper() == '\ufb06'.upper() == 'ST' # 'ſt', 'st' - self.assertTrue(re.match(r'\ufb05', '\ufb06', re.I)) - self.assertTrue(re.match(r'\ufb06', '\ufb05', re.I)) -@@ -863,16 +909,31 @@ - self.assertTrue(re.match(br'[19a]', b'a', re.I)) - self.assertTrue(re.match(br'[19a]', b'A', re.I)) - self.assertTrue(re.match(br'[19A]', b'a', re.I)) -- assert '\u212a'.lower() == 'k' # 'K' -+ -+ # Two different characters have the same lowercase. -+ assert 'K'.lower() == '\u212a'.lower() == 'k' # 'K' - self.assertTrue(re.match(r'[19K]', '\u212a', re.I)) - self.assertTrue(re.match(r'[19k]', '\u212a', re.I)) - self.assertTrue(re.match(r'[19\u212a]', 'K', re.I)) - self.assertTrue(re.match(r'[19\u212a]', 'k', re.I)) -- assert '\u017f'.upper() == 'S' # 'Å¿' -+ -+ # Two different characters have the same uppercase. -+ assert 's'.upper() == '\u017f'.upper() == 'S' # 'Å¿' - self.assertTrue(re.match(r'[19S]', '\u017f', re.I)) - self.assertTrue(re.match(r'[19s]', '\u017f', re.I)) - self.assertTrue(re.match(r'[19\u017f]', 'S', re.I)) - self.assertTrue(re.match(r'[19\u017f]', 's', re.I)) -+ -+ # Two different characters have the same uppercase. Unicode 9.0+. -+ assert '\u0432'.upper() == '\u1c80'.upper() == '\u0412' # 'в', 'á²€', 'Ð’' -+ self.assertTrue(re.match(r'[19\u0412]', '\u0432', re.I)) -+ self.assertTrue(re.match(r'[19\u0412]', '\u1c80', re.I)) -+ self.assertTrue(re.match(r'[19\u0432]', '\u0412', re.I)) -+ self.assertTrue(re.match(r'[19\u0432]', '\u1c80', re.I)) -+ self.assertTrue(re.match(r'[19\u1c80]', '\u0412', re.I)) -+ self.assertTrue(re.match(r'[19\u1c80]', '\u0432', re.I)) -+ -+ # Two different characters have the same multicharacter uppercase. - assert '\ufb05'.upper() == '\ufb06'.upper() == 'ST' # 'ſt', 'st' - self.assertTrue(re.match(r'[19\ufb05]', '\ufb06', re.I)) - self.assertTrue(re.match(r'[19\ufb06]', '\ufb05', re.I)) -@@ -896,16 +957,30 @@ - self.assertTrue(re.match(r'[\U00010400-\U00010427]', '\U00010428', re.I)) - self.assertTrue(re.match(r'[\U00010400-\U00010427]', '\U00010400', re.I)) - -- assert '\u212a'.lower() == 'k' # 'K' -+ # Two different characters have the same lowercase. -+ assert 'K'.lower() == '\u212a'.lower() == 'k' # 'K' - self.assertTrue(re.match(r'[J-M]', '\u212a', re.I)) - self.assertTrue(re.match(r'[j-m]', '\u212a', re.I)) - self.assertTrue(re.match(r'[\u2129-\u212b]', 'K', re.I)) - self.assertTrue(re.match(r'[\u2129-\u212b]', 'k', re.I)) -- assert '\u017f'.upper() == 'S' # 'Å¿' -+ -+ # Two different characters have the same uppercase. -+ assert 's'.upper() == '\u017f'.upper() == 'S' # 'Å¿' - self.assertTrue(re.match(r'[R-T]', '\u017f', re.I)) - self.assertTrue(re.match(r'[r-t]', '\u017f', re.I)) - self.assertTrue(re.match(r'[\u017e-\u0180]', 'S', re.I)) - self.assertTrue(re.match(r'[\u017e-\u0180]', 's', re.I)) -+ -+ # Two different characters have the same uppercase. Unicode 9.0+. -+ assert '\u0432'.upper() == '\u1c80'.upper() == '\u0412' # 'в', 'á²€', 'Ð’' -+ self.assertTrue(re.match(r'[\u0411-\u0413]', '\u0432', re.I)) -+ self.assertTrue(re.match(r'[\u0411-\u0413]', '\u1c80', re.I)) -+ self.assertTrue(re.match(r'[\u0431-\u0433]', '\u0412', re.I)) -+ self.assertTrue(re.match(r'[\u0431-\u0433]', '\u1c80', re.I)) -+ self.assertTrue(re.match(r'[\u1c80-\u1c82]', '\u0412', re.I)) -+ self.assertTrue(re.match(r'[\u1c80-\u1c82]', '\u0432', re.I)) -+ -+ # Two different characters have the same multicharacter uppercase. - assert '\ufb05'.upper() == '\ufb06'.upper() == 'ST' # 'ſt', 'st' - self.assertTrue(re.match(r'[\ufb04-\ufb05]', '\ufb06', re.I)) - self.assertTrue(re.match(r'[\ufb06-\ufb07]', '\ufb05', re.I)) -@@ -1607,11 +1682,6 @@ - self.assertIsNone(re.match(r'(?i:(?-i:a)b)', 'Ab')) - self.assertTrue(re.match(r'(?i:(?-i:a)b)', 'aB')) - -- self.assertTrue(re.match(r'(?x: a) b', 'a b')) -- self.assertIsNone(re.match(r'(?x: a) b', ' a b')) -- self.assertTrue(re.match(r'(?-x: a) b', ' ab', re.VERBOSE)) -- self.assertIsNone(re.match(r'(?-x: a) b', 'ab', re.VERBOSE)) -- - self.assertTrue(re.match(r'\w(?a:\W)\w', '\xe0\xe0\xe0')) - self.assertTrue(re.match(r'(?a:\W(?u:\w)\W)', '\xe0\xe0\xe0')) - self.assertTrue(re.match(r'\W(?u:\w)\W', '\xe0\xe0\xe0', re.ASCII)) -@@ -1637,6 +1707,33 @@ - self.checkPatternError(r'(?i+', 'missing -, : or )', 3) - self.checkPatternError(r'(?iz', 'unknown flag', 3) - -+ def test_ignore_spaces(self): -+ for space in " \t\n\r\v\f": -+ self.assertTrue(re.fullmatch(space + 'a', 'a', re.VERBOSE)) -+ for space in b" ", b"\t", b"\n", b"\r", b"\v", b"\f": -+ self.assertTrue(re.fullmatch(space + b'a', b'a', re.VERBOSE)) -+ self.assertTrue(re.fullmatch('(?x) a', 'a')) -+ self.assertTrue(re.fullmatch(' (?x) a', 'a', re.VERBOSE)) -+ self.assertTrue(re.fullmatch('(?x) (?x) a', 'a')) -+ self.assertTrue(re.fullmatch(' a(?x: b) c', ' ab c')) -+ self.assertTrue(re.fullmatch(' a(?-x: b) c', 'a bc', re.VERBOSE)) -+ self.assertTrue(re.fullmatch('(?x) a(?-x: b) c', 'a bc')) -+ self.assertTrue(re.fullmatch('(?x) a| b', 'a')) -+ self.assertTrue(re.fullmatch('(?x) a| b', 'b')) -+ -+ def test_comments(self): -+ self.assertTrue(re.fullmatch('#x\na', 'a', re.VERBOSE)) -+ self.assertTrue(re.fullmatch(b'#x\na', b'a', re.VERBOSE)) -+ self.assertTrue(re.fullmatch('(?x)#x\na', 'a')) -+ self.assertTrue(re.fullmatch('#x\n(?x)#y\na', 'a', re.VERBOSE)) -+ self.assertTrue(re.fullmatch('(?x)#x\n(?x)#y\na', 'a')) -+ self.assertTrue(re.fullmatch('#x\na(?x:#y\nb)#z\nc', '#x\nab#z\nc')) -+ self.assertTrue(re.fullmatch('#x\na(?-x:#y\nb)#z\nc', 'a#y\nbc', -+ re.VERBOSE)) -+ self.assertTrue(re.fullmatch('(?x)#x\na(?-x:#y\nb)#z\nc', 'a#y\nbc')) -+ self.assertTrue(re.fullmatch('(?x)#x\na|#y\nb', 'a')) -+ self.assertTrue(re.fullmatch('(?x)#x\na|#y\nb', 'b')) -+ - def test_bug_6509(self): - # Replacement strings of both types must parse properly. - # all strings -diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py -index 4aaf75b1bc..c734f0d6ce 100644 ---- a/Lib/test/test_runpy.py -+++ b/Lib/test/test_runpy.py -@@ -740,8 +740,7 @@ - "runpy.run_path(%r)\n") % dummy_dir - script_name = self._make_test_script(script_dir, mod_name, source) - zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name) -- msg = "recursion depth exceeded" -- self.assertRaisesRegex(RecursionError, msg, run_path, zip_name) -+ self.assertRaises(RecursionError, run_path, zip_name) - - def test_encoding(self): - with temp_dir() as script_dir: diff --git a/Lib/test/test_script_helper.py b/Lib/test/test_script_helper.py index 4ade2cbc0d..83ce283a04 100644 --- a/Lib/test/test_script_helper.py @@ -9074,7 +2321,7 @@ index 2accad1aee..4824b6c8d0 100644 import distutils.text_file import distutils.unixccompiler diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py -index 2f1e5e971e..9c3e449272 100644 +index ed85d18541..9c3e449272 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -108,6 +108,7 @@ @@ -9117,76 +2364,6 @@ index 2f1e5e971e..9c3e449272 100644 def c_locale_get_error_handler(self, locale, isolated=False, encoding=None): # Force the POSIX locale env = os.environ.copy() -@@ -982,8 +987,8 @@ - with test.support.captured_stderr() as stderr, \ - test.support.swap_attr(sys, 'unraisablehook', - sys.__unraisablehook__): -- expected = self.write_unraisable_exc( -- A.B.X(), "msg", "obj"); -+ expected = self.write_unraisable_exc( -+ A.B.X(), "msg", "obj"); - report = stderr.getvalue() - testName = 'test_original_unraisablehook_exception_qualname' - self.assertIn(f"{testName}..A.B.X", report) -diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py -index 7519309c5e..8884bf45a9 100644 ---- a/Lib/test/test_sys_settrace.py -+++ b/Lib/test/test_sys_settrace.py -@@ -1603,6 +1603,54 @@ - next(gen()) - output.append(5) - -+ @jump_test(2, 3, [1, 3]) -+ def test_jump_forward_over_listcomp(output): -+ output.append(1) -+ x = [i for i in range(10)] -+ output.append(3) -+ -+ # checking for segfaults. -+ # See https://github.com/python/cpython/issues/92311 -+ @jump_test(3, 1, []) -+ def test_jump_backward_over_listcomp(output): -+ a = 1 -+ x = [i for i in range(10)] -+ c = 3 -+ -+ @jump_test(8, 2, [2, 7, 2]) -+ def test_jump_backward_over_listcomp_v2(output): -+ flag = False -+ output.append(2) -+ if flag: -+ return -+ x = [i for i in range(5)] -+ flag = 6 -+ output.append(7) -+ output.append(8) -+ -+ @async_jump_test(2, 3, [1, 3]) -+ async def test_jump_forward_over_async_listcomp(output): -+ output.append(1) -+ x = [i async for i in asynciter(range(10))] -+ output.append(3) -+ -+ @async_jump_test(3, 1, []) -+ async def test_jump_backward_over_async_listcomp(output): -+ a = 1 -+ x = [i async for i in asynciter(range(10))] -+ c = 3 -+ -+ @async_jump_test(8, 2, [2, 7, 2]) -+ async def test_jump_backward_over_async_listcomp_v2(output): -+ flag = False -+ output.append(2) -+ if flag: -+ return -+ x = [i async for i in asynciter(range(5))] -+ flag = 6 -+ output.append(7) -+ output.append(8) -+ - - if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 0ca5c9390d..32430b41eb 100644 --- a/Lib/test/test_sysconfig.py @@ -9262,30 +2439,6 @@ index c0b3388b2d..00f72730b3 100644 def test_encoded_file(self): # Test that tracebacks are correctly printed for encoded source files: # - correct line number (Issue2384) -diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py -index 08eddb095d..3fec59a879 100644 ---- a/Lib/test/test_typing.py -+++ b/Lib/test/test_typing.py -@@ -4076,6 +4076,19 @@ - {'a': typing.Optional[int], 'b': int} - ) - -+ def test_non_generic_subscript(self): -+ # For backward compatibility, subscription works -+ # on arbitrary TypedDict types. -+ class TD(TypedDict): -+ a: T -+ A = TD[int] -+ self.assertEqual(A.__origin__, TD) -+ self.assertEqual(A.__parameters__, ()) -+ self.assertEqual(A.__args__, (int,)) -+ a = A(a = 1) -+ self.assertIs(type(a), dict) -+ self.assertEqual(a, {'a': 1}) -+ - - class IOTests(BaseTestCase): - diff --git a/Lib/test/test_unicodedata.py b/Lib/test/test_unicodedata.py index edfd860fd5..edbc8ce641 100644 --- a/Lib/test/test_unicodedata.py @@ -9306,26 +2459,6 @@ index edfd860fd5..edbc8ce641 100644 def test_failed_import_during_compiling(self): # Issue 4367 # Decoding \N escapes requires the unicodedata module. If it can't be -diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py -index 74374c0868..162e356d1f 100644 ---- a/Lib/test/test_urllib2_localnet.py -+++ b/Lib/test/test_urllib2_localnet.py -@@ -613,6 +613,15 @@ - pass - self.assertEqual(handler.headers_received["Range"], "bytes=20-39") - -+ def test_sending_headers_camel(self): -+ handler = self.start_server() -+ req = urllib.request.Request("http://localhost:%s/" % handler.port, -+ headers={"X-SoMe-hEader": "foobar"}) -+ with urllib.request.urlopen(req): -+ pass -+ self.assertIn("X-Some-Header", handler.headers_received.keys()) -+ self.assertNotIn("X-SoMe-hEader", handler.headers_received.keys()) -+ - def test_basic(self): - handler = self.start_server() - with urllib.request.urlopen("http://localhost:%s" % handler.port) as open_url: diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index ba4c500e8e..1534b59cef 100644 --- a/Lib/test/test_urllib2net.py @@ -9453,245 +2586,6 @@ index 519a9432ab..63008a88fe 100644 URL = 'http://www.example.com' CMD_NAME = 'test' -diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py -index 0f45fc71ce..956d4c587c 100644 ---- a/Lib/test/test_xml_etree.py -+++ b/Lib/test/test_xml_etree.py -@@ -10,7 +10,6 @@ - import html - import io - import itertools --import locale - import operator - import os - import pickle -@@ -112,6 +111,9 @@ - return newtest - return decorator - -+def convlinesep(data): -+ return data.replace(b'\n', os.linesep.encode()) -+ - - class ModuleTest(unittest.TestCase): - def test_sanity(self): -@@ -957,15 +959,13 @@ - - def test_tostring_xml_declaration_unicode_encoding(self): - elem = ET.XML('') -- preferredencoding = locale.getpreferredencoding() - self.assertEqual( -- f"\n", -- ET.tostring(elem, encoding='unicode', xml_declaration=True) -+ ET.tostring(elem, encoding='unicode', xml_declaration=True), -+ "\n" - ) - - def test_tostring_xml_declaration_cases(self): - elem = ET.XML('ø') -- preferredencoding = locale.getpreferredencoding() - TESTCASES = [ - # (expected_retval, encoding, xml_declaration) - # ... xml_declaration = None -@@ -992,7 +992,7 @@ - b"ø", 'US-ASCII', True), - (b"\n" - b"\xf8", 'ISO-8859-1', True), -- (f"\n" -+ ("\n" - "ø", 'unicode', True), - - ] -@@ -1030,11 +1030,10 @@ - b"\n" - ) - -- preferredencoding = locale.getpreferredencoding() - stringlist = ET.tostringlist(elem, encoding='unicode', xml_declaration=True) - self.assertEqual( - ''.join(stringlist), -- f"\n" -+ "\n" - ) - self.assertRegex(stringlist[0], r"^<\?xml version='1.0' encoding='.+'?>") - self.assertEqual(['', '', ''], stringlist[1:]) -@@ -3654,32 +3653,95 @@ - - def test_write_to_filename(self): - self.addCleanup(support.unlink, TESTFN) -- tree = ET.ElementTree(ET.XML('''''')) -+ tree = ET.ElementTree(ET.XML('''\xf8''')) - tree.write(TESTFN) - with open(TESTFN, 'rb') as f: -- self.assertEqual(f.read(), b'''''') -+ self.assertEqual(f.read(), b'''ø''') -+ -+ def test_write_to_filename_with_encoding(self): -+ self.addCleanup(support.unlink, TESTFN) -+ tree = ET.ElementTree(ET.XML('''\xf8''')) -+ tree.write(TESTFN, encoding='utf-8') -+ with open(TESTFN, 'rb') as f: -+ self.assertEqual(f.read(), b'''\xc3\xb8''') -+ -+ tree.write(TESTFN, encoding='ISO-8859-1') -+ with open(TESTFN, 'rb') as f: -+ self.assertEqual(f.read(), convlinesep( -+ b'''\n''' -+ b'''\xf8''')) -+ -+ def test_write_to_filename_as_unicode(self): -+ self.addCleanup(support.unlink, TESTFN) -+ with open(TESTFN, 'w') as f: -+ encoding = f.encoding -+ support.unlink(TESTFN) -+ -+ tree = ET.ElementTree(ET.XML('''\xf8''')) -+ tree.write(TESTFN, encoding='unicode') -+ with open(TESTFN, 'rb') as f: -+ data = f.read() -+ expected = "\xf8".encode(encoding, 'xmlcharrefreplace') -+ if encoding.lower() in ('utf-8', 'ascii'): -+ self.assertEqual(data, expected) -+ else: -+ self.assertIn(b"''')) -+ tree = ET.ElementTree(ET.XML('''\xf8''')) - with open(TESTFN, 'w', encoding='utf-8') as f: - tree.write(f, encoding='unicode') - self.assertFalse(f.closed) - with open(TESTFN, 'rb') as f: -- self.assertEqual(f.read(), b'''''') -+ self.assertEqual(f.read(), b'''\xc3\xb8''') -+ -+ with open(TESTFN, 'w', encoding='ascii', errors='xmlcharrefreplace') as f: -+ tree.write(f, encoding='unicode') -+ self.assertFalse(f.closed) -+ with open(TESTFN, 'rb') as f: -+ self.assertEqual(f.read(), convlinesep( -+ b'''\n''' -+ b'''ø''')) -+ -+ with open(TESTFN, 'w', encoding='ISO-8859-1') as f: -+ tree.write(f, encoding='unicode') -+ self.assertFalse(f.closed) -+ with open(TESTFN, 'rb') as f: -+ self.assertEqual(f.read(), convlinesep( -+ b'''\n''' -+ b'''\xf8''')) - - def test_write_to_binary_file(self): - self.addCleanup(support.unlink, TESTFN) -- tree = ET.ElementTree(ET.XML('''''')) -+ tree = ET.ElementTree(ET.XML('''\xf8''')) - with open(TESTFN, 'wb') as f: - tree.write(f) - self.assertFalse(f.closed) - with open(TESTFN, 'rb') as f: -- self.assertEqual(f.read(), b'''''') -+ self.assertEqual(f.read(), b'''ø''') -+ -+ def test_write_to_binary_file_with_encoding(self): -+ self.addCleanup(support.unlink, TESTFN) -+ tree = ET.ElementTree(ET.XML('''\xf8''')) -+ with open(TESTFN, 'wb') as f: -+ tree.write(f, encoding='utf-8') -+ self.assertFalse(f.closed) -+ with open(TESTFN, 'rb') as f: -+ self.assertEqual(f.read(), b'''\xc3\xb8''') -+ -+ with open(TESTFN, 'wb') as f: -+ tree.write(f, encoding='ISO-8859-1') -+ self.assertFalse(f.closed) -+ with open(TESTFN, 'rb') as f: -+ self.assertEqual(f.read(), -+ b'''\n''' -+ b'''\xf8''') - - def test_write_to_binary_file_with_bom(self): - self.addCleanup(support.unlink, TESTFN) -- tree = ET.ElementTree(ET.XML('''''')) -+ tree = ET.ElementTree(ET.XML('''\xf8''')) - # test BOM writing to buffered file - with open(TESTFN, 'wb') as f: - tree.write(f, encoding='utf-16') -@@ -3687,7 +3749,7 @@ - with open(TESTFN, 'rb') as f: - self.assertEqual(f.read(), - '''\n''' -- ''''''.encode("utf-16")) -+ '''\xf8'''.encode("utf-16")) - # test BOM writing to non-buffered file - with open(TESTFN, 'wb', buffering=0) as f: - tree.write(f, encoding='utf-16') -@@ -3695,7 +3757,7 @@ - with open(TESTFN, 'rb') as f: - self.assertEqual(f.read(), - '''\n''' -- ''''''.encode("utf-16")) -+ '''\xf8'''.encode("utf-16")) - - def test_read_from_stringio(self): - tree = ET.ElementTree() -@@ -3704,10 +3766,10 @@ - self.assertEqual(tree.getroot().tag, 'site') - - def test_write_to_stringio(self): -- tree = ET.ElementTree(ET.XML('''''')) -+ tree = ET.ElementTree(ET.XML('''\xf8''')) - stream = io.StringIO() - tree.write(stream, encoding='unicode') -- self.assertEqual(stream.getvalue(), '''''') -+ self.assertEqual(stream.getvalue(), '''\xf8''') - - def test_read_from_bytesio(self): - tree = ET.ElementTree() -@@ -3716,10 +3778,10 @@ - self.assertEqual(tree.getroot().tag, 'site') - - def test_write_to_bytesio(self): -- tree = ET.ElementTree(ET.XML('''''')) -+ tree = ET.ElementTree(ET.XML('''\xf8''')) - raw = io.BytesIO() - tree.write(raw) -- self.assertEqual(raw.getvalue(), b'''''') -+ self.assertEqual(raw.getvalue(), b'''ø''') - - class dummy: - pass -@@ -3733,12 +3795,12 @@ - self.assertEqual(tree.getroot().tag, 'site') - - def test_write_to_user_text_writer(self): -- tree = ET.ElementTree(ET.XML('''''')) -+ tree = ET.ElementTree(ET.XML('''\xf8''')) - stream = io.StringIO() - writer = self.dummy() - writer.write = stream.write - tree.write(writer, encoding='unicode') -- self.assertEqual(stream.getvalue(), '''''') -+ self.assertEqual(stream.getvalue(), '''\xf8''') - - def test_read_from_user_binary_reader(self): - raw = io.BytesIO(b'''''') -@@ -3750,12 +3812,12 @@ - tree = ET.ElementTree() - - def test_write_to_user_binary_writer(self): -- tree = ET.ElementTree(ET.XML('''''')) -+ tree = ET.ElementTree(ET.XML('''\xf8''')) - raw = io.BytesIO() - writer = self.dummy() - writer.write = raw.write - tree.write(writer) -- self.assertEqual(raw.getvalue(), b'''''') -+ self.assertEqual(raw.getvalue(), b'''ø''') - - def test_write_to_user_binary_writer_with_bom(self): - tree = ET.ElementTree(ET.XML('''''')) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index bd383d3f68..f5c830e610 100644 --- a/Lib/test/test_zipfile.py @@ -9712,118 +2606,6 @@ index bd383d3f68..f5c830e610 100644 def test_write_filtered_python_package(self): import test packagedir = os.path.dirname(test.__file__) -diff --git a/Lib/threading.py b/Lib/threading.py -index a3cb245ab9..565f868e92 100644 ---- a/Lib/threading.py -+++ b/Lib/threading.py -@@ -360,14 +360,21 @@ - """ - if not self._is_owned(): - raise RuntimeError("cannot notify on un-acquired lock") -- all_waiters = self._waiters -- waiters_to_notify = _deque(_islice(all_waiters, n)) -- if not waiters_to_notify: -- return -- for waiter in waiters_to_notify: -- waiter.release() -+ waiters = self._waiters -+ while waiters and n > 0: -+ waiter = waiters[0] -+ try: -+ waiter.release() -+ except RuntimeError: -+ # gh-92530: The previous call of notify() released the lock, -+ # but was interrupted before removing it from the queue. -+ # It can happen if a signal handler raises an exception, -+ # like CTRL+C which raises KeyboardInterrupt. -+ pass -+ else: -+ n -= 1 - try: -- all_waiters.remove(waiter) -+ waiters.remove(waiter) - except ValueError: - pass - -diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py -index 9049ca308f..7e4f444c71 100644 ---- a/Lib/tkinter/__init__.py -+++ b/Lib/tkinter/__init__.py -@@ -2070,7 +2070,7 @@ - the bitmap if None is given. - - Under Windows, the DEFAULT parameter can be used to set the icon -- for the widget and any descendents that don't have an icon set -+ for the widget and any descendants that don't have an icon set - explicitly. DEFAULT can be the relative path to a .ico file - (example: root.iconbitmap(default='myicon.ico') ). See Tk - documentation for more information.""" -@@ -2316,9 +2316,9 @@ - _default_root = None - - def readprofile(self, baseName, className): -- """Internal function. It reads BASENAME.tcl and CLASSNAME.tcl into -- the Tcl Interpreter and calls exec on the contents of BASENAME.py and -- CLASSNAME.py if such a file exists in the home directory.""" -+ """Internal function. It reads .BASENAME.tcl and .CLASSNAME.tcl into -+ the Tcl Interpreter and calls exec on the contents of .BASENAME.py and -+ .CLASSNAME.py if such a file exists in the home directory.""" - import os - if 'HOME' in os.environ: home = os.environ['HOME'] - else: home = os.curdir -diff --git a/Lib/unittest/async_case.py b/Lib/unittest/async_case.py -index a2980e797a..d9c694e368 100644 ---- a/Lib/unittest/async_case.py -+++ b/Lib/unittest/async_case.py -@@ -134,7 +134,7 @@ - task.cancel() - - loop.run_until_complete( -- asyncio.gather(*to_cancel, loop=loop, return_exceptions=True)) -+ asyncio.gather(*to_cancel, return_exceptions=True)) - - for task in to_cancel: - if task.cancelled(): -@@ -148,6 +148,8 @@ - # shutdown asyncgens - loop.run_until_complete(loop.shutdown_asyncgens()) - finally: -+ # Prevent our executor environment from leaking to future tests. -+ loop.run_until_complete(loop.shutdown_default_executor()) - asyncio.set_event_loop(None) - loop.close() - -diff --git a/Lib/unittest/test/test_async_case.py b/Lib/unittest/test/test_async_case.py -index 2b6e751f3b..22e2d0864e 100644 ---- a/Lib/unittest/test/test_async_case.py -+++ b/Lib/unittest/test/test_async_case.py -@@ -278,6 +278,26 @@ - output = test.run() - self.assertFalse(output.wasSuccessful()) - -+ def test_cancellation_hanging_tasks(self): -+ cancelled = False -+ class Test(unittest.IsolatedAsyncioTestCase): -+ async def test_leaking_task(self): -+ async def coro(): -+ nonlocal cancelled -+ try: -+ await asyncio.sleep(1) -+ except asyncio.CancelledError: -+ cancelled = True -+ raise -+ -+ # Leave this running in the background -+ asyncio.create_task(coro()) -+ -+ test = Test("test_leaking_task") -+ output = test.run() -+ self.assertTrue(cancelled) -+ -+ - - if __name__ == "__main__": - unittest.main() diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py index 453e6c3d11..7f4ece1ace 100644 --- a/Lib/unittest/test/test_runner.py @@ -9836,53 +2618,6 @@ index 453e6c3d11..7f4ece1ace 100644 def test_warnings(self): """ Check that warnings argument of TextTestRunner correctly affects the -diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py -index 4e289fc67d..2e7d01fdac 100644 ---- a/Lib/urllib/request.py -+++ b/Lib/urllib/request.py -@@ -2672,22 +2672,26 @@ - # Returned as Unicode but problems if not converted to ASCII - proxyServer = str(winreg.QueryValueEx(internetSettings, - 'ProxyServer')[0]) -- if '=' in proxyServer: -- # Per-protocol settings -- for p in proxyServer.split(';'): -- protocol, address = p.split('=', 1) -- # See if address has a type:// prefix -- if not re.match('(?:[^/:]+)://', address): -- address = '%s://%s' % (protocol, address) -- proxies[protocol] = address -- else: -- # Use one setting for all protocols -- if proxyServer[:5] == 'http:': -- proxies['http'] = proxyServer -- else: -- proxies['http'] = 'http://%s' % proxyServer -- proxies['https'] = 'https://%s' % proxyServer -- proxies['ftp'] = 'ftp://%s' % proxyServer -+ if '=' not in proxyServer and ';' not in proxyServer: -+ # Use one setting for all protocols. -+ proxyServer = 'http={0};https={0};ftp={0}'.format(proxyServer) -+ for p in proxyServer.split(';'): -+ protocol, address = p.split('=', 1) -+ # See if address has a type:// prefix -+ if not re.match('(?:[^/:]+)://', address): -+ # Add type:// prefix to address without specifying type -+ if protocol in ('http', 'https', 'ftp'): -+ # The default proxy type of Windows is HTTP -+ address = 'http://' + address -+ elif protocol == 'socks': -+ address = 'socks://' + address -+ proxies[protocol] = address -+ # Use SOCKS proxy for HTTP(S) protocols -+ if proxies.get('socks'): -+ # The default SOCKS proxy type of Windows is SOCKS4 -+ address = re.sub(r'^socks://', 'socks4://', proxies['socks']) -+ proxies['http'] = proxies.get('http') or address -+ proxies['https'] = proxies.get('https') or address - internetSettings.Close() - except (OSError, ValueError, TypeError): - # Either registry key not found etc, or the value in an diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index 6023c1e138..ae5803c4c8 100755 --- a/Lib/webbrowser.py @@ -9945,76 +2680,6 @@ index 6023c1e138..ae5803c4c8 100755 # # Platform support for Windows -diff --git a/Lib/xml/etree/ElementPath.py b/Lib/xml/etree/ElementPath.py -index 880ea7bd99..493c026dba 100644 ---- a/Lib/xml/etree/ElementPath.py -+++ b/Lib/xml/etree/ElementPath.py -@@ -225,7 +225,6 @@ - - def prepare_predicate(next, token): - # FIXME: replace with real parser!!! refs: -- # http://effbot.org/zone/simple-iterator-parser.htm - # http://javascript.crockford.com/tdop/tdop.html - signature = [] - predicate = [] -diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py -index dae2251d85..66c43c2d0d 100644 ---- a/Lib/xml/etree/ElementTree.py -+++ b/Lib/xml/etree/ElementTree.py -@@ -728,16 +728,10 @@ - encoding = "utf-8" - else: - encoding = "us-ascii" -- enc_lower = encoding.lower() -- with _get_writer(file_or_filename, enc_lower) as write: -+ with _get_writer(file_or_filename, encoding) as (write, declared_encoding): - if method == "xml" and (xml_declaration or - (xml_declaration is None and -- enc_lower not in ("utf-8", "us-ascii", "unicode"))): -- declared_encoding = encoding -- if enc_lower == "unicode": -- # Retrieve the default encoding for the xml declaration -- import locale -- declared_encoding = locale.getpreferredencoding() -+ declared_encoding.lower() not in ("utf-8", "us-ascii"))): - write("\n" % ( - declared_encoding,)) - if method == "text": -@@ -762,19 +756,20 @@ - write = file_or_filename.write - except AttributeError: - # file_or_filename is a file name -- if encoding == "unicode": -- file = open(file_or_filename, "w") -+ if encoding.lower() == "unicode": -+ file = open(file_or_filename, "w", -+ errors="xmlcharrefreplace") - else: - file = open(file_or_filename, "w", encoding=encoding, - errors="xmlcharrefreplace") - with file: -- yield file.write -+ yield file.write, file.encoding - else: - # file_or_filename is a file-like object - # encoding determines if it is a text or binary writer -- if encoding == "unicode": -+ if encoding.lower() == "unicode": - # use a text writer as is -- yield write -+ yield write, getattr(file_or_filename, "encoding", None) or "utf-8" - else: - # wrap a binary writer with TextIOWrapper - with contextlib.ExitStack() as stack: -@@ -805,7 +800,7 @@ - # Keep the original file open when the TextIOWrapper is - # destroyed - stack.callback(file.detach) -- yield file.write -+ yield file.write, encoding - - def _namespaces(elem, default_namespace=None): - # identify namespaces used in this tree diff --git a/Makefile.pre.in b/Makefile.pre.in index 42b1ec622a..5b847a2ccf 100644 --- a/Makefile.pre.in @@ -10028,385 +2693,6 @@ index 42b1ec622a..5b847a2ccf 100644 ########################################################################## # Parser -diff --git a/Misc/ACKS b/Misc/ACKS -index d043195a7b..a9f15b4f96 100644 ---- a/Misc/ACKS -+++ b/Misc/ACKS -@@ -1269,6 +1269,7 @@ - Piet van Oostrum - Tomas Oppelstrup - Jason Orendorff -+Yan "yyyyyyyan" Orestes - Bastien Orivel - orlnub123 - Douglas Orr -@@ -1663,6 +1664,7 @@ - David Steele - Oliver Steele - Greg Stein -+Itai Steinherz - Marek Stepniowski - Baruch Sterin - Chris Stern -@@ -1840,6 +1842,7 @@ - Kevin Walzer - Rodrigo Steinmuller Wanderley - Dingyuan Wang -+Jiahua Wang - Ke Wang - Liang-Bo Wang - Greg Ward -diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c -index ceae67ebb1..0ffc8858de 100644 ---- a/Modules/_ctypes/_ctypes.c -+++ b/Modules/_ctypes/_ctypes.c -@@ -181,7 +181,7 @@ - 0, /* tp_as_buffer */ - /* XXX should participate in GC? */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ -- "deletes a key from a dictionary", /* tp_doc */ -+ PyDoc_STR("deletes a key from a dictionary"), /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -563,8 +563,8 @@ - return StructUnionType_new(type, args, kwds, 0); - } - --static const char from_address_doc[] = --"C.from_address(integer) -> C instance\naccess a C instance at the specified address"; -+PyDoc_STRVAR(from_address_doc, -+"C.from_address(integer) -> C instance\naccess a C instance at the specified address"); - - static PyObject * - CDataType_from_address(PyObject *type, PyObject *value) -@@ -581,8 +581,8 @@ - return PyCData_AtAddress(type, buf); - } - --static const char from_buffer_doc[] = --"C.from_buffer(object, offset=0) -> C instance\ncreate a C instance from a writeable buffer"; -+PyDoc_STRVAR(from_buffer_doc, -+"C.from_buffer(object, offset=0) -> C instance\ncreate a C instance from a writeable buffer"); - - static int - KeepRef(CDataObject *target, Py_ssize_t index, PyObject *keep); -@@ -661,8 +661,8 @@ - return result; - } - --static const char from_buffer_copy_doc[] = --"C.from_buffer_copy(object, offset=0) -> C instance\ncreate a C instance from a readable buffer"; -+PyDoc_STRVAR(from_buffer_copy_doc, -+"C.from_buffer_copy(object, offset=0) -> C instance\ncreate a C instance from a readable buffer"); - - static PyObject * - GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds); -@@ -712,8 +712,8 @@ - return result; - } - --static const char in_dll_doc[] = --"C.in_dll(dll, name) -> C instance\naccess a C instance in a dll"; -+PyDoc_STRVAR(in_dll_doc, -+"C.in_dll(dll, name) -> C instance\naccess a C instance in a dll"); - - static PyObject * - CDataType_in_dll(PyObject *type, PyObject *args) -@@ -774,8 +774,8 @@ - return PyCData_AtAddress(type, address); - } - --static const char from_param_doc[] = --"Convert a Python object into a function call parameter."; -+PyDoc_STRVAR(from_param_doc, -+"Convert a Python object into a function call parameter."); - - static PyObject * - CDataType_from_param(PyObject *type, PyObject *value) -@@ -929,7 +929,7 @@ - PyCStructType_setattro, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ -- "metatype for the CData Objects", /* tp_doc */ -+ PyDoc_STR("metatype for the CData Objects"), /* tp_doc */ - (traverseproc)CDataType_traverse, /* tp_traverse */ - (inquiry)CDataType_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -971,7 +971,7 @@ - UnionType_setattro, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ -- "metatype for the CData Objects", /* tp_doc */ -+ PyDoc_STR("metatype for the CData Objects"), /* tp_doc */ - (traverseproc)CDataType_traverse, /* tp_traverse */ - (inquiry)CDataType_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -1229,7 +1229,7 @@ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ -- "metatype for the Pointer Objects", /* tp_doc */ -+ PyDoc_STR("metatype for the Pointer Objects"), /* tp_doc */ - (traverseproc)CDataType_traverse, /* tp_traverse */ - (inquiry)CDataType_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -1651,7 +1651,7 @@ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -- "metatype for the Array Objects", /* tp_doc */ -+ PyDoc_STR("metatype for the Array Objects"), /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -2345,7 +2345,7 @@ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -- "metatype for the PyCSimpleType Objects", /* tp_doc */ -+ PyDoc_STR("metatype for the PyCSimpleType Objects"), /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -2627,7 +2627,7 @@ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ -- "metatype for C function pointers", /* tp_doc */ -+ PyDoc_STR("metatype for C function pointers"), /* tp_doc */ - (traverseproc)CDataType_traverse, /* tp_traverse */ - (inquiry)CDataType_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -2932,7 +2932,7 @@ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -- "XXX to be provided", /* tp_doc */ -+ PyDoc_STR("XXX to be provided"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -4327,7 +4327,7 @@ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -- "Function Pointer", /* tp_doc */ -+ PyDoc_STR("Function Pointer"), /* tp_doc */ - (traverseproc)PyCFuncPtr_traverse, /* tp_traverse */ - (inquiry)PyCFuncPtr_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -4481,7 +4481,7 @@ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -- "Structure base class", /* tp_doc */ -+ PyDoc_STR("Structure base class"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -4523,7 +4523,7 @@ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -- "Union base class", /* tp_doc */ -+ PyDoc_STR("Union base class"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -4845,7 +4845,7 @@ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -- "XXX to be provided", /* tp_doc */ -+ PyDoc_STR("XXX to be provided"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -5064,7 +5064,7 @@ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -- "XXX to be provided", /* tp_doc */ -+ PyDoc_STR("XXX to be provided"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -5448,7 +5448,7 @@ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -- "XXX to be provided", /* tp_doc */ -+ PyDoc_STR("XXX to be provided"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* tp_clear */ - 0, /* tp_richcompare */ -@@ -5475,12 +5475,12 @@ - * Module initialization. - */ - --static const char module_docs[] = --"Create and manipulate C compatible data types in Python."; -+PyDoc_STRVAR(module_docs, -+"Create and manipulate C compatible data types in Python."); - - #ifdef MS_WIN32 - --static const char comerror_doc[] = "Raised when a COM method call failed."; -+PyDoc_STRVAR(comerror_doc, "Raised when a COM method call failed."); - - int - comerror_init(PyObject *self, PyObject *args, PyObject *kwds) -diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c -index e6e101829a..dbe6f29fc8 100644 ---- a/Modules/_ctypes/callbacks.c -+++ b/Modules/_ctypes/callbacks.c -@@ -65,7 +65,7 @@ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ -- "CThunkObject", /* tp_doc */ -+ PyDoc_STR("CThunkObject"), /* tp_doc */ - CThunkObject_traverse, /* tp_traverse */ - CThunkObject_clear, /* tp_clear */ - 0, /* tp_richcompare */ -diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c -index 9aff890afe..b83f7364de 100644 ---- a/Modules/_ctypes/callproc.c -+++ b/Modules/_ctypes/callproc.c -@@ -1316,11 +1316,11 @@ - - #ifdef MS_WIN32 - --static const char format_error_doc[] = -+PyDoc_STRVAR(format_error_doc, - "FormatError([integer]) -> string\n\ - \n\ - Convert a win32 error code into a string. If the error code is not\n\ --given, the return value of a call to GetLastError() is used.\n"; -+given, the return value of a call to GetLastError() is used.\n"); - static PyObject *format_error(PyObject *self, PyObject *args) - { - PyObject *result; -@@ -1340,13 +1340,13 @@ - return result; - } - --static const char load_library_doc[] = -+PyDoc_STRVAR(load_library_doc, - "LoadLibrary(name, load_flags) -> handle\n\ - \n\ - Load an executable (usually a DLL), and return a handle to it.\n\ - The handle may be used to locate exported functions in this\n\ - module. load_flags are as defined for LoadLibraryEx in the\n\ --Windows API.\n"; -+Windows API.\n"); - static PyObject *load_library(PyObject *self, PyObject *args) - { - const WCHAR *name; -@@ -1394,10 +1394,10 @@ - #endif - } - --static const char free_library_doc[] = -+PyDoc_STRVAR(free_library_doc, - "FreeLibrary(handle) -> void\n\ - \n\ --Free the handle of an executable previously loaded by LoadLibrary.\n"; -+Free the handle of an executable previously loaded by LoadLibrary.\n"); - static PyObject *free_library(PyObject *self, PyObject *args) - { - void *hMod; -@@ -1417,8 +1417,8 @@ - Py_RETURN_NONE; - } - --static const char copy_com_pointer_doc[] = --"CopyComPointer(src, dst) -> HRESULT value\n"; -+PyDoc_STRVAR(copy_com_pointer_doc, -+"CopyComPointer(src, dst) -> HRESULT value\n"); - - static PyObject * - copy_com_pointer(PyObject *self, PyObject *args) -@@ -1656,10 +1656,10 @@ - /***************************************************************** - * functions - */ --static const char sizeof_doc[] = -+PyDoc_STRVAR(sizeof_doc, - "sizeof(C type) -> integer\n" - "sizeof(C instance) -> integer\n" --"Return the size in bytes of a C instance"; -+"Return the size in bytes of a C instance"); - - static PyObject * - sizeof_func(PyObject *self, PyObject *obj) -@@ -1677,10 +1677,10 @@ - return NULL; - } - --static const char alignment_doc[] = -+PyDoc_STRVAR(alignment_doc, - "alignment(C type) -> integer\n" - "alignment(C instance) -> integer\n" --"Return the alignment requirements of a C instance"; -+"Return the alignment requirements of a C instance"); - - static PyObject * - align_func(PyObject *self, PyObject *obj) -@@ -1700,10 +1700,10 @@ - return NULL; - } - --static const char byref_doc[] = -+PyDoc_STRVAR(byref_doc, - "byref(C instance[, offset=0]) -> byref-object\n" - "Return a pointer lookalike to a C instance, only usable\n" --"as function argument"; -+"as function argument"); - - /* - * We must return something which can be converted to a parameter, -@@ -1744,9 +1744,9 @@ - return (PyObject *)parg; - } - --static const char addressof_doc[] = -+PyDoc_STRVAR(addressof_doc, - "addressof(C instance) -> integer\n" --"Return the address of the C instance internal buffer"; -+"Return the address of the C instance internal buffer"); - - static PyObject * - addressof(PyObject *self, PyObject *obj) -diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c -index 06b98a6d66..8582af9c3a 100644 ---- a/Modules/_ctypes/cfield.c -+++ b/Modules/_ctypes/cfield.c -@@ -320,7 +320,7 @@ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ -- "Structure/Union member", /* tp_doc */ -+ PyDoc_STR("Structure/Union member"), /* tp_doc */ - (traverseproc)PyCField_traverse, /* tp_traverse */ - (inquiry)PyCField_clear, /* tp_clear */ - 0, /* tp_richcompare */ -diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c -index 9602375405..51f363d859 100644 ---- a/Modules/_datetimemodule.c -+++ b/Modules/_datetimemodule.c -@@ -5013,6 +5013,10 @@ - - result_seconds = utc_to_seconds(year, month, day, - hour, minute, second); -+ if (result_seconds == -1 && PyErr_Occurred()) { -+ return NULL; -+ } -+ - /* Probe max_fold_seconds to detect a fold. */ - probe_seconds = local(epoch + timet - max_fold_seconds); - if (probe_seconds == -1) diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index d64e0a1cfa..38770238b2 100644 --- a/Modules/_posixsubprocess.c @@ -10440,318 +2726,6 @@ index d64e0a1cfa..38770238b2 100644 if (pid == 0) { /* Child process */ /* -diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c -index e2635e18c7..5c83eef47e 100644 ---- a/Modules/_sqlite/cursor.c -+++ b/Modules/_sqlite/cursor.c -@@ -27,10 +27,25 @@ - - PyObject* pysqlite_cursor_iternext(pysqlite_Cursor* self); - -+static inline int -+check_cursor_locked(pysqlite_Cursor *cur) -+{ -+ if (cur->locked) { -+ PyErr_SetString(pysqlite_ProgrammingError, -+ "Recursive use of cursors not allowed."); -+ return 0; -+ } -+ return 1; -+} -+ - static const char errmsg_fetch_across_rollback[] = "Cursor needed to be reset because of commit/rollback and can no longer be fetched from."; - - static int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs) - { -+ if (!check_cursor_locked(self)) { -+ return -1; -+ } -+ - pysqlite_Connection* connection; - - if (!PyArg_ParseTuple(args, "O!", &pysqlite_ConnectionType, &connection)) -@@ -357,12 +372,9 @@ - return 0; - } - -- if (cur->locked) { -- PyErr_SetString(pysqlite_ProgrammingError, "Recursive use of cursors not allowed."); -- return 0; -- } -- -- return pysqlite_check_thread(cur->connection) && pysqlite_check_connection(cur->connection); -+ return (pysqlite_check_thread(cur->connection) -+ && pysqlite_check_connection(cur->connection) -+ && check_cursor_locked(cur)); - } - - static PyObject * -@@ -750,27 +762,29 @@ - if (self->statement) { - rc = pysqlite_step(self->statement->st, self->connection); - if (PyErr_Occurred()) { -- (void)pysqlite_statement_reset(self->statement); -- Py_DECREF(next_row); -- return NULL; -+ goto error; - } - if (rc != SQLITE_DONE && rc != SQLITE_ROW) { -- (void)pysqlite_statement_reset(self->statement); -- Py_DECREF(next_row); - _pysqlite_seterror(self->connection->db, NULL); -- return NULL; -+ goto error; - } - - if (rc == SQLITE_ROW) { -+ self->locked = 1; // GH-80254: Prevent recursive use of cursors. - self->next_row = _pysqlite_fetch_one_row(self); -+ self->locked = 0; - if (self->next_row == NULL) { -- (void)pysqlite_statement_reset(self->statement); -- return NULL; -+ goto error; - } - } - } - - return next_row; -+ -+error: -+ (void)pysqlite_statement_reset(self->statement); -+ Py_DECREF(next_row); -+ return NULL; - } - - PyObject* pysqlite_cursor_fetchone(pysqlite_Cursor* self, PyObject* args) -@@ -857,6 +871,10 @@ - - PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args) - { -+ if (!check_cursor_locked(self)) { -+ return NULL; -+ } -+ - if (!self->connection) { - PyErr_SetString(pysqlite_ProgrammingError, - "Base Cursor.__init__ not called."); -diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c -index ad1b074543..90841270e4 100644 ---- a/Modules/_testcapimodule.c -+++ b/Modules/_testcapimodule.c -@@ -5990,7 +5990,7 @@ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -- "Instantiating this exception starts infinite recursion.", /* tp_doc */ -+ PyDoc_STR("Instantiating this exception starts infinite recursion."), /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ -diff --git a/Modules/clinic/_testmultiphase.c.h b/Modules/clinic/_testmultiphase.c.h -index 0d38c230f7..23180d0aa1 100644 ---- a/Modules/clinic/_testmultiphase.c.h -+++ b/Modules/clinic/_testmultiphase.c.h -@@ -18,18 +18,11 @@ - static PyObject * - _testmultiphase_StateAccessType_get_defining_module(StateAccessTypeObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) - { -- PyObject *return_value = NULL; -- static const char * const _keywords[] = { NULL}; -- static _PyArg_Parser _parser = {":get_defining_module", _keywords, 0}; -- -- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser -- )) { -- goto exit; -+ if (nargs) { -+ PyErr_SetString(PyExc_TypeError, "get_defining_module() takes no arguments"); -+ return NULL; - } -- return_value = _testmultiphase_StateAccessType_get_defining_module_impl(self, cls); -- --exit: -- return return_value; -+ return _testmultiphase_StateAccessType_get_defining_module_impl(self, cls); - } - - PyDoc_STRVAR(_testmultiphase_StateAccessType_increment_count_clinic__doc__, -@@ -55,14 +48,42 @@ - { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"n", "twice", NULL}; -- static _PyArg_Parser _parser = {"|i$p:increment_count_clinic", _keywords, 0}; -+ static _PyArg_Parser _parser = {NULL, _keywords, "increment_count_clinic", 0}; -+ PyObject *argsbuf[2]; -+ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - int n = 1; - int twice = 0; - -- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, -- &n, &twice)) { -+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); -+ if (!args) { - goto exit; - } -+ if (!noptargs) { -+ goto skip_optional_pos; -+ } -+ if (args[0]) { -+ if (PyFloat_Check(args[0])) { -+ PyErr_SetString(PyExc_TypeError, -+ "integer argument expected, got float" ); -+ goto exit; -+ } -+ n = _PyLong_AsInt(args[0]); -+ if (n == -1 && PyErr_Occurred()) { -+ goto exit; -+ } -+ if (!--noptargs) { -+ goto skip_optional_pos; -+ } -+ } -+skip_optional_pos: -+ if (!noptargs) { -+ goto skip_optional_kwonly; -+ } -+ twice = PyObject_IsTrue(args[1]); -+ if (twice < 0) { -+ goto exit; -+ } -+skip_optional_kwonly: - return_value = _testmultiphase_StateAccessType_increment_count_clinic_impl(self, cls, n, twice); - - exit: -@@ -85,17 +106,10 @@ - static PyObject * - _testmultiphase_StateAccessType_get_count(StateAccessTypeObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) - { -- PyObject *return_value = NULL; -- static const char * const _keywords[] = { NULL}; -- static _PyArg_Parser _parser = {":get_count", _keywords, 0}; -- -- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser -- )) { -- goto exit; -+ if (nargs) { -+ PyErr_SetString(PyExc_TypeError, "get_count() takes no arguments"); -+ return NULL; - } -- return_value = _testmultiphase_StateAccessType_get_count_impl(self, cls); -- --exit: -- return return_value; -+ return _testmultiphase_StateAccessType_get_count_impl(self, cls); - } --/*[clinic end generated code: output=39eea487e94e7f5d input=a9049054013a1b77]*/ -+/*[clinic end generated code: output=60d68e2336064f96 input=a9049054013a1b77]*/ -diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h -index 28798ab99c..f5df5c3b90 100644 ---- a/Modules/clinic/posixmodule.c.h -+++ b/Modules/clinic/posixmodule.c.h -@@ -8438,12 +8438,10 @@ - os_DirEntry_is_symlink(DirEntry *self, PyTypeObject *defining_class, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) - { - PyObject *return_value = NULL; -- static const char * const _keywords[] = { NULL}; -- static _PyArg_Parser _parser = {":is_symlink", _keywords, 0}; - int _return_value; - -- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser -- )) { -+ if (nargs) { -+ PyErr_SetString(PyExc_TypeError, "is_symlink() takes no arguments"); - goto exit; - } - _return_value = os_DirEntry_is_symlink_impl(self, defining_class); -@@ -8474,13 +8472,23 @@ - { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"follow_symlinks", NULL}; -- static _PyArg_Parser _parser = {"|$p:stat", _keywords, 0}; -+ static _PyArg_Parser _parser = {NULL, _keywords, "stat", 0}; -+ PyObject *argsbuf[1]; -+ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - int follow_symlinks = 1; - -- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, -- &follow_symlinks)) { -+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); -+ if (!args) { - goto exit; - } -+ if (!noptargs) { -+ goto skip_optional_kwonly; -+ } -+ follow_symlinks = PyObject_IsTrue(args[0]); -+ if (follow_symlinks < 0) { -+ goto exit; -+ } -+skip_optional_kwonly: - return_value = os_DirEntry_stat_impl(self, defining_class, follow_symlinks); - - exit: -@@ -8505,14 +8513,24 @@ - { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"follow_symlinks", NULL}; -- static _PyArg_Parser _parser = {"|$p:is_dir", _keywords, 0}; -+ static _PyArg_Parser _parser = {NULL, _keywords, "is_dir", 0}; -+ PyObject *argsbuf[1]; -+ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - int follow_symlinks = 1; - int _return_value; - -- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, -- &follow_symlinks)) { -+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); -+ if (!args) { - goto exit; - } -+ if (!noptargs) { -+ goto skip_optional_kwonly; -+ } -+ follow_symlinks = PyObject_IsTrue(args[0]); -+ if (follow_symlinks < 0) { -+ goto exit; -+ } -+skip_optional_kwonly: - _return_value = os_DirEntry_is_dir_impl(self, defining_class, follow_symlinks); - if ((_return_value == -1) && PyErr_Occurred()) { - goto exit; -@@ -8541,14 +8559,24 @@ - { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"follow_symlinks", NULL}; -- static _PyArg_Parser _parser = {"|$p:is_file", _keywords, 0}; -+ static _PyArg_Parser _parser = {NULL, _keywords, "is_file", 0}; -+ PyObject *argsbuf[1]; -+ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - int follow_symlinks = 1; - int _return_value; - -- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, -- &follow_symlinks)) { -+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); -+ if (!args) { - goto exit; - } -+ if (!noptargs) { -+ goto skip_optional_kwonly; -+ } -+ follow_symlinks = PyObject_IsTrue(args[0]); -+ if (follow_symlinks < 0) { -+ goto exit; -+ } -+skip_optional_kwonly: - _return_value = os_DirEntry_is_file_impl(self, defining_class, follow_symlinks); - if ((_return_value == -1) && PyErr_Occurred()) { - goto exit; -@@ -9441,4 +9469,4 @@ - #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF - #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF - #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ --/*[clinic end generated code: output=c7c8796918b09139 input=a9049054013a1b77]*/ -+/*[clinic end generated code: output=08879489d9e259a1 input=a9049054013a1b77]*/ diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index 16f98ace3b..0d7e276ff2 100644 --- a/Modules/faulthandler.c @@ -10776,23 +2750,6 @@ index 16f98ace3b..0d7e276ff2 100644 /* Using an alternative stack requires sigaltstack() and sigaction() SA_ONSTACK */ #if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) -diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c -index 3cf1a00b00..cb5989e69d 100644 ---- a/Modules/gcmodule.c -+++ b/Modules/gcmodule.c -@@ -2157,6 +2157,12 @@ - for (gc = GC_NEXT(list); gc != list; gc = GC_NEXT(list)) { - PyObject *op = FROM_GC(gc); - _PyObject_GC_UNTRACK(op); -+ // gh-92036: If a deallocator function expect the object to be tracked -+ // by the GC (ex: func_dealloc()), it can crash if called on an object -+ // which is no longer tracked by the GC. Leak one strong reference on -+ // purpose so the object is never deleted and its deallocator is not -+ // called. -+ Py_INCREF(op); - } - } - diff --git a/Modules/makesetup b/Modules/makesetup index 1a767838c9..708c65b25f 100755 --- a/Modules/makesetup @@ -10852,25 +2809,8 @@ index 1f16849a3e..a7b16eadc9 100644 /* sin(pi*x), giving accurate results for all finite x (especially x integral or close to an integer). This is here for use in the -diff --git a/Modules/ossaudiodev.c b/Modules/ossaudiodev.c -index 2a1ac10814..fb57764b6a 100644 ---- a/Modules/ossaudiodev.c -+++ b/Modules/ossaudiodev.c -@@ -1243,8 +1243,12 @@ - _EXPORT_INT(m, SNDCTL_DSP_GETSPDIF); - #endif - _EXPORT_INT(m, SNDCTL_DSP_GETTRIGGER); -+#ifdef SNDCTL_DSP_MAPINBUF - _EXPORT_INT(m, SNDCTL_DSP_MAPINBUF); -+#endif -+#ifdef SNDCTL_DSP_MAPOUTBUF - _EXPORT_INT(m, SNDCTL_DSP_MAPOUTBUF); -+#endif - _EXPORT_INT(m, SNDCTL_DSP_NONBLOCK); - _EXPORT_INT(m, SNDCTL_DSP_POST); - #ifdef SNDCTL_DSP_PROFILE diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c -index 29d61268e1..bd0dd2f385 100644 +index 1270af735e..bd0dd2f385 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -57,6 +57,8 @@ @@ -10998,26 +2938,7 @@ index 29d61268e1..bd0dd2f385 100644 return d; } -@@ -1853,7 +1884,17 @@ - /* Try reading the parent directory. */ - if (!attributes_from_dir(path, &fileInfo, &tagInfo.ReparseTag)) { - /* Cannot read the parent directory. */ -- SetLastError(error); -+ switch (GetLastError()) { -+ case ERROR_FILE_NOT_FOUND: /* File cannot be found */ -+ case ERROR_PATH_NOT_FOUND: /* File parent directory cannot be found */ -+ case ERROR_NOT_READY: /* Drive exists but unavailable */ -+ case ERROR_BAD_NET_NAME: /* Remote drive unavailable */ -+ break; -+ /* Restore the error from CreateFileW(). */ -+ default: -+ SetLastError(error); -+ } -+ - return -1; - } - if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { -@@ -4818,7 +4859,12 @@ +@@ -4828,7 +4859,12 @@ } Py_BEGIN_ALLOW_THREADS @@ -11030,7 +2951,7 @@ index 29d61268e1..bd0dd2f385 100644 Py_END_ALLOW_THREADS return result; } -@@ -13424,6 +13470,7 @@ +@@ -13434,6 +13470,7 @@ int is_symlink; int need_stat; #endif @@ -11038,7 +2959,7 @@ index 29d61268e1..bd0dd2f385 100644 #ifdef MS_WINDOWS unsigned long dir_bits; #endif -@@ -13484,6 +13531,7 @@ +@@ -13494,6 +13531,7 @@ #endif return result; @@ -11107,34 +3028,6 @@ index 901a3ed9a2..5a7536cc7b 100644 PyErr_Format(PyExc_KeyError, "getpwnam(): name not found: %R", name); } -diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c -index a22060d399..6d6c92e4c9 100644 ---- a/Modules/socketmodule.c -+++ b/Modules/socketmodule.c -@@ -1722,6 +1722,8 @@ - "AF_UNIX path too long"); - goto unix_out; - } -+ -+ *len_ret = path.len + offsetof(struct sockaddr_un, sun_path); - } - else - #endif /* linux */ -@@ -1733,10 +1735,13 @@ - goto unix_out; - } - addr->sun_path[path.len] = 0; -+ -+ /* including the tailing NUL */ -+ *len_ret = path.len + offsetof(struct sockaddr_un, sun_path) + 1; - } - addr->sun_family = s->sock_family; - memcpy(addr->sun_path, path.buf, path.len); -- *len_ret = path.len + offsetof(struct sockaddr_un, sun_path); -+ - retval = 1; - unix_out: - PyBuffer_Release(&path); diff --git a/Modules/timemodule.c b/Modules/timemodule.c index df59f2aac5..c0623f394b 100644 --- a/Modules/timemodule.c @@ -11179,628 +3072,6 @@ index df59f2aac5..c0623f394b 100644 Py_RETURN_NONE; } -diff --git a/Objects/exceptions.c b/Objects/exceptions.c -index e67ecfab85..57ddbb0012 100644 ---- a/Objects/exceptions.c -+++ b/Objects/exceptions.c -@@ -844,14 +844,7 @@ - winerrcode = PyLong_AsLong(*winerror); - if (winerrcode == -1 && PyErr_Occurred()) - return -1; -- /* Set errno to the corresponding POSIX errno (overriding -- first argument). Windows Socket error codes (>= 10000) -- have the same value as their POSIX counterparts. -- */ -- if (winerrcode < 10000) -- errcode = winerror_to_errno(winerrcode); -- else -- errcode = winerrcode; -+ errcode = winerror_to_errno(winerrcode); - *myerrno = PyLong_FromLong(errcode); - if (!*myerrno) - return -1; -diff --git a/Objects/frameobject.c b/Objects/frameobject.c -index 4ae17bcfc2..10db68ef1e 100644 ---- a/Objects/frameobject.c -+++ b/Objects/frameobject.c -@@ -173,7 +173,10 @@ - break; - case GET_ITER: - case GET_AITER: -- block_stack = push_block(block_stack, Loop); -+ // For-loops get a Loop block, but comprehensions do not. -+ if (_Py_OPCODE(code[i + 1]) != CALL_FUNCTION) { -+ block_stack = push_block(block_stack, Loop); -+ } - blocks[i+1] = block_stack; - break; - case FOR_ITER: -diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c -index acbb01cfef..36dfbe43cd 100644 ---- a/Objects/genericaliasobject.c -+++ b/Objects/genericaliasobject.c -@@ -293,6 +293,11 @@ - return obj; - } - -+PyDoc_STRVAR(genericalias_doc, -+"Represent a PEP 585 generic type\n" -+"\n" -+"E.g. for t = list[int], t.__origin__ is list and t.__args__ is (int,)."); -+ - static PyObject * - ga_getitem(PyObject *self, PyObject *item) - { -@@ -612,14 +617,11 @@ - - // TODO: - // - argument clinic? --// - __doc__? - // - cache? - PyTypeObject Py_GenericAliasType = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - .tp_name = "types.GenericAlias", -- .tp_doc = "Represent a PEP 585 generic type\n" -- "\n" -- "E.g. for t = list[int], t.__origin__ is list and t.__args__ is (int,).", -+ .tp_doc = genericalias_doc, - .tp_basicsize = sizeof(gaobject), - .tp_dealloc = ga_dealloc, - .tp_repr = ga_repr, -diff --git a/Objects/genobject.c b/Objects/genobject.c -index 5ba4de82ea..95614729c8 100644 ---- a/Objects/genobject.c -+++ b/Objects/genobject.c -@@ -384,8 +384,11 @@ - - - PyDoc_STRVAR(throw_doc, --"throw(typ[,val[,tb]]) -> raise exception in generator,\n\ --return next yielded value or raise StopIteration."); -+"throw(value)\n\ -+throw(type[,value[,tb]])\n\ -+\n\ -+Raise exception in generator, return next yielded value or raise\n\ -+StopIteration."); - - static PyObject * - _gen_throw(PyGenObject *gen, int close_on_genexit, -@@ -943,8 +946,11 @@ - return next iterated value or raise StopIteration."); - - PyDoc_STRVAR(coro_throw_doc, --"throw(typ[,val[,tb]]) -> raise exception in coroutine,\n\ --return next iterated value or raise StopIteration."); -+"throw(value)\n\ -+throw(type[,value[,traceback]])\n\ -+\n\ -+Raise exception in coroutine, return next iterated value or raise\n\ -+StopIteration."); - - PyDoc_STRVAR(coro_close_doc, - "close() -> raise GeneratorExit inside coroutine."); -diff --git a/Objects/picklebufobject.c b/Objects/picklebufobject.c -index a135e5575e..aaa852cfbb 100644 ---- a/Objects/picklebufobject.c -+++ b/Objects/picklebufobject.c -@@ -206,7 +206,7 @@ - PyTypeObject PyPickleBuffer_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pickle.PickleBuffer", -- .tp_doc = "Wrapper for potentially out-of-band buffers", -+ .tp_doc = PyDoc_STR("Wrapper for potentially out-of-band buffers"), - .tp_basicsize = sizeof(PyPickleBufferObject), - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_new = picklebuf_new, -diff --git a/Objects/stringlib/codecs.h b/Objects/stringlib/codecs.h -index 9b2a29ba3b..c8da7d57cd 100644 ---- a/Objects/stringlib/codecs.h -+++ b/Objects/stringlib/codecs.h -@@ -388,8 +388,19 @@ - if (!rep) - goto error; - -- /* subtract preallocated bytes */ -- writer->min_size -= max_char_size * (newpos - startpos); -+ if (newpos < startpos) { -+ writer->overallocate = 1; -+ p = _PyBytesWriter_Prepare(writer, p, -+ max_char_size * (startpos - newpos)); -+ if (p == NULL) -+ goto error; -+ } -+ else { -+ /* subtract preallocated bytes */ -+ writer->min_size -= max_char_size * (newpos - startpos); -+ /* Only overallocate the buffer if it's not the last write */ -+ writer->overallocate = (newpos < size); -+ } - - if (PyBytes_Check(rep)) { - p = _PyBytesWriter_WriteBytes(writer, p, -diff --git a/Objects/stringlib/fastsearch.h b/Objects/stringlib/fastsearch.h -index 56a4467d35..84b415a949 100644 ---- a/Objects/stringlib/fastsearch.h -+++ b/Objects/stringlib/fastsearch.h -@@ -4,7 +4,8 @@ - - /* fast search/count implementation, based on a mix between boyer- - moore and horspool, with a few more bells and whistles on the top. -- for some more background, see: http://effbot.org/zone/stringlib.htm */ -+ for some more background, see: -+ https://web.archive.org/web/20201107074620/http://effbot.org/zone/stringlib.htm */ - - /* note: fastsearch may access s[n], which isn't a problem when using - Python's ordinary string types, but may cause problems if you're -diff --git a/Objects/typeobject.c b/Objects/typeobject.c -index cb0bb46145..755d336191 100644 ---- a/Objects/typeobject.c -+++ b/Objects/typeobject.c -@@ -317,25 +317,29 @@ - Py_ssize_t i, n; - int custom = !Py_IS_TYPE(type, &PyType_Type); - int unbound; -- PyObject *mro_meth = NULL; -- PyObject *type_mro_meth = NULL; - - if (!_PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG)) - return; - - if (custom) { -+ PyObject *mro_meth, *type_mro_meth; - mro_meth = lookup_maybe_method( - (PyObject *)type, &PyId_mro, &unbound); -- if (mro_meth == NULL) -+ if (mro_meth == NULL) { - goto clear; -+ } - type_mro_meth = lookup_maybe_method( - (PyObject *)&PyType_Type, &PyId_mro, &unbound); -- if (type_mro_meth == NULL) -+ if (type_mro_meth == NULL) { -+ Py_DECREF(mro_meth); - goto clear; -- if (mro_meth != type_mro_meth) -+ } -+ int custom_mro = (mro_meth != type_mro_meth); -+ Py_DECREF(mro_meth); -+ Py_DECREF(type_mro_meth); -+ if (custom_mro) { - goto clear; -- Py_XDECREF(mro_meth); -- Py_XDECREF(type_mro_meth); -+ } - } - n = PyTuple_GET_SIZE(bases); - for (i = 0; i < n; i++) { -@@ -352,8 +356,6 @@ - } - return; - clear: -- Py_XDECREF(mro_meth); -- Py_XDECREF(type_mro_meth); - type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG| - Py_TPFLAGS_VALID_VERSION_TAG); - } -diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c -index 7767d140e6..e5e9ba2030 100644 ---- a/Objects/unicodeobject.c -+++ b/Objects/unicodeobject.c -@@ -5219,7 +5219,7 @@ - - /* Note: size will always be longer than the resulting Unicode - character count */ -- if (PY_SSIZE_T_MAX / (Py_ssize_t)sizeof(wchar_t) < (size + 1)) { -+ if (PY_SSIZE_T_MAX / (Py_ssize_t)sizeof(wchar_t) - 1 < size) { - return -1; - } - -@@ -5805,7 +5805,7 @@ - - pos = 0; - while (pos < len) { -- Py_ssize_t repsize, moreunits; -+ Py_ssize_t newpos, repsize, moreunits; - - if (kind == PyUnicode_2BYTE_KIND) { - pos += ucs2lib_utf32_encode((const Py_UCS2 *)data + pos, len - pos, -@@ -5822,7 +5822,7 @@ - rep = unicode_encode_call_errorhandler( - errors, &errorHandler, - encoding, "surrogates not allowed", -- str, &exc, pos, pos + 1, &pos); -+ str, &exc, pos, pos + 1, &newpos); - if (!rep) - goto error; - -@@ -5830,7 +5830,7 @@ - repsize = PyBytes_GET_SIZE(rep); - if (repsize & 3) { - raise_encode_exception(&exc, encoding, -- str, pos - 1, pos, -+ str, pos, pos + 1, - "surrogates not allowed"); - goto error; - } -@@ -5843,28 +5843,30 @@ - moreunits = repsize = PyUnicode_GET_LENGTH(rep); - if (!PyUnicode_IS_ASCII(rep)) { - raise_encode_exception(&exc, encoding, -- str, pos - 1, pos, -+ str, pos, pos + 1, - "surrogates not allowed"); - goto error; - } - } -+ moreunits += pos - newpos; -+ pos = newpos; - - /* four bytes are reserved for each surrogate */ -- if (moreunits > 1) { -+ if (moreunits > 0) { - Py_ssize_t outpos = out - (uint32_t*) PyBytes_AS_STRING(v); - if (moreunits >= (PY_SSIZE_T_MAX - PyBytes_GET_SIZE(v)) / 4) { - /* integer overflow */ - PyErr_NoMemory(); - goto error; - } -- if (_PyBytes_Resize(&v, PyBytes_GET_SIZE(v) + 4 * (moreunits - 1)) < 0) -+ if (_PyBytes_Resize(&v, PyBytes_GET_SIZE(v) + 4 * moreunits) < 0) - goto error; - out = (uint32_t*) PyBytes_AS_STRING(v) + outpos; - } - - if (PyBytes_Check(rep)) { - memcpy(out, PyBytes_AS_STRING(rep), repsize); -- out += moreunits; -+ out += repsize / 4; - } else /* rep is unicode */ { - assert(PyUnicode_KIND(rep) == PyUnicode_1BYTE_KIND); - ucs1lib_utf32_encode(PyUnicode_1BYTE_DATA(rep), repsize, -@@ -6157,7 +6159,7 @@ - - pos = 0; - while (pos < len) { -- Py_ssize_t repsize, moreunits; -+ Py_ssize_t newpos, repsize, moreunits; - - if (kind == PyUnicode_2BYTE_KIND) { - pos += ucs2lib_utf16_encode((const Py_UCS2 *)data + pos, len - pos, -@@ -6174,7 +6176,7 @@ - rep = unicode_encode_call_errorhandler( - errors, &errorHandler, - encoding, "surrogates not allowed", -- str, &exc, pos, pos + 1, &pos); -+ str, &exc, pos, pos + 1, &newpos); - if (!rep) - goto error; - -@@ -6182,7 +6184,7 @@ - repsize = PyBytes_GET_SIZE(rep); - if (repsize & 1) { - raise_encode_exception(&exc, encoding, -- str, pos - 1, pos, -+ str, pos, pos + 1, - "surrogates not allowed"); - goto error; - } -@@ -6195,28 +6197,30 @@ - moreunits = repsize = PyUnicode_GET_LENGTH(rep); - if (!PyUnicode_IS_ASCII(rep)) { - raise_encode_exception(&exc, encoding, -- str, pos - 1, pos, -+ str, pos, pos + 1, - "surrogates not allowed"); - goto error; - } - } -+ moreunits += pos - newpos; -+ pos = newpos; - - /* two bytes are reserved for each surrogate */ -- if (moreunits > 1) { -+ if (moreunits > 0) { - Py_ssize_t outpos = out - (unsigned short*) PyBytes_AS_STRING(v); - if (moreunits >= (PY_SSIZE_T_MAX - PyBytes_GET_SIZE(v)) / 2) { - /* integer overflow */ - PyErr_NoMemory(); - goto error; - } -- if (_PyBytes_Resize(&v, PyBytes_GET_SIZE(v) + 2 * (moreunits - 1)) < 0) -+ if (_PyBytes_Resize(&v, PyBytes_GET_SIZE(v) + 2 * moreunits) < 0) - goto error; - out = (unsigned short*) PyBytes_AS_STRING(v) + outpos; - } - - if (PyBytes_Check(rep)) { - memcpy(out, PyBytes_AS_STRING(rep), repsize); -- out += moreunits; -+ out += repsize / 2; - } else /* rep is unicode */ { - assert(PyUnicode_KIND(rep) == PyUnicode_1BYTE_KIND); - ucs1lib_utf16_encode(PyUnicode_1BYTE_DATA(rep), repsize, -@@ -7143,8 +7147,19 @@ - if (rep == NULL) - goto onError; - -- /* subtract preallocated bytes */ -- writer.min_size -= newpos - collstart; -+ if (newpos < collstart) { -+ writer.overallocate = 1; -+ str = _PyBytesWriter_Prepare(&writer, str, -+ collstart - newpos); -+ if (str == NULL) -+ goto onError; -+ } -+ else { -+ /* subtract preallocated bytes */ -+ writer.min_size -= newpos - collstart; -+ /* Only overallocate the buffer if it's not the last write */ -+ writer.overallocate = (newpos < size); -+ } - - if (PyBytes_Check(rep)) { - /* Directly copy bytes result to output. */ -@@ -7933,13 +7948,14 @@ - pos, pos + 1, &newpos); - if (rep == NULL) - goto error; -- pos = newpos; - -+ Py_ssize_t morebytes = pos - newpos; - if (PyBytes_Check(rep)) { - outsize = PyBytes_GET_SIZE(rep); -- if (outsize != 1) { -+ morebytes += outsize; -+ if (morebytes > 0) { - Py_ssize_t offset = out - PyBytes_AS_STRING(*outbytes); -- newoutsize = PyBytes_GET_SIZE(*outbytes) + (outsize - 1); -+ newoutsize = PyBytes_GET_SIZE(*outbytes) + morebytes; - if (_PyBytes_Resize(outbytes, newoutsize) < 0) { - Py_DECREF(rep); - goto error; -@@ -7960,9 +7976,10 @@ - } - - outsize = PyUnicode_GET_LENGTH(rep); -- if (outsize != 1) { -+ morebytes += outsize; -+ if (morebytes > 0) { - Py_ssize_t offset = out - PyBytes_AS_STRING(*outbytes); -- newoutsize = PyBytes_GET_SIZE(*outbytes) + (outsize - 1); -+ newoutsize = PyBytes_GET_SIZE(*outbytes) + morebytes; - if (_PyBytes_Resize(outbytes, newoutsize) < 0) { - Py_DECREF(rep); - goto error; -@@ -7985,6 +8002,7 @@ - out++; - } - } -+ pos = newpos; - Py_DECREF(rep); - } - /* write a NUL byte */ -diff --git a/PC/readme.txt b/PC/readme.txt -index 0a96d269b0..4e6dcf98c9 100644 ---- a/PC/readme.txt -+++ b/PC/readme.txt -@@ -18,7 +18,7 @@ - - 1) The script location; the current directory without script. - 2) The PYTHONPATH variable, if set. -- 3) For Win32 platforms (NT/95), paths specified in the Registry. -+ 3) Paths specified in the Registry. - 4) Default directories lib, lib/win, lib/test, lib/tkinter; - these are searched relative to the environment variable - PYTHONHOME, if set, or relative to the executable and its -@@ -26,8 +26,8 @@ - or the current directory (not useful). - 5) The directory containing the executable. - --The best installation strategy is to put the Python executable (and --DLL, for Win32 platforms) in some convenient directory such as -+The best installation strategy is to put the Python executable and -+DLL in some convenient directory such as - C:/python, and copy all library files and subdirectories (using XCOPY) - to C:/python/lib. Then you don't need to set PYTHONPATH. Otherwise, - set the environment variable PYTHONPATH to your Python search path. -diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat -index 7129898c6b..ee79addd44 100644 ---- a/PCbuild/get_externals.bat -+++ b/PCbuild/get_externals.bat -@@ -58,8 +58,8 @@ - if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.12.0 - if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.12.0 - if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 --set libraries=%libraries% xz-5.2.2 --set libraries=%libraries% zlib-1.2.11 -+set libraries=%libraries% xz-5.2.5 -+set libraries=%libraries% zlib-1.2.12 - - for %%e in (%libraries%) do ( - if exist "%EXTERNALS_DIR%\%%e" ( -diff --git a/PCbuild/liblzma.vcxproj b/PCbuild/liblzma.vcxproj -index a6bd59ec0b..4dd42ab98a 100644 ---- a/PCbuild/liblzma.vcxproj -+++ b/PCbuild/liblzma.vcxproj -@@ -92,7 +92,7 @@ - - - WIN32;HAVE_CONFIG_H;_LIB;%(PreprocessorDefinitions) -- $(lzmaDir)windows;$(lzmaDir)src/liblzma/common;$(lzmaDir)src/common;$(lzmaDir)src/liblzma/api;$(lzmaDir)src/liblzma/check;$(lzmaDir)src/liblzma/delta;$(lzmaDir)src/liblzma/lz;$(lzmaDir)src/liblzma/lzma;$(lzmaDir)src/liblzma/rangecoder;$(lzmaDir)src/liblzma/simple;%(AdditionalIncludeDirectories) -+ $(lzmaDir)windows/vs2019;$(lzmaDir)src/liblzma/common;$(lzmaDir)src/common;$(lzmaDir)src/liblzma/api;$(lzmaDir)src/liblzma/check;$(lzmaDir)src/liblzma/delta;$(lzmaDir)src/liblzma/lz;$(lzmaDir)src/liblzma/lzma;$(lzmaDir)src/liblzma/rangecoder;$(lzmaDir)src/liblzma/simple;%(AdditionalIncludeDirectories) - 4028;4113;4133;4244;4267;4996;%(DisableSpecificWarnings) - - -@@ -238,7 +238,7 @@ - - - -- -+ - - - -diff --git a/PCbuild/liblzma.vcxproj.filters b/PCbuild/liblzma.vcxproj.filters -index 3f58351fa9..ebe2a7d5fa 100644 ---- a/PCbuild/liblzma.vcxproj.filters -+++ b/PCbuild/liblzma.vcxproj.filters -@@ -428,7 +428,7 @@ - - Header Files - -- -+ - Header Files - - -diff --git a/PCbuild/python.props b/PCbuild/python.props -index 7a11cfafed..c451429da2 100644 ---- a/PCbuild/python.props -+++ b/PCbuild/python.props -@@ -59,7 +59,7 @@ - $(ExternalsDir)\ - $(ExternalsDir)sqlite-3.37.2.0\ - $(ExternalsDir)bzip2-1.0.8\ -- $(ExternalsDir)xz-5.2.2\ -+ $(ExternalsDir)xz-5.2.5\ - $(ExternalsDir)libffi-3.3.0\ - $(ExternalsDir)libffi-3.3.0\$(ArchName)\ - $(libffiOutDir)include -@@ -67,7 +67,7 @@ - $(ExternalsDir)openssl-bin-1.1.1n\$(ArchName)\ - $(opensslOutDir)include - $(ExternalsDir)\nasm-2.11.06\ -- $(ExternalsDir)\zlib-1.2.11\ -+ $(ExternalsDir)\zlib-1.2.12\ - - - _d -diff --git a/PCbuild/python.vcxproj b/PCbuild/python.vcxproj -index 2094420a8d..42b27084e0 100644 ---- a/PCbuild/python.vcxproj -+++ b/PCbuild/python.vcxproj -@@ -126,9 +126,6 @@ - - - -- <_PGOPath Condition="$(Configuration) == 'PGInstrument' and $(Platform) == 'Win32'">@set PATH=%PATH%%3B$(VCInstallDir)bin -- <_PGOPath Condition="$(Configuration) == 'PGInstrument' and $(Platform) == 'x64'">@set PATH=%PATH%%3B$(VCInstallDir)bin\amd64 -- <_PGOPath Condition="$(Configuration) == 'PGInstrument' and $(VC_PGO_RunTime_Dir) != ''">@set PATH=%PATH%%3B$(VC_PGO_RunTime_Dir) - <_Content>@rem This script invokes the most recently built Python with all arguments - @rem passed through to the interpreter. This file is generated by the - @rem build process and any changes *will* be thrown away by the next -@@ -138,7 +135,6 @@ - @echo Running $(Configuration)^|$(Platform) interpreter... - @setlocal - @set PYTHONHOME=$(PySourcePath) --$(_PGOPath) - @"$(OutDir)python$(PyDebugExt).exe" %* - - <_ExistingContent Condition="Exists('$(PySourcePath)python.bat')">$([System.IO.File]::ReadAllText('$(PySourcePath)python.bat')) -@@ -164,4 +160,15 @@ - Overwrite="true" - Lines="@(_LicenseFiles->'%(Content)')" /> - -+ -+ -+ <_PGORT Include="$(VCToolsInstallDir)bin\Hostx86\x86\pgort140.dll" Condition="$(Platform) == 'Win32'" /> -+ <_PGORT Include="$(VCToolsInstallDir)bin\Hostx64\x64\pgort140.dll" Condition="$(Platform) == 'x64'" /> -+ <_PGORT Include="$(VCToolsInstallDir)bin\arm64\pgort140.dll" Condition="$(Platform) == 'ARM64'" /> -+ -+ -+ -+ -+ -+ - -diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj -index 9a40905fe0..c899a3b901 100644 ---- a/PCbuild/pythoncore.vcxproj -+++ b/PCbuild/pythoncore.vcxproj -@@ -500,7 +500,9 @@ - - - -- -+ -+ 4244 -+ - - - -diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt -index e8a973c167..80b597cce3 100644 ---- a/PCbuild/readme.txt -+++ b/PCbuild/readme.txt -@@ -13,12 +13,10 @@ - Building Python using Microsoft Visual C++ - ------------------------------------------ - --This directory is used to build CPython for Microsoft Windows NT version --6.0 or higher (Windows Vista, Windows Server 2008, or later) on 32 and 64 -+This directory is used to build CPython for Microsoft Windows on 32- and 64- - bit platforms. Using this directory requires an installation of --Microsoft Visual Studio 2017 (MSVC 14.1) with the *Python workload* and --its optional *Python native development* component selected. (For --command-line builds, Visual Studio 2015 may also be used.) -+Microsoft Visual Studio (MSVC) with the *Python workload* and -+its optional *Python native development* component selected. - - Building from the command line is recommended in order to obtain any - external dependencies. To build, simply run the "build.bat" script without -@@ -105,7 +103,7 @@ - Prompt window - pylauncher - py.exe, the Python Launcher for Windows, see -- http://docs.python.org/3/using/windows.html#launcher -+ https://docs.python.org/3/using/windows.html#launcher - pywlauncher - pyw.exe, a variant of py.exe that doesn't open a Command Prompt - window -@@ -164,14 +162,14 @@ - _lzma - Python wrapper for version 5.2.2 of the liblzma compression library - Homepage: -- http://tukaani.org/xz/ -+ https://tukaani.org/xz/ - _ssl - Python wrapper for version 1.1.1k of the OpenSSL secure sockets - library, which is downloaded from our binaries repository at - https://github.com/python/cpython-bin-deps. - - Homepage: -- http://www.openssl.org/ -+ https://www.openssl.org/ - - Building OpenSSL requires Perl on your path, and can be performed by - running PCbuild\prepare_ssl.bat. This will retrieve the version of -@@ -187,14 +185,14 @@ - _sqlite3 - Wraps SQLite 3.37.2, which is itself built by sqlite3.vcxproj - Homepage: -- http://www.sqlite.org/ -+ https://www.sqlite.org/ - _tkinter - Wraps version 8.6.6 of the Tk windowing system, which is downloaded - from our binaries repository at - https://github.com/python/cpython-bin-deps. - - Homepage: -- http://www.tcl.tk/ -+ https://www.tcl.tk/ - - Building Tcl and Tk can be performed by running - PCbuild\prepare_tcltk.bat. This will retrieve the version of the -@@ -253,7 +251,7 @@ - PGI python, and finally creates the optimized files. - - See -- http://msdn.microsoft.com/en-us/library/e7k32f4k(VS.140).aspx -+ https://docs.microsoft.com/en-us/cpp/build/profile-guided-optimizations - for more on this topic. - - diff --git a/Python/bootstrap_hash.c b/Python/bootstrap_hash.c index e189ce0d90..5a53988ffa 100644 --- a/Python/bootstrap_hash.c @@ -11857,139 +3128,6 @@ index baafa3ecfb..496ef9cf58 100644 #endif #define TYPE_NULL '0' -diff --git a/README.rst b/README.rst -index d3b2521a87..7a842390b0 100644 ---- a/README.rst -+++ b/README.rst -@@ -1,4 +1,4 @@ --This is Python version 3.9.12 -+This is Python version 3.9.13 - ============================= - - .. image:: https://travis-ci.org/python/cpython.svg?branch=3.9 -diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py -index 61d30b01c0..c68ee9a232 100755 ---- a/Tools/clinic/clinic.py -+++ b/Tools/clinic/clinic.py -@@ -636,6 +636,10 @@ - assert parameters - assert isinstance(parameters[0].converter, self_converter) - del parameters[0] -+ requires_defining_class = False -+ if parameters and isinstance(parameters[0].converter, defining_class_converter): -+ requires_defining_class = True -+ del parameters[0] - converters = [p.converter for p in parameters] - - has_option_groups = parameters and (parameters[0].group or parameters[-1].group) -@@ -657,10 +661,6 @@ - if not p.is_optional(): - min_pos = i - -- requires_defining_class = any( -- isinstance(p.converter, defining_class_converter) -- for p in parameters) -- - meth_o = (len(parameters) == 1 and - parameters[0].is_positional_only() and - not converters[0].is_optional() and -@@ -763,24 +763,40 @@ - return linear_format(output(), parser_declarations=declarations) - - if not parameters: -- # no parameters, METH_NOARGS -+ if not requires_defining_class: -+ # no parameters, METH_NOARGS -+ flags = "METH_NOARGS" - -- flags = "METH_NOARGS" -+ parser_prototype = normalize_snippet(""" -+ static PyObject * -+ {c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored)) -+ """) -+ parser_code = [] - -- parser_prototype = normalize_snippet(""" -- static PyObject * -- {c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored)) -- """) -- parser_definition = parser_prototype -+ else: -+ assert not new_or_init - -- if default_return_converter: -- parser_definition = parser_prototype + '\n' + normalize_snippet(""" -- {{ -- return {c_basename}_impl({impl_arguments}); -+ flags = "METH_METHOD|METH_FASTCALL|METH_KEYWORDS" -+ -+ parser_prototype = parser_prototype_def_class -+ return_error = ('return NULL;' if default_return_converter -+ else 'goto exit;') -+ parser_code = [normalize_snippet(""" -+ if (nargs) {{ -+ PyErr_SetString(PyExc_TypeError, "{name}() takes no arguments"); -+ %s - }} -- """) -+ """ % return_error, indent=4)] -+ -+ if default_return_converter: -+ parser_definition = '\n'.join([ -+ parser_prototype, -+ '{{', -+ *parser_code, -+ ' return {c_basename}_impl({impl_arguments});', -+ '}}']) - else: -- parser_definition = parser_body(parser_prototype) -+ parser_definition = parser_body(parser_prototype, *parser_code) - - elif meth_o: - flags = "METH_O" -@@ -939,6 +955,9 @@ - - add_label = None - for i, p in enumerate(parameters): -+ if isinstance(p.converter, defining_class_converter): -+ raise ValueError("defining_class should be the first " -+ "parameter (after self)") - displayname = p.get_displayname(i+1) - parsearg = p.converter.parse_arg(argname_fmt % i, displayname) - if parsearg is None: -@@ -1547,10 +1566,16 @@ - def is_stop_line(line): - # make sure to recognize stop line even if it - # doesn't end with EOL (it could be the very end of the file) -- if not line.startswith(stop_line): -+ if line.startswith(stop_line): -+ remainder = line[len(stop_line):] -+ if remainder and not remainder.isspace(): -+ fail(f"Garbage after stop line: {remainder!r}") -+ return True -+ else: -+ # gh-92256: don't allow incorrectly formatted stop lines -+ if line.lstrip().startswith(stop_line): -+ fail(f"Whitespace is not allowed before the stop line: {line!r}") - return False -- remainder = line[len(stop_line):] -- return (not remainder) or remainder.isspace() - - # consume body of program - while self.input: -diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py -index 45be5ab9bb..cc0cd2fd1f 100755 ---- a/Tools/gdb/libpython.py -+++ b/Tools/gdb/libpython.py -@@ -1285,7 +1285,7 @@ - out.write('\\r') - - # Map non-printable US ASCII to '\xhh' */ -- elif ch < ' ' or ch == 0x7F: -+ elif ch < ' ' or ord(ch) == 0x7F: - out.write('\\x') - out.write(hexdigits[(ord(ch) >> 4) & 0x000F]) - out.write(hexdigits[ord(ch) & 0x000F]) --- /dev/null +++ b/Tools/iOS-test/app/iOS-test/main.py @@ -0,0 +1,12 @@ From 7c57bbd76984a854cb387f9f881c316b75a8edee Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 7 Sep 2022 12:19:01 +0800 Subject: [PATCH 31/37] Update to Python 3.9.14, and remove vestigial patch pieces. --- Makefile | 2 +- README.rst | 2 +- patch/Python/Python.patch | 6095 +++++-------------------------------- 3 files changed, 810 insertions(+), 5289 deletions(-) diff --git a/Makefile b/Makefile index c70ce90a..fa58a1be 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ BUILD_NUMBER=custom # PYTHON_VERSION is the full version number (e.g., 3.10.0b3) # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.9.13 +PYTHON_VERSION=3.9.14 PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) diff --git a/README.rst b/README.rst index 2869a6dc..a323a6f9 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ Python Apple Support ==================== -**This repository branch builds a packaged version of Python 3.9.13**. +**This repository branch builds a packaged version of Python 3.9.14**. Other Python versions are available by cloning other branches of the main repository. diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 2fdeaa9f..fbf14484 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,49 +1,3 @@ -diff --git a/Doc/library/os.rst b/Doc/library/os.rst -index 35a7e1e96d..3da4f3daa9 100644 ---- a/Doc/library/os.rst -+++ b/Doc/library/os.rst -@@ -3172,6 +3172,13 @@ - - .. versionadded:: 3.8 - -+.. data:: allows_subprocesses -+ -+ Boolean that describes whether subprocesses can be by the operating system. -+ Some platforms (e.g., iOS mobile devices) *implement* calls like -+ :func:`execv` and :func:`spawnv`, but will raise errors or break if -+ called. Calls to create subprocesses should only be invoked if -+ :data:`os.allows_subprocesses` is `True`. - - .. data:: MFD_CLOEXEC - MFD_ALLOW_SEALING -diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst -index 45eff7ffcc..9af070c61f 100644 ---- a/Doc/library/subprocess.rst -+++ b/Doc/library/subprocess.rst -@@ -25,6 +25,11 @@ - - :pep:`324` -- PEP proposing the subprocess module - -+:mod:`subprocess` can only be used on platforms that support subprocess -+creation. Some platforms (especially mobile platforms) may not support -+subprocesses; if :data:`os.allows_subprocesses` is `False`, any calls in -+this module that request a subprocess be created will raise a -+:exc:`RuntimeError`. - - Using the :mod:`subprocess` Module - ---------------------------------- -diff --git a/Include/datetime.h b/Include/datetime.h -index 5d9f2558f9..fc66471923 100644 ---- a/Include/datetime.h -+++ b/Include/datetime.h -@@ -179,7 +179,6 @@ - - #define PyDateTime_CAPSULE_NAME "datetime.datetime_CAPI" - -- - /* This block is only used as part of the public API and should not be - * included in _datetimemodule.c, which does not use the C API capsule. - * See bpo-35081 for more details. --- /dev/null +++ b/Lib/_ios_support.py @@ -0,0 +1,36 @@ @@ -83,480 +37,6 @@ index 5d9f2558f9..fc66471923 100644 + model = objc.objc_msgSend(systemModel, SEL_UTF8String).decode() + + return system, release, model -diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py -index f9d27cb89d..da172b345a 100644 ---- a/Lib/ctypes/test/test_as_parameter.py -+++ b/Lib/ctypes/test/test_as_parameter.py -@@ -1,9 +1,11 @@ -+import os - import unittest - from ctypes import * - from ctypes.test import need_symbol - import _ctypes_test - --dll = CDLL(_ctypes_test.__file__) -+ -+dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - try: - CALLBACK_FUNCTYPE = WINFUNCTYPE -diff --git a/Lib/ctypes/test/test_bitfields.py b/Lib/ctypes/test/test_bitfields.py -index 66acd62e68..95216920e6 100644 ---- a/Lib/ctypes/test/test_bitfields.py -+++ b/Lib/ctypes/test/test_bitfields.py -@@ -25,7 +25,7 @@ - ("R", c_short, 6), - ("S", c_short, 7)] - --func = CDLL(_ctypes_test.__file__).unpack_bitfields -+func = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])).unpack_bitfields - func.argtypes = POINTER(BITS), c_char - - ##for n in "ABCDEFGHIMNOPQRS": -diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py -index d8e9c5a760..52820932b0 100644 ---- a/Lib/ctypes/test/test_callbacks.py -+++ b/Lib/ctypes/test/test_callbacks.py -@@ -160,7 +160,7 @@ - - def test_integrate(self): - # Derived from some then non-working code, posted by David Foster -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - # The function prototype called by 'integrate': double func(double); - CALLBACK = CFUNCTYPE(c_double, c_double) -@@ -211,7 +211,7 @@ - def test_callback_register_int(self): - # Issue #8275: buggy handling of callback args under Win64 - # NOTE: should be run on release builds as well -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - CALLBACK = CFUNCTYPE(c_int, c_int, c_int, c_int, c_int, c_int) - # All this function does is call the callback with its args squared - func = dll._testfunc_cbk_reg_int -@@ -227,7 +227,7 @@ - def test_callback_register_double(self): - # Issue #8275: buggy handling of callback args under Win64 - # NOTE: should be run on release builds as well -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - CALLBACK = CFUNCTYPE(c_double, c_double, c_double, c_double, - c_double, c_double) - # All this function does is call the callback with its args squared -diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py -index ac2240fa19..b08b5f7581 100644 ---- a/Lib/ctypes/test/test_cfuncs.py -+++ b/Lib/ctypes/test/test_cfuncs.py -@@ -1,6 +1,7 @@ - # A lot of failures in these tests on Mac OS X. - # Byte order related? - -+import os - import unittest - from ctypes import * - from ctypes.test import need_symbol -@@ -8,7 +9,7 @@ - import _ctypes_test - - class CFunctions(unittest.TestCase): -- _dll = CDLL(_ctypes_test.__file__) -+ _dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - def S(self): - return c_longlong.in_dll(self._dll, "last_tf_arg_s").value -@@ -206,7 +207,7 @@ - - @need_symbol('WinDLL') - class stdcallCFunctions(CFunctions): -- _dll = stdcall_dll(_ctypes_test.__file__) -+ _dll = stdcall_dll(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - if __name__ == '__main__': - unittest.main() -diff --git a/Lib/ctypes/test/test_checkretval.py b/Lib/ctypes/test/test_checkretval.py -index e9567dc391..3c3fbf103c 100644 ---- a/Lib/ctypes/test/test_checkretval.py -+++ b/Lib/ctypes/test/test_checkretval.py -@@ -1,3 +1,4 @@ -+import os - import unittest - - from ctypes import * -@@ -14,7 +15,7 @@ - def test_checkretval(self): - - import _ctypes_test -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - self.assertEqual(42, dll._testfunc_p_p(42)) - - dll._testfunc_p_p.restype = CHECKED -diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py -index e0b9b54e97..a5b1b55294 100644 ---- a/Lib/ctypes/test/test_funcptr.py -+++ b/Lib/ctypes/test/test_funcptr.py -@@ -1,4 +1,4 @@ --import unittest -+import os, unittest - from ctypes import * - - try: -@@ -8,7 +8,10 @@ - WINFUNCTYPE = CFUNCTYPE - - import _ctypes_test --lib = CDLL(_ctypes_test.__file__) -+ -+ -+lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) -+ - - class CFuncPtrTestCase(unittest.TestCase): - def test_basic(self): -diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py -index bdb044e594..624ce9a388 100644 ---- a/Lib/ctypes/test/test_functions.py -+++ b/Lib/ctypes/test/test_functions.py -@@ -7,6 +7,7 @@ - - from ctypes import * - from ctypes.test import need_symbol -+import os - import sys, unittest - - try: -@@ -16,7 +17,9 @@ - WINFUNCTYPE = CFUNCTYPE - - import _ctypes_test --dll = CDLL(_ctypes_test.__file__) -+ -+ -+dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - if sys.platform == "win32": - windll = WinDLL(_ctypes_test.__file__) - -diff --git a/Lib/ctypes/test/test_libc.py b/Lib/ctypes/test/test_libc.py -index 56285b5ff8..f78a152ade 100644 ---- a/Lib/ctypes/test/test_libc.py -+++ b/Lib/ctypes/test/test_libc.py -@@ -1,9 +1,12 @@ -+import os - import unittest - - from ctypes import * - import _ctypes_test - --lib = CDLL(_ctypes_test.__file__) -+ -+lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) -+ - - def three_way_cmp(x, y): - """Return -1 if x < y, 0 if x == y and 1 if x > y""" -diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py -index 38af7ac13d..db0d4986d6 100644 ---- a/Lib/ctypes/test/test_parameters.py -+++ b/Lib/ctypes/test/test_parameters.py -@@ -140,7 +140,7 @@ - import _ctypes_test - from ctypes import CDLL, c_void_p, ArgumentError - -- func = CDLL(_ctypes_test.__file__)._testfunc_p_p -+ func = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))._testfunc_p_p - func.restype = c_void_p - # TypeError: has no from_param method - self.assertRaises(TypeError, setattr, func, "argtypes", (object,)) -diff --git a/Lib/ctypes/test/test_pickling.py b/Lib/ctypes/test/test_pickling.py -index c4a79b9779..833608b629 100644 ---- a/Lib/ctypes/test/test_pickling.py -+++ b/Lib/ctypes/test/test_pickling.py -@@ -1,8 +1,11 @@ - import unittest -+import os - import pickle - from ctypes import * - import _ctypes_test --dll = CDLL(_ctypes_test.__file__) -+ -+ -+dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - class X(Structure): - _fields_ = [("a", c_int), ("b", c_double)] -diff --git a/Lib/ctypes/test/test_pointers.py b/Lib/ctypes/test/test_pointers.py -index e97515879f..d678be3800 100644 ---- a/Lib/ctypes/test/test_pointers.py -+++ b/Lib/ctypes/test/test_pointers.py -@@ -1,3 +1,4 @@ -+import os - import unittest, sys - - from ctypes import * -@@ -20,7 +21,7 @@ - self.assertRaises(TypeError, A, c_ulong(33)) - - def test_pass_pointers(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - func = dll._testfunc_p_p - if sizeof(c_longlong) == sizeof(c_void_p): - func.restype = c_longlong -@@ -38,7 +39,7 @@ - self.assertEqual(res[0], 12345678) - - def test_change_pointers(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - func = dll._testfunc_p_p - - i = c_int(87654) -@@ -77,7 +78,7 @@ - return 0 - callback = PROTOTYPE(func) - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - # This function expects a function pointer, - # and calls this with an integer pointer as parameter. - # The int pointer points to a table containing the numbers 1..10 -@@ -143,7 +144,7 @@ - - def test_charpp(self): - """Test that a character pointer-to-pointer is correctly passed""" -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - func = dll._testfunc_c_p_p - func.restype = c_char_p - argv = (c_char_p * 2)() -diff --git a/Lib/ctypes/test/test_prototypes.py b/Lib/ctypes/test/test_prototypes.py -index cd0c649de3..539351f798 100644 ---- a/Lib/ctypes/test/test_prototypes.py -+++ b/Lib/ctypes/test/test_prototypes.py -@@ -1,3 +1,4 @@ -+import os - from ctypes import * - from ctypes.test import need_symbol - import unittest -@@ -23,7 +24,9 @@ - # In this case, there would have to be an additional reference to the argument... - - import _ctypes_test --testdll = CDLL(_ctypes_test.__file__) -+ -+ -+testdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - # Return machine address `a` as a (possibly long) non-negative integer. - # Starting with Python 2.5, id(anything) is always non-negative, and -diff --git a/Lib/ctypes/test/test_refcounts.py b/Lib/ctypes/test/test_refcounts.py -index f2edfa6400..0e4dd7c126 100644 ---- a/Lib/ctypes/test/test_refcounts.py -+++ b/Lib/ctypes/test/test_refcounts.py -@@ -1,3 +1,4 @@ -+import os - import unittest - from test import support - import ctypes -@@ -7,7 +8,10 @@ - OtherCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_ulonglong) - - import _ctypes_test --dll = ctypes.CDLL(_ctypes_test.__file__) -+ -+ -+dll = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) -+ - - class RefcountTestCase(unittest.TestCase): - -diff --git a/Lib/ctypes/test/test_returnfuncptrs.py b/Lib/ctypes/test/test_returnfuncptrs.py -index 1974f40df6..7b76fae44c 100644 ---- a/Lib/ctypes/test/test_returnfuncptrs.py -+++ b/Lib/ctypes/test/test_returnfuncptrs.py -@@ -1,5 +1,6 @@ - import unittest - from ctypes import * -+import os - - import _ctypes_test - -@@ -8,7 +9,7 @@ - def test_with_prototype(self): - # The _ctypes_test shared lib/dll exports quite some functions for testing. - # The get_strchr function returns a *pointer* to the C strchr function. -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - get_strchr = dll.get_strchr - get_strchr.restype = CFUNCTYPE(c_char_p, c_char_p, c_char) - strchr = get_strchr() -@@ -20,7 +21,7 @@ - self.assertRaises(TypeError, strchr, b"abcdef") - - def test_without_prototype(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - get_strchr = dll.get_strchr - # the default 'c_int' would not work on systems where sizeof(int) != sizeof(void *) - get_strchr.restype = c_void_p -@@ -34,7 +35,7 @@ - self.assertRaises(TypeError, strchr, b"abcdef") - - def test_from_dll(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - # _CFuncPtr instances are now callable with a tuple argument - # which denotes a function name and a dll: - strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)(("my_strchr", dll)) -@@ -50,13 +51,13 @@ - if key == 0: - return "my_strchr" - if key == 1: -- return CDLL(_ctypes_test.__file__) -+ return CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - raise IndexError - - # _CFuncPtr instances are now callable with a tuple argument - # which denotes a function name and a dll: - strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)( -- BadSequence(("my_strchr", CDLL(_ctypes_test.__file__)))) -+ BadSequence(("my_strchr", CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))))) - self.assertTrue(strchr(b"abcdef", b"b"), "bcdef") - self.assertEqual(strchr(b"abcdef", b"x"), None) - self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0) -diff --git a/Lib/ctypes/test/test_slicing.py b/Lib/ctypes/test/test_slicing.py -index a3932f1767..6d7bfff8f2 100644 ---- a/Lib/ctypes/test/test_slicing.py -+++ b/Lib/ctypes/test/test_slicing.py -@@ -1,3 +1,4 @@ -+import os - import unittest - from ctypes import * - from ctypes.test import need_symbol -@@ -62,7 +63,7 @@ - def test_char_ptr(self): - s = b"abcdefghijklmnopqrstuvwxyz" - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - dll.my_strdup.restype = POINTER(c_char) - dll.my_free.restype = None - res = dll.my_strdup(s) -@@ -94,7 +95,7 @@ - dll.my_free(res) - - def test_char_ptr_with_free(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - s = b"abcdefghijklmnopqrstuvwxyz" - - class allocated_c_char_p(c_char_p): -@@ -130,7 +131,7 @@ - def test_wchar_ptr(self): - s = "abcdefghijklmnopqrstuvwxyz\0" - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - dll.my_wcsdup.restype = POINTER(c_wchar) - dll.my_wcsdup.argtypes = POINTER(c_wchar), - dll.my_free.restype = None -diff --git a/Lib/ctypes/test/test_stringptr.py b/Lib/ctypes/test/test_stringptr.py -index c20951f4ce..fde0eef1c7 100644 ---- a/Lib/ctypes/test/test_stringptr.py -+++ b/Lib/ctypes/test/test_stringptr.py -@@ -1,10 +1,12 @@ -+import os - import unittest - from test import support - from ctypes import * - - import _ctypes_test - --lib = CDLL(_ctypes_test.__file__) -+ -+lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - class StringPtrTestCase(unittest.TestCase): - -diff --git a/Lib/ctypes/test/test_unicode.py b/Lib/ctypes/test/test_unicode.py -index 60c75424b7..8c008b466b 100644 ---- a/Lib/ctypes/test/test_unicode.py -+++ b/Lib/ctypes/test/test_unicode.py -@@ -1,3 +1,4 @@ -+import os - import unittest - import ctypes - from ctypes.test import need_symbol -@@ -7,7 +8,7 @@ - @need_symbol('c_wchar') - class UnicodeTestCase(unittest.TestCase): - def test_wcslen(self): -- dll = ctypes.CDLL(_ctypes_test.__file__) -+ dll = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - wcslen = dll.my_wcslen - wcslen.argtypes = [ctypes.c_wchar_p] - -@@ -34,7 +35,7 @@ - t.unicode = "foo\0bar\0\0" - - --func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p -+func = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))._testfunc_p_p - - class StringTestCase(UnicodeTestCase): - def setUp(self): -diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py -index 87eb9198ad..e40b82506f 100644 ---- a/Lib/ctypes/test/test_values.py -+++ b/Lib/ctypes/test/test_values.py -@@ -2,6 +2,7 @@ - A testcase which accesses *values* in a dll. - """ - -+import os - import unittest - import sys - from ctypes import * -@@ -13,7 +14,7 @@ - def test_an_integer(self): - # This test checks and changes an integer stored inside the - # _ctypes_test dll/shared lib. -- ctdll = CDLL(_ctypes_test.__file__) -+ ctdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - an_integer = c_int.in_dll(ctdll, "an_integer") - x = an_integer.value - self.assertEqual(x, ctdll.get_an_integer()) -@@ -25,7 +26,7 @@ - self.assertEqual(x, ctdll.get_an_integer()) - - def test_undefined(self): -- ctdll = CDLL(_ctypes_test.__file__) -+ ctdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") - - class PythonValuesTestCase(unittest.TestCase): -diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py -index e51bdc8ad6..78ba8e6786 100644 ---- a/Lib/ctypes/test/test_win32.py -+++ b/Lib/ctypes/test/test_win32.py -@@ -1,6 +1,7 @@ - # Windows specific tests - - from ctypes import * -+import os - import unittest, sys - from test import support - -@@ -102,7 +103,7 @@ - ("right", c_long), - ("bottom", c_long)] - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - pt = POINT(15, 25) - left = c_long.in_dll(dll, 'left') diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 0c2510e161..6c3c43f11d 100644 --- a/Lib/ctypes/util.py @@ -571,54 +51,77 @@ index 0c2510e161..6c3c43f11d 100644 def find_library(name): possible = ['lib%s.dylib' % name, diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py -index 808c0dc287..b4813efe0c 100644 +index 808c0dc287..a7a7887f70 100644 --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py -@@ -52,6 +52,8 @@ +@@ -15,6 +15,7 @@ + + import unittest + from test import support ++from test.support import os_helper, has_subprocess_support + from test.support.script_helper import assert_python_ok + + # http://bugs.python.org/issue4373 +@@ -52,6 +53,7 @@ def build_ext(self, *args, **kwargs): return build_ext(*args, **kwargs) -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_build_ext(self): cmd = support.missing_compiler_executable() if cmd is not None: -@@ -328,6 +330,8 @@ +@@ -328,6 +330,7 @@ cmd.run() self.assertEqual(cmd.compiler, 'unix') -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_get_outputs(self): cmd = support.missing_compiler_executable() if cmd is not None: diff --git a/Lib/distutils/tests/test_build_py.py b/Lib/distutils/tests/test_build_py.py -index 0712e92c6a..52a0121250 100644 +index 0712e92c6a..4c05e8d968 100644 --- a/Lib/distutils/tests/test_build_py.py +++ b/Lib/distutils/tests/test_build_py.py -@@ -106,6 +106,8 @@ +@@ -9,7 +9,7 @@ + from distutils.errors import DistutilsFileError + + from distutils.tests import support +-from test.support import run_unittest ++from test.support import run_unittest, has_subprocess_support + + + class BuildPyTestCase(support.TempdirManager, +@@ -106,6 +106,7 @@ ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]) @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_byte_compile_optimized(self): project_dir, dist = self.create_dist(py_modules=['boiledeggs']) os.chdir(project_dir) diff --git a/Lib/distutils/tests/test_config_cmd.py b/Lib/distutils/tests/test_config_cmd.py -index 9aeab07b46..3c6dfc0439 100644 +index 9aeab07b46..a729c0907b 100644 --- a/Lib/distutils/tests/test_config_cmd.py +++ b/Lib/distutils/tests/test_config_cmd.py +@@ -2,7 +2,7 @@ + import unittest + import os + import sys +-from test.support import run_unittest, missing_compiler_executable ++from test.support import run_unittest, missing_compiler_executable, has_subprocess_support + + from distutils.command.config import dump_file, config + from distutils.tests import support @@ -38,6 +38,7 @@ self.assertEqual(len(self._logs), numlines+1) @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_search_cpp(self): cmd = missing_compiler_executable(['preprocessor']) if cmd is not None: diff --git a/Lib/distutils/tests/test_cygwinccompiler.py b/Lib/distutils/tests/test_cygwinccompiler.py -index 9dc869de4c..176a87f8a8 100644 +index 9dc869de4c..9a1b18aba9 100644 --- a/Lib/distutils/tests/test_cygwinccompiler.py +++ b/Lib/distutils/tests/test_cygwinccompiler.py @@ -5,11 +5,14 @@ @@ -649,78 +152,108 @@ index 9dc869de4c..176a87f8a8 100644 class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase): -@@ -118,7 +122,7 @@ - def test_get_msvcr(self): - - # none -- sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' -+ sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' - '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEqual(get_msvcr(), None) - diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py -index 51c80e0421..11008da5f2 100644 +index 51c80e0421..57b50380fd 100644 --- a/Lib/distutils/tests/test_install.py +++ b/Lib/distutils/tests/test_install.py -@@ -197,6 +197,8 @@ +@@ -5,7 +5,7 @@ + import unittest + import site + +-from test.support import captured_stdout, run_unittest ++from test.support import captured_stdout, run_unittest, has_subprocess_support + + from distutils import sysconfig + from distutils.command.install import install +@@ -197,6 +197,7 @@ 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_record_extensions(self): cmd = test_support.missing_compiler_executable() if cmd is not None: diff --git a/Lib/distutils/tests/test_install_lib.py b/Lib/distutils/tests/test_install_lib.py -index fda6315bbc..63c71a8d46 100644 +index fda6315bbc..121664b722 100644 --- a/Lib/distutils/tests/test_install_lib.py +++ b/Lib/distutils/tests/test_install_lib.py -@@ -35,6 +35,8 @@ +@@ -8,7 +8,7 @@ + from distutils.extension import Extension + from distutils.tests import support + from distutils.errors import DistutilsOptionError +-from test.support import run_unittest ++from test.support import run_unittest, has_subprocess_support + + + class InstallLibTestCase(support.TempdirManager, +@@ -35,6 +35,7 @@ self.assertEqual(cmd.optimize, 2) @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_byte_compile(self): project_dir, dist = self.create_dist() os.chdir(project_dir) diff --git a/Lib/distutils/tests/test_spawn.py b/Lib/distutils/tests/test_spawn.py -index ad5038142f..856afe86c1 100644 +index ad5038142f..62236514e2 100644 --- a/Lib/distutils/tests/test_spawn.py +++ b/Lib/distutils/tests/test_spawn.py -@@ -15,8 +15,8 @@ - support.LoggingSilencer, - unittest.TestCase): - -- @unittest.skipUnless(os.name in ('nt', 'posix'), -- 'Runs only under posix or nt') -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") +@@ -3,7 +3,7 @@ + import stat + import sys + import unittest.mock +-from test.support import run_unittest, unix_shell ++from test.support import run_unittest, unix_shell, has_subprocess_support + from test import support as test_support + + from distutils.spawn import find_executable +@@ -17,6 +17,7 @@ + + @unittest.skipUnless(os.name in ('nt', 'posix'), + 'Runs only under posix or nt') ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_spawn(self): tmpdir = self.mkdtemp() diff --git a/Lib/distutils/tests/test_sysconfig.py b/Lib/distutils/tests/test_sysconfig.py -index 236755d095..721bc91093 100644 +index 236755d095..a941290dc1 100644 --- a/Lib/distutils/tests/test_sysconfig.py +++ b/Lib/distutils/tests/test_sysconfig.py +@@ -10,7 +10,7 @@ + from distutils import sysconfig + from distutils.ccompiler import get_default_compiler + from distutils.tests import support +-from test.support import TESTFN, run_unittest, check_warnings, swap_item ++from test.support import TESTFN, run_unittest, check_warnings, swap_item, has_subprocess_support + + class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): + def setUp(self): @@ -244,6 +244,7 @@ self.assertIsNotNone(vars['SO']) self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_customize_compiler_before_get_config_vars(self): # Issue #21923: test that a Distribution compiler # instance can be called without an explicit call to diff --git a/Lib/distutils/tests/test_util.py b/Lib/distutils/tests/test_util.py -index bf0d4333f9..09ba51bcea 100644 +index bf0d4333f9..8a4ba3666a 100644 --- a/Lib/distutils/tests/test_util.py +++ b/Lib/distutils/tests/test_util.py -@@ -233,6 +233,8 @@ +@@ -3,7 +3,7 @@ + import sys + import unittest + from copy import copy +-from test.support import run_unittest ++from test.support import run_unittest, has_subprocess_support + from unittest import mock + + from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError +@@ -233,6 +233,7 @@ # XXX platforms to be covered: mac -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_check_environ(self): util._environ_checked = 0 os.environ.pop('HOME', None) @@ -737,33 +270,6 @@ index f3828b10e1..7e86539bfa 100644 _CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) -diff --git a/Lib/os.py b/Lib/os.py -index b794159f86..78aab1ea2a 100644 ---- a/Lib/os.py -+++ b/Lib/os.py -@@ -36,7 +36,7 @@ - __all__ = ["altsep", "curdir", "pardir", "sep", "pathsep", "linesep", - "defpath", "name", "path", "devnull", "SEEK_SET", "SEEK_CUR", - "SEEK_END", "fsencode", "fsdecode", "get_exec_path", "fdopen", -- "popen", "extsep"] -+ "popen", "extsep", "allows_subprocesses"] - - def _exists(name): - return name in globals() -@@ -830,6 +830,13 @@ - fsencode, fsdecode = _fscodec() - del _fscodec - -+ -+if sys.platform in ('ios', 'tvos', 'watchos'): -+ allows_subprocesses = False -+else: -+ allows_subprocesses = True -+ -+ - # Supply spawn*() (probably only for Unix) - if _exists("fork") and not _exists("spawnv") and _exists("execv"): - diff --git a/Lib/platform.py b/Lib/platform.py index d6412e169b..3229a13a39 100755 --- a/Lib/platform.py @@ -886,20 +392,29 @@ index 9e617afb00..41305298d3 100644 return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' diff --git a/Lib/subprocess.py b/Lib/subprocess.py -index 4effc1d8b3..b0e4a5acac 100644 +index 4effc1d8b3..0a96cd3a75 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py -@@ -762,6 +762,9 @@ +@@ -100,6 +100,8 @@ + "CREATE_NO_WINDOW", "DETACHED_PROCESS", + "CREATE_DEFAULT_ERROR_MODE", "CREATE_BREAKAWAY_FROM_JOB"]) + ++# Some platforms do not support processes ++_can_fork_exec = sys.platform not in {"ios", "tvos", "watchos"} + + # Exception classes used by this module. + class SubprocessError(Exception): pass +@@ -762,6 +764,9 @@ pass_fds=(), *, user=None, group=None, extra_groups=None, encoding=None, errors=None, text=None, umask=-1): """Create new Popen instance.""" -+ if not os.allows_subprocesses: ++ if not _can_fork_exec: + raise RuntimeError(f"Subprocesses are not supported on {sys.platform}") + _cleanup() # Held while anything is calling waitpid before returncode has been # updated to prevent clobbering returncode if wait() or poll() are -@@ -1834,7 +1837,7 @@ +@@ -1834,7 +1839,7 @@ else: self.returncode = waitstatus_to_exitcode(sts) @@ -908,7 +423,7 @@ index 4effc1d8b3..b0e4a5acac 100644 _WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD): """Check if child process has terminated. Returns returncode attribute. -@@ -1843,6 +1846,8 @@ +@@ -1843,6 +1848,8 @@ outside of the local scope (nor can any methods it calls). """ @@ -998,10 +513,45 @@ index e3f79bfde5..8c0d7908a9 100644 return "%s-%s-%s" % (osname, release, machine) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index 86ac8f0966..0777ccc56d 100644 +index 6dc08135e0..5b268e98bc 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py -@@ -813,8 +813,8 @@ +@@ -56,7 +56,7 @@ + "check__all__", "skip_if_buggy_ucrt_strfptime", + "ignore_warnings", "check_sanitizer", "skip_if_sanitizer", + # sys +- "is_jython", "is_android", "check_impl_detail", "unix_shell", ++ "is_jython", "is_android", "is_apple_mobile", "check_impl_detail", "unix_shell", + "setswitchinterval", + # network + "open_urlresource", +@@ -743,11 +743,24 @@ + + is_android = hasattr(sys, 'getandroidapilevel') + +-if sys.platform != 'win32': ++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 + ++# 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') ++ ++has_fork_support = ( ++ hasattr(os, "fork") ++ and not is_apple_mobile ++) ++ ++has_subprocess_support = ( ++ not is_apple_mobile ++) ++ + # Filename used for testing + if os.name == 'java': + # Jython disallows @ in module names +@@ -813,8 +826,8 @@ # TESTFN_UNICODE is a non-ascii filename TESTFN_UNICODE = TESTFN_ASCII + "-\xe0\xf2\u0258\u0141\u011f" @@ -1012,7 +562,7 @@ index 86ac8f0966..0777ccc56d 100644 # decomposed Unicode, encoded using UTF-8. See QA1173: # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html import unicodedata -@@ -840,8 +840,8 @@ +@@ -840,8 +853,8 @@ 'Unicode filename tests may not be effective' % (TESTFN_UNENCODABLE, TESTFN_ENCODING)) TESTFN_UNENCODABLE = None @@ -1024,10 +574,10 @@ index 86ac8f0966..0777ccc56d 100644 # ascii and utf-8 cannot encode the byte 0xff b'\xff'.decode(TESTFN_ENCODING) diff --git a/Lib/test/support/script_helper.py b/Lib/test/support/script_helper.py -index 37e576d4a7..1d7176bf13 100644 +index 37e576d4a7..bb458f566b 100644 --- a/Lib/test/support/script_helper.py +++ b/Lib/test/support/script_helper.py -@@ -8,6 +8,7 @@ +@@ -8,15 +8,19 @@ import os.path import subprocess import py_compile @@ -1035,203 +585,216 @@ index 37e576d4a7..1d7176bf13 100644 import zipfile from importlib.util import source_from_cache -@@ -17,6 +18,8 @@ ++from test.support import has_subprocess_support + from test.support import make_legacy_pyc + + # Cached result of the expensive test performed in the function below. __cached_interp_requires_environment = None + -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def interpreter_requires_environment(): """ Returns True if our sys.executable interpreter requires environment -@@ -136,6 +139,7 @@ +@@ -136,6 +140,7 @@ rc = proc.returncode return _PythonRunResult(rc, out, err), cmd_line -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def _assert_python(expected_success, /, *args, **env_vars): res, cmd_line = run_python_until_end(*args, **env_vars) if (res.rc and expected_success) or (not res.rc and not expected_success): -@@ -165,6 +169,8 @@ +@@ -165,6 +170,8 @@ """ return _assert_python(False, *args, **env_vars) + -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def spawn_python(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw): """Run a Python subprocess with the given arguments. diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py -index 72189bfd38..84940caf8a 100644 +index 72189bfd38..c5a5ce79da 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py -@@ -516,6 +516,8 @@ +@@ -32,6 +32,7 @@ + from asyncio import selector_events + from test.test_asyncio import utils as test_utils + from test import support ++from test.support import is_apple_mobile, has_subprocess_support + from test.support import socket_helper + from test.support import ALWAYS_EQ, LARGEST, SMALLEST + +@@ -516,6 +517,7 @@ self._basetest_create_connection(conn_fut) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_connection(self): # Issue #20682: On Mac OS X Tiger, getsockname() returns a # zero-length address for UNIX socket. -@@ -608,6 +610,8 @@ +@@ -608,6 +610,7 @@ self.assertEqual(cm.exception.reason, 'CERTIFICATE_VERIFY_FAILED') @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_ssl_connection(self): with test_utils.run_test_server(use_ssl=True) as httpd: create_connection = functools.partial( -@@ -619,6 +623,8 @@ +@@ -619,6 +622,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_ssl_unix_connection(self): # Issue #20682: On Mac OS X Tiger, getsockname() returns a # zero-length address for UNIX socket. -@@ -842,6 +848,8 @@ +@@ -842,6 +846,7 @@ return server, path @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server(self): proto = MyProto(loop=self.loop) server, path = self._make_unix_server(lambda: proto) -@@ -870,6 +878,8 @@ +@@ -870,6 +875,7 @@ server.close() @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_path_socket_error(self): proto = MyProto(loop=self.loop) sock = socket.socket() -@@ -935,6 +945,8 @@ +@@ -935,6 +941,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( -@@ -965,6 +977,8 @@ +@@ -965,6 +972,7 @@ server.close() @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) server, host, port = self._make_ssl_server( -@@ -995,6 +1009,8 @@ +@@ -995,6 +1003,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( -@@ -1055,6 +1071,8 @@ +@@ -1055,6 +1064,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl_verified(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( -@@ -1714,6 +1732,7 @@ +@@ -1714,6 +1724,7 @@ next(it) -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class SubprocessTestsMixin: def check_terminated(self, returncode): diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py -index 38f4d84d47..1ed2ceb5ae 100644 +index 38f4d84d47..b6c725937c 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py -@@ -5,6 +5,7 @@ - import queue - import pickle - import socket -+import subprocess - import sys - import threading - import unittest -@@ -67,6 +68,8 @@ +@@ -17,6 +17,7 @@ + + import asyncio + from test.test_asyncio import utils as test_utils ++from test.support import is_apple_mobile, has_subprocess_support + + + def tearDownModule(): +@@ -67,6 +68,7 @@ self._basetest_open_connection(conn_fut) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection(self): with test_utils.run_test_unix_server() as httpd: conn_fut = asyncio.open_unix_connection(httpd.address, -@@ -101,6 +104,8 @@ +@@ -101,6 +103,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection_no_loop_ssl(self): with test_utils.run_test_unix_server(use_ssl=True) as httpd: conn_fut = asyncio.open_unix_connection( -@@ -131,6 +136,8 @@ +@@ -131,6 +134,7 @@ self._basetest_open_connection_error(conn_fut) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection_error(self): with test_utils.run_test_unix_server() as httpd: conn_fut = asyncio.open_unix_connection(httpd.address, -@@ -656,6 +663,8 @@ +@@ -656,6 +660,7 @@ self.assertEqual(messages, []) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_start_unix_server(self): class MyServer: -@@ -732,6 +741,7 @@ +@@ -732,6 +737,7 @@ self.assertEqual(messages, []) @unittest.skipIf(sys.platform == 'win32', "Don't have pipes") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') 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 3f6fc15b27..2633e04fc8 100644 +index 3f6fc15b27..9d8d43efbe 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py -@@ -103,6 +103,7 @@ +@@ -10,6 +10,7 @@ + from asyncio import subprocess + from test.test_asyncio import utils as test_utils + from test import support ++from test.support import has_subprocess_support + + if sys.platform != 'win32': + from asyncio import unix_events +@@ -103,6 +104,7 @@ transport.close() -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') 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 70d306ffe8..590dfc9fd4 100644 +index 70d306ffe8..1b0fcf631e 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py -@@ -267,6 +267,8 @@ +@@ -15,6 +15,7 @@ + import unittest + from unittest import mock + from test import support ++from test.support import is_apple_mobile, has_subprocess_support + from test.support import socket_helper + + if sys.platform == 'win32': +@@ -267,6 +268,7 @@ @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'UNIX Sockets are not supported') -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class SelectorEventLoopUnixSocketTests(test_utils.TestCase): def setUp(self): @@ -1239,36 +802,43 @@ index 70d306ffe8..590dfc9fd4 100644 )) -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class ChildWatcherTestsMixin: ignore_warnings = mock.patch.object(log.logger, "warning") diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py -index 1dbeac41dc..73e63d1c31 100644 +index 1dbeac41dc..94959a2534 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py -@@ -644,7 +644,7 @@ - def test_ErrorHeritage(self): +@@ -645,6 +645,7 @@ self.assertTrue(issubclass(binascii.Error, ValueError)) -- + +@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') class TestMain(unittest.TestCase): def tearDown(self): if os.path.exists(support.TESTFN): diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py -index 39b1b64651..f9c0614003 100644 +index 39b1b64651..e6552352e5 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py -@@ -54,6 +54,7 @@ +@@ -17,6 +17,7 @@ + import importlib.util + from test import support + from test.support import MISSING_C_DOCSTRINGS ++from test.support import has_subprocess_support + from test.support.script_helper import assert_python_failure, assert_python_ok + try: + import _posixsubprocess +@@ -54,6 +55,7 @@ self.assertEqual(testfunction.attribute, "test") self.assertRaises(AttributeError, setattr, inst.testfunction, "attribute", "test") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_no_FatalError_infinite_loop(self): with support.SuppressCrashReport(): p = subprocess.Popen([sys.executable, "-c", -@@ -720,6 +721,7 @@ +@@ -720,6 +722,7 @@ self.assertEqual(main_attr_id, subinterp_attr_id) @@ -1277,88 +847,91 @@ index 39b1b64651..f9c0614003 100644 @support.reap_threads diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py -index 4b3e33c4fd..3f15078807 100644 +index e38fc698c5..ae503199d1 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py -@@ -67,6 +67,7 @@ +@@ -9,6 +9,7 @@ + import textwrap + import unittest + from test import support ++from test.support import has_subprocess_support + from test.support.script_helper import ( + spawn_python, kill_python, assert_python_ok, assert_python_failure, + interpreter_requires_environment +@@ -67,6 +68,7 @@ rc, out, err = assert_python_ok('-vv') self.assertNotIn(b'stack overflow', err) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @unittest.skipIf(interpreter_requires_environment(), 'Cannot run -E tests when PYTHON env vars are required.') def test_xoptions(self): -@@ -85,6 +86,7 @@ +@@ -85,6 +87,7 @@ opts = get_xoptions('-Xa', '-Xb=c,d=e') self.assertEqual(opts, {'a': True, 'b': 'c,d=e'}) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_showrefcount(self): def run_python(*args): # this is similar to assert_python_ok but doesn't strip -@@ -169,6 +171,7 @@ +@@ -169,6 +172,7 @@ # arguments as unicode (using wmain() instead of main()). @unittest.skipIf(sys.platform == 'win32', 'Windows has a native unicode API') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_undecodable_code(self): undecodable = b"\xff" env = os.environ.copy() -@@ -284,6 +287,7 @@ +@@ -284,6 +288,7 @@ 'False False False\n' 'False False True\n') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_unbuffered_output(self): # Test expected operation of the '-u' switch for stream in ('stdout', 'stderr'): -@@ -342,6 +346,7 @@ +@@ -342,6 +347,7 @@ # for empty and unset PYTHONPATH self.assertEqual(out1, out2) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_displayhook_unencodable(self): for encoding in ('ascii', 'latin-1', 'utf-8'): env = os.environ.copy() -@@ -360,6 +365,7 @@ +@@ -360,6 +366,7 @@ escaped = repr(text).encode(encoding, 'backslashreplace') self.assertIn(escaped, data) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def check_input(self, code, expected): with tempfile.NamedTemporaryFile("wb+") as stdin: sep = os.linesep.encode('ASCII') -@@ -435,6 +441,7 @@ +@@ -435,6 +442,7 @@ @unittest.skipIf(os.name != 'posix', "test needs POSIX semantics") @unittest.skipIf(sys.platform == "vxworks", "test needs preexec support in subprocess.Popen") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def _test_no_stdio(self, streams): code = """if 1: import os, sys -diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py -index ab647d66f0..9de34a3efd 100644 ---- a/Lib/test/test_compileall.py -+++ b/Lib/test/test_compileall.py -@@ -219,7 +219,8 @@ - compileall.compile_dir(self.directory, quiet=True, workers=5) - self.assertTrue(pool_mock.called) - -- def test_compile_workers_non_positive(self): -+ @mock.patch('compileall.ProcessPoolExecutor') -+ def test_compile_workers_non_positive(self, pool_mock): - with self.assertRaisesRegex(ValueError, - "workers must be greater or equal to 0"): - compileall.compile_dir(self.directory, workers=-1) diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py -index 2d27a294b1..7916bafb21 100644 +index 2d27a294b1..a091f30929 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py +@@ -5,7 +5,7 @@ + # Skip tests if sem_open implementation is broken. + support.skip_if_broken_multiprocessing_synchronize() + +-from test.support import hashlib_helper ++from test.support import hashlib_helper, has_subprocess_support + from test.support.script_helper import assert_python_ok + + import contextlib @@ -151,6 +151,7 @@ executor_type = futures.ThreadPoolExecutor -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class ProcessPoolForkMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "fork" @@ -1366,26 +939,34 @@ index 2d27a294b1..7916bafb21 100644 return super().get_context() -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class ProcessPoolSpawnMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "spawn" -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class ProcessPoolForkserverMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "forkserver" diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py -index dba19103e8..68e0d5cff9 100644 +index dba19103e8..d56399e18a 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py -@@ -2838,7 +2838,12 @@ +@@ -3,6 +3,7 @@ + """ + + from test import support ++from test.support import is_apple_mobile + import doctest + import functools + import os +@@ -2838,7 +2839,12 @@ TestResults(failed=1, attempted=1) """ -def test_CLI(): r""" -+if sys.platform in ('iOS', 'tvos', 'watchos'): ++if is_apple_mobile: + # Mobile platforms can't invoke doctest from the command line, + # so skip this test. + pass @@ -1395,34 +976,39 @@ index dba19103e8..68e0d5cff9 100644 These tests test this CLI functionality. diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py -index a5f8f6465e..00287fea2b 100644 +index a5f8f6465e..e4366a1289 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py -@@ -9,6 +9,7 @@ +@@ -5,10 +5,11 @@ + import unittest + + from test import support +-from test.support import script_helper ++from test.support import script_helper, has_subprocess_support @unittest.skipUnless(os.name == "posix", "only supported on Unix") -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class EINTRTests(unittest.TestCase): @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py -index 2f8f5eec47..9629ef5226 100644 +index 2f8f5eec47..2d4f1456ff 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py -@@ -43,6 +43,7 @@ - finally: - support.unlink(filename) - -+ - class FaultHandlerTests(unittest.TestCase): - def get_output(self, code, filename=None, fd=None): - """ +@@ -6,6 +6,7 @@ + import subprocess + import sys + from test import support ++from test.support import has_subprocess_support + from test.support import script_helper, is_android + from test.support import skip_if_sanitizer + import tempfile @@ -342,6 +343,7 @@ finally: sys.stderr = orig_stderr -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_disabled_by_default(self): # By default, the module should be disabled code = "import faulthandler; print(faulthandler.is_enabled())" @@ -1430,7 +1016,7 @@ index 2f8f5eec47..9629ef5226 100644 output = subprocess.check_output(args) self.assertEqual(output.rstrip(), b"False") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_sys_xoptions(self): # Test python -X faulthandler code = "import faulthandler; print(faulthandler.is_enabled())" @@ -1438,45 +1024,71 @@ index 2f8f5eec47..9629ef5226 100644 output = subprocess.check_output(args, env=env) self.assertEqual(output.rstrip(), b"True") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_env_var(self): # empty env var code = "import faulthandler; print(faulthandler.is_enabled())" diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py -index 2a3209eb7d..fb27155fa4 100644 +index 2a3209eb7d..8e2a041f4b 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py +@@ -7,7 +7,7 @@ + import unittest + from multiprocessing import Process + from test.support import (verbose, TESTFN, unlink, import_module, +- cpython_only) ++ cpython_only, is_apple_mobile) + + # Skip test if no fcntl module. + fcntl = import_module('fcntl') @@ -23,7 +23,7 @@ start_len = "qq" if (sys.platform.startswith(('netbsd', 'freebsd', 'openbsd')) - or sys.platform == 'darwin'): -+ or sys.platform in ('darwin', 'ios', 'tvos', 'watchos')): ++ or sys.platform == 'darwin' or is_apple_mobile): if struct.calcsize('l') == 8: off_t = 'l' pid_t = 'i' diff --git a/Lib/test/test_file_eintr.py b/Lib/test/test_file_eintr.py -index f1efd266ff..db6d3ec5ad 100644 +index f1efd266ff..5a68c60cc2 100644 --- a/Lib/test/test_file_eintr.py +++ b/Lib/test/test_file_eintr.py -@@ -69,6 +69,7 @@ +@@ -15,6 +15,7 @@ + import sys + import time + import unittest ++from test.support import has_subprocess_support + + # Test import all of the things we're about to try testing up front. + import _io +@@ -69,6 +70,7 @@ self.fail('Error from IO process %s:\nSTDOUT:\n%sSTDERR:\n%s\n' % (why, stdout.decode(), stderr.decode())) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def _test_reading(self, data_to_write, read_and_verify_code): """Generic buffered read method test harness to validate EINTR behavior. diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py -index 3313804703..1e0cfc7324 100644 +index 3313804703..10415ed4b2 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py +@@ -3,7 +3,7 @@ + from test.support import (verbose, refcount_test, + cpython_only, start_threads, + temp_dir, TESTFN, unlink, +- import_module) ++ import_modul, has_subprocess_support) + from test.support.script_helper import assert_python_ok, make_script + + import gc @@ -680,6 +680,8 @@ del x gc.set_debug(%s) """ + -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def run_command(code): p = subprocess.Popen([sys.executable, "-Wd", "-c", code], stdout=subprocess.PIPE, @@ -1494,110 +1106,90 @@ index b3aa855a5b..3304cb9606 100644 # Regex to parse: # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7 diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py -index c1494d29ca..e42244ccfe 100644 +index 4acf7a6fea..8f3de2f4cc 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py -@@ -398,7 +398,7 @@ +@@ -30,6 +30,8 @@ + + import unittest + from test import support ++from test.support import is_apple_mobile ++from test.support import has_subprocess_support + + + class NoLogRequestHandler: +@@ -398,7 +400,7 @@ with open(os.path.join(self.tempdir, filename), 'wb') as f: f.write(support.TESTFN_UNDECODABLE) response = self.request(self.base_url + '/') - if sys.platform == 'darwin': -+ if sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform == 'darwin' or is_apple_mobile: # On Mac OS the HFS+ filesystem replaces bytes that aren't valid # UTF-8 into a percent-encoded value. for name in os.listdir(self.tempdir): -@@ -608,6 +608,7 @@ +@@ -657,6 +659,7 @@ @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, "This test can't be run reliably as root (issue #13308).") -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class CGIHTTPServerTestCase(BaseTestCase): class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler): pass -diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py -index fe394dc50c..6974ae7ea8 100644 ---- a/Lib/test/test_imp.py -+++ b/Lib/test/test_imp.py -@@ -18,7 +18,7 @@ - """Decorator to skip a test if not running under CPython or lacking - imp.load_dynamic().""" - meth = support.cpython_only(meth) -- return unittest.skipIf(not hasattr(imp, 'load_dynamic'), -+ return unittest.skipIf(not getattr(imp, 'load_dynamic'), - 'imp.load_dynamic() required')(meth) - - -diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py -index c9b4a3772c..1d87c8df65 100644 ---- a/Lib/test/test_importlib/extension/test_finder.py -+++ b/Lib/test/test_importlib/extension/test_finder.py -@@ -3,10 +3,13 @@ - - machinery = util.import_importlib('importlib.machinery') - -+import sys - import unittest - import warnings - - -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ '%s does not support dynamic loading' % sys.platform) - class FinderTests(abc.FinderTests): - - """Test the finder for extension modules.""" -diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py -index abd612fcd9..ebf7bc9aa5 100644 ---- a/Lib/test/test_importlib/extension/test_loader.py -+++ b/Lib/test/test_importlib/extension/test_loader.py -@@ -11,6 +11,8 @@ - import importlib - from test.support.script_helper import assert_python_failure - -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ '%s does not support dynamic loading' % sys.platform) - class LoaderTests(abc.LoaderTests): - - """Test load_module() for extension modules.""" -@@ -82,6 +84,9 @@ - Source_LoaderTests - ) = util.test_both(LoaderTests, machinery=machinery) - -+ -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ '%s does not support dynamic loading' % sys.platform) - class MultiPhaseExtensionModuleTests(abc.LoaderTests): - """Test loading extension modules with multi-phase initialization (PEP 489) - """ diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py -index feee861830..401b7ca493 100644 +index feee861830..5c2863acaf 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py +@@ -39,7 +39,7 @@ + from test import support + from test.support.script_helper import ( + assert_python_ok, assert_python_failure, run_python_until_end) +-from test.support import FakePath, skip_if_sanitizer ++from test.support import FakePath, skip_if_sanitizer, is_apple_mobile + + import codecs + import io # C implementation of io @@ -594,7 +594,7 @@ # 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': -+ if sys.platform[:3] == 'win' or sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform[:3] == 'win' or sys.platform == 'darwin' or is_apple_mobile: support.requires( 'largefile', 'test requires %s bytes and a long time to run' % self.LARGE) diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py -index d441bb15a7..c3c7a052ba 100644 +index d441bb15a7..1e9ba2f3fe 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py -@@ -84,6 +84,7 @@ +@@ -6,6 +6,7 @@ + import subprocess + + from test import support ++from test.support import has_subprocess_support + from test.support.script_helper import assert_python_ok + + +@@ -84,6 +85,7 @@ } """) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_stdin_stdout(self): args = sys.executable, '-m', 'json.tool' process = subprocess.run(args, input=self.data, capture_output=True, text=True, check=True) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py -index 0a9d449078..e58842a3fe 100644 +index 0a9d449078..af9395e33a 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py -@@ -1768,9 +1768,21 @@ +@@ -43,6 +43,7 @@ + import tempfile + from test.support.script_helper import assert_python_ok, assert_python_failure + from test import support ++from test.support import is_apple_mobile + from test.support import socket_helper + from test.support.logging_helper import TestHandler + import textwrap +@@ -1768,9 +1769,20 @@ # just need a name - file can't be present, or we'll get an # 'address already in use' error. os.remove(fn) @@ -1614,61 +1206,66 @@ index 0a9d449078..e58842a3fe 100644 return fn @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixSocketHandlerTest(SocketHandlerTest): """Test for SocketHandler with unix sockets.""" -@@ -1852,6 +1864,8 @@ +@@ -1852,6 +1864,7 @@ self.assertEqual(self.log_output, "spam\neggs\n") @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixDatagramHandlerTest(DatagramHandlerTest): """Test for DatagramHandler using Unix sockets.""" -@@ -1936,6 +1950,8 @@ +@@ -1936,6 +1949,7 @@ self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m') @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixSysLogHandlerTest(SysLogHandlerTest): """Test for SysLogHandler with Unix sockets.""" diff --git a/Lib/test/test_mailcap.py b/Lib/test/test_mailcap.py -index c08423c670..9673b07df3 100644 +index c08423c670..29b8ea06a3 100644 --- a/Lib/test/test_mailcap.py +++ b/Lib/test/test_mailcap.py -@@ -1,6 +1,7 @@ +@@ -1,8 +1,10 @@ import mailcap import os import copy +import sys import test.support import unittest ++from test.support import is_apple_mobile -@@ -212,7 +213,8 @@ + # Location of mailcap file + MAILCAPFILE = test.support.findfile("mailcap.txt") +@@ -212,7 +214,7 @@ ] self._run_cases(cases) - @unittest.skipUnless(os.name == "posix", "Requires 'test' command on system") -+ @unittest.skipUnless(os.name == "posix" and sys.platform not in('ios', 'tvos', 'watchos'), -+ "Requires 'test' command on system") ++ @unittest.skipUnless(os.name == "posix" and not is_apple_mobile, "Requires 'test' command on system") def test_test(self): # findmatch() will automatically check any "test" conditions and skip # the entry if the check fails. diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py -index ace1593999..69cc2cd06a 100644 +index ace1593999..2fa6603ed3 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py -@@ -240,7 +240,10 @@ +@@ -1,4 +1,5 @@ + from test import support ++from test.support import is_apple_mobile + import array + import io + import marshal +@@ -240,7 +241,10 @@ if os.name == 'nt': MAX_MARSHAL_STACK_DEPTH = 1000 else: - MAX_MARSHAL_STACK_DEPTH = 2000 -+ if sys.platform in ('ios', 'tvos', 'watchos'): ++ if is_apple_mobile: + MAX_MARSHAL_STACK_DEPTH = 1500 + else: + MAX_MARSHAL_STACK_DEPTH = 2000 @@ -1676,15 +1273,22 @@ index ace1593999..69cc2cd06a 100644 last.append([0]) last = last[-1] diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py -index 5400f25f50..5c7fb278ea 100644 +index 5400f25f50..c617345b2e 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py +@@ -1,5 +1,5 @@ + from test.support import (TESTFN, import_module, unlink, +- requires, _2G, _4G, gc_collect, cpython_only) ++ requires, _2G, _4G, gc_collect, cpython_only, is_apple_mobile) + import unittest + import os + import re @@ -230,7 +230,7 @@ with open(TESTFN, "r+b") as f: self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4) - if os.name == "posix": -+ if os.name == "posix" and sys.platform not in ('iOS', 'tvos', 'watchos'): ++ if os.name == "posix" and not is_apple_mobile: # Try incompatible flags, prot and access parameters. with open(TESTFN, "r+b") as f: self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, @@ -1693,229 +1297,282 @@ index 5400f25f50..5c7fb278ea 100644 def _make_test_file(self, num_zeroes, tail): - if sys.platform[:3] == 'win' or sys.platform == 'darwin': -+ if sys.platform[:3] == 'win' or sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform[:3] == 'win' or sys.platform == 'darwin' or is_apple_mobile: 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_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork.py -index 5000edb7c5..5d1d2917bf 100644 +index 5000edb7c5..65a0fb67f9 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork.py @@ -1,4 +1,9 @@ -+import os import unittest ++from test.support import has_subprocess_support + -+if not os.allows_subprocesses: ++if not has_subprocess_support: + raise unittest.SkipTest('Test requires support for subprocesses.') + import test._test_multiprocessing import sys diff --git a/Lib/test/test_multiprocessing_forkserver.py b/Lib/test/test_multiprocessing_forkserver.py -index 6ad5faf9e8..4e05efb6df 100644 +index 6ad5faf9e8..a5285ef77a 100644 --- a/Lib/test/test_multiprocessing_forkserver.py +++ b/Lib/test/test_multiprocessing_forkserver.py @@ -1,4 +1,9 @@ -+import os import unittest ++from test.support import has_subprocess_support + -+if not os.allows_subprocesses: ++if not has_subprocess_support: + raise unittest.SkipTest('Test requires support for subprocesses.') + import test._test_multiprocessing import sys diff --git a/Lib/test/test_multiprocessing_spawn.py b/Lib/test/test_multiprocessing_spawn.py -index 6558952308..a121e8c2dd 100644 +index 6558952308..192096e2ac 100644 --- a/Lib/test/test_multiprocessing_spawn.py +++ b/Lib/test/test_multiprocessing_spawn.py @@ -1,4 +1,9 @@ -+import os import unittest ++from test.support import has_subprocess_support + -+if not os.allows_subprocesses: ++if not has_subprocess_support: + raise unittest.SkipTest('Test requires support for subprocesses.') + import test._test_multiprocessing from test import support diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py -index 78dd3151b3..71561f03c8 100644 +index 78dd3151b3..cd6e6cbd83 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py -@@ -852,6 +852,7 @@ +@@ -31,6 +31,7 @@ + import uuid + import warnings + from test import support ++from test.support import has_subprocess_support + from test.support import socket_helper + from platform import win32_is_iot + +@@ -852,6 +853,7 @@ # Bug 1110478 @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_update2(self): os.environ.clear() os.environ.update(HELLO="World") -@@ -861,6 +862,7 @@ +@@ -861,6 +863,7 @@ @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_os_popen_iter(self): with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" % unix_shell) as popen: -@@ -1812,6 +1814,7 @@ +@@ -1812,6 +1815,7 @@ @unittest.skipUnless(hasattr(os, 'execv'), "need os.execv()") -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class ExecTests(unittest.TestCase): @unittest.skipIf(USING_LINUXTHREADS, "avoid triggering a linuxthreads bug: see issue #4970") -@@ -2133,6 +2136,7 @@ +@@ -2133,6 +2137,7 @@ self.assertRaises(OverflowError, os.setreuid, 0, self.UID_OVERFLOW) @unittest.skipUnless(hasattr(os, 'setreuid'), 'test needs os.setreuid()') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_setreuid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). -@@ -2141,6 +2145,7 @@ +@@ -2141,6 +2146,7 @@ 'import os,sys;os.setreuid(-1,-1);sys.exit(0)']) @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_setregid(self): if os.getuid() != 0 and not HAVE_WHEEL_GROUP: self.assertRaises(OSError, os.setregid, 0, 0) -@@ -2150,6 +2155,7 @@ +@@ -2150,6 +2156,7 @@ self.assertRaises(OverflowError, os.setregid, 0, self.GID_OVERFLOW) @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_setregid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). -@@ -2822,6 +2828,7 @@ +@@ -2822,6 +2829,7 @@ class PidTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'getppid'), "test needs os.getppid") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_getppid(self): p = subprocess.Popen([sys.executable, '-c', 'import os; print(os.getppid())'], -@@ -2848,6 +2855,9 @@ +@@ -2848,6 +2856,7 @@ self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) self.assertEqual(pid2, pid) -+ @unittest.skipUnless(hasattr(os, 'spawnv'), "test needs os.spawnv") -+ @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_waitpid(self): self.check_waitpid(code='pass', exitcode=0) -@@ -3517,6 +3527,7 @@ +@@ -3517,6 +3526,7 @@ self.assertGreaterEqual(size.columns, 0) self.assertGreaterEqual(size.lines, 0) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_stty_match(self): """Check if stty returns the same results diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py -index 58778300ee..a38a21dc99 100644 +index 58778300ee..7856be8429 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py -@@ -1293,6 +1293,7 @@ +@@ -14,6 +14,7 @@ + from contextlib import ExitStack, redirect_stdout + from io import StringIO + from test import support ++from test.support import has_subprocess_support + # This little helper class is essential for testing pdb under doctest. + from test.test_doctest import _FakeInput + from unittest.mock import patch +@@ -1293,6 +1294,7 @@ def tearDown(self): support.unlink(support.TESTFN) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def _run_pdb(self, pdb_args, commands): self.addCleanup(support.rmtree, '__pycache__') cmd = [sys.executable, '-m', 'pdb'] + pdb_args -@@ -1385,6 +1386,7 @@ +@@ -1385,6 +1387,7 @@ ('bÅ“r', 1), ) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_issue7964(self): # open the file as binary so we can force \r\n newline with open(support.TESTFN, 'wb') as f: diff --git a/Lib/test/test_pipes.py b/Lib/test/test_pipes.py -index 72e1cabab2..bd4da160e8 100644 +index 72e1cabab2..d62b516e5a 100644 --- a/Lib/test/test_pipes.py +++ b/Lib/test/test_pipes.py -@@ -1,12 +1,15 @@ +@@ -1,13 +1,17 @@ import pipes import os import string +import sys import unittest import shutil - from test.support import TESTFN, unlink, reap_children +-from test.support import TESTFN, unlink, reap_children ++from test.support import TESTFN, unlink, reap_children, is_apple_mobile if os.name != 'posix': raise unittest.SkipTest('pipes module only works on posix') -+if sys.platform in ('ios', 'tvos', 'watchos'): -+ raise unittest.SkipTest('pipes tests cannot run on %s' % sys.platform) ++if is_apple_mobile: ++ raise unittest.SkipTest('pipes tests cannot run on %s' % sys.platform) ++ TESTFN2 = TESTFN + "2" + # tr a-z A-Z is not portable, so make the ranges explicit diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py -index 9f04c79e09..8c92a660ed 100644 +index 9f04c79e09..ab2c1e507a 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py -@@ -9,7 +9,6 @@ +@@ -8,7 +8,7 @@ + from unittest import mock from test import support - - ++from test.support import has_subprocess_support, is_apple_mobile + class PlatformTest(unittest.TestCase): def clear_caches(self): - platform._platform_cache.clear() -@@ -20,6 +19,7 @@ +@@ -20,6 +20,7 @@ res = platform.architecture() @support.skip_unless_symlink -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_architecture_via_symlink(self): # issue3762 with support.PythonSymlink() as py: cmd = "-c", "import platform; print(platform.architecture())" -@@ -254,7 +254,7 @@ +@@ -254,7 +255,7 @@ def test_mac_ver(self): res = platform.mac_ver() - if platform.uname().system == 'Darwin': -+ if platform.uname().system == 'Darwin' and sys.platform not in ('ios', 'tvos', 'watchos'): ++ if platform.uname().system == 'Darwin' and not is_apple_mobile: # We are on a macOS system, check that the right version # information is returned output = subprocess.check_output(['sw_vers'], text=True) +@@ -286,6 +287,9 @@ + else: + self.assertEqual(res[2], 'PowerPC') + ++ @unittest.skipUnless(is_apple_mobile, "iOS/tvOS/watchOS only test") ++ def test_ios_ver(self): ++ res = platform.ios_ver() + + @unittest.skipUnless(sys.platform == 'darwin', "OSX only test") + def test_mac_ver_with_fork(self): diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py -index 294131da79..a675153295 100644 +index 294131da79..c74979d9a7 100644 --- a/Lib/test/test_poll.py +++ b/Lib/test/test_poll.py +@@ -7,7 +7,7 @@ + import threading + import time + import unittest +-from test.support import TESTFN, reap_threads, cpython_only ++from test.support import TESTFN, reap_threads, cpython_only, has_subprocess_support + + try: + select.poll @@ -117,6 +117,7 @@ # Another test case for poll(). This is copied from the test case for # select(), modified to use poll() instead. -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_poll2(self): cmd = 'for i in 0 1 2 3 4 5 6 7 8 9; do echo testing...; sleep 1; done' proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, diff --git a/Lib/test/test_popen.py b/Lib/test/test_popen.py -index ab1bc77655..60be660d25 100644 +index ab1bc77655..c5850faddc 100644 --- a/Lib/test/test_popen.py +++ b/Lib/test/test_popen.py -@@ -16,6 +16,8 @@ +@@ -5,6 +5,7 @@ + + import unittest + from test import support ++from test.support import has_subprocess_support + import os, sys + + # Test that command-lines get down as we expect. +@@ -16,6 +17,7 @@ if ' ' in python: python = '"' + python + '"' # quote embedded space for cmdline -+ -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class PopenTest(unittest.TestCase): def _do_test_commandline(self, cmdline, expected): diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py -index 6ba14547bd..30f0088291 100644 +index 6ba14547bd..8ed2d7441d 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py -@@ -61,15 +61,21 @@ +@@ -1,6 +1,8 @@ + "Test posix functions" + + from test import support ++from test.support import has_subprocess_support ++from test.support import is_apple_mobile + from test.support.script_helper import assert_python_ok + + # Skip these tests if there is no posix module. +@@ -61,15 +63,22 @@ # no side-effects which we need to cleanup (e.g., fork, wait, abort) NO_ARG_FUNCTIONS = [ "ctermid", "getcwd", "getcwdb", "uname", "times", "getloadavg", @@ -1924,7 +1581,8 @@ index 6ba14547bd..30f0088291 100644 "getpid", "getpgrp", "getppid", "getuid", "sync", ] -+ if sys.platform not in ('ios', 'tvos', 'watchos'): ++ # getgroups can't be invoked on iOS/tvOS/watchOS. ++ if is_apple_mobile: + NO_ARG_FUNCTIONS.append("getgroups") + for name in NO_ARG_FUNCTIONS: @@ -1940,313 +1598,317 @@ index 6ba14547bd..30f0088291 100644 @unittest.skipUnless(hasattr(posix, 'getresuid'), 'test needs posix.getresuid()') -@@ -745,9 +751,10 @@ +@@ -745,9 +754,10 @@ 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') and sys.platform not in ('ios', 'tvos', 'watchos'): ++ if hasattr(os, 'getgroups') and not is_apple_mobile: + 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) -@@ -1020,6 +1027,7 @@ - os.chdir(curdir) - support.rmtree(base_path) - -+ - @unittest.skipUnless(hasattr(posix, 'getgrouplist'), "test needs posix.getgrouplist()") - @unittest.skipUnless(hasattr(pwd, 'getpwuid'), "test needs pwd.getpwuid()") - @unittest.skipUnless(hasattr(os, 'getuid'), "test needs os.getuid()") -@@ -1028,8 +1036,8 @@ +@@ -1028,8 +1038,8 @@ group = pwd.getpwuid(os.getuid())[3] self.assertIn(group, posix.getgrouplist(user, group)) - @unittest.skipUnless(hasattr(os, 'getegid'), "test needs os.getegid()") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_getgroups(self): with os.popen('id -G 2>/dev/null') as idg: groups = idg.read().strip() -@@ -1088,7 +1096,7 @@ +@@ -1088,7 +1098,7 @@ self.assertIsInstance(hi, int) self.assertGreaterEqual(hi, lo) # OSX evidently just returns 15 without checking the argument. - if sys.platform != "darwin": -+ if sys.platform not in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform != 'darwin' and not is_apple_mobile: self.assertRaises(OSError, posix.sched_get_priority_min, -23) self.assertRaises(OSError, posix.sched_get_priority_max, -23) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py -index 9c32467cbb..ce273f0b4d 100644 +index 9c32467cbb..89d266b27e 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py +@@ -1,4 +1,4 @@ +-from test.support import verbose, import_module, reap_children ++from test.support import verbose, import_module, reap_children, has_subprocess_support + + # Skip these tests if termios is not available + import_module('termios') @@ -138,6 +138,7 @@ # to ignore this signal. os.close(master_fd) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_fork(self): debug("calling pty.fork()") pid, master_fd = pty.fork() diff --git a/Lib/test/test_quopri.py b/Lib/test/test_quopri.py -index 715544c8a9..6e66178816 100644 +index 715544c8a9..0641b2fcef 100644 --- a/Lib/test/test_quopri.py +++ b/Lib/test/test_quopri.py -@@ -180,6 +180,7 @@ +@@ -1,4 +1,5 @@ + import unittest ++from test.support import has_subprocess_support + + import sys, io, subprocess + import quopri +@@ -180,6 +181,7 @@ for p, e in self.HSTRINGS: self.assertEqual(quopri.decodestring(e, header=True), p) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_scriptencode(self): (p, e) = self.STRINGS[-1] process = subprocess.Popen([sys.executable, "-mquopri"], -@@ -196,6 +197,7 @@ +@@ -196,6 +198,7 @@ self.assertEqual(cout[i], e[i]) self.assertEqual(cout, e) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_scriptdecode(self): (p, e) = self.STRINGS[-1] process = subprocess.Popen([sys.executable, "-mquopri", "-d"], diff --git a/Lib/test/test_script_helper.py b/Lib/test/test_script_helper.py -index 4ade2cbc0d..83ce283a04 100644 +index 4ade2cbc0d..63175fcc13 100644 --- a/Lib/test/test_script_helper.py +++ b/Lib/test/test_script_helper.py -@@ -1,5 +1,6 @@ - """Unittests for test.support.script_helper. Who tests the test helper?""" - -+import os +@@ -3,7 +3,7 @@ import subprocess import sys import os -@@ -35,6 +36,7 @@ +-from test.support import script_helper ++from test.support import script_helper, has_subprocess_support + import unittest + from unittest import mock + +@@ -35,6 +35,7 @@ self.assertIn('import sys; sys.exit(0)', error_msg, msg='unexpected command line.') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @mock.patch('subprocess.Popen') def test_assert_python_isolated_when_env_not_required(self, mock_popen): with mock.patch.object(script_helper, -@@ -53,6 +55,7 @@ +@@ -53,6 +54,7 @@ self.assertIn('-I', popen_command) self.assertNotIn('-E', popen_command) # -I overrides this -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @mock.patch('subprocess.Popen') def test_assert_python_not_isolated_when_env_is_required(self, mock_popen): """Ensure that -I is not passed when the environment is required.""" -@@ -82,6 +85,7 @@ +@@ -82,6 +84,7 @@ # Reset the private cached state. script_helper.__dict__['__cached_interp_requires_environment'] = None -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @mock.patch('subprocess.check_call') def test_interpreter_requires_environment_true(self, mock_check_call): with mock.patch.dict(os.environ): -@@ -91,6 +95,7 @@ +@@ -91,6 +94,7 @@ self.assertTrue(script_helper.interpreter_requires_environment()) self.assertEqual(1, mock_check_call.call_count) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @mock.patch('subprocess.check_call') def test_interpreter_requires_environment_false(self, mock_check_call): with mock.patch.dict(os.environ): -@@ -100,6 +105,7 @@ +@@ -100,6 +104,7 @@ self.assertFalse(script_helper.interpreter_requires_environment()) self.assertEqual(1, mock_check_call.call_count) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @mock.patch('subprocess.check_call') def test_interpreter_requires_environment_details(self, mock_check_call): with mock.patch.dict(os.environ): diff --git a/Lib/test/test_select.py b/Lib/test/test_select.py -index 458998a62f..577cf5f385 100644 +index 458998a62f..607c533e12 100644 --- a/Lib/test/test_select.py +++ b/Lib/test/test_select.py -@@ -5,7 +5,8 @@ +@@ -4,6 +4,7 @@ + import sys import unittest from test import support ++from test.support import has_subprocess_support --@unittest.skipIf((sys.platform[:3]=='win'), -+ -+@unittest.skipIf((sys.platform[:3] == 'win'), + @unittest.skipIf((sys.platform[:3]=='win'), "can't easily test on this system") - class SelectTestCase(unittest.TestCase): - @@ -44,6 +45,7 @@ self.assertIsNot(r, x) self.assertIsNot(w, x) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_select(self): cmd = 'for i in 0 1 2 3 4 5 6 7 8 9; do echo testing...; sleep 1; done' with os.popen(cmd) as p: diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py -index 3890df93bb..663c5d1908 100644 +index 3890df93bb..8c84a05794 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py -@@ -1731,6 +1731,8 @@ +@@ -30,7 +30,7 @@ + posix = None + + from test import support +-from test.support import TESTFN, FakePath ++from test.support import TESTFN, FakePath, has_subprocess_support + + TESTFN2 = TESTFN + "2" + TESTFN_SRC = TESTFN + "_SRC" +@@ -1731,6 +1731,7 @@ check_chown(dirname, uid, gid) -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't support other executable." % sys.platform) ++@unittest.skipIf(support.has_subprocess_support, 'Test requires support for subprocesses.') class TestWhich(BaseTest, unittest.TestCase): def setUp(self): -@@ -2598,6 +2600,7 @@ +@@ -2598,6 +2599,7 @@ self.assertGreaterEqual(size.lines, 0) @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @unittest.skipUnless(hasattr(os, 'get_terminal_size'), 'need os.get_terminal_size()') def test_stty_match(self): diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py -index 3d25d7e473..3f9859a90f 100644 +index 3d25d7e473..4ea33b64c9 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py -@@ -200,6 +200,7 @@ - +@@ -7,6 +7,7 @@ + import unittest + import test.support + from test import support ++from test.support import has_subprocess_support + from test.support import socket_helper + from test.support import (captured_stderr, TESTFN, EnvironmentVarGuard, + change_cwd) +@@ -200,6 +201,7 @@ + @unittest.skipUnless(site.ENABLE_USER_SITE, "requires access to PEP 370 " "user-site (site.ENABLE_USER_SITE)") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_s_option(self): # (ncoghlan) Change this to use script_helper... usersite = site.USER_SITE -@@ -474,6 +475,7 @@ +@@ -474,6 +476,7 @@ class StartupImportTests(unittest.TestCase): -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_startup_imports(self): # Get sys.path in isolated mode (python3 -I) popen = subprocess.Popen([sys.executable, '-I', '-c', diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 5712b46f7f..769c66e5ac 100755 +index 127d61cb6a..c182b83edc 100755 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py -@@ -1030,6 +1030,12 @@ - with self.assertRaises(OSError, msg=explanation): - socket.gethostbyaddr(addr) - -+ @unittest.skipUnless(socket.has_ipv6, "test needs IPv6 support") -+ def test_host_resolution_ipv6(self): -+ for addr in ['::1q', '::1::2', '1:1:1:1:1:1:1:1:1']: -+ self.assertRaises(OSError, socket.gethostbyname, addr) -+ self.assertRaises(OSError, socket.gethostbyaddr, addr) -+ - @unittest.skipUnless(hasattr(socket, 'sethostname'), "test needs socket.sethostname()") - @unittest.skipUnless(hasattr(socket, 'gethostname'), "test needs socket.gethostname()") - def test_sethostname(self): -@@ -1143,7 +1149,7 @@ +@@ -1,5 +1,6 @@ + import unittest + from test import support ++from test.support import is_apple_mobile + from test.support import socket_helper + + import errno +@@ -1143,7 +1144,7 @@ # 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')): -+ or sys.platform in ('linux', 'darwin', 'ios', 'tvos', 'watchos')): ++ or sys.platform in ('linux', 'darwin') or is_apple_mobile): # avoid the 'echo' service on this platform, as there is an # assumption breaking non-standard port/protocol entry services = ('daytime', 'qotd', 'domain') -@@ -3496,7 +3502,8 @@ +@@ -3496,7 +3497,7 @@ def _testFDPassCMSG_LEN(self): self.createAndSendFDs(1) - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparate(self): -@@ -3507,7 +3514,8 @@ +@@ -3507,7 +3508,7 @@ maxcmsgs=2) @testFDPassSeparate.client_skip - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparate(self): fd0, fd1 = self.newFDs(2) -@@ -3520,7 +3528,8 @@ +@@ -3520,7 +3521,7 @@ array.array("i", [fd1]))]), len(MSG)) - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparateMinSpace(self): -@@ -3534,7 +3543,8 @@ +@@ -3534,7 +3535,7 @@ maxcmsgs=2, ignoreflags=socket.MSG_CTRUNC) @testFDPassSeparateMinSpace.client_skip - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparateMinSpace(self): fd0, fd1 = self.newFDs(2) -@@ -3558,7 +3568,8 @@ +@@ -3558,7 +3559,7 @@ nbytes = self.sendmsgToServer([msg]) self.assertEqual(nbytes, len(msg)) - @unittest.skipIf(sys.platform == "darwin", "see issue #24725") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") def testFDPassEmpty(self): # Try to pass an empty FD array. Can receive either no array # or an empty array. -@@ -4378,28 +4389,38 @@ +@@ -4378,28 +4379,33 @@ pass @requireAttrs(socket.socket, "sendmsg") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class SendmsgUnixStreamTest(SendmsgStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "recvmsg") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class RecvmsgUnixStreamTest(RecvmsgTests, RecvmsgGenericStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "recvmsg_into") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class RecvmsgIntoUnixStreamTest(RecvmsgIntoTests, RecvmsgGenericStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "sendmsg", "recvmsg") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") class RecvmsgSCMRightsStreamTest(SCMRightsTest, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "sendmsg", "recvmsg_into") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") class RecvmsgIntoSCMRightsStreamTest(RecvmsgIntoMixin, SCMRightsTest, SendrecvmsgUnixStreamTestBase): diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py -index c9455adfd8..3ada09dcba 100644 +index c9455adfd8..a06d325d1e 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py -@@ -8,6 +8,7 @@ +@@ -8,13 +8,14 @@ import select import signal import socket @@ -2254,53 +1916,90 @@ index c9455adfd8..3ada09dcba 100644 import tempfile import threading import unittest + import socketserver + + import test.support +-from test.support import reap_children, reap_threads, verbose ++from test.support import reap_children, reap_threads, verbose, has_subprocess_support, is_apple_mobile + from test.support import socket_helper + + @@ -26,7 +27,7 @@ HAVE_UNIX_SOCKETS = hasattr(socket, "AF_UNIX") requires_unix_sockets = unittest.skipUnless(HAVE_UNIX_SOCKETS, 'requires Unix sockets') -HAVE_FORKING = hasattr(os, "fork") -+HAVE_FORKING = hasattr(os, "fork") and os.allows_subprocesses ++HAVE_FORKING = hasattr(os, "fork") and has_subprocess_support requires_forking = unittest.skipUnless(HAVE_FORKING, 'requires forking') def signal_alarm(n): -@@ -194,12 +195,16 @@ +@@ -194,12 +195,14 @@ self.stream_examine) @requires_unix_sockets -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_UnixStreamServer(self): self.run_server(socketserver.UnixStreamServer, socketserver.StreamRequestHandler, self.stream_examine) @requires_unix_sockets -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_ThreadingUnixStreamServer(self): self.run_server(socketserver.ThreadingUnixStreamServer, socketserver.StreamRequestHandler, diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py -index 5ca43461d9..1a66b1d258 100644 +index 5ca43461d9..2841c401a5 100644 --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py +@@ -1,7 +1,7 @@ + # -*- coding: koi8-r -*- + + import unittest +-from test.support import TESTFN, unlink, unload, rmtree, script_helper, captured_stdout ++from test.support import TESTFN, unlink, unload, rmtree, script_helper, captured_stdout, has_subprocess_support + import importlib + import os + import sys +@@ -12,11 +12,11 @@ + + def test_pep263(self): + self.assertEqual( +- "ðÉÔÏÎ".encode("utf-8"), ++ "�����".encode("utf-8"), + b'\xd0\x9f\xd0\xb8\xd1\x82\xd0\xbe\xd0\xbd' + ) + self.assertEqual( +- "\ð".encode("utf-8"), ++ "\�".encode("utf-8"), + b'\\\xd0\x9f' + ) + @@ -63,6 +63,7 @@ # two bytes in common with the UTF-8 BOM self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_20731(self): sub = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py -index f93fbe9264..ef16d62332 100644 +index f93fbe9264..a917f229cf 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py -@@ -42,6 +42,9 @@ +@@ -21,6 +21,7 @@ + import textwrap + import json + import pathlib ++from test.support import has_subprocess_support + from test.support import FakePath + + try: +@@ -42,6 +43,9 @@ mswindows = (sys.platform == "win32") -+if not os.allows_subprocesses: ++if not has_subprocess_support: + raise unittest.SkipTest('Test requires support for subprocesses.') + # @@ -2321,53 +2020,62 @@ index 2accad1aee..4824b6c8d0 100644 import distutils.text_file import distutils.unixccompiler diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py -index ed85d18541..9c3e449272 100644 +index ef32424eab..22a8914261 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py -@@ -108,6 +108,7 @@ - # Python/pythonrun.c::PyErr_PrintEx() is tricky. - - -+ - class SysModuleTest(unittest.TestCase): - - def tearDown(self): -@@ -551,6 +552,7 @@ +@@ -11,6 +11,7 @@ + import sys + import sysconfig + import test.support ++from test.support import has_subprocess_support + import textwrap + import unittest + import warnings +@@ -558,6 +559,7 @@ def test_clear_type_cache(self): sys._clear_type_cache() -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_ioencoding(self): env = dict(os.environ) -@@ -598,6 +600,7 @@ +@@ -605,6 +607,7 @@ 'requires OS support of non-ASCII encodings') @unittest.skipUnless(sys.getfilesystemencoding() == locale.getpreferredencoding(False), 'requires FS encoding to match locale') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_ioencoding_nonascii(self): env = dict(os.environ) -@@ -610,6 +613,7 @@ +@@ -617,6 +620,7 @@ @unittest.skipIf(sys.base_prefix != sys.prefix, 'Test is not venv-compatible') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_executable(self): # sys.executable should be absolute self.assertEqual(os.path.abspath(sys.executable), sys.executable) -@@ -644,6 +648,7 @@ +@@ -651,6 +655,7 @@ expected = None self.check_fsencoding(fs_encoding, expected) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def c_locale_get_error_handler(self, locale, isolated=False, encoding=None): # Force the POSIX locale env = os.environ.copy() diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py -index 0ca5c9390d..32430b41eb 100644 +index 0ca5c9390d..eca8dad7c7 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py +@@ -7,7 +7,7 @@ + + from test.support import (import_module, TESTFN, unlink, check_warnings, + captured_stdout, skip_unless_symlink, change_cwd, +- PythonSymlink) ++ PythonSymlink, has_subprocess_support) + + import sysconfig + from sysconfig import (get_paths, get_platform, get_config_vars, @@ -229,10 +229,12 @@ def test_get_scheme_names(self): @@ -2378,84 +2086,96 @@ index 0ca5c9390d..32430b41eb 100644 self.assertEqual(get_scheme_names(), wanted) @skip_unless_symlink -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_symlink(self): # Issue 7880 with PythonSymlink() as py: cmd = "-c", "import sysconfig; print(sysconfig.get_platform())" diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py -index 8ad1bb98e8..5bd5413dc9 100644 +index 8ad1bb98e8..eedc7fcf21 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py -@@ -19,7 +19,7 @@ +@@ -15,11 +15,12 @@ + + import unittest + from test import support ++from test_support import has_subprocess_support + from test.support import script_helper has_textmode = (tempfile._text_openflags != tempfile._bin_openflags) -has_spawnl = hasattr(os, 'spawnl') -+has_spawnl = hasattr(os, 'spawnl') and os.allows_subprocesses ++has_spawnl = hasattr(os, 'spawnl') and has_subprocess_support # TEST_FILES may need to be tweaked for systems depending on the maximum # number of files that can be opened at one time (see ulimit -n) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py -index af480b9014..79fdf54e72 100644 +index af480b9014..a088feb7e9 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py -@@ -1028,6 +1028,8 @@ +@@ -3,7 +3,7 @@ + """ + + import test.support +-from test.support import verbose, import_module, cpython_only, unlink ++from test.support import verbose, import_module, cpython_only, unlink, is_apple_mobile + from test.support.script_helper import assert_python_ok, assert_python_failure + + import random +@@ -1028,6 +1028,7 @@ os.set_blocking(r, False) return (r, w) -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't have os.pipe" % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't have os.pipe" % sys.platform) def test_threads_join(self): # Non-daemon threads should be joined at subinterpreter shutdown # (issue #18808) -@@ -1056,6 +1058,8 @@ +@@ -1056,6 +1057,7 @@ # The thread was joined properly. self.assertEqual(os.read(r, 1), b"x") -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't have os.pipe" % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't have os.pipe" % sys.platform) def test_threads_join_2(self): # Same as above, but a delay gets introduced after the thread's # Python code returned but before the thread state is deleted. diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py -index c0b3388b2d..00f72730b3 100644 +index c0b3388b2d..68ffd3d669 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py -@@ -5,7 +5,9 @@ - import linecache - import sys +@@ -7,7 +7,7 @@ import unittest -+import os import re -+import subprocess from test import support - from test.support import TESTFN, Error, captured_output, unlink, cpython_only, ALWAYS_EQ +-from test.support import TESTFN, Error, captured_output, unlink, cpython_only, ALWAYS_EQ ++from test.support import TESTFN, Error, captured_output, unlink, cpython_only, ALWAYS_EQ, has_subprocess_support from test.support.script_helper import assert_python_ok -@@ -142,6 +144,7 @@ + import textwrap + +@@ -142,6 +142,7 @@ str_name = '.'.join([X.__module__, X.__qualname__]) self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value)) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_encoded_file(self): # Test that tracebacks are correctly printed for encoded source files: # - correct line number (Issue2384) diff --git a/Lib/test/test_unicodedata.py b/Lib/test/test_unicodedata.py -index edfd860fd5..edbc8ce641 100644 +index edfd860fd5..e3bc801a8f 100644 --- a/Lib/test/test_unicodedata.py +++ b/Lib/test/test_unicodedata.py -@@ -8,6 +8,7 @@ - - import hashlib - from http.client import HTTPException -+import os +@@ -11,7 +11,7 @@ import sys import unicodedata import unittest -@@ -225,6 +226,7 @@ +-from test.support import open_urlresource, requires_resource, script_helper ++from test.support import open_urlresource, requires_resource, script_helper, has_subprocess_support + + + class UnicodeMethodsTest(unittest.TestCase): +@@ -225,6 +225,7 @@ class UnicodeMiscTest(UnicodeDatabaseTest): -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_failed_import_during_compiling(self): # Issue 4367 # Decoding \N escapes requires the unicodedata module. If it can't be @@ -2491,97 +2211,115 @@ index ba4c500e8e..1534b59cef 100644 redirect_url_with_frag = "http://www.pythontest.net/redir/with_frag/" with socket_helper.transient_internet(redirect_url_with_frag): diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py -index b1c92427dd..5141c9dece 100644 +index b1c92427dd..925a1f0354 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py -@@ -639,6 +639,7 @@ +@@ -1,5 +1,6 @@ + import unittest + from test import support ++from test.support import has_subprocess_support + import builtins + import contextlib + import copy +@@ -639,6 +640,7 @@ equal(str(u), v) @unittest.skipUnless(os.name == 'posix', 'requires Posix') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def testIssue8621(self): # On at least some versions of OSX self.uuid.uuid4 generates # the same sequence of UUIDs in the parent and any -@@ -824,6 +825,7 @@ +@@ -824,6 +826,7 @@ @unittest.skipUnless(_uuid._ifconfig_getnode in _uuid._GETTERS, "ifconfig is not used for introspection on this platform") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_ifconfig_getnode(self): node = self.uuid._ifconfig_getnode() self.check_node(node, 'ifconfig') -@@ -836,6 +838,7 @@ +@@ -836,6 +839,7 @@ @unittest.skipUnless(_uuid._arp_getnode in _uuid._GETTERS, "arp is not used for introspection on this platform") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_arp_getnode(self): node = self.uuid._arp_getnode() self.check_node(node, 'arp') -@@ -848,6 +851,7 @@ +@@ -848,6 +852,7 @@ @unittest.skipUnless(_uuid._netstat_getnode in _uuid._GETTERS, "netstat is not used for introspection on this platform") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_netstat_getnode(self): node = self.uuid._netstat_getnode() self.check_node(node, 'netstat') diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py -index 480cb29f35..b1c4d2014f 100644 +index 480cb29f35..1a63f66028 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py -@@ -179,6 +179,7 @@ +@@ -16,8 +16,7 @@ + import tempfile + from test.support import (captured_stdout, captured_stderr, requires_zlib, + can_symlink, EnvironmentVarGuard, rmtree, +- import_module, +- skip_if_broken_multiprocessing_synchronize) ++ skip_if_broken_multiprocessing_synchronize, has_subprocess_support) + import unittest + import venv + from unittest.mock import patch +@@ -179,6 +178,7 @@ builder.upgrade_dependencies(fake_context) @requireVenvCreate -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_prefixes(self): """ Test that the prefix values are as expected. -@@ -316,6 +317,7 @@ +@@ -316,6 +316,7 @@ # point to the venv being used to run the test, and we lose the link # to the source build - so Python can't initialise properly. @requireVenvCreate -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_executable(self): """ Test that the sys.executable value is as expected. -@@ -329,6 +331,7 @@ +@@ -329,6 +330,7 @@ self.assertEqual(out.strip(), envpy.encode()) @unittest.skipUnless(can_symlink(), 'Needs symlinks') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_executable_symlinks(self): """ Test that the sys.executable value is as expected. -@@ -414,6 +417,7 @@ +@@ -414,6 +416,7 @@ @requireVenvCreate class EnsurePipTest(BaseTest): """Test venv module installation of pip.""" -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def assert_pip_not_installed(self): envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe) -@@ -538,6 +542,7 @@ +@@ -538,6 +541,7 @@ # Issue #26610: pip/pep425tags.py requires ctypes @unittest.skipUnless(ctypes, 'pip requires ctypes') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @requires_zlib() def test_with_pip(self): self.do_test_with_pip(False) diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py -index 519a9432ab..63008a88fe 100644 +index 519a9432ab..1a2ff78ca7 100644 --- a/Lib/test/test_webbrowser.py +++ b/Lib/test/test_webbrowser.py -@@ -4,8 +4,11 @@ +@@ -4,8 +4,12 @@ import sys import subprocess from unittest import mock +import sys from test import support ++from test.support import is_apple_mobile -+if sys.platform in ('ios', 'tvos', 'watchos'): ++if is_apple_mobile: + raise unittest.SkipTest("Can't run webbrowser tests on %s" % sys.platform) URL = 'http://www.example.com' @@ -2607,14 +2345,22 @@ index bd383d3f68..f5c830e610 100644 import test packagedir = os.path.dirname(test.__file__) diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py -index 453e6c3d11..7f4ece1ace 100644 +index 453e6c3d11..dd9880f1d8 100644 --- a/Lib/unittest/test/test_runner.py +++ b/Lib/unittest/test/test_runner.py -@@ -1135,6 +1135,7 @@ +@@ -9,6 +9,7 @@ + + from unittest.test.support import (LoggingResult, + ResultWithNoStartTestRunStopTestRun) ++from test.support import has_subprocess_support + + + def resultFactory(*_): +@@ -1135,6 +1136,7 @@ expectedresult = (runner.stream, DESCRIPTIONS, VERBOSITY) self.assertEqual(runner._makeResult(), expectedresult) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_warnings(self): """ Check that warnings argument of TextTestRunner correctly affects the @@ -2681,7 +2427,7 @@ index 6023c1e138..ae5803c4c8 100755 # # Platform support for Windows diff --git a/Makefile.pre.in b/Makefile.pre.in -index 42b1ec622a..5b847a2ccf 100644 +index c0272bfcdd..81e09ad4ec 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -301,6 +301,8 @@ @@ -2750,50 +2496,6 @@ index 16f98ace3b..0d7e276ff2 100644 /* Using an alternative stack requires sigaltstack() and sigaction() SA_ONSTACK */ #if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) -diff --git a/Modules/makesetup b/Modules/makesetup -index 1a767838c9..708c65b25f 100755 ---- a/Modules/makesetup -+++ b/Modules/makesetup -@@ -133,7 +133,7 @@ - - # Output DEFS in reverse order so first definition overrides - case $line in -- *=*) DEFS="$line$NL$DEFS"; continue;; -+ [A-Z]*=*) DEFS="$line$NL$DEFS"; continue;; - 'include '*) DEFS="$line$NL$DEFS"; continue;; - '*noobjects*') - case $noobjects in -@@ -162,9 +162,12 @@ - esac - case $arg in - -framework) libs="$libs $arg"; skip=libs; -- # OSX/OSXS/Darwin framework link cmd -+ # OSX/iOS/Darwin framework - ;; -- -[IDUCfF]*) cpps="$cpps $arg";; -+ -F*) libs="$libs $arg"; skip=libs; -+ # OSX/iOS/Darwin framework directory -+ ;; -+ -[IDUCf]*) cpps="$cpps $arg";; - -Xcompiler) skip=cpps;; - -Xlinker) libs="$libs $arg"; skip=libs;; - -rpath) libs="$libs $arg"; skip=libs;; -@@ -182,6 +185,7 @@ - *.c++) srcs="$srcs $arg";; - *.cxx) srcs="$srcs $arg";; - *.cpp) srcs="$srcs $arg";; -+ *.S) srcs="$srcs $arg";; - \$*) libs="$libs $arg" - cpps="$cpps $arg";; - *.*) echo 1>&2 "bad word $arg in $line" -@@ -219,6 +223,7 @@ - *.C) obj=`basename $src .C`.o; cc='$(CXX)';; - *.cxx) obj=`basename $src .cxx`.o; cc='$(CXX)';; - *.cpp) obj=`basename $src .cpp`.o; cc='$(CXX)';; -+ *.S) obj=`basename $src .S`.o; cc='$(CC)';; # Assembly - *.m) obj=`basename $src .m`.o; cc='$(CC)';; # Obj-C - *) continue;; - esac diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 1f16849a3e..a7b16eadc9 100644 --- a/Modules/mathmodule.c @@ -3128,1621 +2830,6 @@ index baafa3ecfb..496ef9cf58 100644 #endif #define TYPE_NULL '0' ---- /dev/null -+++ b/Tools/iOS-test/app/iOS-test/main.py -@@ -0,0 +1,12 @@ -+from datetime import datetime -+import platform -+from test import regrtest -+ -+regrtest.start = datetime.now() -+print("Testing on %s" % platform.machine()) -+print("START:", regrtest.start) -+regrtest.main_in_temp_cwd() -+regrtest.end = datetime.now() -+print("END:", regrtest.end) -+print("Duration:", regrtest.end - regrtest.start) -+ ---- /dev/null -+++ b/Tools/iOS-test/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test.xcodeproj/project.pbxproj -@@ -0,0 +1,369 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+ 60EAF0931C26F7310003B8F5 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */; }; -+ 60EAF0951C26F73D0003B8F5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 60EAF0941C26F73D0003B8F5 /* libz.tbd */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* iOS-test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS-test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* iOS-test-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iOS-test-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* iOS-test-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "iOS-test-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60DBD4B01B47DEF700068095 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; -+ 60EAF0941C26F73D0003B8F5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60EAF0951C26F73D0003B8F5 /* libz.tbd in Frameworks */, -+ 60EAF0931C26F7310003B8F5 /* libsqlite3.tbd in Frameworks */, -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* iOS-test */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* iOS-test.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60EAF0941C26F73D0003B8F5 /* libz.tbd */, -+ 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* iOS-test */ = { -+ isa = PBXGroup; -+ children = ( -+ 60DBD4B01B47DEF700068095 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = "iOS-test"; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* iOS-test-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* iOS-test-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* iOS-test */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "iOS-test" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = "iOS-test"; -+ productName = "iOS-test"; -+ productReference = 60796EE219190F4100A9926B /* iOS-test.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0720; -+ ORGANIZATIONNAME = "Python Software Foundation"; -+ TargetAttributes = { -+ 60796EE119190F4100A9926B = { -+ DevelopmentTeam = 383DLEZ2K4; -+ }; -+ }; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "iOS-test" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* iOS-test */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ ENABLE_BITCODE = NO; -+ ENABLE_TESTABILITY = YES; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_BITCODE = NO; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ CODE_SIGN_IDENTITY = "iPhone Developer"; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "iOS-test/iOS-test-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "iOS-test/iOS-test-Info.plist"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.$(PRODUCT_NAME:rfc1034identifier)"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ PROVISIONING_PROFILE = ""; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ CODE_SIGN_IDENTITY = "iPhone Developer"; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "iOS-test/iOS-test-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "iOS-test/iOS-test-Info.plist"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.$(PRODUCT_NAME:rfc1034identifier)"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ PROVISIONING_PROFILE = ""; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "iOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "iOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,58 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "3x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/iOS-test-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ $(PRODUCT_BUNDLE_IDENTIFIER) -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/iOS-test-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/main.m -@@ -0,0 +1,149 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *exe; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = Py_DecodeLocale([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ // Since iOS doesn't allow dynamic linking, we have to know -+ // the name of the executable so that we can find the ctypes -+ // test objects. However, sys.argv[0] will be updated to -+ // reflect the script name; the TEST_EXECUTABLE environment -+ // variable provides the mechanism for specifying the filename. -+ exe = [NSString stringWithFormat:@"TEST_EXECUTABLE=%s", argv[0], nil]; -+ putenv((char *)[exe UTF8String]); -+ -+ NSLog(@"Initializing Python runtime..."); -+ Py_Initialize(); -+ -+ /******************************************************* -+ To tell lldb not to stop on signals, use the following commands: -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ *******************************************************/ -+ -+ // Arguments to pass to test runner -+ char *test_args[] = { -+ "-j", "1", -+ "-u", "all,-audio,-curses,-largefile,-subprocess,-gui", -+// "-v", // Verbose test output -+ "-W", // Display test output on failure -+ -+ "-x", // Arguments are tests to *exclude* -+// Simulator failures -+// "test_coroutines", // docstring not being populated -+// "test_module", // docstring not being populated -+ -+// ARM64 failures -+// "test_coroutines", // docstring not being populated -+// "test_ctypes", // DL loading? -+// "test_module" // docstring not being populated -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+// "test_unicode", // encoding problem -+ -+// ARMv7 failures -+// "test_cmath", // math domain error -+// "test_ctypes", // DL loading? -+// "test_float", // rounding? -+// "test_math", // math domain error -+// "test_numeric_tower", // -+// "test_strtod", // -+// "test_importlib", // Thread locking problem -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+ -+// COMMON FAILURES -+ "test_bytes" // HARD CRASH ctypes related; PyBytes_FromFormat -+ -+ }; -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.iOS-test/app/iOS-test/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/iOS-test/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ int n_test_args = sizeof(test_args) / sizeof (*test_args) + 1; -+ -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * n_test_args); -+ python_argv[0] = Py_DecodeLocale(main_script, NULL); -+ for (i = 1; i < n_test_args; i++) { -+ python_argv[i] = Py_DecodeLocale(test_args[i-1], NULL); -+ } -+ -+ PySys_SetArgv(n_test_args, python_argv); -+ -+ // If other modules are using thread, we need to initialize them before. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/app/README -@@ -0,0 +1,3 @@ -+Your application code should be placed in this directory. -+ -+The native code will be looking for a tvOS-test/__main__.py file as the entry point. -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/app/tvOS-test/main.py -@@ -0,0 +1,14 @@ -+from __future__ import print_function -+ -+from datetime import datetime -+import platform -+from test import regrtest -+ -+regrtest.start = datetime.now() -+print("Testing on %s" % platform.machine()) -+print("START:", regrtest.start) -+regrtest.main_in_temp_cwd() -+regrtest.end = datetime.now() -+print("END:", regrtest.end) -+print("Duration:", regrtest.end - regrtest.start) -+ ---- /dev/null -+++ b/Tools/tvOS-test/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test.xcodeproj/project.pbxproj -@@ -0,0 +1,356 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 6023B2AE1C28BA7A006F2562 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6023B2AD1C28BA7A006F2562 /* main.m */; }; -+ 6023B2B71C28BA7A006F2562 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2B51C28BA7A006F2562 /* Main.storyboard */; }; -+ 6023B2B91C28BA7A006F2562 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2B81C28BA7A006F2562 /* Assets.xcassets */; }; -+ 6023B2C71C28BD44006F2562 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */; }; -+ 6023B2C81C28BD44006F2562 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */; }; -+ 6023B2C91C28BD44006F2562 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C31C28BD44006F2562 /* Foundation.framework */; }; -+ 6023B2CA1C28BD44006F2562 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */; }; -+ 6023B2CB1C28BD44006F2562 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C51C28BD44006F2562 /* libz.tbd */; }; -+ 6023B2CC1C28BD44006F2562 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C61C28BD44006F2562 /* UIKit.framework */; }; -+ 6023B2D01C28BDA3006F2562 /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2CE1C28BDA3006F2562 /* Python.framework */; }; -+ 6023B2D31C28BDB7006F2562 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2D11C28BDB7006F2562 /* app */; }; -+ 6023B2D41C28BDB7006F2562 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2D21C28BDB7006F2562 /* app_packages */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 6023B2A91C28BA7A006F2562 /* tvOS-test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "tvOS-test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 6023B2AD1C28BA7A006F2562 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 6023B2B61C28BA7A006F2562 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; -+ 6023B2B81C28BA7A006F2562 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; -+ 6023B2BA1C28BA7A006F2562 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -+ 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 6023B2C31C28BD44006F2562 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; -+ 6023B2C51C28BD44006F2562 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; -+ 6023B2C61C28BD44006F2562 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 6023B2CD1C28BDA3006F2562 /* OpenSSL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OpenSSL.framework; sourceTree = ""; }; -+ 6023B2CE1C28BDA3006F2562 /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 6023B2D11C28BDB7006F2562 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; name = app; path = ../app; sourceTree = ""; }; -+ 6023B2D21C28BDB7006F2562 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; name = app_packages; path = ../app_packages; sourceTree = ""; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 6023B2A61C28BA7A006F2562 /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 6023B2CB1C28BD44006F2562 /* libz.tbd in Frameworks */, -+ 6023B2CA1C28BD44006F2562 /* libsqlite3.tbd in Frameworks */, -+ 6023B2D01C28BDA3006F2562 /* Python.framework in Frameworks */, -+ 6023B2C71C28BD44006F2562 /* CoreFoundation.framework in Frameworks */, -+ 6023B2C81C28BD44006F2562 /* CoreGraphics.framework in Frameworks */, -+ 6023B2C91C28BD44006F2562 /* Foundation.framework in Frameworks */, -+ 6023B2CC1C28BD44006F2562 /* UIKit.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 6023B2A01C28BA7A006F2562 = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2AB1C28BA7A006F2562 /* tvOS-test */, -+ 6023B2C01C28BD23006F2562 /* Frameworks */, -+ 6023B2AA1C28BA7A006F2562 /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 6023B2AA1C28BA7A006F2562 /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2A91C28BA7A006F2562 /* tvOS-test.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 6023B2AB1C28BA7A006F2562 /* tvOS-test */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2D11C28BDB7006F2562 /* app */, -+ 6023B2D21C28BDB7006F2562 /* app_packages */, -+ 6023B2B81C28BA7A006F2562 /* Assets.xcassets */, -+ 6023B2AC1C28BA7A006F2562 /* Supporting Files */, -+ ); -+ path = "tvOS-test"; -+ sourceTree = ""; -+ }; -+ 6023B2AC1C28BA7A006F2562 /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2B51C28BA7A006F2562 /* Main.storyboard */, -+ 6023B2BA1C28BA7A006F2562 /* Info.plist */, -+ 6023B2AD1C28BA7A006F2562 /* main.m */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+ 6023B2C01C28BD23006F2562 /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */, -+ 6023B2C51C28BD44006F2562 /* libz.tbd */, -+ 6023B2CD1C28BDA3006F2562 /* OpenSSL.framework */, -+ 6023B2CE1C28BDA3006F2562 /* Python.framework */, -+ 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */, -+ 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */, -+ 6023B2C31C28BD44006F2562 /* Foundation.framework */, -+ 6023B2C61C28BD44006F2562 /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 6023B2A81C28BA7A006F2562 /* tvOS-test */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 6023B2BD1C28BA7A006F2562 /* Build configuration list for PBXNativeTarget "tvOS-test" */; -+ buildPhases = ( -+ 6023B2D61C28CB97006F2562 /* Refresh Python source */, -+ 6023B2A51C28BA7A006F2562 /* Sources */, -+ 6023B2A61C28BA7A006F2562 /* Frameworks */, -+ 6023B2A71C28BA7A006F2562 /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = "tvOS-test"; -+ productName = "tvOS-test"; -+ productReference = 6023B2A91C28BA7A006F2562 /* tvOS-test.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 6023B2A11C28BA7A006F2562 /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ LastUpgradeCheck = 0720; -+ ORGANIZATIONNAME = "Python Software Foundation"; -+ TargetAttributes = { -+ 6023B2A81C28BA7A006F2562 = { -+ CreatedOnToolsVersion = 7.2; -+ }; -+ }; -+ }; -+ buildConfigurationList = 6023B2A41C28BA7A006F2562 /* Build configuration list for PBXProject "tvOS-test" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ Base, -+ ); -+ mainGroup = 6023B2A01C28BA7A006F2562; -+ productRefGroup = 6023B2AA1C28BA7A006F2562 /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 6023B2A81C28BA7A006F2562 /* tvOS-test */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 6023B2A71C28BA7A006F2562 /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 6023B2D31C28BDB7006F2562 /* app in Resources */, -+ 6023B2B91C28BA7A006F2562 /* Assets.xcassets in Resources */, -+ 6023B2B71C28BA7A006F2562 /* Main.storyboard in Resources */, -+ 6023B2D41C28BDB7006F2562 /* app_packages in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 6023B2D61C28CB97006F2562 /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/org.python.tvOS-test\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/lib\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/include\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/org.python.tvOS-test\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app_packages/\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 6023B2A51C28BA7A006F2562 /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 6023B2AE1C28BA7A006F2562 /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 6023B2B51C28BA7A006F2562 /* Main.storyboard */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 6023B2B61C28BA7A006F2562 /* Base */, -+ ); -+ name = Main.storyboard; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 6023B2BB1C28BA7A006F2562 /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN_UNREACHABLE_CODE = YES; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ COPY_PHASE_STRIP = NO; -+ DEBUG_INFORMATION_FORMAT = dwarf; -+ ENABLE_STRICT_OBJC_MSGSEND = YES; -+ ENABLE_TESTABILITY = YES; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_NO_COMMON_BLOCKS = YES; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ MTL_ENABLE_DEBUG_INFO = YES; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = appletvos; -+ TARGETED_DEVICE_FAMILY = 3; -+ TVOS_DEPLOYMENT_TARGET = 9.1; -+ }; -+ name = Debug; -+ }; -+ 6023B2BC1C28BA7A006F2562 /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN_UNREACHABLE_CODE = YES; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ COPY_PHASE_STRIP = NO; -+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; -+ ENABLE_NS_ASSERTIONS = NO; -+ ENABLE_STRICT_OBJC_MSGSEND = YES; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_NO_COMMON_BLOCKS = YES; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ MTL_ENABLE_DEBUG_INFO = NO; -+ SDKROOT = appletvos; -+ TARGETED_DEVICE_FAMILY = 3; -+ TVOS_DEPLOYMENT_TARGET = 9.1; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 6023B2BE1C28BA7A006F2562 /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ INFOPLIST_FILE = "tvOS-test/Info.plist"; -+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.tvOS-test"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ }; -+ name = Debug; -+ }; -+ 6023B2BF1C28BA7A006F2562 /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ INFOPLIST_FILE = "tvOS-test/Info.plist"; -+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.tvOS-test"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 6023B2A41C28BA7A006F2562 /* Build configuration list for PBXProject "tvOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 6023B2BB1C28BA7A006F2562 /* Debug */, -+ 6023B2BC1C28BA7A006F2562 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 6023B2BD1C28BA7A006F2562 /* Build configuration list for PBXNativeTarget "tvOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 6023B2BE1C28BA7A006F2562 /* Debug */, -+ 6023B2BF1C28BA7A006F2562 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 6023B2A11C28BA7A006F2562 /* Project object */; -+} ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "large.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json -@@ -0,0 +1,17 @@ -+{ -+ "layers" : [ -+ { -+ "filename" : "Front.imagestacklayer" -+ }, -+ { -+ "filename" : "Middle.imagestacklayer" -+ }, -+ { -+ "filename" : "Back.imagestacklayer" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "large.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "large.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "small.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json -@@ -0,0 +1,17 @@ -+{ -+ "layers" : [ -+ { -+ "filename" : "Front.imagestacklayer" -+ }, -+ { -+ "filename" : "Middle.imagestacklayer" -+ }, -+ { -+ "filename" : "Back.imagestacklayer" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "small.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "small.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json -@@ -0,0 +1,26 @@ -+{ -+ "assets" : [ -+ { -+ "size" : "1280x768", -+ "idiom" : "tv", -+ "filename" : "App Icon - Large.imagestack", -+ "role" : "primary-app-icon" -+ }, -+ { -+ "size" : "400x240", -+ "idiom" : "tv", -+ "filename" : "App Icon - Small.imagestack", -+ "role" : "primary-app-icon" -+ }, -+ { -+ "size" : "1920x720", -+ "idiom" : "tv", -+ "filename" : "Top Shelf Image.imageset", -+ "role" : "top-shelf-image" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "shelf.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,16 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "landscape", -+ "idiom" : "tv", -+ "filename" : "launch.png", -+ "extent" : "full-screen", -+ "minimum-system-version" : "9.0", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Base.lproj/Main.storyboard -@@ -0,0 +1,25 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Info.plist -@@ -0,0 +1,32 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleExecutable -+ $(EXECUTABLE_NAME) -+ CFBundleIdentifier -+ $(PRODUCT_BUNDLE_IDENTIFIER) -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ $(PRODUCT_NAME) -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1 -+ LSRequiresIPhoneOS -+ -+ UIMainStoryboardFile -+ Main -+ UIRequiredDeviceCapabilities -+ -+ arm64 -+ -+ -+ ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/main.m -@@ -0,0 +1,149 @@ -+// -+// main.m -+// A main module for starting Python projects under tvOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *exe; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = Py_DecodeLocale([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // tvOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ // Since tvOS doesn't allow dynamic linking, we have to know -+ // the name of the executable so that we can find the ctypes -+ // test objects. However, sys.argv[0] will be updated to -+ // reflect the script name; the TEST_EXECUTABLE environment -+ // variable provides the mechanism for specifying the filename. -+ exe = [NSString stringWithFormat:@"TEST_EXECUTABLE=%s", argv[0], nil]; -+ putenv((char *)[exe UTF8String]); -+ -+ NSLog(@"Initializing Python runtime..."); -+ Py_Initialize(); -+ -+ /******************************************************* -+ To tell lldb not to stop on signals, use the following commands: -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ *******************************************************/ -+ -+ // Arguments to pass to test runner -+ char *test_args[] = { -+ "-j", "1", -+ "-u", "all,-audio,-curses,-largefile,-subprocess,-gui", -+// "-v", // Verbose test output -+ "-W", // Display test output on failure -+ -+ "-x", // Arguments are tests to *exclude* -+// Simulator failures -+// "test_coroutines", // docstring not being populated -+// "test_module", // docstring not being populated -+ -+// ARM64 failures -+// "test_coroutines", // docstring not being populated -+// "test_ctypes", // DL loading? -+// "test_module" // docstring not being populated -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+// "test_unicode", // encoding problem -+ -+// ARMv7 failures -+// "test_cmath", // math domain error -+// "test_ctypes", // DL loading? -+// "test_float", // rounding? -+// "test_math", // math domain error -+// "test_numeric_tower", // -+// "test_strtod", // -+// "test_importlib", // Thread locking problem -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+ -+// COMMON FAILURES -+ "test_bytes" // HARD CRASH ctypes related; PyBytes_FromFormat -+ -+ }; -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.tvOS-test/app/tvOS-test/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/tvOS-test/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ int n_test_args = sizeof(test_args) / sizeof (*test_args) + 1; -+ -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * n_test_args); -+ python_argv[0] = Py_DecodeLocale(main_script, NULL); -+ for (i = 1; i < n_test_args; i++) { -+ python_argv[i] = Py_DecodeLocale(test_args[i-1], NULL); -+ } -+ -+ PySys_SetArgv(n_test_args, python_argv); -+ -+ // If other modules are using thread, we need to initialize them before. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} diff --git a/aclocal.m4 b/aclocal.m4 index e5e804276f..52102fe4bb 100644 --- a/aclocal.m4 @@ -5341,864 +3428,6 @@ index aa515da465..81f956f3d2 100644 # Check for use of the system libmpdec library AC_MSG_CHECKING(for --with-system-libmpdec) ---- /dev/null -+++ b/iOS/Info.plist -@@ -0,0 +1,20 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ English -+ CFBundleExecutable -+ Python -+ CFBundleIdentifier -+ org.python -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ FMWK -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ xxxVERSIONxxx -+ -+ ---- /dev/null -+++ b/iOS/README -@@ -0,0 +1,165 @@ -+==================== -+Python on iOS README -+==================== -+ -+:Authors: -+ Russell Keith-Magee (2015) -+ -+:Version: 3.5.2 -+ -+This document provides a overview of eccentricities of building and using -+Python on iOS. -+ -+Build instructions -+================== -+ -+The iOS build must be run on an Mac with XCode installed. To build the iOS -+framework, unpack the Python sources, move into the iOS subdirectory, and -+run ``make``. There are no configuration options to this build process - -+it will use XCode utilities to identify the location of compilers, -+resource directories, and so on. -+ -+The build process will configure and build Python 6 times, producing: -+ -+ * A "host" version of Python -+ * A version of Python compiled for the x86-64 iOS Simulator -+ * A version of Python compiled for the i386 iOS Simulator -+ * A version of Python compiled for ARM64 iOS devices -+ * A version of Python compiled for ARMv7s iOS devices -+ * A version of Python compiled for ARMv7 iOS devices -+ -+Build products will be "installed" into iOS/build. The built products will -+then be combined into a single "fat" ``Python.framework`` that can be added to -+an XCode project. The resulting framework will be located in the root -+directory of the Python source tree. -+ -+A ``make clean`` target also exists to clean out all build products; -+``make distclean`` will clean out all user-specific files from the test and -+sample projects. -+ -+Test instructions -+----------------- -+ -+The ``Tools`` directory contains an ``iOS-Test`` project that enables you to -+run the Python regression test suite. When you run ``make`` in the iOS -+directory, a copy of ``Python.framework`` will also be installed into this -+test project. -+ -+To run the test project, load the project into XCode, and run (either on a -+device or in the simulator). The test suite takes around 10 minutes to run on -+an iPhone6S. -+ -+.. note:: If you run the test project in debug mode, the XCode debugger will -+ stop whenever a signal is raised. The Python regression test suite checks -+ a number of signal handlers, and the test suite will stop mid-execution -+ when this occurs. -+ -+ To disable this signal handling, set a breakpoint at the start of -+ ``main.c``; when execution stops at the breakpoint, run the following -+ commands in the debugger (at the ``(lldb)`` prompt in the console log -+ window):: -+ -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ -+ Unfortunately, this has to be done every time the test suite is executed. -+ -+iOS-specific details -+==================== -+ -+* ``import sys; sys.platform`` will report as `'ios'`, regardless of whether you -+ are on a simulator or a real platform. -+ -+* ``import platform; platform.machine()`` will return the device identifier. -+ For example, an iPhone 5S will return `'iPhone6,2'` -+ -+* The following modules are not currently supported: -+ -+ - ``bsddb`` -+ - ``bz2`` -+ - ``curses`` -+ - ``dbm`` -+ - ``gdbm`` -+ - ``hotshot`` -+ - ``idlelib`` -+ - ``lzma`` -+ - ``nis`` -+ - ``ossaudiodev`` -+ - ``readline`` -+ - ``spwd`` -+ - ``sqlite3`` -+ - ``ssl`` -+ - ``tkinter`` -+ - ``turtledemo`` -+ - ``wsgiref`` -+ -+* Due to limitations in using dynamic loading on iOS, binary Python modules must be -+ statically-linked into the executable. The framework package produced by the iOS -+ ``make install`` statically links all the supported standard library modules. -+ If you have a third-party Python binary module, you'll need to incorporate the -+ source files for that module into the sources for your own app. -+ -+ If you want to add or remove a binary module from the set that is included in the -+ Python library, you can do so by providing module setup files for each platform. -+ There are three default module configuration files: -+ -+ - ``Modules/Setup.iOS-aarch64`` for ARM64 iOS builds -+ - ``Modules/Setup.iOS-arm`` for ARMv7 iOS builds -+ - ``Modules/Setup.iOS-x86_64`` for x86_64 iOS simulator builds -+ -+ If you copy these files to a ``.local`` version (e.g., -+ ``Modules/Setup.iOS-aarch64.local``), the local version will override the -+ default. You can then make modifications to the modules that will be included -+ in the iOS framework, and the flags passed to the compiler when compiling those -+ modules. -+ -+Adding Python to an iOS project -+=============================== -+ -+The iOS subdirectory contains a sample XCode 6.1 project to demonstrate how -+Python can be added to an iOS project. After building the Python iOS framework, -+copy it into the ``iOS/XCode-sample`` directory. You should end up with a directory -+structure that looks like this:: -+ -+ XCode-sample/ -+ Python.framework/ - Manually copied into the project -+ app/ -+ sample/ -+ __init__.py -+ main.py - The Python script to be executed -+ app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime -+ sample -+ Images.xcassets -+ en.lproj -+ main.c - The main() definition for the iOS application -+ sample-Info.plist -+ sample-Prefix.pch -+ sample.xcodeproj - The XCode project file -+ -+If you open the project file is project and run it, you should get output -+similar to the following:: -+ -+ 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app -+ 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime -+ 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py -+ Hello, World. -+ 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving -+ -+You can now modify the provide Python source code, import and use -+code from the Python standard library, and add third-party modules to -+app_packages. -+ -+The sample app is a console-only app, so it isn't of any real practical use. -+Python can be embedded into any Objective-C project using the normal Python -+APIs for embedding; but if you want to write a full iOS app in Python, or -+you want to access iOS services from within embedded code, you'll need to -+bridge between the Objective-C environment and the Python environment. -+This binding isn't something that Python does out of the box; you'll need -+to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. -+ -+.. _Rubicon ObjC: http://pybee.org/rubicon -+.. _Pyobjus: http://pyobjus.readthedocs.org/ -+.. _PyObjC: https://pythonhosted.org/pyobjc/ ---- /dev/null -+++ b/iOS/XCode-sample/app/sample/main.py -@@ -0,0 +1,3 @@ -+ -+if __name__ == '__main__': -+ print("Hello, World.") ---- /dev/null -+++ b/iOS/XCode-sample/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample.xcodeproj/project.pbxproj -@@ -0,0 +1,353 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* sample */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* sample.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */, -+ 60796F1819190FBB00A9926B /* libz.dylib */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* sample */ = { -+ isa = PBXGroup; -+ children = ( -+ 60F0BABD191FC83F006EC268 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = sample; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* sample-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* sample */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = sample; -+ productName = sample; -+ productReference = 60796EE219190F4100A9926B /* sample.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0630; -+ ORGANIZATIONNAME = "Example Corp"; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* sample */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/iOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,53 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/iOS/XCode-sample/sample/main.m -@@ -0,0 +1,111 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = _Py_char2wchar([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ NSLog(@"Initializing Python runtime"); -+ Py_Initialize(); -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/sample/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc); -+ -+ python_argv[0] = _Py_char2wchar(main_script, NULL); -+ for (i = 1; i < argc; i++) { -+ python_argv[i] = _Py_char2wchar(argv[i], NULL); -+ } -+ -+ PySys_SetArgv(argc, python_argv); -+ -+ // If other modules are using threads, we need to initialize them. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } else { -+ // In a normal iOS application, the following line is what -+ // actually runs the application. It requires that the -+ // Objective-C runtime environment has a class named -+ // "PythonAppDelegate". This project doesn't define -+ // one, because Objective-C bridging isn't something -+ // Python does out of the box. You'll need to use -+ // a library like Rubicon-ObjC [1], Pyobjus [2] or -+ // PyObjC [3] if you want to run an *actual* iOS app. -+ // [1] http://pybee.org/rubicon -+ // [2] http://pyobjus.readthedocs.org/ -+ // [3] https://pythonhosted.org/pyobjc/ -+ -+ UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/sample-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ com.example.${PRODUCT_NAME:rfc1034identifier} -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/sample-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file diff --git a/setup.py b/setup.py index 0bec170d3f..4e64c30d26 100644 --- a/setup.py @@ -6253,1711 +3482,3 @@ index 0bec170d3f..4e64c30d26 100644 if sysconfig.get_config_var('HAVE_LIBDL'): # for dlopen, see bpo-32647 ---- /dev/null -+++ b/tvOS/Info.plist -@@ -0,0 +1,20 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ English -+ CFBundleExecutable -+ Python -+ CFBundleIdentifier -+ org.python -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ FMWK -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ xxxVERSIONxxx -+ -+ ---- /dev/null -+++ b/tvOS/README -@@ -0,0 +1,161 @@ -+===================== -+Python on tvOS README -+===================== -+ -+:Authors: -+ Russell Keith-Magee (2015) -+ -+:Version: 3.5.2 -+ -+This document provides a overview of eccentricities of building and using -+Python on tvOS devices (i.e., AppleTV). -+ -+Build instructions -+================== -+ -+The tvOS build must be run on an Mac with XCode installed. To build the tvOS -+framework, unpack the Python sources, move into the tvOS subdirectory, and -+run ``make``. There are no configuration options to this build process - -+it will use XCode utilities to identify the location of compilers, -+resource directories, and so on. -+ -+The build process will configure and build Python 3 times, producing: -+ -+ * A "host" version of Python -+ * A version of Python compiled for the x86-64 tvOS Simulator -+ * A version of Python compiled for ARM64 tvOS devices -+ -+Build products will be "installed" into tvOS/build. The built products will -+then be combined into a single "fat" ``Python.framework`` that can be added to -+an XCode project. The resulting framework will be located in the root -+directory of the Python source tree. -+ -+A ``make clean`` target also exists to clean out all build products; -+``make distclean`` will clean out all user-specific files from the test and -+sample projects. -+ -+Test instructions -+----------------- -+ -+The ``Tools`` directory contains an ``tvOS-Test`` project that enables you to -+run the Python regression test suite. When you run ``make`` in the tvOS -+directory, a copy of ``Python.framework`` will also be installed into this -+test project. -+ -+To run the test project, load the project into XCode, and run (either on a -+device or in the simulator). The test suite takes around 10 minutes to run on -+an gen 4 AppleTV. -+ -+.. note:: If you run the test project in debug mode, the XCode debugger will -+ stop whenever a signal is raised. The Python regression test suite checks -+ a number of signal handlers, and the test suite will stop mid-execution -+ when this occurs. -+ -+ To disable this signal handling, set a breakpoint at the start of -+ ``main.c``; when execution stops at the breakpoint, run the following -+ commands in the debugger (at the ``(lldb)`` prompt in the console log -+ window):: -+ -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ -+ Unfortunately, this has to be done every time the test suite is executed. -+ -+tvOS-specific details -+==================== -+ -+* ``import sys; sys.platform`` will report as `'tvos'`, regardless of whether you -+ are on a simulator or a real platform. -+ -+* ``import platform; platform.machine()`` will return the device identifier. -+ For example, a Generation 4 Apple TV will return `'AppleTV5,3'` -+ -+* The following modules are not currently supported: -+ -+ - ``bsddb`` -+ - ``bz2`` -+ - ``curses`` -+ - ``dbm`` -+ - ``gdbm`` -+ - ``hotshot`` -+ - ``idlelib`` -+ - ``lzma`` -+ - ``nis`` -+ - ``ossaudiodev`` -+ - ``readline`` -+ - ``spwd`` -+ - ``sqlite3`` -+ - ``ssl`` -+ - ``tkinter`` -+ - ``turtledemo`` -+ - ``wsgiref`` -+ -+* Due to limitations in using dynamic loading on tvOS, binary Python modules must be -+ statically-linked into the executable. The framework package produced by the tvOS -+ ``make install`` statically links all the supported standard library modules. -+ If you have a third-party Python binary module, you'll need to incorporate the -+ source files for that module into the sources for your own app. -+ -+ If you want to add or remove a binary module from the set that is included in the -+ Python library, you can do so by providing module setup files for each platform. -+ There are two default module configuration files: -+ -+ - ``Modules/Setup.tvOS-aarch64`` for ARM64 tvOS builds -+ - ``Modules/Setup.tvOS-x86_64`` for x86_64 tvOS simulator builds -+ -+ If you copy these files to a ``.local`` version (e.g., -+ ``Modules/Setup.tvOS-aarch64.local``), the local version will override the -+ default. You can then make modifications to the modules that will be included -+ in the tvOS framework, and the flags passed to the compiler when compiling those -+ modules. -+ -+Adding Python to an tvOS project -+=============================== -+ -+The tvOS subdirectory contains a sample XCode 6.1 project to demonstrate how -+Python can be added to an tvOS project. After building the Python tvOS framework, -+copy it into the ``tvOS/XCode-sample`` directory. You should end up with a directory -+structure that looks like this:: -+ -+ XCode-sample/ -+ Python.framework/ - Manually copied into the project -+ app/ -+ sample/ -+ __init__.py -+ main.py - The Python script to be executed -+ app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime -+ sample -+ Images.xcassets -+ en.lproj -+ main.c - The main() definition for the tvOS application -+ sample-Info.plist -+ sample-Prefix.pch -+ sample.xcodeproj - The XCode project file -+ -+If you open the project file is project and run it, you should get output -+similar to the following:: -+ -+ 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app -+ 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime -+ 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py -+ Hello, World. -+ 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving -+ -+You can now modify the provide Python source code, import and use -+code from the Python standard library, and add third-party modules to -+app_packages. -+ -+The sample app is a console-only app, so it isn't of any real practical use. -+Python can be embedded into any Objective-C project using the normal Python -+APIs for embedding; but if you want to write a full tvOS app in Python, or -+you want to access tvOS services from within embedded code, you'll need to -+bridge between the Objective-C environment and the Python environment. -+This binding isn't something that Python does out of the box; you'll need -+to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. -+ -+.. _Rubicon ObjC: http://pybee.org/rubicon -+.. _Pyobjus: http://pyobjus.readthedocs.org/ -+.. _PyObjC: https://pythonhosted.org/pyobjc/ ---- /dev/null -+++ b/tvOS/XCode-sample/app/sample/main.py -@@ -0,0 +1,3 @@ -+ -+if __name__ == '__main__': -+ print("Hello, World.") ---- /dev/null -+++ b/tvOS/XCode-sample/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample.xcodeproj/project.pbxproj -@@ -0,0 +1,353 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* sample */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* sample.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */, -+ 60796F1819190FBB00A9926B /* libz.dylib */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* sample */ = { -+ isa = PBXGroup; -+ children = ( -+ 60F0BABD191FC83F006EC268 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = sample; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* sample-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* sample */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = sample; -+ productName = sample; -+ productReference = 60796EE219190F4100A9926B /* sample.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0630; -+ ORGANIZATIONNAME = "Example Corp"; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* sample */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/tvOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,53 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/tvOS/XCode-sample/sample/main.m -@@ -0,0 +1,111 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = _Py_char2wchar([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ NSLog(@"Initializing Python runtime"); -+ Py_Initialize(); -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/sample/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc); -+ -+ python_argv[0] = _Py_char2wchar(main_script, NULL); -+ for (i = 1; i < argc; i++) { -+ python_argv[i] = _Py_char2wchar(argv[i], NULL); -+ } -+ -+ PySys_SetArgv(argc, python_argv); -+ -+ // If other modules are using threads, we need to initialize them. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } else { -+ // In a normal iOS application, the following line is what -+ // actually runs the application. It requires that the -+ // Objective-C runtime environment has a class named -+ // "PythonAppDelegate". This project doesn't define -+ // one, because Objective-C bridging isn't something -+ // Python does out of the box. You'll need to use -+ // a library like Rubicon-ObjC [1], Pyobjus [2] or -+ // PyObjC [3] if you want to run an *actual* iOS app. -+ // [1] http://pybee.org/rubicon -+ // [2] http://pyobjus.readthedocs.org/ -+ // [3] https://pythonhosted.org/pyobjc/ -+ -+ UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/sample-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ com.example.${PRODUCT_NAME:rfc1034identifier} -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/sample-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file ---- /dev/null -+++ b/watchOS/Info.plist -@@ -0,0 +1,20 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ English -+ CFBundleExecutable -+ Python -+ CFBundleIdentifier -+ org.python -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ FMWK -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ xxxVERSIONxxx -+ -+ ---- /dev/null -+++ b/watchOS/README -@@ -0,0 +1,161 @@ -+======================== -+Python on watchOS README -+======================== -+ -+:Authors: -+ Russell Keith-Magee (2015) -+ -+:Version: 3.5.2 -+ -+This document provides a overview of eccentricities of building and using -+Python on watchOS devices (i.e., AppleTV). -+ -+Build instructions -+================== -+ -+The watchOS build must be run on an Mac with XCode installed. To build the watchOS -+framework, unpack the Python sources, move into the watchOS subdirectory, and -+run ``make``. There are no configuration options to this build process - -+it will use XCode utilities to identify the location of compilers, -+resource directories, and so on. -+ -+The build process will configure and build Python 3 times, producing: -+ -+ * A "host" version of Python -+ * A version of Python compiled for the i386 watchOS Simulator -+ * A version of Python compiled for ARMv7k watchOS devices -+ -+Build products will be "installed" into watchOS/build. The built products will -+then be combined into a single "fat" ``Python.framework`` that can be added to -+an XCode project. The resulting framework will be located in the root -+directory of the Python source tree. -+ -+A ``make clean`` target also exists to clean out all build products; -+``make distclean`` will clean out all user-specific files from the test and -+sample projects. -+ -+Test instructions -+----------------- -+ -+The ``Tools`` directory contains an ``watchOS-Test`` project that enables you to -+run the Python regression test suite. When you run ``make`` in the watchOS -+directory, a copy of ``Python.framework`` will also be installed into this -+test project. -+ -+To run the test project, load the project into XCode, and run (either on a -+device or in the simulator). The test suite takes around 10 minutes to run on -+an Apple Watch. -+ -+.. note:: If you run the test project in debug mode, the XCode debugger will -+ stop whenever a signal is raised. The Python regression test suite checks -+ a number of signal handlers, and the test suite will stop mid-execution -+ when this occurs. -+ -+ To disable this signal handling, set a breakpoint at the start of -+ ``main.c``; when execution stops at the breakpoint, run the following -+ commands in the debugger (at the ``(lldb)`` prompt in the console log -+ window):: -+ -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ -+ Unfortunately, this has to be done every time the test suite is executed. -+ -+watchOS-specific details -+==================== -+ -+* ``import sys; sys.platform`` will report as `'watchos'`, regardless of whether you -+ are on a simulator or a real platform. -+ -+* ``import platform; platform.machine()`` will return the device identifier. -+ For example, an origianl Apple Watch will return `'Watch1,1'` -+ -+* The following modules are not currently supported: -+ -+ - ``bsddb`` -+ - ``bz2`` -+ - ``curses`` -+ - ``dbm`` -+ - ``gdbm`` -+ - ``hotshot`` -+ - ``idlelib`` -+ - ``lzma`` -+ - ``nis`` -+ - ``ossaudiodev`` -+ - ``readline`` -+ - ``spwd`` -+ - ``sqlite3`` -+ - ``ssl`` -+ - ``tkinter`` -+ - ``turtledemo`` -+ - ``wsgiref`` -+ -+* Due to limitations in using dynamic loading on watchOS, binary Python modules must be -+ statically-linked into the executable. The framework package produced by the watchOS -+ ``make install`` statically links all the supported standard library modules. -+ If you have a third-party Python binary module, you'll need to incorporate the -+ source files for that module into the sources for your own app. -+ -+ If you want to add or remove a binary module from the set that is included in the -+ Python library, you can do so by providing module setup files for each platform. -+ There are two default module configuration files: -+ -+ - ``Modules/Setup.watchOS-aarch64`` for ARM64 watchOS builds -+ - ``Modules/Setup.watchOS-x86_64`` for x86_64 watchOS simulator builds -+ -+ If you copy these files to a ``.local`` version (e.g., -+ ``Modules/Setup.watchOS-aarch64.local``), the local version will override the -+ default. You can then make modifications to the modules that will be included -+ in the watchOS framework, and the flags passed to the compiler when compiling those -+ modules. -+ -+Adding Python to an watchOS project -+=============================== -+ -+The watchOS subdirectory contains a sample XCode 6.1 project to demonstrate how -+Python can be added to an watchOS project. After building the Python watchOS framework, -+copy it into the ``watchOS/XCode-sample`` directory. You should end up with a directory -+structure that looks like this:: -+ -+ XCode-sample/ -+ Python.framework/ - Manually copied into the project -+ app/ -+ sample/ -+ __init__.py -+ main.py - The Python script to be executed -+ app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime -+ sample -+ Images.xcassets -+ en.lproj -+ main.c - The main() definition for the watchOS application -+ sample-Info.plist -+ sample-Prefix.pch -+ sample.xcodeproj - The XCode project file -+ -+If you open the project file is project and run it, you should get output -+similar to the following:: -+ -+ 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app -+ 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime -+ 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py -+ Hello, World. -+ 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving -+ -+You can now modify the provide Python source code, import and use -+code from the Python standard library, and add third-party modules to -+app_packages. -+ -+The sample app is a console-only app, so it isn't of any real practical use. -+Python can be embedded into any Objective-C project using the normal Python -+APIs for embedding; but if you want to write a full watchOS app in Python, or -+you want to access watchOS services from within embedded code, you'll need to -+bridge between the Objective-C environment and the Python environment. -+This binding isn't something that Python does out of the box; you'll need -+to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. -+ -+.. _Rubicon ObjC: http://pybee.org/rubicon -+.. _Pyobjus: http://pyobjus.readthedocs.org/ -+.. _PyObjC: https://pythonhosted.org/pyobjc/ ---- /dev/null -+++ b/watchOS/XCode-sample/app/sample/main.py -@@ -0,0 +1,3 @@ -+ -+if __name__ == '__main__': -+ print("Hello, World.") ---- /dev/null -+++ b/watchOS/XCode-sample/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample.xcodeproj/project.pbxproj -@@ -0,0 +1,353 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* sample */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* sample.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */, -+ 60796F1819190FBB00A9926B /* libz.dylib */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* sample */ = { -+ isa = PBXGroup; -+ children = ( -+ 60F0BABD191FC83F006EC268 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = sample; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* sample-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* sample */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = sample; -+ productName = sample; -+ productReference = 60796EE219190F4100A9926B /* sample.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0630; -+ ORGANIZATIONNAME = "Example Corp"; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* sample */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/watchOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,53 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/watchOS/XCode-sample/sample/main.m -@@ -0,0 +1,111 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *python_home; -+ char *wpython_home; -+ const char* main_script; -+ char** python_argv; -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = strdup([python_home UTF8String]); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ NSLog(@"Initializing Python runtime"); -+ Py_Initialize(); -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/sample/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ python_argv = PyMem_Malloc(sizeof(char *) * argc); -+ -+ -+ python_argv[0] = strdup(main_script); -+ for (i = 1; i < argc; i++) { -+ python_argv[i] = argv[i]; -+ } -+ -+ PySys_SetArgv(argc, python_argv); -+ -+ // If other modules are using threads, we need to initialize them. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } else { -+ // In a normal iOS application, the following line is what -+ // actually runs the application. It requires that the -+ // Objective-C runtime environment has a class named -+ // "PythonAppDelegate". This project doesn't define -+ // one, because Objective-C bridging isn't something -+ // Python does out of the box. You'll need to use -+ // a library like Rubicon-ObjC [1], Pyobjus [2] or -+ // PyObjC [3] if you want to run an *actual* iOS app. -+ // [1] http://pybee.org/rubicon -+ // [2] http://pyobjus.readthedocs.org/ -+ // [3] https://pythonhosted.org/pyobjc/ -+ -+ UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_Free(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_Free(python_argv[i]); -+ } -+ PyMem_Free(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/sample-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ com.example.${PRODUCT_NAME:rfc1034identifier} -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/sample-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file From df4921b4b386115741da00dd30a5522f70ddbe95 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 7 Sep 2022 13:11:17 +0800 Subject: [PATCH 32/37] Remove the testbed project (now maintained as a standalone project) --- tests/testbed/README.rst | 13 - tests/testbed/pyproject.toml | 40 --- tests/testbed/src/testbed/__init__.py | 0 tests/testbed/src/testbed/__main__.py | 4 - tests/testbed/src/testbed/app.py | 60 ---- tests/testbed/src/testbed/common.py | 340 ------------------ tests/testbed/src/testbed/darwin.py | 68 ---- tests/testbed/src/testbed/ios.py | 23 -- .../testbed/src/testbed/resources/__init__.py | 0 .../src/testbed/resources/testbed-1024.png | Bin 198231 -> 0 bytes .../src/testbed/resources/testbed-120.png | Bin 13453 -> 0 bytes .../src/testbed/resources/testbed-128.png | Bin 14514 -> 0 bytes .../src/testbed/resources/testbed-152.png | Bin 17874 -> 0 bytes .../src/testbed/resources/testbed-16.png | Bin 1283 -> 0 bytes .../src/testbed/resources/testbed-167.png | Bin 20060 -> 0 bytes .../src/testbed/resources/testbed-180.png | Bin 22053 -> 0 bytes .../src/testbed/resources/testbed-20.png | Bin 1569 -> 0 bytes .../src/testbed/resources/testbed-256.png | Bin 33111 -> 0 bytes .../src/testbed/resources/testbed-29.png | Bin 2342 -> 0 bytes .../src/testbed/resources/testbed-32.png | Bin 2638 -> 0 bytes .../src/testbed/resources/testbed-40.png | Bin 3458 -> 0 bytes .../src/testbed/resources/testbed-512.png | Bin 74433 -> 0 bytes .../src/testbed/resources/testbed-58.png | Bin 5517 -> 0 bytes .../src/testbed/resources/testbed-60.png | Bin 5904 -> 0 bytes .../src/testbed/resources/testbed-64.png | Bin 6229 -> 0 bytes .../src/testbed/resources/testbed-76.png | Bin 7677 -> 0 bytes .../src/testbed/resources/testbed-80.png | Bin 8234 -> 0 bytes .../src/testbed/resources/testbed-87.png | Bin 8826 -> 0 bytes tests/testbed/src/testbed/utils.py | 8 - 29 files changed, 556 deletions(-) delete mode 100644 tests/testbed/README.rst delete mode 100644 tests/testbed/pyproject.toml delete mode 100644 tests/testbed/src/testbed/__init__.py delete mode 100644 tests/testbed/src/testbed/__main__.py delete mode 100644 tests/testbed/src/testbed/app.py delete mode 100644 tests/testbed/src/testbed/common.py delete mode 100644 tests/testbed/src/testbed/darwin.py delete mode 100644 tests/testbed/src/testbed/ios.py delete mode 100644 tests/testbed/src/testbed/resources/__init__.py delete mode 100644 tests/testbed/src/testbed/resources/testbed-1024.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-120.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-128.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-152.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-16.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-167.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-180.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-20.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-256.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-29.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-32.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-40.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-512.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-58.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-60.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-64.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-76.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-80.png delete mode 100644 tests/testbed/src/testbed/resources/testbed-87.png delete mode 100644 tests/testbed/src/testbed/utils.py diff --git a/tests/testbed/README.rst b/tests/testbed/README.rst deleted file mode 100644 index 077b3dc6..00000000 --- a/tests/testbed/README.rst +++ /dev/null @@ -1,13 +0,0 @@ -Python Apple Support Testbed -============================ - -This is a testbed application that can be used to do basic verification checks -of the Python Apple Support builds. - -The app can be deployed with Briefcase. When executed, (using `briefcase run -macOS Xcode` or `briefcase run iOS`) the app will generate output on the console -log that is similar to a unit test suite. If it returns 0 test failures, you can -have some confidence that the support build is functioning as expected. - -The default configuration assumes that you have already run `make` in the root -directory of this repository. diff --git a/tests/testbed/pyproject.toml b/tests/testbed/pyproject.toml deleted file mode 100644 index 266a88d8..00000000 --- a/tests/testbed/pyproject.toml +++ /dev/null @@ -1,40 +0,0 @@ -[tool.briefcase] -project_name = "Testbed" -bundle = "org.beeware" -version = "0.0.1" -url = "https://beeware.org" -license = "BSD license" -author = 'Russell Keith-Magee' -author_email = "russell@beeware.org" - -[tool.briefcase.app.testbed] -formal_name = "Testbed" -description = "A testbed for the Apple Support packages." -icon = "src/testbed/resources/testbed" -sources = ['src/testbed'] -requires = [ -] - -[tool.briefcase.app.testbed.macOS] -requires = [ - "rubicon-objc", - "std-nslog", -] -support_package = "../../dist/Python-3.11-macOS-support.custom.tar.gz" - -[tool.briefcase.app.testbed.linux] -supported = false - -[tool.briefcase.app.testbed.windows] -supported = false - -# Mobile deployments -[tool.briefcase.app.testbed.iOS] -requires = [ - "rubicon-objc", - "std-nslog", -] -support_package = "../../dist/Python-3.11-iOS-support.custom.tar.gz" - -[tool.briefcase.app.testbed.android] -supported = false diff --git a/tests/testbed/src/testbed/__init__.py b/tests/testbed/src/testbed/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/testbed/src/testbed/__main__.py b/tests/testbed/src/testbed/__main__.py deleted file mode 100644 index 00c3da21..00000000 --- a/tests/testbed/src/testbed/__main__.py +++ /dev/null @@ -1,4 +0,0 @@ -from testbed.app import main - -if __name__ == "__main__": - main() diff --git a/tests/testbed/src/testbed/app.py b/tests/testbed/src/testbed/app.py deleted file mode 100644 index 0f641091..00000000 --- a/tests/testbed/src/testbed/app.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -A testbed for the Apple Support packages. -""" -import importlib -import platform -import sys -import traceback - -from . import common - - -def discover_tests(module): - "Discover all the test methods in the given module" - return [ - (getattr(module, "__name__").split(".")[-1], getattr(module, name)) - for name in dir(module) - if name.startswith("test_") - ] - - -def main(): - # This should start and launch your app! - print("=" * 80) - print(f"Python {platform.python_version()} Apple Support verification suite") - print(f"Running on {platform.platform()}") - print("=" * 80) - # Discover the common test suite - suite = discover_tests(common) - - # Discover the platform-specific tests - try: - module = importlib.import_module(f".{sys.platform}", "testbed") - suite.extend(discover_tests(module)) - except ModuleNotFoundError: - print(f"No platform-specific tests for {sys.platform}") - - # Run the suite - failures = 0 - tests = 0 - for sys_platform, test in suite: - try: - tests += 1 - # If the test has a docstring, use that text; - # otherwise, use the test name - if test.__doc__: - print(f"{sys_platform}: {test.__doc__}", end="...") - else: - print(f"{sys_platform}: {test.__name__}", end="...") - test() - print(" ok") - except Exception: - failures += 1 - print(" FAILED!") - print("-" * 80) - traceback.print_exc() - print("-" * 80) - - print("=" * 80) - print(f"Tests complete; {tests} tests, {failures} failures.") - sys.exit(failures) diff --git a/tests/testbed/src/testbed/common.py b/tests/testbed/src/testbed/common.py deleted file mode 100644 index 6e16db01..00000000 --- a/tests/testbed/src/testbed/common.py +++ /dev/null @@ -1,340 +0,0 @@ -########################################################################### -# Common tests -########################################################################### -import importlib -import os - -from .utils import assert_ - - -def test_bootstrap_modules(): - "All the bootstrap modules are importable" - missing = [] - - # The list of bootstrap modules that don't have explicit tests. - for module in [ - '_abc', - '_codecs', - '_collections', - '_functools', - '_io', - '_locale', - '_operator', - '_signal', - '_sre', - '_stat', - '_symtable', - '_thread', - '_tracemalloc', - '_weakref', - 'atexit', - 'errno', - 'faulthandler', - 'itertools', - 'posix', - 'pwd', - 'time', - ]: - try: - importlib.import_module(module) - except ModuleNotFoundError: - missing.append(module) - - assert_(len(missing) == 0, msg=f"Missing bootstrap modules: {', '.join(str(m) for m in missing)}") - - -def test_stdlib_modules(): - "All the stdlib modules exist" - missing = [] - for module in [ - "_asyncio", - "_bisect", - "_codecs_cn", - "_codecs_hk", - "_codecs_iso2022", - "_codecs_jp", - "_codecs_kr", - "_codecs_tw", - "_contextvars", - "_csv", - "_datetime", - "_heapq", - "_json", - "_lsprof", - "_multibytecodec", - "_multiprocessing", - "_opcode", - "_pickle", - "_posixsubprocess", - "_queue", - "_random", - "_socket", - "_statistics", - "_struct", - "_typing", - "_uuid", - "array", - "binascii", - "cmath", - "fcntl", - "grp", - "math", - "mmap", - "resource", - "select", - "syslog", - "termios", - "unicodedata", - "zlib", - # Scheduled for deprecation - "_crypt", - "audioop", - ]: - try: - importlib.import_module(module) - except ModuleNotFoundError: - missing.append(module) - - assert_(len(missing) == 0, msg=f"Missing stdlib modules: {', '.join(str(m) for m in missing)}") - - -def test_bzip2(): - "BZip2 compression with the bz2 module works" - import bz2 - - data = bz2.compress(b"Hello world") - assert_( - data == b"BZh91AY&SY\x059\x88u\x00\x00\x00\x95\x80@\x00\x00@\x06\x04" - b'\x90\x80 \x00"\x06\x9bHC\x02\x1a|\n\xa1<]\xc9\x14\xe1B@\x14\xe6!\xd4' - ) - - -def test_ctypes(): - "The FFI module has been compiled, and ctypes works on ObjC objects" - from rubicon.objc import ObjCClass - - NSURL = ObjCClass("NSURL") - - base = NSURL.URLWithString("https://beeware.org/") - full = NSURL.URLWithString("contributing", relativeToURL=base) - absolute = full.absoluteURL - assert_(absolute.description == "https://beeware.org/contributing") - -def test_dbm(): - "The DBM module is accessible" - import dbm - - cache_name = f'{os.path.dirname(__file__)}/dbm' - try: - with dbm.open(cache_name, 'c') as db: - db['hello'] = 'world' - - assert_(db['hello'] == b'world') - finally: - os.remove(f'{cache_name}.db') - - -def test_dbm_dumb(): - "The dumb DBM module has been compiled and works" - from dbm import dumb as ddbm - - cache_name = f'{os.path.dirname(__file__)}/ddbm' - try: - with ddbm.open(cache_name, 'c') as db: - db['hello'] = 'world' - - assert_(db['hello'] == b'world') - finally: - os.remove(f'{cache_name}.bak') - os.remove(f'{cache_name}.dat') - os.remove(f'{cache_name}.dir') - - -def test_dbm_ndbm(): - "The ndbm DBM module has been compiled and works" - from dbm import ndbm - - cache_name = f'{os.path.dirname(__file__)}/ndbm' - try: - with ndbm.open(cache_name, 'c') as db: - db['hello'] = 'world' - - assert_(db['hello'] == b'world') - finally: - os.remove(f'{cache_name}.db') - - -def test_decimal(): - "The decimal module works" - from decimal import Decimal, getcontext - - getcontext().prec = 28 - assert str(Decimal(1) / Decimal(7)) == "0.1428571428571428571428571429" - - -def test_hashlib(): - "Hashlib can compute hashes" - import hashlib - - algorithms = { - "md5": "3e25960a79dbc69b674cd4ec67a72c62", - "sha1": "7b502c3a1f48c8609ae212cdfb639dee39673f5e", - "sha224": "ac230f15fcae7f77d8f76e99adf45864a1c6f800655da78dea956112", - "sha256": ("64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c"), - "sha384": ( - "9203b0c4439fd1e6ae5878866337b7c532acd6d9260150c80318e8ab8c27ce33" - "0189f8df94fb890df1d298ff360627e1" - ), - "sha512": ( - "b7f783baed8297f0db917462184ff4f08e69c2d5e5f79a942600f9725f58ce1f" - "29c18139bf80b06c0fff2bdd34738452ecf40c488c22a7e3d80cdf6f9c1c0d47" - ), - "blake2b": ( - "6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33" - "343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183" - ), - "blake2s": "619a15b0f4dd21ef4bd626a9146af64561caf1325b21bccf755e4d7fbc31a65f", - "sha3_224": "3b8570ec1335c461747d016460ff91cb41fad08051911c50dd8e1995", - "sha3_256": ( - "369183d3786773cef4e56c7b849e7ef5f742867510b676d6b38f8e38a222d8a2" - ), - "sha3_384": ( - "ff3917192427ea1aa7f3ad47ac10152d179af30126c52835ee8dc7e6ea12aed9" - "1ad91b316e15c3b250469ef17a03e529" - ), - "sha3_512": ( - "e2e1c9e522efb2495a178434c8bb8f11000ca23f1fd679058b7d7e141f0cf343" - "3f94fc427ec0b9bebb12f327a3240021053db6091196576d5e6d9bd8fac71c0c" - ), - "shake_128": ( - 40, - "c1301df86b1dc67ce3b5a067dc9b47affca8caa08f41d1efa614cea56f526897" - "d61ded8ab01421f1", - ), - "shake_256": ( - 40, - "20740b4c7a7997765e9cc254b44a1589e60849be0fe70b68a6fb732415edaa13" - "3bb6eb7825ffa531", - ), - } - for algorithm, details in algorithms.items(): - try: - length, expected = details - digest_args = {"length": length} - except ValueError: - expected = details - digest_args = {} - msg = getattr(hashlib, algorithm)() - msg.update(b"Hello world") - assert_( - msg.hexdigest(**digest_args) == expected, - msg=f"{algorithm} digest was {msg.hexdigest(**digest_args)}", - ) - - -def test_sqlite3(): - "The sqlite3 module works" - import sqlite3 - - conn = sqlite3.connect(":memory:") - try: - cursor = conn.cursor() - - cursor.execute( - "CREATE TABLE stonks (date text, symbol text, qty real, price real)" - ) - cursor.execute("INSERT INTO stonks VALUES ('2022-05-04', 'JEDI', 10, 2.50)") - cursor.execute("INSERT INTO stonks VALUES ('2022-05-04', 'SITH', 2, 6.66)") - conn.commit() - - assert_( - list(cursor.execute("SELECT * FROM stonks ORDER BY symbol DESC")) - == [("2022-05-04", "SITH", 2.0, 6.66), ("2022-05-04", "JEDI", 10.0, 2.5)] - ) - finally: - conn.close() - - -def test_ssl(): - "The SSL modules has been compiled" - import ssl - - # Result doesn't really matter; we just need to be able to invoke - # a method whose implementation is in the C module - ssl.get_default_verify_paths() - - -def test_tempfile(): - "A tempfile can be written" - import tempfile - - msg = b"I've watched C-beams glitter in the dark near the Tannhauser Gate." - with tempfile.TemporaryFile() as f: - # Write content to the temp file - f.write(msg) - - # Reset the file pointer to 0 and read back the content - f.seek(0) - assert_(f.read() == msg) - - -XML_DOCUMENT = """ - - - iOS - phone - - - macOS - laptop - - -""" - - -def test_xml_elementtree(): - "The elementtree XML parser works" - import xml.etree.ElementTree as ET - - root = ET.fromstring(XML_DOCUMENT) - assert_( - [(child.tag, child.attrib["name"]) for child in root] - == [("device", "iPhone"), ("device", "macBook")] - ) - - -def test_xml_expat(): - "The expat XML parser works" - from xml.parsers.expat import ParserCreate - - starts = [] - - def start_element(name, attrs): - starts.append(name) - - parser = ParserCreate() - parser.StartElementHandler = start_element - parser.Parse(XML_DOCUMENT) - - assert_(starts == ["data", "device", "os", "type", "device", "os", "type"]) - - -def test_xz(): - "XZ compression with the lzma module works" - import lzma - - data = lzma.compress(b"Hello world") - assert_( - data == b"\xfd7zXZ\x00\x00\x04\xe6\xd6\xb4F\x02\x00!\x01\x16\x00\x00\x00t/" - b"\xe5\xa3\x01\x00\nHello world\x00\x00\xbfVw\xd4\xb9\xf2\xa5\xf4\x00" - b"\x01#\x0b\xc2\x1b\xfd\t\x1f\xb6\xf3}\x01\x00\x00\x00\x00\x04YZ" - ) - - -def test_zoneinfo(): - "Zoneinfo database is available" - from zoneinfo import ZoneInfo - from datetime import datetime - - dt = datetime(2022, 5, 4, 13, 40, 42, tzinfo=ZoneInfo("Australia/Perth")) - assert_(str(dt) == "2022-05-04 13:40:42+08:00") diff --git a/tests/testbed/src/testbed/darwin.py b/tests/testbed/src/testbed/darwin.py deleted file mode 100644 index a557e65e..00000000 --- a/tests/testbed/src/testbed/darwin.py +++ /dev/null @@ -1,68 +0,0 @@ -########################################################################### -# macOS specific tests -########################################################################### -import importlib - -from .utils import assert_ - - -def test_scproxy(): - "The _scproxy module has been compiled" - import _scproxy - - _scproxy._get_proxy_settings() - - -def test_curses(): - "The curses module has been compiled" - import curses - - try: - curses.can_change_color() - except curses.error: - # We can't invoke curses methods without raising a curses error; - # but if we get the error, the module works. - pass - - -def test_posix_shmem(): - "POSIX shared memory works" - from multiprocessing import shared_memory - - # FIXME: For now, we can't actually test multiprocessing - # because it involves invoking a subprocess, and the macOS app - # shim doesn't support process duplication. The import is - # enough to test that the _posixshmem C module exists. - # try: - # obj = shared_memory.ShareableList( - # ["howdy", b"HoWdY", -273.154, 100, None, True, 42] - # ) - # - # assert obj[3] == 100 - # finally: - # obj.shm.close() - # obj.shm.unlink() - # del obj - - -def test_posix_subprocess(): - "Subprocesses can be invoked" - import subprocess - - result = subprocess.run(["uname", "-s"], capture_output=True) - assert_(result.stdout == b"Darwin\n") - - -def test_stdlib_modules(): - "All the macOS-specific stdlib modules exist" - missing = [] - for module in [ - "_posixshmem", - "_scproxy", - ]: - try: - importlib.import_module(module) - except ModuleNotFoundError: - missing.append(module) - - assert_(len(missing) == 0, msg=f"Missing stdlib modules: {', '.join(str(m) for m in missing)}") diff --git a/tests/testbed/src/testbed/ios.py b/tests/testbed/src/testbed/ios.py deleted file mode 100644 index 2917f160..00000000 --- a/tests/testbed/src/testbed/ios.py +++ /dev/null @@ -1,23 +0,0 @@ -from rubicon.objc import ObjCClass - -from .utils import assert_ - - -UIResponder = ObjCClass('UIResponder') - -# iOS apps need an AppDelegate or they crash -class PythonAppDelegate(UIResponder): - pass - - -def test_subprocess(): - "Subprocesses should raise exceptions" - import errno - import subprocess - - try: - subprocess.call(['uname', '-a']) - raise AssertionError('Subprocesses should not be possible') - except OSError as e: - assert_(e.errno == errno.ENOTSUP) - assert_(str(e) == "[Errno 45] ios does not support processes.") \ No newline at end of file diff --git a/tests/testbed/src/testbed/resources/__init__.py b/tests/testbed/src/testbed/resources/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/testbed/src/testbed/resources/testbed-1024.png b/tests/testbed/src/testbed/resources/testbed-1024.png deleted file mode 100644 index 3672c84b87e06f2c03e5bd7a608cf99770edcd1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198231 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

$#Pn}1_Qb=15ZaXyR8dgig^ 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%0DkJW!n9u^g=dmdoO$~Fj6Y~w%M`;Q@aMf zP>hnmL>`XmeMT=9;Q=D$-^K}iAEjuX+vewS0eDmU*KB1|l|}W=R9BA z)>=I_w=khgpT35l6aHeeoVqkSUfru+uilJp*R_{dx}P%@wjB#)9B6S^ecUOAs*p8w zy|>ySRIv6T_n6USWbu;9hBO;f9}IE2 zC||yu-E;GC9MI+npEV^-GPo-*I+#K6q01(+UI61zKaA!^w?cn`8;W=|!GbwVvX*;M zDy(_;7&9Qvva)PQx{>iUszZC3l)#gsqIdXANHCJs&=ABl3kTyrYbHU%G1_W+-0Lla zQ6VwW{m*Q5bU)>S0Jfgt5&(iRro{6nMbzp^iE`H7cUmd)MFg|xHGXzVlixVYPBlvN zKU@1Otfp^ss_1k=){jt`nhygO%Vx97dJlIV^{sl*t>_#;{f@8#!WZL-k>(AL55h&{ z5kRq?XmmAO8&E5n4U*CVl}G5nz#Lga=m$j{C?ZnwaKvjag~AL%#JJPb`5HSOflI;Q z4DK2`8cU#E=q*F$5N73d=qBM%AV3>(5ESH*9q!kWa}Gjw;I(+dO+~>oOXy(?H#e)e zuZNov_4*)w-EGDBJR+&|{*8-|!jOq!62*e#@O63<9uJKs{vn?7zk5ADEbS^3;tU&1 zBg({x!5hQd!c&Sg(*~xm*(~UB`j}=sGx=(b2={M`st2FSe^+D$CC2L9@5N3tjeAZNv&RjWv!5e5dat8Kb|?J8M5(FN2eO?Bk&jWl7&7NC16>ArQ+Y9 zi}johu4|`JWwDX%O1Q_9ud^z0a!&9udZ$nnJ;YKoy7mj7d6juc0%k5*4Y z>Ff|ZCW3CG$~62R^<~dpx;dHVwVE>wKDF!r9^vW1kBJ51abmmIR$skt6P*&Y%`}Ct zQ%Gg48P^9J{oC9sS53->oZ9BsK9!Y9Pi=PneN#mF$lOepO^~L_nHoN~>QLsSdrk+7 z^;1N;tC{7y*IhZ^tIr1*77Rwt1R1S$r5!iE z4}}3JlYj6tw)ZOQ=`A=@?&R~V@Nl~nn+h$=ONm$kWeer(Mo({Pcq(4A<7L#hLkoWn z(O#YPrukX7hlR{fKe6rM3C8n5CBfCthSX9;r$(~Om^8P8p)v_x$Ce`7Ky}4&JC=zcOs9e5G+=0lrGB zKiK#{DPcWQszwX!9$sLMffxy1^NR8NMVIq7st5P`7$NrG*6+eLX8S{`3akf+!7yU| zaL5Q^fURVN`uq5itcczy^F-pTX?qXlONU$9*0TTC+Lwn@o&I5e&v9&#IM(btZKM_78A@5gAlrLCw9N0luJ^xp zuDPz?^}D7x=lgx0&vJk6`~E1zPPF&Cg>~w8eq4NGxI22Us>CYA)V}dlF&4i;VWhav5>EYP>fnE^y3BDff0$ zn>nxLe)|(cM-*k(ii#>h2cU2r$@FC5LK}=-kbrDV5$_&pMz+0w@6eA24uq93uOXfQ zh&8>M4>S!TCJzf6t+9Up6zLlhXB@b1?ZJ)8j=#Pjac6~@V|T&rcVXw$k1Ah=F~S0y zH7H_NBE%uJ5wQq{q690&PxJ7&e`|FNAP3*u+csNT2Lh}}uCZ#`zzSS}e)M2~XF{`QKF z!>t!9J>yn^`}m?o<4cW%_2-&lw#e5LE0Z?k&ppZuTG>(&IQ6$NS+&GF$-6V{OP^(r z_(@hp?%{XkGW5)t$61O7sTddmaR#>}6kCNbSs=#iJ9b=tVd&xN3U(5iTfE(w^CTo> zPL=KZ4+&SXy7e6}0P;4Ros(UnZr{8a0}W7FM16{?ctR-!-xU1d^{xSOC= zV3w`{?@Q2ih`{}>jZ@i7qImxtx4IM+D+LqVg>(JX7#z0B%iHYc{QrqzoMs z`00`9ELHK!!iQ$u@xf)R(sv(SlDigir)c@TbIDovwPmL zIJW}H!3jp~svyA)E=_*QO?EduIQPj1+1PRUnA=lBN+rz&jZL#7!$PaAs1)uT6uDFk?C=9a1M{Gj%_gPThxb%Jp$+C#OK`P4o`3!j}+f` zqfKBZOpNalDTtobKL9BgXT;;~N==yr|KQ+9xC%JMHdk=j4MLH(hxjd` z&o%;vWP<3N+QPevhWCvpEg3wx#0!9+p;56wcLXgwdcV`1XH)aI$_g`O{iDtF1cD9XLR^$?9G50g^P(TmHir1`vso)kA!{g z=_~4g+B|a?^NfZ5dy$^tdC}nvr=i=^w5U&+^{!pq*ovzXv0Ojp?-gai?*9Hf)Fl&W)Z)PDv?D-2^{N zcS74g`b3E~>D+-Dl_TOeVkIGQX@|fY11&xGk%uu7 z966uN*sew4b-Euze5buyH7{v@VseH%OGl-FUw7*|rhk5T-<~SaT+v)ma-ge#P6q%_ zLz{TgpfN<5h-tFv?O80@T@{1ttdb&Z-;T;wv>pjxAWP8iDUo)4N*n zQKMaM6gjT5s&Pna^I&$w*qsa|m0S_?k7v)CW@g8HQm)i{e|9WX;b!~mgv=X~ zOU7>`3+&Dp?=H#NAG(@7P-oBW9n7|!Bco`#XiiJuAjg=o>~1CC&+W zda(6+2$~c|?rLUcVDD~E9KV}1d@#I%0#65|!V8nk1)v0ku+}x8(DrGz1rHTER!SgYq3!H8}_8~q2u zblnXtMS^1|XPG!7?G>IvMIrS1iEsulde4-04FTuj>f+ZZ`WFjV6C5FJrMpZOYr{kRw)4GbM=7@X>hu`>}J4|u|hhCv@`QW;YlZ}uEX$Qu)@ z=8ek?ev%Jgf3Kq~W`F2~490z*nvI3iS82C5F7iNkcDd;mJ_0~sWR>kBX2>!`6;;(L zkx+^|)>=9CsdA7=HS+G=8|}NT$Sq47Gw9y>+V4!X5hIS2ZFtCWrRU08*7S_!-5jZ!!FU*f ze?Hd4qRz!PCZVCAAg*%2)T#Hf8Z%Dr)*dqD2PFsG^z*>5@lJs~OkG!i)yAMzeb@yF zQrUZcG^w*?vOva|q(=}IDvUSr!N7xnJRh+Q^&y4<6Ovxoh5=z~s(iVt4^|OZR@+GR zEA9(uI$@?FpS;+yZ&knX+{AcVV3u-=va)Q$Kbg@VMW7ZSluIZ!L55s-`Ut)yHsTSl zp3#AZwhqtn{q8T$8mm(t6(M2L3?iBWEFBa4@wq4CX1LWCaeM#1Kd#r)r!% zcFg+{806&ZT zy`4BDD6dDx=D+^aA_dE8+oaAMQCXlDAT}tZ)xL_WxJ0R3MTDI*ONalTy(+O^Qy-xj z2O%G_axBB_saV|Qt~<$ZKMtTFW&&ui2;OYG8Tt$vGi6ArWZy0}Ef|ituEUcm->jp* zB;=iwa;~xd;t!%#F_CYk7Zr7X%p9L%F^4U~9?09!sPB?+P~P=wKJArSV`4}Ag6EI^ zz$9fJj^QNkK`g0&C_(iERw|-6+toFoHLI*Xz-de8u7wI9FabC82az4s3Cs`Mpavqq z0{}e~t=pgX=}!%NAPYc0#uBu=>o;zckj3l|U%{GZ=cZ@+*Xx#aErPWLsKhvx5YS=@ z2hi-)c?^6|Tim&!(8Q#(G{<&?ZE!Nbzi4boBb)iaGvkg=yi1SDsDX!r;&DcR+6V$*@%x4$aYvnLO1TYr!kTMh4Td{Lk z^@;!9y;BmI+r!Ean@2cfB2K@=HlyvgQdQ7mriDcH*@+^*`NtKVI=^0uR&_xM-F_PE zX%N@&ubVTqz}`=6Q10fiXN-?r7fU)p`pYIO)vI=ds7$yBCMG_g%5Z32*4I0&Tc-dh z9hEFt--)rD6d{i!h(QQ&zH673xDcTefVYqj&r3|9K^V;|Dsnbfyx$WG_-+d(_%O4( zKk{Wcq=AIvh31ra(BS6+M?ujD=cmFw1A=ulKc}u@M=PrNLBdQAB@45lYPJD4h&y$a zYHH>5tX+=Ul9>LG3sC22ezvQy*!~_L+$@{iCx%vdS^4{&%FNVt5A){wq`l4IIBI2!MoVL#&F>xq&|h<14gr5QiO$eXLdWxs$>fLX$=m zRNJ;S-VX1$O=wqO1tV2~P6B7#{1@*O5=s-Tn@|@MQp1%i#0vxxU{b#3=DtXsjr|*= zPij@XbKh8tam4kP(8Z_imrbnixE{%zbsoD=wY!zOZ@Wlv|2RJW7A9w?+mYy2Euu8( zswFYs-_q;kvX&K3&$T>V5xi6Fs)Zx7N`wA3`^yuNhRfDjF7W<&TUWO^C`bnII~0qU z6ccJ83JUbjXn)B3^Y`E59yaBF$;>yBuIoGlav=a*3PA_+Czx!5O&|n_2_K&fxxn3< zV1yXud0@3FTY!8l2qu5MscHH0Ari)HA z7oUhazx|s^7rHnw0yQ!F#YCGF0BEDov7!paatG1ngO{&sjF=EM6dGmXoQi#ETVY6s z83Uo1qn#t75Kw?I_tIn*yojZEnAZ@Gh`V>+u*Y?$aFfPraBoI);LdJ-!Pvl9^NtQoPAgwEr!49q%+cj2{! ztlcnNKY($THz?k=$;#wmL-~PVXQWMUA2_^B^GnHS268p{ut-MWiZAG#raxbudy=~E zy^+d^XVeHi*!q{d4rEJbc0kzt;6Wt3FJ&#sGzy@eDn>=n72 zQZ%^y`7u87`YcB|GlNlDL z=hgd7PmQyU254UV?U3!-yYNTdEVs3Ace}FPN?jr=+ZQbhnlTVP2m7j>5$*_O=^N7< zo*sA!=PS!GtdXeNNC*7_mjdl&*>G18_CHSnMV4T!m{6jqLbneYQ$Hf`mA~Eq8s3=oSfyB!H*|^V*oM)^NF=BXh86)R)0ySUEcBXoWWU`h;}f z96J0mcNzu+aQVe}3&5i~K6qS3EF=^tiZPf(e)9DBZL}Is5@l+|ySi|t<#2=;DLTF~ zXq*O$wbfNczOH7Te1` zVwLjs#j@PBqP{M>3pL)P_ZIihKaW5x1LiPNeR1oa4GT}_)XI#V-?;EJEFG{xsC#U< zD-U72N;FDZV~-%WX1{I7noVW19kgw%MtWq z;5D|qK@bq4c|*aAe$`8gy@6|MPer=cbdJx*ke#r@mMx1NR>Vn9Z>6d}j}My$i3oo~ z_Q?zS%3NyA*Y~MQEm=o6H1U)s`%t`KO@=+AI-jdU&AtACP%mc{p$

Mwy|7u!|Rg zgMoHU4TL6~u!O6>G(L_cS^?loh`V@`ggcM!f&dzmjw(2MvU85*oB-ebUZO9eFWAcF z<0E@dojyI(cyJmq@?2QbCNFT1;0t;0>ErB<252eMY#1QH&%$9Jm z>wK~7A`K=OL|1#|N(pQtCiy@Bw{X<_`AZ%=w#>(ovCI%h`-)IRXbG5<5J+a{PKdz5 z>V3PHt6^#fei1SUhIKfQ#NNPI@gHo0Aq4)bGnj~c7?bCn`n*xs;Av&Y&EaVuds2$0 zi*cS#p)iB#5u%23nnIYq)C7`>C|vK*$9%Bai*Hc$=BzcGc)?nMwUNawPaRFqsQYzZ zT7Nsn8Ju~{*PO$@Na8{OQNpb3G|m{@Jg`IzejuRb!{@G?iu6$}gVsAnFLZ7ghK%q}uMx?pwG-;7+D@Vy}+8%b7guWCF za@7}##Fx9GLJZ}?gf9_}5eg`%_Hc}l|Kd^b<_8^W2y(Jv5S@yh-GdJS$KbSqi9Y@c zqzHznC&n?hr?gNh9r<1< z?Vcuo$#vJ*VAZ~vPdPHK4qQbo(Lb`(=`*r*9~89r)((JnprTT~C6DYy3kmSRTj^tu$gRWI-t2@e{+E zvUzwDFc`5Gqf|bI51V9&P;M`2eip*2rh2p#q5nS3d+iBV)Li`=&t};O{gvTOSJHJ+ zRa)@ZGb=@iNlG{4RH8NR#&`zCjFaAjV6zZn%$++#){L<;gqp_Dy#~KKVDP*b)eQdo zB9Nl7c#b$xV^)q)14=N8{p-R#Z)J9z<^FULO{XPmHrtgYvz-*p~+Ptj2+}i$vAp_fU$e zVw9TL_tYz{w_H5c%={T%r*u{PDQ&j!`z#rYi{+lT#ShPCoz(=pCm!2+KL=HV;yBrl zf`~+JGrS$q=wf>GWZMH{=v?p}OqG*e6pcZ71%DP{8stAx-=Y?o^b(%o2Bdr*;}Eud z=4+dl7zlI+o}qL1QKm}^#VE&WG>Y&-?=AUVRh%U=ZkhJuc@_#8)Tr;0OBDzg-30w# zdbzFAuT$T+BoyR_jsTJYMThXzv8)QGp5Tt_)-A`&hAj`?JRq_yu@qAxUk=3(J~EIx zeEyz;DkB^eGz8*a00<~v5=)1yy*Z`cO(1L$t3C8wrGbm}ULg8yun?v$tXi(nV;?DS>#&h3<3P5UQun5D*!XYxNuUgF$D zS7SBM`X>Zgn4=SeDSdr{84}hvSU<=;gqr|F5mZts!Lmp+29wh>3g-b8BPmKr^1*E`Eak`jbyqFKtPC!|k zQ7fZhm;1dJ>7Hn~CRRO6yZ!D6mdv;ADNVl_e)`4JVd5Fk-VRm<+(9g`0~+UnB=kFX z>|ycI-Y(==J=u%F>Zr0|XmP}0g6k)jOAb_m)#;osdPY6|$;~3)l@ybmdva@M0IWU_ zKeWIwrZ!g^k<;x*3Mvkiv~gs*S+~ z6Gh>tk$}+9fp7`DZL%@N6cn!UWS9N?`MoPdBG?gmDEFYZ3#p@Mo}~QBLZ)Z{g*^6X{@Dp+D#}lC5TA~673Bνxlj%RX9i-#uB#-FV!& z+7Razc^H#2Bp8(Z1jhLLZ+kpMG#6#X3uep^zFVgk#p4H*3bcu=ExvZGJLc*v`6G}H z?yK*N5WktH6gR%sb2>B9(5WEjqZkK|HNQ^R--h~wj}@yBavq8r(l&eW_u0|&cn#bj zms#u{ju>V*O~)i?j@578zRkcz1!o-I5057uMgUa+QXsaA_~Pdy#%~{*uudKe$@_dT zJ7$-0=d7^6DkaK|NsG;6uo0wxCbhyT+p=REs5j>KcS>jp^>Mvv`yIGa3)ZOqZd_k! zljQ<26O8BJK6)I(t&PWcAX1@pA?sNXX7Sibarhy?d!eWO4)JD?lHK1^Lfrx;)KxV) zadtZQ<3Y z-)#OUy)*dem`Q-B9SRT}H37yBBuPUh2sb#4-2IL9(p+^*EYg&y;lg@4Z+^$@ytknj z#i-xus_uf{Yf+&bKKW!>(*r#DxznCeExH@Ph?A^J4sD(-R-+?KW(okZP?~{KMU+WM zVH)Z{o2@3@pqqt)odRg8Nn)JWE|i~NnG|*Wall0(%DtWX7262@^Q@F!utiE^TKH0C zahW)UO53cCNMiQ$j2X97ylT!zgj_g=(7z$N$lipX22cR>H@%Z0ASI$(^3JX8TLk7p zW!JkmG`}^1WUCCQHko1+rc3@B#EyetvEAnz`D|RG!0(G$tyLBMQFI$)O8zD*>YM-9 zJXA`aqpv&(qyhXKqC8Myh}AKX#Ep;T@cWw}c7|P`{_y8tNkIeyXw)=AGb-M`FsaQh zU+qVJt(VH=j1RtHH~_(>zfxcyLXHgxeNXdkEdFzNnP%~%rXNrck-dYo4U3I{efw5U z1{~aa47hRL_g)O?+AVo>*?VbB;xI1PHY zNf4FFP6*erX9N?64aC#Svq;LwdA(d$%H;D?d7ps@mDu&co1-Qz%qP&c+rsyZpNq`S zG~v^ZG~g;|QBpKzuJ|MRr;-;6TxTYhbQEuzM?e~=vC&+TObY@2gqyEma`{2b;e2l1 z4>X80=I1DRJf%QPBI5Xq7KaW!_&mvD9F zsdq0=3A-yNEsx1)oAjo6*}V*cx^1~m=V3FB*bD~k9`v3|MT^9Jn6fP>8;B`38icm~4v z%b!Kk=WV7QJ3F)N`Cdor1B92#<~!vg$-jr~%`0^aHN8&=v7qfVbZtduIXm)RXL3So zho_IjUGwS{Fov&X2^4|&W9DQF*$Dd_?YR5lor2C5RcS5CR6}h2FTe=Mc7jk{LBeA^ z_oL(ySC(A5riu^2;Rq}m`1n}TKK^Tk-oql}kG+u^R7(H)vzio9rsFXHEeAwP=B7xf z=(8X(Lx?&}jK9!#E_+`a9HG=*BygVHnRudQqr!=>2oE}Rm@J7G&2_~ac4lrfHNV78c#x2@ zx;nAc559b0nPD~q9w3xvS1wq;yAph4;QzOvFP%|YkC`tQecC5Hr1-t`1 z?Ms(8h_{x>Sz9ltcrIC}sh9Z8PY@14vy`af6Fw6cJtrs!&M^14) z>Q&!!G}7mAkza?ooG5@r&=@J`%;y0E01SmJnh8-XM$o>#dW3%h5&@jSU%a|M4;?KC z>(+FhonpDIxlitaSSJ4|m-SQgcuf10HLd^b?12&5)qFRqNLHSl(4_~wmt zj36fJ_v>oz(GNFx=cc-(28(T}F+3!bLisUvQW`8cGtG^ZSCg|WWvCaXt^Xpj7#?>6 z=M>4>wm%r`e8V62wKyS5za%rew9VMZf4|BjXhn7&ki7I>Pw>@rKRst#24Gm!OuN7U(%wmk)X{XLlKhD4~8rVM&hxw+aJ4+Zy->5t^0Ow;Y0lMv$-k9`geSUCh zVx`au5MT{DQe=E65QxVHKwR*OOx?Cx7R;zEKOA1)?|&pqm*yoQJ?#y3i#_$L+w&9q zs1gYp;lBTjCPJ?h&a79lilY`H6AR=|t)*Xzbl@d1=~qe&>{L;(?L=26@4j;rF zCRV0U{tzqE-+q%%aK@V}^~M;81K#mu^NK z=IWC!(>td(wq+l?JH^$MOWVBNSv%?*Shg0 zxpKR?{kFGO{Rn9RgpyqZD&xaV^WdgYnK!PIktSyAHtZ%* zFYxozLx|a}tilbtx|c07Zj!Q?D=t0l(LX(!kp%dGzOSt85FR)P&bXHtt)P7+{$(JE zLC=Ol9Ot4TUzH#f6DoWK_Toki9mog8r3SPDw79J6+`E<*KYpatFPbT{s2ZVie^)Cn z(Nr7j1q<2q2egy2jM=&m{;4TCs!WS*GX=|=-#9v|H~t*`g^E#W|_Z zf7SJAD&Snd7!iLeuqgV;m31z6-|Dy(YB&1{wa;$_bBP=Zva$!23_uk>Y_dRAwk0oP z`>9;+{SOrsE;67$kPnanzx3xsq@$0-v`FRGkM}xfxa`Od7?ym1jmZ*{m6Q zm|fR@JY7_F#?-gG2J5(mmBc}A_t-zXJ&dorOmruJ3`4&>*16zF6Uwqt4v&gY5ki6z z{8sF#)|$#E_qKC`g3H+n_dHy;qAO2WoBvCewupQFxhvaG9nBIXwg?9;aNUNm(U};t zgVF~Y4sc+yFkDBc&BhvK>n4YJ=v!5PEE5R9&;WXBvW^Pf5%h7y$O=GwR#v`y&lcr@ zS#M~vJ_Gj^2Nyn>I#)zA%bT93>;KP{vam3e+GF9P1L&3FFH`JlLo^(3Ax-yIa+a_u zuqmObn*J;?iZ91?4X!pH+sE{2^4u5dN)LgbhC_||zm^uNKut05fW!$ep7`v*#cnxf zv|tGl^(ufG4B;TGF;1^gP?^&{u>%uVlo@A5>@P68LqnCNG%nerF#2n%GX{^XO+=@&tg0GhqXD>iqV~E8Vc7Mi>OUrgT+dwg?4e zYip@Oadu~HV~m@&(1i)Reo~jJ7AiB?x4I;1DgtLdk?`vn%(fP$?-#CroZh(?9d{G) zLJ1udM~{!s3mPnug`yRLMHI=AutmvT4e6_qi5TK?oodx5ehg+5C;>PfnQ&h92^{qu=U}B5Si_QMXxy zp}dy-5k#?QR99%lA?E`|yJYl(GgcSkB`E08P+?YoC^V4ZZJM_M#L5--z;{MthZ_v) zKyO`FIMMA87V4yP8Qxd*y#5OwX35r4Z2lv9DY{kkk@KbAYtZ{wvm!K^J-<7g7j=rG z+QlgBy?fntZo535ysR}v&W+K}RM!zF@JU&vB|E)iHdlF@l`yR|JL*nAjb@}(v#uBx zb&%ozty_mCmUA}3jRl<{h#A0gzm4`bZNjr|d6Mrbt9|^#?{;()CHSC#&5ooJl_EiI zKYeQK-`^t5c$OU$p>t_p}Nln2mjc6zGzY|f08wR5RJ{{Fy8W(#bd6j~(D9)9XXqniDxWl(+ngiVBYPZP|ZL0`%9%wj~E zaHmR3Y_puW$Dp&Z7rzG1fQdI8%Sn*LXF+HuYBfwzFckpPTtk^FJ5-0|?lZ_r&)V8V z+!(M(fWBfGV=yWL3|1Db~AJwOr6Wr^h`oZ~?E0H{j@5}@E?+^jHX z+6_=ZH6ww_Kputajo>I?*Wj)ZVGEqRC;6#x7}@I4@YsOUV@yHdg&7k8%@wI(^dF`= zKW=^$H;bmgljZU8_}_C&IP0K?USu!IS#HGuR)Izwuv2$WxeA!oqz5L9ji4YR%>)MO zbYbQCo>}4&-~1O)XWk!rz8l*+c@VZj{RMb}SVs}A41<2bhT&v`Q!6!P`s+Xe+iW@8 zkwpv(7mi_{3)~(_%zBsjQW@s2Hf(nk@&;Gy1Mm*DY<41 zVccMRM0SmU(}i&ep+8qw=dRuD`xvK`V!==ax_gvEa2h2H9x@Gq!#>z$h@Zfx7l02W z!AU4d#N&MM;6X@Il$_tAmcc`+?vwJGiOSnJdG58~&{WQ*8si7QaQPS5sjB#4Z27n( zw*s7Yc#!}lz_KYc?0_n8v(Y2Iz|#Q`3k;ZaMj(#CLE(rEBVc*N&#>-At zHY00ekWlfRCi&Rt>hudc!BWByzd@rJKIYZumQ>dNd_7P3@3LkJgr8r4X~nMNkCFQ z5G%qJXc+m&h%g#7G%)F#jpdkpP?_izDub+q4uLG7K>}Lzd0kMXEl)=mTO;(2OlUeL zK2E-EaJoy(?On8@+HgBDc44WaM`pVf-hB#7BRngjB?4Hr0>~A@ zG>jcEYAa38DbfRY1j9Kbc2s@H^nkIkM+t9+28i8%y|bbiXDPB)q3X}G+pTWJNpIOG zck`Vm2cT*EO!3)m{#HDG`B`3R}&NP69 zYEbBV?j61gYvR^GcAtVnOabM`wI+*(5DC!K5jPop6Egd&u5Ovxbay*>CZgzE3A%-U zqdTBTMj?|(nen79E%s6o%48fA_lewLZmt&~NYD+mWT3s^>}5h_gy{twQ&7xlN==b~ z-$Tzqm@Z&fn!S|J86wait1ZwX!1A)%Gl{k|J1J(yCMjhGZ$Mh(c8s@r zp(@1KXD$2%Sr}D7>`Su zrWo7du;L?wQ;vZm_PEW%IU?H5NtGIGO)z;Xbaf+)b)UUK&ZM zAXk{9z(6uY)sIjHpb+cK(HetvOQic`kLB=*auT0_9A42cr z`#xuL<6c$iR)K9c!t{P{h0kGX@cA=zT$PBlq*S(Zb|%pt>5oiwz=DERuzk2Ns=&h! z@%{mbW{uu&X>b$w5c+x95HHyY&D1q0%%5RsnbKpd%w`0$)h&vx-4u`5V1V-;I$cyY ztP_2X4i04DJVN-Sy#k6w!rsN)Affe(QU?w-^md3!xo<9ZjtM>as#4h6Qld&x{MvR_ z7Oo#8^$N!xNlj8a@xAJ$@QydKrge+IQn)6fm|?|t&y#YE^a5cG)^o5SMk`3R5#oZw z%@Dnv@ZndlKpsZ+ow$fnZgE~%vWv71SsykQB~;r}OL@$0;Y;Hz@iPiI0~1XX<`mi) zN)e{?g^_wS#Y46%y-P-z%}MIo7SNmjmV0Fw_zId|xUyqF2Oa_DJ1E?N1Cd~b0o_D4 z@3oXr6%%BomS>6cH^uzt*zTSw?;|)$X$j0$QMt^UJyY*m>{>5dnxMb$F$};^h2TsQ zauy13G+Bf&^z0cqWr+7^Hc}n53I3EfRMYYM>C;&dC<17D=;#-<)Jt*jz%Qj z`>_pmb*&ILfbi6l;5iZnCm1N+Nxs0~gPi5OytAnP2Ho*jG7p*TXCmue&Q8p48_p2p(DT8mkZ^K5Jd)mhCVP{hyM_tM)%1iN zGx&cz31tTzF_B+shScYBTeDx|;srHPpyeylzit9Fl8B6kxgQ+aL1%^t8Xs9A&g#tA z?iSuS-=M+VuxcIK;CnxYi{Q5IoSp5&apg_jl$Azv%O( zUF~URtZ&R=z5c0R^ZTVuf`q2r3D8|;`B`OC+!~Yoj(}j>lBKDgm!?;ozBVeaz9Y`$N1E}U12RXhsOTY++pYOM;Xmmr{J`_Wd7NYqWP&U z(zMyMO+EqS4?1i~+cf|2Yt74c^#cuOSESQaR`5r8UR39u8woRLTz{;5bbN#Q6!Isl ztIw_Ne|@ab@yqPTy)_HRZ)#?_?qOA*eb+iogC6F?JAHop8AHdskiU$|hPE>B7rgG= zL4?jd@I z_-#e{8>1EeQ3)5rX#0-M#_g%Kp9j+C-i(er3m+%<@l&ZObo^=F2l)&C@`c)+6LNRh zvv;nXDP#`^AU0RNnv+uri;RdJ?}D^eVTD?^kbu&qsWK+|M(9t;-gV? zW3G0R$SN!krL+R9R|jm`5U|-~i+1egtVSFj7KDj990h@cl1>~sKkko{(|v|OhnOuk4)zC1s>XwBk8S_q zVUdf``!8cUqh6y#UsaK@VA)}E#L?cMLSWwep}6}KXOl9*4#F@{hUb#TSi`Wqfieg+ zA8VP3S0+GV6KBb0{4m!v&wa}+<{Cb$?NN!_88WgX@4lv8tMo?onZ$dby}9`sDr`s) zi(-xFc*+;h#||gkDd2K`53A=dL-~gNubuGpz}1=^q_@*&tx)i`#h>}+gt|rVPkvm% z6~a4sdbmdjZCQQ2Iuk1^EgM_R!-MzW;9%9#kwt3of;BxWYh+_1e|&tr%!d!u^z=v( zz3cEO{!yKUor2=U}v9A^gs}Z+d%s)u7P7t}QCir0i_U+}vCl zdHH;&ol#nPdTe6i@LWb7aluo>DpIk9_GhHrrGM!Ap-}&)&bOT=SbJ(}YWevE1vNg8 zp7pjXAJo9N`76P8a}HgWs1IMF+@rVJRbw=k_o5mSfK@zpCiXXmJY zO>J$OsEi6SCT5{hmM9u2e{AijV1(^TGZr46q@f`tQXp*QoScFJI@sI8)sC#ArG{FY zCEl8v8v7<)>9-LJ)&*8`6=;O)ezjwwX_+Ek@v*Vu78Z(}R_ zLMJjlKJD&pe>URZQh(IZVX?Hd~MZw)8qN^`k;Sn3_|+>Fn0qM$z^Y zD9XXZg<3jc;nsnR{ul~24-XFpI-NNetqz|yG!ou2tscEP3zbKklTevcU-W@UGl66m|<9R6u*(F?Od%Nk>dz<>L>z-CV0s~o&@X5B;JE6l;I6oWq| zvl+3ml%?wB*jQ(wzYQRe$C-rX&}Ay>_=JQ6nw}6$yN|^$)nQ_8p2g{SkaN)NeYIdd zS*%(}pO7a_?vW40si4UzDw3&a+D@fES*$b=1EouFR+-d4SouY`!pmP7OKaP)X){{r z*y6ljo-kxt^V_{8Dw%Dz<$Gfo0K}=#oX`lCzlMU*wF{#(RE@w2-%CPL3 z_vjr2Gss4)a1Q6I^9D9gBDqFmMZB)+J_J`BtWR;Dxwox5xBQqjN-5D!OH@l7__t(> z7m1_-H=L_Ea-PCKo-kR0;^4LH%T&QAbX0d!#M{u^9SoP1l$@;3?ywH2w_bn~FI3FX zZ1)y`e}!?l>t+$$(o)7)^p|1*Pf&E z=cCV_BU=s|SMK&6!tSl)gUrl~jI1z{?_o-3K3v-L#iALp%R6Yd)`DE^Yd8s?(#~T z&9NkI-ra4!dB)2~z9PlNQE9%pcw z>CZ@?s}!9>QK2&=WInlQ!3Dk)K6;jrqTE@WN7TRJ)K3*kXLE65=ACA>AJyIvX+rV5 zMZ&SEsHdP2)$%86f&$4%A@Dm}TU*r*8-2kEpv>3YI_~ve5wr;;+iCW5UF_trB>mfm z%FBcX_Su|57B|k&is=)i)YRVQg7vgs zZO?dkcnD4)F!1Jpg@q+k-%XCyQR@V6sd7+4Dexn)id~xPKS^z0QZ~o~gu#`$-{QPD zTYj2n&Ij*%BX~deS_|0s&&Y9;UjWz^=&=ZV)F#cad(6y++;MJp-jC>gtb#6U$ zvknhJ$v!TjoK2#9m&=%;RJ3FvQ72=ccXlmbIagXxSjeY)F!=*@rP&2sP7!_BE#Y0a zjaml#ZheDAs7?_IW#IqyT84DIGH-`zpQFO%=hD&U)+6VNlAXQNy zJs|zl#;;FZL$78fR4Do9!%D(mC)Prtl4Q$yJ%di%0VCqpV5H#T;h{4m#K*G}3%TkF zkfymlMi#WZ#&mObjzRY084sK&g8g@W*J{>lv0GI`8pXu*X5F6Oo}Uz*Aul&qu&nBB z*I&nKF+P%n1!=z-C$%FR`F3CIACz#-71NIfu&~7q&&cQn*>`l<+6|DW%lN`i#_$Mm3Z<@*(8hu7eXDj`oWIvAWX0kuA?-E84`}^C< zn^7o+AXfGxzX=fw*r=mz{3}ADgsaG>Q@XO3Kl``SWKwy$XRyGp-!wm$^IQ12^5IWd z_qIMk?1M<+LAJgqPk5<7Dv9z+*p7ND!O)^mLXx@;{fh&V;9O+t4{hop;fw@Wq7tVtZ{*s#p*jup4f0&x02;7Yd%< z2(xpKbK{b5!lf5saMiB`9mzDt2y!w)ZEfa$lF?j2z``i{*2S)6c5DLq3TCFOs|x`U zktdz??OTyvTynD1f}B0UlCuGN+H z36B3(zTcfKZ*Z*JYZjING;TorMy4o7P}x5%IhE*;-6-yej?|-$rDaF@#|wx0)ilh^ z7SojBTsv=h)+a1oTx{(BoSnH}VUWU5vaw;t^an0j+fD_fGdg2A>-87;RTSgmOU3#! z5?9WNYc^*5Qrrp(S>hnf_wU3ZH_gxK7o}E%R%y3smh0!{exBe)v?Ke=n}(;OVCdIl zw6}Lj>=odNE)$l^EEXoMP9ibw!dW-xBMEI24T1L=71(=Oe>f9FM8t`oRvI5wRblzv zPbtAE2Pmz9M}t9k4@SSz|FBFmJe-1Qm^crZ@jC4@4@ zonlNQ{=g@q6LS|CIdz%Uo`Es!?EHmCe2N?5?5x*od^X-D4a>n%Kh?eFKs%jK&S+Te z9u`)f;}!GYU1m}b(ST%7upnzVGtGMsRp`uD%E#dXnd$dT!T|exD__0ej0qVsgxujQ&44O!YHhwOxMl$xnu#uIQAT%ZOM~;#0{05%;-u7!q8kl~YOd%8H> z%|?5woh)CU1fKKapb@hT073H~l&J!d(2XaS=T3=n>ksd~M+r4l+ZYHgd_}~?oz9KG zOi*FyEV*sC2w2vHUqL`{XHWY=80AT$nomK-;W{T~@Jr0HZt1ZtHrJ1`IbcA zi>7oH-c3xfo1Fcaf^H!Z~5QU zii5;CH3v$z6dNM18_Sb-u4KzfwnkKV^`C|H*}`)_aqerval^qq45h-97Gmvb2U?9y z)IGYBbWiwH9-yQatUILH3qEok66;Wc-lCxDB570}x(vH{!+T^26(Jj|kHyi%B_{Sf z1ArO(bj58IaKz%Wvd!C_Ec3Na|3dHk@tuomlL72>MTR`7DC6#+*9sM-B_HJ7X*vGJ zp&hvKHdm;As-&yT4~G=Z=X@t12HNHL@8>1@F3tWcDqBb;~T zZzZQmEyz7!ru@lxh;*wNgsIFp+g5&kD!9$4R@iIQ8K0V(8d1@9rQLqBj_P~BPVBK? zTJco^;zMlqM%$@MWne~cD9MlcjiW3k+fq%|KiXhIOtLW^#^ z1*r{{p>3nhP;^{ol>0aJuj84kRGnIw@e+t+ML1CB&alzgUu)I@P8D9)Rw~qLRfm8Sm}y z<{v(P-e-!?zgb#+ntkInjc=4&-j(6DLgyDr=2nfHh%0BLRlJFh_q*#scp)W$&Z~yg zROt%#<&XDHwOfWhnWW6`;sFL;Xc3d}yxOPpYdz~Dp{CxVc69YR9{<_L(}O+?g}UrU z5^>!eQq8wRv4T0vf?-_eU5Bop|~f{T<6t$I2{d*nn&=K6*q=9r zliMgrvo!_~>ZsZL@Y0is?Ya^kYy~@^V!4pKwKXdY4-e1$(Utn+tTitVz(3+zT0}Q^ ztYsoYUdzPG7J994?#HIBsTYdpX?@y$Q3XVmO=pR%h7lj|T%F&gj<& z2ng&R=O$(&sl5y4Sy3hA8qyAGnVZUdid{={LmtV*`6-Ol((;hd;w}3uek)Sj^wqwut=2(O#O4 zBSjE~TTaQ$wEZsNL~k`)W;vNEhBAG=b?T(6wt0)%jC_{hb@J+isS4ent!7jV%yt5aLUHOY3fx|M_ z)@ktJ!-ww7FIQUnzK@r|ls`RbX=&~MekI@ptO*H|T;AAtokPL^5|o-s%*n;2^*qA2 zX|1nVQyo;@ASRool9FddYy;bYnLK$qoSYhwTcWl}v6)b4Mi9j$} zGhgG-bJOG(a8qQ>Norb8s%(8u?E^@~Zw5EG&!okatJ)6e6xsjb!x+oa*jv`nUaJ zPgF)mMkARxxVW@2qIX3=KBVusb^S}~akeNLf4k94GdRPop#SA^hJL|kAG36Z8B_Sw zkDoM}zGLBH1;yj6S@is2T0f@EUC-``fFIc z!_wXEvgbM*DLq>*^ZXI_BRe z^+;^TSP5AD!!66fDt~7WNMKZtQA|)|B--H6kSx%dKV2|;+cGfiM84;#Z#^zAFE<5B zT7P2N_wTZ@vabQH*nD^pNs?U1F0x8y{iDIZRZ<`GpYX!FW|DDVzx^msuK(@V z4B5LGxSS!=UU3Kt*45phZyT4jyduYVs5=fq{jzLjE}0|D7Wi0f6?!}wQSm5UAEn!A zQ)zb1mz8_lYRc&CeaX{ipR7i4eG)^!Aa@E=I7M{-c)iNkF^=l#5?EhH9p2gX;rX-i z{l4!f78Z-2k>lgao|pSB!}lLQendunO@B1>b)H3Tmv5UnpgS+d-Phip9gH5)f@Q8z z{=jeF2(P>w(z}O`io)-TcRf=>Pj?&WY&5i7_^AR&eop&KN=gQ|d8PHwMUoexqW$?E!L`^}us`U6-GL5s zXKH5lQFobJkh2p`;;w#7`nww5D7l(sA?+)nj=P|Lc49U^LzWvIHr!9UUcJV^uzmUn z3p?C+trYp7huMCAiYO23k9PmKqA3wA@+XE|Bo&OT#@@b9)tZeL6)oSK2MBpt?}T{h zLYBiz9(pWZKP=dNu%JjEre%TKO^u72I1D6mh#Sw7o6@q^d5`elf83)adV_;wk>1Y} za^K3k{t=5lH9i_D(BssajpVwW{29&?%L2aq)v*VVN_{_}ciUa3^*{B1%Mt`|}eewN%w%A&yyllpky>9Qy zrLN(aB{nVX($}`rBG(A(*C%%RjiYC8a29+j3aMkiZLSAY|0vV>QFYu_XfI^_ug#DX zY2!>FByGfuUMX|<=4=BXTZrH9P#IQFw;VyLL?rBmawpjB|HG#p3=#>exo($!dS*L>8L1WYxp@K(m~G zAqKfHp*lt4ETJ9_B_j-J#jK2-i08jDN!a$=51P+UPN0TAjsHq}9dYEn%n1hvCnF=% zxlv|%%|}tYRqJ*-O>W#N!23at^`XuYHtFjETFd#n_4xZ-#(usMEmnMNZ0vDw@|wwZ z(~HSDM$vDIV@_-vt;Jj@JsT~_)ZJg6ZBccm;v94l_$C)+41>pZ(y9S22<<2LDx+G= z;SlT%nEd0Ry&pc^rt#MQy=eP$-_V$qD5c#s{$13q$jYl2I__qiL5{23jQS7ft~O@< z%`pRV&vD~#glO0*vahvx=1Za;s=DN)7Bz4wWD3VTQXIB_hk;SwWhi_nTGhXm&FJ*1 zW$rno%;uoDsEnRxNwFhM8i#z92dOHXan!Zq$0r;hKiB$qK-JVDpLfKbE#0@0G@>og z!^5&OI;<8O{hp3JyYapr&g1u0WQlmUoKh}n_{w}ddGEZ>6C3)=79m*5i2eJ73%Z64 z4>+cb!^`jW?_v_Sj{A#dCtG4NoPyP`adu$+tB@%OqSy+YG+r&$d)_$o>Kr~@abbI* zQx=a=<|cT)!Nm$3FqVY$Z`6VIkk8MOA+e{!IPyoKygW7V$Gi3g4Oz3WgyJpen%fk| z#TVtoh{_1M3 z=K4PH@X3`(sur)72YJR20_i_+Tz6T3<)fm~zsua(92VupPS7QcjLL0Al&8;GUEoQN zJ2do_dUJm*_*r5K}+K1p7rJKN0vAV&QEVLF5t5vs4wd z!oJ6^!!WbR^B*F4i{e4?;L3=N{)!qK>;39$dj0w$hxk-yHs5riD@POwt)$>Yi+6Bn zBoSAOk&RE)ifiqXP#L!iI~xnT1dHUL@6FnFd58N#+?<->ihfgE0v`?qLG)FuVrNTp zg~0=W^+T#kB44LG4#|5@s2rHXeGsN+%nf6uQcM{1;xn>U3AEPOzKNA$bj-IPiZ%69 z%~?;#$~eZwZoay>k>e()rWD z!GTTqVwkJG;g4qou+qq(79Myuc0o@`IkT^?dWf653@L?$N%iJdF1ANhi5ax)*hqhl zUA|Igf}!i|F{{130@UwVr`ee)fk8<$SIPc8Q(+OjphAwg){m^szA8=DU%!4;d0cG! z#E4wYKL8)~8uR9Dz!7hrt-@|NfhO8RJ8&$rMzh3LuZOwD>UP~TRd-J^6&aqZRil=v z?a9psO2-6v4LkkTzM>K+rV2Q1cL2Ej0JXP2gTz+L&TF$6pO~2VlRm)`!>32J)c!vN zJ`{#WCMKq^&b>`fJNdH%Bi z<{!DElhX^U+%K(s*LYYngduX;34b?d;HvqGFTKJz+E+#MM*d)+Enwlvx5z(7y$ zIICrj_2x~dp4&?A`PPsYK!%_CB6A7~g7^3LC5wxT?FtJD^lyN_Zs0kq^@s$9yP(-* z{ty~?CO{GA&q%Vgzb4S}!_T`t-R@xiugUd`b_>5dI7|F!_j?_jDU|)l==ZSI{ketU z!x^#rjjMQe(Xi6#U;dqRr%-SlczBY)b31{8Nyy52VS6{Af4{az_Vs&w0)W-c72lTd zVuOU_snJGfaUK|yT_3fy0JIde>>t3^Mf4W37|%9ZX>qe#w=3H_-J2^6fyzxiZWtR0 z{b;VJ!0i9{80C3ld8&tat& z2?+s^fUN~bt*ws=# zj=jKIxe~F+YMeoD&`l`jy%KvcI2N*aL|Cgk~y zyOZg4jMby92K@mt1rEunp&hmc0eO@erbGt|u4_7&>ZLsI=;?!jFYdSA1Ah?n%I0V2 z0Gw$1X1p9BhcP70$w^znf1T6ahiaOfHwO(3?Zr0E&d&C`Kk3KfO0;%|t}Naf5z+AUZQ_s7e*rn~)8A|Y2+Op$+YNce0ab1uBL%fBTs#pEST z>A1`uCaR2k&qo2XZ@;T(Irgjt&16Rk0s*IRzt673nrCqmEe^I-$fz4ZAjyM82p387 z5x%v)@wV?jH8tGpKVEFq-GCdvSh-+0u<)iu0VnrGu4upACV9~Uq@VBRw!JFbT(^S4 zaB8!FwlQMJetL7b&<9L!!u9oaMkXc)nEoupR;f|^432W{>>rc zU6BQa7@U!O#ONR4TOH1RAJ_<3eADCA(!ki5`D~e1nNCAAxRDr8qfPD-~_P_m?fRx?%XE7-yFAH>s#}Z(9zMo2oB4xOEy5W3pUU62repOB=@;7YIZwO zbTEo*$I>!_gxw})Cprd*X=`jOk;bJqI|B=XzqGWpn9DJJoMC4%@@2>VQ%OX0TXvAH zg{&y}b~62YYb{Zv428#RL&)--dvFAcT5CQ=588215CRBp{7&Vux;Zd-{s7D)RD4!L zkUWvk6#hOuEYHls+$vc$7ulNElX1(=h9KdHZ$kU4KMmY2kn*Xs*0&4tvOE0Ex$Kb=UjgS;UH5y{jxYE2%l19!FFi-vnJqdx+HCz-*l-$O z3{XWvcXurS+qTk|Zuh8Z%V1wZdDW<{Q1qi z#gSh`Xqv3}hps|NczD~+{}P>-$3}kLMZ%g|AwC!=`~6gvmq!DsB8yaY5E^s&PtuM6 z-4=3wAD;X0Oc-(MZ|>|Fe06crRqCA$8T)_A=hQPE68|4?bglyOEI zO~EoQ=z@WKh(m-YDJvTW)60;`*|bv71mqY5a5QH9DbSH5#z7ebgXvb3#%E`nJT$p+ zxzo4UO!5&srvyK_!m5e0g|7P&Cpy#cC_m@Ms-OxX(~IEgE_Kx-hb|J3UjVfxenhM; zPzwqY7fuaqkECe1o02vgO=nvDa-Ff;{O+XnA{vlwwN=l9 z@(NJqzd`ozB?qTouK(8Q`N<7*ixk-edMMn{b4Cy^rGN}1^ZeF3x;KJAuEBOikyr>C=)lzcEW%#I}H`JnIq z7jdD@s{wT7Aq|5sbZ)C(8o*72<}zaA;n9J9n^>6TqwUx4KUA)M(DHbHJqEljFX1fT z_K)DC{{Z1EsYt@OS9MRNiDZx(Nr+jf%mUaA)9E6W+?x(smj?vQ|44QA>yAvge@3gl zMIU&?_Hg6nwB^S(5xKhn`u!#_(^|lQ=B35Gy!8fqMFYA-EK6nU$K1R;lH&|c({OJ9 zS>w~vSOf$FYAhzGJ^oEd)3LEBnGJvY3tun|jf36AUW_V~$Q@of>3H^eDuBJ1}9N*{a9F0i8euC1jH(_$mX zVq-ETY||qD?FJB{OZ!&jGID}AqgI10*dd=^k+rrBIxj&rkZVecqmh1D#i9~s0F6>W z|Ba~u>f}p`4#c|Vbe*Ee8etGepSg5T-|R#^vB>p&{N@C1!RzIEAi0s8n$L+*rJsM5 z%`2}F?X<3(^SUj8mJW}J-UB%K1_C-K|02CN zkOjffEYqwGVC#5@2jZX_bgQJaV*SNf>m#vaHF!MQmu;Nq-ycgaW^6TKzc`;$n)ouB zF7Qk$>dl`(ub{0Bh4SF}{@$yEcS+S1yKUBiVBq564JweWl4T zj=T$1&8^e>X7`H(AJy-Pf0Vg0^z5djmc7tN_qngp(JT{_r`~( z>y8w#1|O|?)2vsTq3Y$@bho#+HD1@Y9=BU5ra)iECM9JJ&y@kSwTbC_6X*@V_eXVg z*I|>*)B;)Xp8pOfxXMq|==Dwsd_+bFQ!y1RP`3kYQ3D*nFL^$ zZBU=SRoN4WPE1v$hN;GW9% z#dOpOzvDe-0iHfAO^eGxy$i_qIs%&F<d;K)*32(!zW1_B} z-2AB@V{&qGzQv8Lsi_I1UR8B;Kp5EarYIydyM(!<#F+CtusVW5LU1T3lz_;+x7r~F zkA#*DRLx&~eS&zB0*xjE@w;Hz`Yv}TggF|QL#7yK#sfLDU+n?;`haIux?_B1y@3d+*vDZRor&leNPK_Y&2-!DskyHs^7@(Qp$*_Wgi>_&nJiC00;K z>ieLC;nQM^;iDuum&l}g1Jnzcb^kbje}5dWt3!{cB9%PB7!tlwb^u&K#9jLV*;P^! z5isPJXlt2XOY-5Nb(uN~h{&1&7^&+#AqleKHQ+eB;5;xb-NAEu09CRMI4`%_YgUFq zy~a)4babZwLa}H@8^K{50cKkyg_zIt)zK0gjCR)TLkD71vTm@ToK|dTP`Hfd3g<90 zbzk$s-B~!#x*b1XNN}(Zr}3}QfR3lj%DaYDAC{S!8R>_7MOW8)AdwqXG_IqBg@ey~hPL8?DK`W_Cc{hcxpZdHaOCdAgagTa#|cS-q;-nV z#L9CL!{4~fqpcTeB#n)Y)n`SX`O>Ko;3U+bU+Po~6~%OYZnx?|v{+$sYN~hRtWL%i z3QYoRL-FZfp_pu?ewzs(=P#2Ltf=U3BiI#?l4J}&KYX~o*cxK%NCd&XYr(s(&nZ|) zk-WrFzDlyP3!*lQ+&ck?sL>WPliUixxy#SZ?Y7(8UEo6Zoqgz2hJ%M+eO!-V+XS8t zfuu^N(2N29Gx|slOm(Lze#BN(`xnsZH=>1vK`kY9cX#9S(Sr{qFoRDZ6nFlm_>Ws! zTG}@=HI*4mSiChQ2AxKOCiKHPf3n}>+(!|h>`g(ue5yW)MZ3%}_1FTV0KoM!RRFa6w-L#BDiKintUXLx`TX= ze54_eU*{@$Qh1>A=&Uw61-ZEdxj0!k*ag|yVLZPmux4 Q-~t#~NkvG-e})171N6wP3;+NC diff --git a/tests/testbed/src/testbed/resources/testbed-128.png b/tests/testbed/src/testbed/resources/testbed-128.png deleted file mode 100644 index b8d131fc20799cd29b7c61b25f19d75b5ed5beb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14514 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

M3m~?b>bSXCrK{!vS$o=6C{m~Ct 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 X|Nk4FqXHSg8=zz$iW1dgMuGnWT3I+M diff --git a/tests/testbed/src/testbed/resources/testbed-16.png b/tests/testbed/src/testbed/resources/testbed-16.png deleted file mode 100644 index ac5d46f9d09409a77cb763ed711c5fe14ded63c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1283 zcmZ`%X;2eJ6n+Ur0Sz!Jh}9~hLDY$a2t@8Jm~VXKirvpZ{L33H*eoJtBeQ_!drb~ z1pxS9T0j)awu{@s3`G~BO*Bg8JpV9%fToknJ~42pc4X0_!T@B>0NHr}^XMvj9N-WM zU?LHKQVy^>?R;CLHvl#>A~Ys&Y;3H(y?s&X=y<>XA4bV`j5`raCIT$r_iWbbbj?L6 zCX?x6W^AwnP9~F%jErPv$rctC@Yc3csnlpRO2p!;*KY85ypWI(8jVIE5D;2eScp)C zLV+Te%gxTtmdoX+?C$PXDwPEV1r_Q_sVoaIhlYj_#g${^I#)F9!eA6HuXY=O<* z&CSiZxw-DiSXZuv)BaV<{G1VACX?;Y2-p_40q4CDbw6+*{&#IfONp@i?qCp=tPpd% zD#eJ#WbwS+ZI1{Tf@G18r$feJnOd#pALJt_DA4C@nJVkp;ncKbVVZc8<7%x&b6k)l z5~a<|&i`~EE{tkdccD5bnJ*Fw2L?y@2h;p~DY2nW^yu}`p~Q@gL+bM?(Gh_#Rdm01 z;B=YxxKdqRYtZTR_4V}{jYg$Xp{bD}$ko!)(&FM`RQme*_Vx8mPfsHQ(fF;cZBL&* z8y+4;?|CrTmyj7L*YJ{0#m>#mY1=ezd>n~qMoPkw`AbW*XFLyX*p+JODa7s)V)T0b z=g*&CPrhn4=uHngbyw?(YO^?sNV$q55b(7%6{_Oghh5hvM+aLPw12fU^xSOG*Hvx) z7V=YIkB4<%r(texp2=jFC^DvAK2J!X`+0yiA7~V~cgbn|j;z0pIo#~}(j>chhUG;9 z?Qx)a!RTf9*bVPHV5Sr1I-x|ebgez+Vm>^*0;5+zodqg6)aC&*W?gj-eEAz@Z^KL% zsAQNV9-Zt0y$$fL1BMJxBsQnVhg4Klo-I3vjKW&DJVPEy)1$%{|MFLt**@cBPshZ7 z)H?Fj{4sNzYtyk?Mq3@t*Y$mTq_W4Jc%EvrrgMD9N|VuUY3_VJ_iaa{jaBQrU%cW! zOzTM&1+&lVoGcS=yqFw$GknG#?@U}r$uqSdwZ_v$&2R| z!2$j;4!x|zQ>V*He~5^QijCy3vDhVCkN3APO{fPc_%NdVsqB{UEGm`Cik1}W)^A@M z;rF=O&gI_2M?C|aB=()Vev536FZ1jeo# zoHr!N!^z!}7bbe+y&h$yQehn6^o$ZhR784{xU6BV>&o2r{1+1*g8ttgH;Q_>uFgKr zONhcMV*KTl$4!#{sqe2H{qku!7Nd~QqzwK1F`>$oo}E;eB!9xm9?u^-&ct{{5|>4E#x*^G1!g(c>o1Z0xicSMpW(ak-EOrp5& qr0gVbcOy|qq?0|AbN>^h2-$qD^y`8zMLFA$0D=R<19W>5bN&OG=PK_2 diff --git a/tests/testbed/src/testbed/resources/testbed-167.png b/tests/testbed/src/testbed/resources/testbed-167.png deleted file mode 100644 index bf1f9ac47416cfe5547750519edcf8719fc5d3dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20060 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 Tg-cPJ@c@^Zu{o{6$R+AO5-lQ4 diff --git a/tests/testbed/src/testbed/resources/testbed-20.png b/tests/testbed/src/testbed/resources/testbed-20.png deleted file mode 100644 index 80c1614c8844c52ee1660685d4cdafec28ff00b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1569 zcmV++2HyFJP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv0RI600RN!9r;`8x00(qQO+^Rh1qKE+1K#&-EdT%oB}qgX$ZddT}}( zA78loZvI~;fIuLyIv5Oo(9+V9TU}j^)9J+H@lab^i!?jSMCb}tciqO~(h>%S25vrj z=D6*gJaaT1OQ^F2R`iMKYe2y;3~}@3%^kJ1wN3T)^*2o1*RzNAo~`5+SkZKqjG97D zWt31j?`4Z6vp3*(A({+y9n}8wh(NZYC|crNs^=+fT>i9sgQ<}&LYbylVeL_TaY9-H_1ZJ z^vcSrvEK5Alb_3S)?=Rxem$>YS67!_S6Am49UXn5rltmy$#e}kG&Dqsql}t6S5lB& zO18yCCS}DW+0%gZ*Ql+e_&~jX<~sC{)_vTd`mLg$JsMIpr^4Fxw#Oy z%E=Qyk)7{C%yn}1_w)4sbj+{JhK^l4^*uXERP=#S%z_}WfB*ivwzf}R_7uiQ#KE2q z+1X$-_c1frgl=!7xzUSqwI4mS8>_yY7aQ&-FrMgp{Ed&#uY9N)OW`fR1w_Lzh{xmJ z8EIyrVd(R27+@G+wd!~Z595$GQf_;jJ1WmnYVW`)KgVA~Z3JT`PiNd%?=1ZM8;o7in3%RuR#v)9PQ^c5 zzN+@Ek3MR%R#sLb#^Z6iySsVl!3Pe@dP!(%8pP}KE!YHg z+;k~?F%gkMMT^`^L|K+G=jG*L7zV%}Jv}`KCMJT-N@ngqs1Cjo@OkIAl_K!!j%@C) zUBjF-lPU7YY`SfM-ge?ZTAWMFpDaldM~)oPnwy($R_ev&cB^VmwxMaNWc+eQ*L58;fZcAVzrUZ+QU8_J)^|7V{J^ngsH1u zjbGl=zWep=?(R?fPM&^b^y<{j1_<`WMPut~?seMjd8;NTC(Dz`q@}=KkSMY)u^5^l z$#d!dfY(FA;V^(YH#axXwrl5rs;bxg+5NG>mMvS}ud1qg(B*PHnMfoSp^G!=bSe^! zM&TcYxE@+uT#O(HG&MDm$z(7L<6qMd1OZ8suKIkwFB=*fUKk!8eoYj`^N~ozKRG!G zz~4tGY*P>kKb-&o03~!qSaf7zbY(hYa%Ew3WdJfTGBPbNGc7SOR4_L>GBP?eF)J`I zIxsM+gBjxh001R)MObuXVRU6WZEs|0W_bWIFfleQFgGnRG*mG#Iy5snGdU|TFgh?W Ty^7kq00000NkvXXu0mjf=pNtS diff --git a/tests/testbed/src/testbed/resources/testbed-256.png b/tests/testbed/src/testbed/resources/testbed-256.png deleted file mode 100644 index c2c8c72b626995ee971b817489d5ac2339fbcfdb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33111 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 E54+Xq?EnA( diff --git a/tests/testbed/src/testbed/resources/testbed-29.png b/tests/testbed/src/testbed/resources/testbed-29.png deleted file mode 100644 index 016229c368ab7b38e764c5ca0a4d6d3eeded5ab5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2342 zcmV+>3EB3EP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 bFMAiqp#QIMnGh*TRsgg$Q5tpX){*}OM>Smk diff --git a/tests/testbed/src/testbed/resources/testbed-512.png b/tests/testbed/src/testbed/resources/testbed-512.png deleted file mode 100644 index 7b15c675fcb54f3803c772b74b66a9ed869b177b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74433 zcmXV11z6S1)4ilL(v5UCm+tOX2|>C$q(M5QyGu%t?vRjfq+38hL`u5hzr5e4kAM$w ze|L9h&YW{*Hd0+x4jqLU1pKwB6PK*11!6r>IRkqHG+n$z&# z-ws1um<}fmb*#w5twaL+#n5qq-KAkKZ$S_3jxI6pUk+E>8Allcle=0ob6Rt6c4y1V zuSAZ$Chvk|3#Y80#4-PU$}EUebccbKg-1ZEiS6;m=kkYD7|*Tss{tr7{)>2Ivj$z4Ku&SWc#p9}xE6@z zjAM>U6nX^gW(n=|IT3_7Krg>)ToWM~j=bAr44W)A=u4T%0qHuqK)00<{K-nMimec| zIWkAb2ih#&rPpNnM580Mz_yIoFt$;|CyuxLc z);@S|ZodzCL!A2>`G{Yv^hN@)HyG4cEy@ro3TQ$ymwRQEhl%BEv-)h~eD!?p#?Slv z`I|(PO%x?$vWnpuKKuE+hWo8`Vc#Mt@E-Y$9;~=RXD=T&6#h=4rHdoTm zeJpS(4SFc_5nJ7C7h&V2IM42RfzUxD{NLb_qLA#KD|NoOusH@b`} z**7*tZbVQ+u7i3&OP4NO6p@l&CW2;i$oVP#*9Yv{%X9=YEL%*==bxntXtrKbst}pS zvduy!O8hqwmbG@xW~_QC&9cUb>T86wpOKk04qHYlgh`wLt- zS_9`^9PK>$Swv&MLf_c!VCKJY;&CaY{)VPOUXw$O=(mW_%w#FCgy0ZUYnWCB}) zzd>e%SG$>&{HkrrDh%!xS=l)gJ|-%i(F?{2%ZZm$u9iiwbjMncGcqF|6AMSU8i!j; zduEgf(Vm5!Is(1X7oYz{y7U;eVe{aLPG6YSr+}+~ojzZr@_?GR@X*Zd{Jd|t#R@;R zVU;3~@1r6!QgYD5+Yw-4h>3~+-S}QVJf^C3t*h~q(hwWvY=*-hmBkG zIy5)?w&}A?&&&)D4N-`Qul!Ev5%RjLj);4h< zY!;XLaBFGBm&F_87;rxq6u?w3tW0B!+fT2O|MwxxxWN{@{`@q#{R#~=b@1w{zCPQ6 zctiNhtWPaOo|R=Hyl^pkSU;=~@gMq%U)m2O48jq>=E0=$_#Mxj>7<65>s~+jfBN)^ zfr;sJMMd=VwC2d@XjxU&Q5~$2?%UDYI6T19v%TeuJQ>0i31-4GqIZ3Q6f4#;^{}?ZqOu zI`XJa@YjT%U#*Fpp3q|~T0ZC1(acz|2;?j-o}_dWL}Vm$-~A#jvHo@8ec~ycoR(H^ zyTImtxZHoV)-K@rj{+SX9jp>I5c1t9-U=#cbm1YG)3W9;Jv}Y2rdA3bx;Inm+Jj8* z1paZj{#KTaB~^I)7JqMVôiE}+bq#Fn1mt2Hb6Wl| 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 C8xslu diff --git a/tests/testbed/src/testbed/resources/testbed-58.png b/tests/testbed/src/testbed/resources/testbed-58.png deleted file mode 100644 index 0ecb9f1c279875bf58ad458966411e80e056b4e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5517 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 N6$N#8t-M*t{{WS0_3i)w diff --git a/tests/testbed/src/testbed/resources/testbed-64.png b/tests/testbed/src/testbed/resources/testbed-64.png deleted file mode 100644 index a3cd3614c1bf40f14d19e781dc86df5ea85bb8b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6229 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 diff --git a/tests/testbed/src/testbed/resources/testbed-76.png b/tests/testbed/src/testbed/resources/testbed-76.png deleted file mode 100644 index 70aca2ec942c8a94d9fbf3c6ba2f9dcf43d3547b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7677 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 W|DSLl7xEZ90Z@`tL)5%53;7=tkARZ^ diff --git a/tests/testbed/src/testbed/resources/testbed-80.png b/tests/testbed/src/testbed/resources/testbed-80.png deleted file mode 100644 index b0a3f1a9cc02091ddfbe728f6bc59015df6f3a29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8234 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 JmG2D#{s%tUbRz%& diff --git a/tests/testbed/src/testbed/resources/testbed-87.png b/tests/testbed/src/testbed/resources/testbed-87.png deleted file mode 100644 index cfe22b2d76ad0e29b40febd195bd6dbdf6e40e1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8826 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 Date: Wed, 7 Sep 2022 10:04:26 +0800 Subject: [PATCH 33/37] Update to Python 3.10.7, and remove vestigial patch pieces. --- Makefile | 2 +- patch/Python/Python.patch | 6102 +++++-------------------------------- 2 files changed, 824 insertions(+), 5280 deletions(-) diff --git a/Makefile b/Makefile index 3761b9fc..fef26630 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ BUILD_NUMBER=custom # PYTHON_VERSION is the full version number (e.g., 3.10.0b3) # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.10.6 +PYTHON_VERSION=3.10.7 PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index b4b449ec..0178a4b4 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,49 +1,3 @@ -diff --git a/Doc/library/os.rst b/Doc/library/os.rst -index 6694768419..59b4feab56 100644 ---- a/Doc/library/os.rst -+++ b/Doc/library/os.rst -@@ -3303,6 +3303,13 @@ - - .. versionadded:: 3.8 - -+.. data:: allows_subprocesses -+ -+ Boolean that describes whether subprocesses can be by the operating system. -+ Some platforms (e.g., iOS mobile devices) *implement* calls like -+ :func:`execv` and :func:`spawnv`, but will raise errors or break if -+ called. Calls to create subprocesses should only be invoked if -+ :data:`os.allows_subprocesses` is `True`. - - .. data:: MFD_CLOEXEC - MFD_ALLOW_SEALING -diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst -index 45fcfc7952..04ea3b488e 100644 ---- a/Doc/library/subprocess.rst -+++ b/Doc/library/subprocess.rst -@@ -25,6 +25,11 @@ - - :pep:`324` -- PEP proposing the subprocess module - -+:mod:`subprocess` can only be used on platforms that support subprocess -+creation. Some platforms (especially mobile platforms) may not support -+subprocesses; if :data:`os.allows_subprocesses` is `False`, any calls in -+this module that request a subprocess be created will raise a -+:exc:`RuntimeError`. - - Using the :mod:`subprocess` Module - ---------------------------------- -diff --git a/Include/datetime.h b/Include/datetime.h -index bb565201a1..c01db0cf51 100644 ---- a/Include/datetime.h -+++ b/Include/datetime.h -@@ -187,7 +187,6 @@ - - #define PyDateTime_CAPSULE_NAME "datetime.datetime_CAPI" - -- - /* This block is only used as part of the public API and should not be - * included in _datetimemodule.c, which does not use the C API capsule. - * See bpo-35081 for more details. --- /dev/null +++ b/Lib/_ios_support.py @@ -0,0 +1,36 @@ @@ -83,480 +37,6 @@ index bb565201a1..c01db0cf51 100644 + model = objc.objc_msgSend(systemModel, SEL_UTF8String).decode() + + return system, release, model -diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py -index f9d27cb89d..da172b345a 100644 ---- a/Lib/ctypes/test/test_as_parameter.py -+++ b/Lib/ctypes/test/test_as_parameter.py -@@ -1,9 +1,11 @@ -+import os - import unittest - from ctypes import * - from ctypes.test import need_symbol - import _ctypes_test - --dll = CDLL(_ctypes_test.__file__) -+ -+dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - try: - CALLBACK_FUNCTYPE = WINFUNCTYPE -diff --git a/Lib/ctypes/test/test_bitfields.py b/Lib/ctypes/test/test_bitfields.py -index 66acd62e68..95216920e6 100644 ---- a/Lib/ctypes/test/test_bitfields.py -+++ b/Lib/ctypes/test/test_bitfields.py -@@ -25,7 +25,7 @@ - ("R", c_short, 6), - ("S", c_short, 7)] - --func = CDLL(_ctypes_test.__file__).unpack_bitfields -+func = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])).unpack_bitfields - func.argtypes = POINTER(BITS), c_char - - ##for n in "ABCDEFGHIMNOPQRS": -diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py -index d8e9c5a760..52820932b0 100644 ---- a/Lib/ctypes/test/test_callbacks.py -+++ b/Lib/ctypes/test/test_callbacks.py -@@ -160,7 +160,7 @@ - - def test_integrate(self): - # Derived from some then non-working code, posted by David Foster -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - # The function prototype called by 'integrate': double func(double); - CALLBACK = CFUNCTYPE(c_double, c_double) -@@ -211,7 +211,7 @@ - def test_callback_register_int(self): - # Issue #8275: buggy handling of callback args under Win64 - # NOTE: should be run on release builds as well -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - CALLBACK = CFUNCTYPE(c_int, c_int, c_int, c_int, c_int, c_int) - # All this function does is call the callback with its args squared - func = dll._testfunc_cbk_reg_int -@@ -227,7 +227,7 @@ - def test_callback_register_double(self): - # Issue #8275: buggy handling of callback args under Win64 - # NOTE: should be run on release builds as well -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - CALLBACK = CFUNCTYPE(c_double, c_double, c_double, c_double, - c_double, c_double) - # All this function does is call the callback with its args squared -diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py -index ac2240fa19..b08b5f7581 100644 ---- a/Lib/ctypes/test/test_cfuncs.py -+++ b/Lib/ctypes/test/test_cfuncs.py -@@ -1,6 +1,7 @@ - # A lot of failures in these tests on Mac OS X. - # Byte order related? - -+import os - import unittest - from ctypes import * - from ctypes.test import need_symbol -@@ -8,7 +9,7 @@ - import _ctypes_test - - class CFunctions(unittest.TestCase): -- _dll = CDLL(_ctypes_test.__file__) -+ _dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - def S(self): - return c_longlong.in_dll(self._dll, "last_tf_arg_s").value -@@ -206,7 +207,7 @@ - - @need_symbol('WinDLL') - class stdcallCFunctions(CFunctions): -- _dll = stdcall_dll(_ctypes_test.__file__) -+ _dll = stdcall_dll(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - if __name__ == '__main__': - unittest.main() -diff --git a/Lib/ctypes/test/test_checkretval.py b/Lib/ctypes/test/test_checkretval.py -index e9567dc391..3c3fbf103c 100644 ---- a/Lib/ctypes/test/test_checkretval.py -+++ b/Lib/ctypes/test/test_checkretval.py -@@ -1,3 +1,4 @@ -+import os - import unittest - - from ctypes import * -@@ -14,7 +15,7 @@ - def test_checkretval(self): - - import _ctypes_test -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - self.assertEqual(42, dll._testfunc_p_p(42)) - - dll._testfunc_p_p.restype = CHECKED -diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py -index e0b9b54e97..a5b1b55294 100644 ---- a/Lib/ctypes/test/test_funcptr.py -+++ b/Lib/ctypes/test/test_funcptr.py -@@ -1,4 +1,4 @@ --import unittest -+import os, unittest - from ctypes import * - - try: -@@ -8,7 +8,10 @@ - WINFUNCTYPE = CFUNCTYPE - - import _ctypes_test --lib = CDLL(_ctypes_test.__file__) -+ -+ -+lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) -+ - - class CFuncPtrTestCase(unittest.TestCase): - def test_basic(self): -diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py -index bdb044e594..624ce9a388 100644 ---- a/Lib/ctypes/test/test_functions.py -+++ b/Lib/ctypes/test/test_functions.py -@@ -7,6 +7,7 @@ - - from ctypes import * - from ctypes.test import need_symbol -+import os - import sys, unittest - - try: -@@ -16,7 +17,9 @@ - WINFUNCTYPE = CFUNCTYPE - - import _ctypes_test --dll = CDLL(_ctypes_test.__file__) -+ -+ -+dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - if sys.platform == "win32": - windll = WinDLL(_ctypes_test.__file__) - -diff --git a/Lib/ctypes/test/test_libc.py b/Lib/ctypes/test/test_libc.py -index 56285b5ff8..f78a152ade 100644 ---- a/Lib/ctypes/test/test_libc.py -+++ b/Lib/ctypes/test/test_libc.py -@@ -1,9 +1,12 @@ -+import os - import unittest - - from ctypes import * - import _ctypes_test - --lib = CDLL(_ctypes_test.__file__) -+ -+lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) -+ - - def three_way_cmp(x, y): - """Return -1 if x < y, 0 if x == y and 1 if x > y""" -diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py -index 38af7ac13d..db0d4986d6 100644 ---- a/Lib/ctypes/test/test_parameters.py -+++ b/Lib/ctypes/test/test_parameters.py -@@ -140,7 +140,7 @@ - import _ctypes_test - from ctypes import CDLL, c_void_p, ArgumentError - -- func = CDLL(_ctypes_test.__file__)._testfunc_p_p -+ func = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))._testfunc_p_p - func.restype = c_void_p - # TypeError: has no from_param method - self.assertRaises(TypeError, setattr, func, "argtypes", (object,)) -diff --git a/Lib/ctypes/test/test_pickling.py b/Lib/ctypes/test/test_pickling.py -index c4a79b9779..833608b629 100644 ---- a/Lib/ctypes/test/test_pickling.py -+++ b/Lib/ctypes/test/test_pickling.py -@@ -1,8 +1,11 @@ - import unittest -+import os - import pickle - from ctypes import * - import _ctypes_test --dll = CDLL(_ctypes_test.__file__) -+ -+ -+dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - class X(Structure): - _fields_ = [("a", c_int), ("b", c_double)] -diff --git a/Lib/ctypes/test/test_pointers.py b/Lib/ctypes/test/test_pointers.py -index e97515879f..d678be3800 100644 ---- a/Lib/ctypes/test/test_pointers.py -+++ b/Lib/ctypes/test/test_pointers.py -@@ -1,3 +1,4 @@ -+import os - import unittest, sys - - from ctypes import * -@@ -20,7 +21,7 @@ - self.assertRaises(TypeError, A, c_ulong(33)) - - def test_pass_pointers(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - func = dll._testfunc_p_p - if sizeof(c_longlong) == sizeof(c_void_p): - func.restype = c_longlong -@@ -38,7 +39,7 @@ - self.assertEqual(res[0], 12345678) - - def test_change_pointers(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - func = dll._testfunc_p_p - - i = c_int(87654) -@@ -77,7 +78,7 @@ - return 0 - callback = PROTOTYPE(func) - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - # This function expects a function pointer, - # and calls this with an integer pointer as parameter. - # The int pointer points to a table containing the numbers 1..10 -@@ -143,7 +144,7 @@ - - def test_charpp(self): - """Test that a character pointer-to-pointer is correctly passed""" -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - func = dll._testfunc_c_p_p - func.restype = c_char_p - argv = (c_char_p * 2)() -diff --git a/Lib/ctypes/test/test_prototypes.py b/Lib/ctypes/test/test_prototypes.py -index cd0c649de3..539351f798 100644 ---- a/Lib/ctypes/test/test_prototypes.py -+++ b/Lib/ctypes/test/test_prototypes.py -@@ -1,3 +1,4 @@ -+import os - from ctypes import * - from ctypes.test import need_symbol - import unittest -@@ -23,7 +24,9 @@ - # In this case, there would have to be an additional reference to the argument... - - import _ctypes_test --testdll = CDLL(_ctypes_test.__file__) -+ -+ -+testdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - # Return machine address `a` as a (possibly long) non-negative integer. - # Starting with Python 2.5, id(anything) is always non-negative, and -diff --git a/Lib/ctypes/test/test_refcounts.py b/Lib/ctypes/test/test_refcounts.py -index f2edfa6400..0e4dd7c126 100644 ---- a/Lib/ctypes/test/test_refcounts.py -+++ b/Lib/ctypes/test/test_refcounts.py -@@ -1,3 +1,4 @@ -+import os - import unittest - from test import support - import ctypes -@@ -7,7 +8,10 @@ - OtherCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_ulonglong) - - import _ctypes_test --dll = ctypes.CDLL(_ctypes_test.__file__) -+ -+ -+dll = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) -+ - - class RefcountTestCase(unittest.TestCase): - -diff --git a/Lib/ctypes/test/test_returnfuncptrs.py b/Lib/ctypes/test/test_returnfuncptrs.py -index 1974f40df6..7b76fae44c 100644 ---- a/Lib/ctypes/test/test_returnfuncptrs.py -+++ b/Lib/ctypes/test/test_returnfuncptrs.py -@@ -1,5 +1,6 @@ - import unittest - from ctypes import * -+import os - - import _ctypes_test - -@@ -8,7 +9,7 @@ - def test_with_prototype(self): - # The _ctypes_test shared lib/dll exports quite some functions for testing. - # The get_strchr function returns a *pointer* to the C strchr function. -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - get_strchr = dll.get_strchr - get_strchr.restype = CFUNCTYPE(c_char_p, c_char_p, c_char) - strchr = get_strchr() -@@ -20,7 +21,7 @@ - self.assertRaises(TypeError, strchr, b"abcdef") - - def test_without_prototype(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - get_strchr = dll.get_strchr - # the default 'c_int' would not work on systems where sizeof(int) != sizeof(void *) - get_strchr.restype = c_void_p -@@ -34,7 +35,7 @@ - self.assertRaises(TypeError, strchr, b"abcdef") - - def test_from_dll(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - # _CFuncPtr instances are now callable with a tuple argument - # which denotes a function name and a dll: - strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)(("my_strchr", dll)) -@@ -50,13 +51,13 @@ - if key == 0: - return "my_strchr" - if key == 1: -- return CDLL(_ctypes_test.__file__) -+ return CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - raise IndexError - - # _CFuncPtr instances are now callable with a tuple argument - # which denotes a function name and a dll: - strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)( -- BadSequence(("my_strchr", CDLL(_ctypes_test.__file__)))) -+ BadSequence(("my_strchr", CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))))) - self.assertTrue(strchr(b"abcdef", b"b"), "bcdef") - self.assertEqual(strchr(b"abcdef", b"x"), None) - self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0) -diff --git a/Lib/ctypes/test/test_slicing.py b/Lib/ctypes/test/test_slicing.py -index a3932f1767..6d7bfff8f2 100644 ---- a/Lib/ctypes/test/test_slicing.py -+++ b/Lib/ctypes/test/test_slicing.py -@@ -1,3 +1,4 @@ -+import os - import unittest - from ctypes import * - from ctypes.test import need_symbol -@@ -62,7 +63,7 @@ - def test_char_ptr(self): - s = b"abcdefghijklmnopqrstuvwxyz" - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - dll.my_strdup.restype = POINTER(c_char) - dll.my_free.restype = None - res = dll.my_strdup(s) -@@ -94,7 +95,7 @@ - dll.my_free(res) - - def test_char_ptr_with_free(self): -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - s = b"abcdefghijklmnopqrstuvwxyz" - - class allocated_c_char_p(c_char_p): -@@ -130,7 +131,7 @@ - def test_wchar_ptr(self): - s = "abcdefghijklmnopqrstuvwxyz\0" - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - dll.my_wcsdup.restype = POINTER(c_wchar) - dll.my_wcsdup.argtypes = POINTER(c_wchar), - dll.my_free.restype = None -diff --git a/Lib/ctypes/test/test_stringptr.py b/Lib/ctypes/test/test_stringptr.py -index c20951f4ce..fde0eef1c7 100644 ---- a/Lib/ctypes/test/test_stringptr.py -+++ b/Lib/ctypes/test/test_stringptr.py -@@ -1,10 +1,12 @@ -+import os - import unittest - from test import support - from ctypes import * - - import _ctypes_test - --lib = CDLL(_ctypes_test.__file__) -+ -+lib = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - class StringPtrTestCase(unittest.TestCase): - -diff --git a/Lib/ctypes/test/test_unicode.py b/Lib/ctypes/test/test_unicode.py -index 60c75424b7..8c008b466b 100644 ---- a/Lib/ctypes/test/test_unicode.py -+++ b/Lib/ctypes/test/test_unicode.py -@@ -1,3 +1,4 @@ -+import os - import unittest - import ctypes - from ctypes.test import need_symbol -@@ -7,7 +8,7 @@ - @need_symbol('c_wchar') - class UnicodeTestCase(unittest.TestCase): - def test_wcslen(self): -- dll = ctypes.CDLL(_ctypes_test.__file__) -+ dll = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - wcslen = dll.my_wcslen - wcslen.argtypes = [ctypes.c_wchar_p] - -@@ -34,7 +35,7 @@ - t.unicode = "foo\0bar\0\0" - - --func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p -+func = ctypes.CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE']))._testfunc_p_p - - class StringTestCase(UnicodeTestCase): - def setUp(self): -diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py -index 7514fe84ff..ecb3364e50 100644 ---- a/Lib/ctypes/test/test_values.py -+++ b/Lib/ctypes/test/test_values.py -@@ -2,6 +2,7 @@ - A testcase which accesses *values* in a dll. - """ - -+import os - import unittest - import sys - from ctypes import * -@@ -13,7 +14,7 @@ - def test_an_integer(self): - # This test checks and changes an integer stored inside the - # _ctypes_test dll/shared lib. -- ctdll = CDLL(_ctypes_test.__file__) -+ ctdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - an_integer = c_int.in_dll(ctdll, "an_integer") - x = an_integer.value - self.assertEqual(x, ctdll.get_an_integer()) -@@ -25,7 +26,7 @@ - self.assertEqual(x, ctdll.get_an_integer()) - - def test_undefined(self): -- ctdll = CDLL(_ctypes_test.__file__) -+ ctdll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") - - class PythonValuesTestCase(unittest.TestCase): -diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py -index e51bdc8ad6..78ba8e6786 100644 ---- a/Lib/ctypes/test/test_win32.py -+++ b/Lib/ctypes/test/test_win32.py -@@ -1,6 +1,7 @@ - # Windows specific tests - - from ctypes import * -+import os - import unittest, sys - from test import support - -@@ -102,7 +103,7 @@ - ("right", c_long), - ("bottom", c_long)] - -- dll = CDLL(_ctypes_test.__file__) -+ dll = CDLL(getattr(_ctypes_test, '__file__', os.environ['TEST_EXECUTABLE'])) - - pt = POINT(15, 25) - left = c_long.in_dll(dll, 'left') diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 0c2510e161..6c3c43f11d 100644 --- a/Lib/ctypes/util.py @@ -571,54 +51,78 @@ index 0c2510e161..6c3c43f11d 100644 def find_library(name): possible = ['lib%s.dylib' % name, diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py -index 8e7364d2a2..01d9e08de8 100644 +index 8e7364d2a2..eec325fecc 100644 --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py -@@ -56,6 +56,8 @@ +@@ -15,7 +15,7 @@ + + import unittest + from test import support +-from test.support import os_helper ++from test.support import os_helper, has_subprocess_support + from test.support.script_helper import assert_python_ok + + # http://bugs.python.org/issue4373 +@@ -56,6 +56,7 @@ def build_ext(self, *args, **kwargs): return build_ext(*args, **kwargs) -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_build_ext(self): cmd = support.missing_compiler_executable() if cmd is not None: -@@ -332,6 +334,8 @@ +@@ -332,6 +333,7 @@ cmd.run() self.assertEqual(cmd.compiler, 'unix') -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_get_outputs(self): cmd = support.missing_compiler_executable() if cmd is not None: diff --git a/Lib/distutils/tests/test_build_py.py b/Lib/distutils/tests/test_build_py.py -index 0712e92c6a..52a0121250 100644 +index 0712e92c6a..4c05e8d968 100644 --- a/Lib/distutils/tests/test_build_py.py +++ b/Lib/distutils/tests/test_build_py.py -@@ -106,6 +106,8 @@ +@@ -9,7 +9,7 @@ + from distutils.errors import DistutilsFileError + + from distutils.tests import support +-from test.support import run_unittest ++from test.support import run_unittest, has_subprocess_support + + + class BuildPyTestCase(support.TempdirManager, +@@ -106,6 +106,7 @@ ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]) @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_byte_compile_optimized(self): project_dir, dist = self.create_dist(py_modules=['boiledeggs']) os.chdir(project_dir) diff --git a/Lib/distutils/tests/test_config_cmd.py b/Lib/distutils/tests/test_config_cmd.py -index 0127ba71fc..bc3ae7f23a 100644 +index 0127ba71fc..d03356af1b 100644 --- a/Lib/distutils/tests/test_config_cmd.py +++ b/Lib/distutils/tests/test_config_cmd.py +@@ -3,7 +3,7 @@ + import os + import sys + import sysconfig +-from test.support import run_unittest, missing_compiler_executable ++from test.support import run_unittest, missing_compiler_executable, has_subprocess_support + + from distutils.command.config import dump_file, config + from distutils.tests import support @@ -42,6 +42,7 @@ self.assertEqual(len(self._logs), numlines+1) @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_search_cpp(self): cmd = missing_compiler_executable(['preprocessor']) if cmd is not None: diff --git a/Lib/distutils/tests/test_cygwinccompiler.py b/Lib/distutils/tests/test_cygwinccompiler.py -index 9dc869de4c..176a87f8a8 100644 +index 9dc869de4c..9a1b18aba9 100644 --- a/Lib/distutils/tests/test_cygwinccompiler.py +++ b/Lib/distutils/tests/test_cygwinccompiler.py @@ -5,11 +5,14 @@ @@ -649,78 +153,110 @@ index 9dc869de4c..176a87f8a8 100644 class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase): -@@ -118,7 +122,7 @@ - def test_get_msvcr(self): - - # none -- sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' -+ sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' - '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEqual(get_msvcr(), None) - diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py -index 0632024b35..1cd0f80103 100644 +index 0632024b35..a21962792b 100644 --- a/Lib/distutils/tests/test_install.py +++ b/Lib/distutils/tests/test_install.py -@@ -208,6 +208,8 @@ +@@ -5,7 +5,7 @@ + import unittest + import site + +-from test.support import captured_stdout, run_unittest ++from test.support import captured_stdout, run_unittest, has_subprocess_support + + from distutils import sysconfig + from distutils.command.install import install, HAS_USER_SITE +@@ -208,6 +208,7 @@ 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_record_extensions(self): cmd = test_support.missing_compiler_executable() if cmd is not None: diff --git a/Lib/distutils/tests/test_install_lib.py b/Lib/distutils/tests/test_install_lib.py -index fda6315bbc..63c71a8d46 100644 +index fda6315bbc..121664b722 100644 --- a/Lib/distutils/tests/test_install_lib.py +++ b/Lib/distutils/tests/test_install_lib.py -@@ -35,6 +35,8 @@ +@@ -8,7 +8,7 @@ + from distutils.extension import Extension + from distutils.tests import support + from distutils.errors import DistutilsOptionError +-from test.support import run_unittest ++from test.support import run_unittest, has_subprocess_support + + + class InstallLibTestCase(support.TempdirManager, +@@ -35,6 +35,7 @@ self.assertEqual(cmd.optimize, 2) @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_byte_compile(self): project_dir, dist = self.create_dist() os.chdir(project_dir) diff --git a/Lib/distutils/tests/test_spawn.py b/Lib/distutils/tests/test_spawn.py -index 4ec767b120..b75a8f8a14 100644 +index 4ec767b120..10003aa99e 100644 --- a/Lib/distutils/tests/test_spawn.py +++ b/Lib/distutils/tests/test_spawn.py -@@ -15,8 +15,8 @@ +@@ -3,7 +3,7 @@ + import stat + import sys + import unittest.mock +-from test.support import run_unittest, unix_shell ++from test.support import run_unittest, unix_shell, has_subprocess_support + from test.support import os_helper + + from distutils.spawn import find_executable +@@ -15,8 +15,7 @@ support.LoggingSilencer, unittest.TestCase): - @unittest.skipUnless(os.name in ('nt', 'posix'), - 'Runs only under posix or nt') -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_spawn(self): tmpdir = self.mkdtemp() diff --git a/Lib/distutils/tests/test_sysconfig.py b/Lib/distutils/tests/test_sysconfig.py -index 59676b0e0b..8a31bbdf61 100644 +index 59676b0e0b..6bbeea0df7 100644 --- a/Lib/distutils/tests/test_sysconfig.py +++ b/Lib/distutils/tests/test_sysconfig.py +@@ -10,7 +10,7 @@ + from distutils import sysconfig + from distutils.ccompiler import get_default_compiler + from distutils.tests import support +-from test.support import run_unittest, swap_item ++from test.support import run_unittest, swap_item, has_subprocess_support + from test.support.os_helper import TESTFN + from test.support.warnings_helper import check_warnings + @@ -247,6 +247,7 @@ self.assertIsNotNone(vars['SO']) self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_customize_compiler_before_get_config_vars(self): # Issue #21923: test that a Distribution compiler # instance can be called without an explicit call to diff --git a/Lib/distutils/tests/test_util.py b/Lib/distutils/tests/test_util.py -index d4a01c6e91..d687e2f44f 100644 +index d4a01c6e91..f3fc4607e1 100644 --- a/Lib/distutils/tests/test_util.py +++ b/Lib/distutils/tests/test_util.py -@@ -234,6 +234,8 @@ +@@ -3,7 +3,7 @@ + import sys + import unittest + from copy import copy +-from test.support import run_unittest ++from test.support import run_unittest, has_subprocess_support + from unittest import mock + + from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError +@@ -234,6 +234,7 @@ # XXX platforms to be covered: mac -+ @unittest.skipUnless(os.name == 'nt' or (os.name == 'posix' and hasattr(os, 'fork') and os.allows_subprocesses), -+ "distutils cannot spawn child processes") ++ @unittest.skipUnless(has_subprocess_support, "distutils cannot spawn child processes") def test_check_environ(self): util._environ_checked = 0 os.environ.pop('HOME', None) @@ -737,33 +273,6 @@ index 49bcaea78d..891356e54d 100644 _CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) -diff --git a/Lib/os.py b/Lib/os.py -index d26cfc9993..ae6629424e 100644 ---- a/Lib/os.py -+++ b/Lib/os.py -@@ -36,7 +36,7 @@ - __all__ = ["altsep", "curdir", "pardir", "sep", "pathsep", "linesep", - "defpath", "name", "path", "devnull", "SEEK_SET", "SEEK_CUR", - "SEEK_END", "fsencode", "fsdecode", "get_exec_path", "fdopen", -- "extsep"] -+ "extsep", "allows_subprocesses"] - - def _exists(name): - return name in globals() -@@ -830,6 +830,13 @@ - fsencode, fsdecode = _fscodec() - del _fscodec - -+ -+if sys.platform in ('ios', 'tvos', 'watchos'): -+ allows_subprocesses = False -+else: -+ allows_subprocesses = True -+ -+ - # Supply spawn*() (probably only for Unix) - if _exists("fork") and not _exists("spawnv") and _exists("execv"): - diff --git a/Lib/platform.py b/Lib/platform.py index e32f9c11cd..be08f544e7 100755 --- a/Lib/platform.py @@ -886,20 +395,29 @@ index 939893eb5e..8c550ed95a 100644 return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' diff --git a/Lib/subprocess.py b/Lib/subprocess.py -index a414321b9d..6b073a2e85 100644 +index a414321b9d..f2846db171 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py -@@ -762,6 +762,9 @@ +@@ -96,6 +96,8 @@ + "CREATE_NO_WINDOW", "DETACHED_PROCESS", + "CREATE_DEFAULT_ERROR_MODE", "CREATE_BREAKAWAY_FROM_JOB"]) + ++# Some platforms do not support processes ++_can_fork_exec = sys.platform not in {"ios", "tvos", "watchos"} + + # Exception classes used by this module. + class SubprocessError(Exception): pass +@@ -762,6 +764,9 @@ pass_fds=(), *, user=None, group=None, extra_groups=None, encoding=None, errors=None, text=None, umask=-1, pipesize=-1): """Create new Popen instance.""" -+ if not os.allows_subprocesses: ++ if not _can_fork_exec: + raise RuntimeError(f"Subprocesses are not supported on {sys.platform}") + _cleanup() # Held while anything is calling waitpid before returncode has been # updated to prevent clobbering returncode if wait() or poll() are -@@ -1858,7 +1861,7 @@ +@@ -1858,7 +1863,7 @@ else: self.returncode = waitstatus_to_exitcode(sts) @@ -908,7 +426,7 @@ index a414321b9d..6b073a2e85 100644 _WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD): """Check if child process has terminated. Returns returncode attribute. -@@ -1867,6 +1870,8 @@ +@@ -1867,6 +1872,8 @@ outside of the local scope (nor can any methods it calls). """ @@ -996,11 +514,50 @@ index daf9f00006..82d5af767c 100644 return f"{osname}-{release}-{machine}" +diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py +index c9a80c2a62..4bcfe50b5c 100644 +--- a/Lib/test/support/__init__.py ++++ b/Lib/test/support/__init__.py +@@ -43,7 +43,7 @@ + "check__all__", "skip_if_buggy_ucrt_strfptime", + "check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer", + # sys +- "is_jython", "is_android", "check_impl_detail", "unix_shell", ++ "is_jython", "is_android", "is_apple_mobile", "check_impl_detail", "unix_shell", + "setswitchinterval", + # network + "open_urlresource", +@@ -469,11 +469,24 @@ + + 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 + ++# 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') ++ ++has_fork_support = ( ++ hasattr(os, "fork") ++ and not is_apple_mobile ++) ++ ++has_subprocess_support = ( ++ not is_apple_mobile ++) ++ + # 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/support/script_helper.py b/Lib/test/support/script_helper.py -index 6d699c8486..8a4085d4da 100644 +index 6d699c8486..8e7341bd21 100644 --- a/Lib/test/support/script_helper.py +++ b/Lib/test/support/script_helper.py -@@ -8,6 +8,7 @@ +@@ -8,10 +8,12 @@ import os.path import subprocess import py_compile @@ -1008,12 +565,16 @@ index 6d699c8486..8a4085d4da 100644 import zipfile from importlib.util import source_from_cache -@@ -19,6 +20,8 @@ + from test import support ++from test.support import has_subprocess_support + from test.support.import_helper import make_legacy_pyc + + +@@ -19,6 +21,7 @@ __cached_interp_requires_environment = None -+ -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def interpreter_requires_environment(): """ Returns True if our sys.executable interpreter requires environment @@ -1021,190 +582,200 @@ index 6d699c8486..8a4085d4da 100644 return _PythonRunResult(rc, out, err), cmd_line -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def _assert_python(expected_success, /, *args, **env_vars): res, cmd_line = run_python_until_end(*args, **env_vars) if (res.rc and expected_success) or (not res.rc and not expected_success): -@@ -171,6 +175,8 @@ +@@ -171,6 +175,7 @@ return _assert_python(False, *args, **env_vars) -+ -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def spawn_python(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw): """Run a Python subprocess with the given arguments. diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py -index bed7b5d30b..4a614f6070 100644 +index bed7b5d30b..d17fc9c9ff 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py -@@ -541,6 +541,8 @@ +@@ -32,6 +32,7 @@ + from asyncio import selector_events + from test.test_asyncio import utils as test_utils + from test import support ++from test.support import is_apple_mobile, has_subprocess_support + from test.support import socket_helper + from test.support import threading_helper + from test.support import ALWAYS_EQ, LARGEST, SMALLEST +@@ -541,6 +542,7 @@ self._basetest_create_connection(conn_fut) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_connection(self): # Issue #20682: On Mac OS X Tiger, getsockname() returns a # zero-length address for UNIX socket. -@@ -633,6 +635,8 @@ +@@ -633,6 +635,7 @@ self.assertEqual(cm.exception.reason, 'CERTIFICATE_VERIFY_FAILED') @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_ssl_connection(self): with test_utils.run_test_server(use_ssl=True) as httpd: create_connection = functools.partial( -@@ -644,6 +648,8 @@ +@@ -644,6 +647,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_ssl_unix_connection(self): # Issue #20682: On Mac OS X Tiger, getsockname() returns a # zero-length address for UNIX socket. -@@ -867,6 +873,8 @@ +@@ -867,6 +871,7 @@ return server, path @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server(self): proto = MyProto(loop=self.loop) server, path = self._make_unix_server(lambda: proto) -@@ -895,6 +903,8 @@ +@@ -895,6 +900,7 @@ server.close() @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_path_socket_error(self): proto = MyProto(loop=self.loop) sock = socket.socket() -@@ -960,6 +970,8 @@ +@@ -960,6 +966,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( -@@ -990,6 +1002,8 @@ +@@ -990,6 +997,7 @@ server.close() @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) server, host, port = self._make_ssl_server( -@@ -1020,6 +1034,8 @@ +@@ -1020,6 +1028,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( -@@ -1080,6 +1096,8 @@ +@@ -1080,6 +1089,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl_verified(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( -@@ -1742,6 +1760,7 @@ +@@ -1742,6 +1752,7 @@ next(it) -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class SubprocessTestsMixin: def check_terminated(self, returncode): diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py -index 227b2279e1..7b8ed82778 100644 +index 227b2279e1..54143e8dfc 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py -@@ -5,6 +5,7 @@ - import queue - import pickle - import socket -+import subprocess - import sys - import threading - import unittest -@@ -60,6 +61,8 @@ +@@ -17,6 +17,7 @@ + + import asyncio + from test.test_asyncio import utils as test_utils ++from test.support import is_apple_mobile, has_subprocess_support + + + def tearDownModule(): +@@ -60,6 +61,7 @@ self._basetest_open_connection(conn_fut) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection(self): with test_utils.run_test_unix_server() as httpd: conn_fut = asyncio.open_unix_connection(httpd.address) -@@ -91,6 +94,8 @@ +@@ -91,6 +93,7 @@ @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection_no_loop_ssl(self): with test_utils.run_test_unix_server(use_ssl=True) as httpd: conn_fut = asyncio.open_unix_connection( -@@ -119,6 +124,8 @@ +@@ -119,6 +122,7 @@ self._basetest_open_connection_error(conn_fut) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection_error(self): with test_utils.run_test_unix_server() as httpd: conn_fut = asyncio.open_unix_connection(httpd.address) -@@ -637,6 +644,8 @@ +@@ -637,6 +641,7 @@ self.assertEqual(messages, []) @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_start_unix_server(self): class MyServer: -@@ -707,6 +716,7 @@ +@@ -707,6 +712,7 @@ self.assertEqual(messages, []) @unittest.skipIf(sys.platform == 'win32', "Don't have pipes") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') 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 14fa6dd76f..72c86b93c4 100644 +index 14fa6dd76f..a9a9561509 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py +@@ -10,7 +10,7 @@ + from asyncio import subprocess + from test.test_asyncio import utils as test_utils + from test import support +-from test.support import os_helper ++from test.support import os_helper, has_subprocess_support + + if sys.platform != 'win32': + from asyncio import unix_events @@ -104,6 +104,7 @@ transport.close() -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') 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 2f68459d30..580e9660a6 100644 +index 1d922783ce..d40943c4c3 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py -@@ -276,6 +276,8 @@ +@@ -13,6 +13,7 @@ + import threading + import unittest + from unittest import mock ++from test.support import is_apple_mobile, has_subprocess_support + from test.support import os_helper + from test.support import socket_helper + +@@ -276,6 +277,7 @@ @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'UNIX Sockets are not supported') -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class SelectorEventLoopUnixSocketTests(test_utils.TestCase): def setUp(self): @@ -1212,35 +783,51 @@ index 2f68459d30..580e9660a6 100644 NotImplementedError, watcher._do_waitpid, f) -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class ChildWatcherTestsMixin: ignore_warnings = mock.patch.object(log.logger, "warning") diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py -index 418492432a..22b12e4a1a 100644 +index 418492432a..88a302f13f 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py -@@ -754,6 +754,7 @@ +@@ -3,6 +3,7 @@ + import binascii + import os + from array import array ++from test.support import has_subprocess_support + from test.support import os_helper + from test.support import script_helper + +@@ -754,6 +755,7 @@ self.assertEqual(b16encode(b"foobar"), b"666F6F626172") -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class TestMain(unittest.TestCase): def tearDown(self): if os.path.exists(os_helper.TESTFN): diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py -index 7b208c9006..03384ea599 100644 +index 0adb689beb..3b8f2b8b65 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py -@@ -61,6 +61,7 @@ +@@ -17,6 +17,7 @@ + import weakref + from test import support + from test.support import MISSING_C_DOCSTRINGS ++from test.support import has_subprocess_support + from test.support import import_helper + from test.support import threading_helper + from test.support import warnings_helper +@@ -61,6 +62,7 @@ self.assertEqual(testfunction.attribute, "test") self.assertRaises(AttributeError, setattr, inst.testfunction, "attribute", "test") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_no_FatalError_infinite_loop(self): with support.SuppressCrashReport(): p = subprocess.Popen([sys.executable, "-c", -@@ -802,6 +803,7 @@ +@@ -802,6 +804,7 @@ self.assertEqual(main_attr_id, subinterp_attr_id) @@ -1249,14 +836,23 @@ index 7b208c9006..03384ea599 100644 @threading_helper.reap_threads diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py -index d299f766dc..ebeead6832 100644 +index 84bd883609..ab13da4fa5 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py +@@ -9,7 +9,7 @@ + import textwrap + import unittest + from test import support +-from test.support import os_helper ++from test.support import os_helper, has_subprocess_support + from test.support.script_helper import ( + spawn_python, kill_python, assert_python_ok, assert_python_failure, + interpreter_requires_environment @@ -68,6 +68,7 @@ rc, out, err = assert_python_ok('-vv') self.assertNotIn(b'stack overflow', err) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @unittest.skipIf(interpreter_requires_environment(), 'Cannot run -E tests when PYTHON env vars are required.') def test_xoptions(self): @@ -1264,7 +860,7 @@ index d299f766dc..ebeead6832 100644 opts = get_xoptions('-Xa', '-Xb=c,d=e') self.assertEqual(opts, {'a': True, 'b': 'c,d=e'}) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_showrefcount(self): def run_python(*args): # this is similar to assert_python_ok but doesn't strip @@ -1272,7 +868,7 @@ index d299f766dc..ebeead6832 100644 # arguments as unicode (using wmain() instead of main()). @unittest.skipIf(sys.platform == 'win32', 'Windows has a native unicode API') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_undecodable_code(self): undecodable = b"\xff" env = os.environ.copy() @@ -1280,7 +876,7 @@ index d299f766dc..ebeead6832 100644 'False False False\n' 'False False True\n') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_unbuffered_output(self): # Test expected operation of the '-u' switch for stream in ('stdout', 'stderr'): @@ -1288,7 +884,7 @@ index d299f766dc..ebeead6832 100644 # for empty and unset PYTHONPATH self.assertEqual(out1, out2) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_displayhook_unencodable(self): for encoding in ('ascii', 'latin-1', 'utf-8'): env = os.environ.copy() @@ -1296,7 +892,7 @@ index d299f766dc..ebeead6832 100644 escaped = repr(text).encode(encoding, 'backslashreplace') self.assertIn(escaped, data) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def check_input(self, code, expected): with tempfile.NamedTemporaryFile("wb+") as stdin: sep = os.linesep.encode('ASCII') @@ -1304,33 +900,28 @@ index d299f766dc..ebeead6832 100644 @unittest.skipIf(os.name != 'posix', "test needs POSIX semantics") @unittest.skipIf(sys.platform == "vxworks", "test needs preexec support in subprocess.Popen") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def _test_no_stdio(self, streams): code = """if 1: import os, sys -diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py -index a7318bf404..e98f27d2b2 100644 ---- a/Lib/test/test_compileall.py -+++ b/Lib/test/test_compileall.py -@@ -225,7 +225,8 @@ - compileall.compile_dir(self.directory, quiet=True, workers=5) - self.assertTrue(pool_mock.called) - -- def test_compile_workers_non_positive(self): -+ @mock.patch('compileall.ProcessPoolExecutor') -+ def test_compile_workers_non_positive(self, pool_mock): - with self.assertRaisesRegex(ValueError, - "workers must be greater or equal to 0"): - compileall.compile_dir(self.directory, workers=-1) diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py -index e174d5464d..a79fb48b4a 100644 +index e174d5464d..56a6a6f5a4 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py +@@ -5,7 +5,7 @@ + # Skip tests if _multiprocessing wasn't built. + import_helper.import_module('_multiprocessing') + +-from test.support import hashlib_helper ++from test.support import hashlib_helper, has_subprocess_support + from test.support.script_helper import assert_python_ok + + import contextlib @@ -151,6 +151,7 @@ executor_type = futures.ThreadPoolExecutor -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class ProcessPoolForkMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "fork" @@ -1338,7 +929,7 @@ index e174d5464d..a79fb48b4a 100644 return super().get_context() -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class ProcessPoolSpawnMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "spawn" @@ -1346,20 +937,28 @@ index e174d5464d..a79fb48b4a 100644 return super().get_context() -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class ProcessPoolForkserverMixin(ExecutorMixin): executor_type = futures.ProcessPoolExecutor ctx = "forkserver" diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py -index ebd4ad9192..ff3d467bf7 100644 +index ebd4ad9192..bc2809f199 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py -@@ -2875,7 +2875,12 @@ +@@ -4,6 +4,7 @@ + + from test import support + from test.support import import_helper ++from test.support import is_apple_mobile + from test.support import os_helper + import doctest + import functools +@@ -2875,7 +2876,12 @@ TestResults(failed=1, attempted=1) """ -def test_CLI(): r""" -+if sys.platform in ('iOS', 'tvos', 'watchos'): ++if is_apple_mobile: + # Mobile platforms can't invoke doctest from the command line, + # so skip this test. + pass @@ -1369,34 +968,39 @@ index ebd4ad9192..ff3d467bf7 100644 These tests test this CLI functionality. diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py -index 528147802b..73a10334db 100644 +index 528147802b..f8f0d9c3c7 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py -@@ -6,6 +6,7 @@ +@@ -2,10 +2,11 @@ + import signal + import unittest + from test import support +-from test.support import script_helper ++from test.support import script_helper, has_subprocess_support @unittest.skipUnless(os.name == "posix", "only supported on Unix") -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class EINTRTests(unittest.TestCase): @unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()") diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py -index e0f09e821d..acbe4ed050 100644 +index e0f09e821d..ae84d480ba 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py -@@ -45,6 +45,7 @@ - finally: - os_helper.unlink(filename) - -+ - class FaultHandlerTests(unittest.TestCase): - def get_output(self, code, filename=None, fd=None): - """ +@@ -7,6 +7,7 @@ + import subprocess + import sys + from test import support ++from test.support import has_subprocess_support + from test.support import os_helper + from test.support import script_helper, is_android + from test.support import skip_if_sanitizer @@ -401,6 +402,7 @@ finally: sys.stderr = orig_stderr -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_disabled_by_default(self): # By default, the module should be disabled code = "import faulthandler; print(faulthandler.is_enabled())" @@ -1404,7 +1008,7 @@ index e0f09e821d..acbe4ed050 100644 output = subprocess.check_output(args) self.assertEqual(output.rstrip(), b"False") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_sys_xoptions(self): # Test python -X faulthandler code = "import faulthandler; print(faulthandler.is_enabled())" @@ -1412,45 +1016,71 @@ index e0f09e821d..acbe4ed050 100644 output = subprocess.check_output(args, env=env) self.assertEqual(output.rstrip(), b"True") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_env_var(self): # empty env var code = "import faulthandler; print(faulthandler.is_enabled())" diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py -index fc8c39365f..00e988fdcc 100644 +index fc8c39365f..16235b8397 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py +@@ -6,7 +6,7 @@ + import sys + import unittest + from multiprocessing import Process +-from test.support import verbose, cpython_only ++from test.support import verbose, cpython_only, is_apple_mobile + from test.support.import_helper import import_module + from test.support.os_helper import TESTFN, unlink + @@ -25,7 +25,7 @@ start_len = "qq" if (sys.platform.startswith(('netbsd', 'freebsd', 'openbsd')) - or sys.platform == 'darwin'): -+ or sys.platform in ('darwin', 'ios', 'tvos', 'watchos')): ++ or sys.platform == 'darwin' or is_apple_mobile): if struct.calcsize('l') == 8: off_t = 'l' pid_t = 'i' diff --git a/Lib/test/test_file_eintr.py b/Lib/test/test_file_eintr.py -index 01408d838a..2e024d17bb 100644 +index 01408d838a..faf039fc69 100644 --- a/Lib/test/test_file_eintr.py +++ b/Lib/test/test_file_eintr.py -@@ -69,6 +69,7 @@ +@@ -15,6 +15,7 @@ + import sys + import time + import unittest ++from test.support import has_subprocess_support + + # Test import all of the things we're about to try testing up front. + import _io +@@ -69,6 +70,7 @@ self.fail('Error from IO process %s:\nSTDOUT:\n%sSTDERR:\n%s\n' % (why, stdout.decode(), stderr.decode())) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def _test_reading(self, data_to_write, read_and_verify_code): """Generic buffered read method test harness to validate EINTR behavior. diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py -index 6c28b2b677..4480dc8ca0 100644 +index 6c28b2b677..503495e653 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py +@@ -1,7 +1,7 @@ + import unittest + import unittest.mock + from test.support import (verbose, refcount_test, +- cpython_only) ++ cpython_only, has_subprocess_support) + from test.support.import_helper import import_module + from test.support.os_helper import temp_dir, TESTFN, unlink + from test.support.script_helper import assert_python_ok, make_script @@ -681,6 +681,8 @@ del x gc.set_debug(%s) """ + -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def run_command(code): p = subprocess.Popen([sys.executable, "-Wd", "-c", code], stdout=subprocess.PIPE, @@ -1468,110 +1098,90 @@ index 5f554897f8..18955cb173 100644 # Regex to parse: # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7 diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py -index 8fdbab4ec0..93cafe1165 100644 +index 8fdbab4ec0..4ac1ef2158 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py -@@ -400,7 +400,7 @@ +@@ -30,6 +30,8 @@ + + import unittest + from test import support ++from test.support import is_apple_mobile ++from test.support import has_subprocess_support + from test.support import os_helper + from test.support import threading_helper + +@@ -400,7 +402,7 @@ 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': -+ if sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform == 'darwin' or is_apple_mobile: # On Mac OS the HFS+ filesystem replaces bytes that aren't valid # UTF-8 into a percent-encoded value. for name in os.listdir(self.tempdir): -@@ -659,6 +659,7 @@ +@@ -659,6 +661,7 @@ @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, "This test can't be run reliably as root (issue #13308).") -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class CGIHTTPServerTestCase(BaseTestCase): class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler): pass -diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py -index 5abe28ef62..80506cf932 100644 ---- a/Lib/test/test_imp.py -+++ b/Lib/test/test_imp.py -@@ -20,7 +20,7 @@ - """Decorator to skip a test if not running under CPython or lacking - imp.load_dynamic().""" - meth = support.cpython_only(meth) -- return unittest.skipIf(not hasattr(imp, 'load_dynamic'), -+ return unittest.skipIf(not getattr(imp, 'load_dynamic'), - 'imp.load_dynamic() required')(meth) - - -diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py -index e8065d7dad..951b63e972 100644 ---- a/Lib/test/test_importlib/extension/test_finder.py -+++ b/Lib/test/test_importlib/extension/test_finder.py -@@ -3,10 +3,13 @@ - - machinery = util.import_importlib('importlib.machinery') - -+import sys - import unittest - import warnings - - -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ '%s does not support dynamic loading' % sys.platform) - class FinderTests(abc.FinderTests): - - """Test the finder for extension modules.""" -diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py -index 8fd556dbed..bbefcee9f8 100644 ---- a/Lib/test/test_importlib/extension/test_loader.py -+++ b/Lib/test/test_importlib/extension/test_loader.py -@@ -13,6 +13,8 @@ - import importlib - from test.support.script_helper import assert_python_failure - -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ '%s does not support dynamic loading' % sys.platform) - class LoaderTests(abc.LoaderTests): - - """Test load_module() for extension modules.""" -@@ -88,6 +90,9 @@ - Source_LoaderTests - ) = util.test_both(LoaderTests, machinery=machinery) - -+ -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ '%s does not support dynamic loading' % sys.platform) - class MultiPhaseExtensionModuleTests(abc.LoaderTests): - # Test loading extension modules with multi-phase initialization (PEP 489). - diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py -index fb83762cb6..568866ec16 100644 +index fb83762cb6..cd231e461c 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py -@@ -601,7 +601,7 @@ +@@ -40,6 +40,7 @@ + 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 is_apple_mobile + from test.support import os_helper + from test.support import threading_helper + from test.support import warnings_helper +@@ -601,7 +602,7 @@ # 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': -+ if sys.platform[:3] == 'win' or sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform[:3] == 'win' or sys.platform == 'darwin' or is_apple_mobile: support.requires( 'largefile', 'test requires %s bytes and a long time to run' % self.LARGE) diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py -index 1d7fca6efb..00bba81def 100644 +index 1d7fca6efb..0e0235f44f 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py +@@ -6,7 +6,7 @@ + import subprocess + + from test import support +-from test.support import os_helper ++from test.support import os_helper, has_subprocess_support + from test.support.script_helper import assert_python_ok + + @@ -85,6 +85,7 @@ } """) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_stdin_stdout(self): args = sys.executable, '-m', 'json.tool' process = subprocess.run(args, input=self.data, capture_output=True, text=True, check=True) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py -index af68f25820..15875b380e 100644 +index 74e950450c..dbcfc1ddd0 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py -@@ -1776,9 +1776,21 @@ +@@ -43,6 +43,7 @@ + import tempfile + from test.support.script_helper import assert_python_ok, assert_python_failure + from test import support ++from test.support import is_apple_mobile + from test.support import os_helper + from test.support import socket_helper + from test.support import threading_helper +@@ -1776,9 +1777,20 @@ # just need a name - file can't be present, or we'll get an # 'address already in use' error. os.remove(fn) @@ -1588,61 +1198,68 @@ index af68f25820..15875b380e 100644 return fn @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixSocketHandlerTest(SocketHandlerTest): """Test for SocketHandler with unix sockets.""" -@@ -1860,6 +1872,8 @@ +@@ -1860,6 +1872,7 @@ self.assertEqual(self.log_output, "spam\neggs\n") @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixDatagramHandlerTest(DatagramHandlerTest): """Test for DatagramHandler using Unix sockets.""" -@@ -1944,6 +1958,8 @@ +@@ -1944,6 +1957,7 @@ self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m') @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixSysLogHandlerTest(SysLogHandlerTest): """Test for SysLogHandler with Unix sockets.""" diff --git a/Lib/test/test_mailcap.py b/Lib/test/test_mailcap.py -index ef9cad498a..d124622613 100644 +index ef9cad498a..79d19aa860 100644 --- a/Lib/test/test_mailcap.py +++ b/Lib/test/test_mailcap.py -@@ -1,6 +1,7 @@ +@@ -1,8 +1,9 @@ import mailcap import os import copy +import sys import test.support - from test.support import os_helper +-from test.support import os_helper ++from test.support import os_helper, is_apple_mobile import unittest + import sys + @@ -214,7 +215,8 @@ ] self._run_cases(cases) - @unittest.skipUnless(os.name == "posix", "Requires 'test' command on system") -+ @unittest.skipUnless(os.name == "posix" and sys.platform not in ('ios', 'tvos', 'watchos'), ++ @unittest.skipUnless(os.name == "posix" and not is_apple_mobile, + "Requires 'test' command on system") @unittest.skipIf(sys.platform == "vxworks", "'test' command is not supported on VxWorks") def test_test(self): # findmatch() will automatically check any "test" conditions and skip diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py -index 7bcf8e8399..44d5a63be0 100644 +index 7bcf8e8399..5e808dc0b8 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 ++from test.support import os_helper, is_apple_mobile + import array + import io + import marshal @@ -234,7 +234,10 @@ if os.name == 'nt': MAX_MARSHAL_STACK_DEPTH = 1000 else: - MAX_MARSHAL_STACK_DEPTH = 2000 -+ if sys.platform in ('ios', 'tvos', 'watchos'): ++ if is_apple_mobile: + MAX_MARSHAL_STACK_DEPTH = 1500 + else: + MAX_MARSHAL_STACK_DEPTH = 2000 @@ -1650,156 +1267,179 @@ index 7bcf8e8399..44d5a63be0 100644 last.append([0]) last = last[-1] diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py -index 8f34c182f8..18e4a13d35 100644 +index 8f34c182f8..95eb37140a 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py -@@ -231,7 +231,7 @@ +@@ -1,4 +1,6 @@ +-from test.support import (requires, _2G, _4G, gc_collect, cpython_only) ++from test.support import ( ++ requires, _2G, _4G, gc_collect, cpython_only, is_apple_mobile ++) + from test.support.import_helper import import_module + from test.support.os_helper import TESTFN, unlink + import unittest +@@ -231,7 +233,7 @@ with open(TESTFN, "r+b") as f: self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4) - if os.name == "posix": -+ if os.name == "posix" and sys.platform not in ('iOS', 'tvos', 'watchos'): ++ if os.name == "posix" and not is_apple_mobile: # Try incompatible flags, prot and access parameters. with open(TESTFN, "r+b") as f: self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, -@@ -806,7 +806,7 @@ +@@ -806,7 +808,7 @@ 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 sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform[:3] == 'win' or sys.platform == 'darwin' or is_apple_mobile: 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_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork.py -index 5000edb7c5..5d1d2917bf 100644 +index 5000edb7c5..65a0fb67f9 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork.py @@ -1,4 +1,9 @@ -+import os import unittest ++from test.support import has_subprocess_support + -+if not os.allows_subprocesses: ++if not has_subprocess_support: + raise unittest.SkipTest('Test requires support for subprocesses.') + import test._test_multiprocessing import sys diff --git a/Lib/test/test_multiprocessing_forkserver.py b/Lib/test/test_multiprocessing_forkserver.py -index 6ad5faf9e8..4e05efb6df 100644 +index 6ad5faf9e8..a5285ef77a 100644 --- a/Lib/test/test_multiprocessing_forkserver.py +++ b/Lib/test/test_multiprocessing_forkserver.py @@ -1,4 +1,9 @@ -+import os import unittest ++from test.support import has_subprocess_support + -+if not os.allows_subprocesses: ++if not has_subprocess_support: + raise unittest.SkipTest('Test requires support for subprocesses.') + import test._test_multiprocessing import sys diff --git a/Lib/test/test_multiprocessing_spawn.py b/Lib/test/test_multiprocessing_spawn.py -index 6558952308..a121e8c2dd 100644 +index 6558952308..192096e2ac 100644 --- a/Lib/test/test_multiprocessing_spawn.py +++ b/Lib/test/test_multiprocessing_spawn.py @@ -1,4 +1,9 @@ -+import os import unittest ++from test.support import has_subprocess_support + -+if not os.allows_subprocesses: ++if not has_subprocess_support: + raise unittest.SkipTest('Test requires support for subprocesses.') + import test._test_multiprocessing from test import support diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py -index 1243b575dc..a05ed400c4 100644 +index 1243b575dc..2c8ff44a99 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py -@@ -993,6 +993,7 @@ +@@ -32,6 +32,7 @@ + import warnings + from test import support + from test.support import import_helper ++from test.support import has_subprocess_support + from test.support import os_helper + from test.support import socket_helper + from test.support import threading_helper +@@ -993,6 +994,7 @@ @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') @unittest.skipUnless(hasattr(os, 'popen'), "needs os.popen()") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_update2(self): os.environ.clear() os.environ.update(HELLO="World") -@@ -1003,6 +1004,7 @@ +@@ -1003,6 +1005,7 @@ @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') @unittest.skipUnless(hasattr(os, 'popen'), "needs os.popen()") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_os_popen_iter(self): with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" % unix_shell) as popen: -@@ -1954,6 +1956,7 @@ +@@ -1954,6 +1957,7 @@ @unittest.skipUnless(hasattr(os, 'execv'), "need os.execv()") -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class ExecTests(unittest.TestCase): @unittest.skipIf(USING_LINUXTHREADS, "avoid triggering a linuxthreads bug: see issue #4970") -@@ -2278,6 +2281,7 @@ +@@ -2278,6 +2282,7 @@ self.assertRaises(OverflowError, os.setreuid, 0, self.UID_OVERFLOW) @unittest.skipUnless(hasattr(os, 'setreuid'), 'test needs os.setreuid()') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_setreuid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). -@@ -2286,6 +2290,7 @@ +@@ -2286,6 +2291,7 @@ 'import os,sys;os.setreuid(-1,-1);sys.exit(0)']) @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_setregid(self): if os.getuid() != 0 and not HAVE_WHEEL_GROUP: self.assertRaises(OSError, os.setregid, 0, 0) -@@ -2295,6 +2300,7 @@ +@@ -2295,6 +2301,7 @@ self.assertRaises(OverflowError, os.setregid, 0, self.GID_OVERFLOW) @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_setregid_neg1(self): # Needs to accept -1. We run this in a subprocess to avoid # altering the test runner's process state (issue8045). -@@ -2967,6 +2973,7 @@ +@@ -2967,6 +2974,7 @@ class PidTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'getppid'), "test needs os.getppid") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_getppid(self): p = subprocess.Popen([sys.executable, '-c', 'import os; print(os.getppid())'], -@@ -2993,6 +3000,9 @@ +@@ -2993,6 +3001,7 @@ self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) self.assertEqual(pid2, pid) -+ @unittest.skipUnless(hasattr(os, 'spawnv'), "test needs os.spawnv") -+ @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_waitpid(self): self.check_waitpid(code='pass', exitcode=0) -@@ -3662,6 +3672,7 @@ +@@ -3662,6 +3671,7 @@ self.assertGreaterEqual(size.columns, 0) self.assertGreaterEqual(size.lines, 0) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_stty_match(self): """Check if stty returns the same results diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py -index 6ac1a4a3c3..698e04ded1 100644 +index 6ac1a4a3c3..178b8ab092 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py +@@ -13,7 +13,7 @@ + + from contextlib import ExitStack, redirect_stdout + from io import StringIO +-from test.support import os_helper ++from test.support import os_helper, has_subprocess_support + # This little helper class is essential for testing pdb under doctest. + from test.test_doctest import _FakeInput + from unittest.mock import patch @@ -1356,6 +1356,7 @@ def tearDown(self): os_helper.unlink(os_helper.TESTFN) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def _run_pdb(self, pdb_args, commands): self.addCleanup(os_helper.rmtree, '__pycache__') cmd = [sys.executable, '-m', 'pdb'] + pdb_args @@ -1807,91 +1447,131 @@ index 6ac1a4a3c3..698e04ded1 100644 ('bÅ“r', 1), ) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_issue7964(self): # open the file as binary so we can force \r\n newline with open(os_helper.TESTFN, 'wb') as f: diff --git a/Lib/test/test_pipes.py b/Lib/test/test_pipes.py -index 6335e7cbe0..6173e68c02 100644 +index 6335e7cbe0..9f424e67a3 100644 --- a/Lib/test/test_pipes.py +++ b/Lib/test/test_pipes.py -@@ -1,6 +1,7 @@ +@@ -1,15 +1,19 @@ import pipes import os import string +import sys import unittest import shutil - from test.support import reap_children, unix_shell -@@ -10,6 +11,9 @@ +-from test.support import reap_children, unix_shell ++from test.support import reap_children, unix_shell, is_apple_mobile + from test.support.os_helper import TESTFN, unlink + + if os.name != 'posix': raise unittest.SkipTest('pipes module only works on posix') -+if sys.platform in ('ios', 'tvos', 'watchos'): ++if is_apple_mobile: + raise unittest.SkipTest('pipes tests cannot run on %s' % sys.platform) + if not (unix_shell and os.path.exists(unix_shell)): raise unittest.SkipTest('pipes module requires a shell') diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py -index 1a688775f4..7bb9a783d4 100644 +index 1a688775f4..3ac10fbc26 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py -@@ -67,7 +67,6 @@ - IN VALID=value - """ +@@ -8,7 +8,7 @@ + from unittest import mock -- - class PlatformTest(unittest.TestCase): - def clear_caches(self): - platform._platform_cache.clear() -@@ -79,6 +78,7 @@ + from test import support +-from test.support import os_helper ++from test.support import os_helper, has_subprocess_support, is_apple_mobile + + FEDORA_OS_RELEASE = """\ + NAME=Fedora +@@ -79,6 +79,7 @@ res = platform.architecture() @os_helper.skip_unless_symlink -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_architecture_via_symlink(self): # issue3762 with support.PythonSymlink() as py: cmd = "-c", "import platform; print(platform.architecture())" -@@ -313,7 +313,7 @@ +@@ -313,7 +314,7 @@ def test_mac_ver(self): res = platform.mac_ver() - if platform.uname().system == 'Darwin': -+ if platform.uname().system == 'Darwin' and sys.platform not in ('ios', 'tvos', 'watchos'): ++ if platform.uname().system == 'Darwin' and not is_apple_mobile: # We are on a macOS system, check that the right version # information is returned output = subprocess.check_output(['sw_vers'], text=True) +@@ -345,6 +346,10 @@ + else: + self.assertEqual(res[2], 'PowerPC') + ++ @unittest.skipUnless(is_apple_mobile, "iOS/tvOS/watchOS only test") ++ def test_ios_ver(self): ++ res = platform.ios_ver() ++ + + @unittest.skipUnless(sys.platform == 'darwin', "OSX only test") + def test_mac_ver_with_fork(self): diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py -index 82bbb3af9f..3a35332919 100644 +index 82bbb3af9f..485d7602a0 100644 --- a/Lib/test/test_poll.py +++ b/Lib/test/test_poll.py -@@ -120,6 +120,7 @@ +@@ -8,6 +8,7 @@ + import time + import unittest + from test.support import cpython_only ++from test.support import has_subprocess_support + from test.support import threading_helper + from test.support.os_helper import TESTFN + +@@ -120,6 +121,7 @@ # Another test case for poll(). This is copied from the test case for # select(), modified to use poll() instead. -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_poll2(self): cmd = 'for i in 0 1 2 3 4 5 6 7 8 9; do echo testing...; sleep 1; done' proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, diff --git a/Lib/test/test_popen.py b/Lib/test/test_popen.py -index cac2f6177f..f24ca18099 100644 +index cac2f6177f..467c1dbb13 100644 --- a/Lib/test/test_popen.py +++ b/Lib/test/test_popen.py -@@ -19,6 +19,8 @@ +@@ -5,6 +5,7 @@ + + import unittest + from test import support ++from test.support import has_subprocess_support + import os, sys + + if not hasattr(os, 'popen'): +@@ -19,6 +20,8 @@ if ' ' in python: python = '"' + python + '"' # quote embedded space for cmdline + -+@unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++@unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') class PopenTest(unittest.TestCase): def _do_test_commandline(self, cmdline, expected): diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py -index 701543bb6a..8c5a6636aa 100644 +index 701543bb6a..dd6ad77088 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py -@@ -64,15 +64,21 @@ +@@ -2,6 +2,8 @@ + + from test import support + from test.support import import_helper ++from test.support import has_subprocess_support ++from test.support import is_apple_mobile + from test.support import os_helper + from test.support import warnings_helper + from test.support.script_helper import assert_python_ok +@@ -64,15 +66,22 @@ # no side-effects which we need to cleanup (e.g., fork, wait, abort) NO_ARG_FUNCTIONS = [ "ctermid", "getcwd", "getcwdb", "uname", "times", "getloadavg", @@ -1900,7 +1580,8 @@ index 701543bb6a..8c5a6636aa 100644 "getpid", "getpgrp", "getppid", "getuid", "sync", ] -+ if sys.platform not in ('ios', 'tvos', 'watchos'): ++ # getgroups can't be invoked on iOS/tvOS/watchOS. ++ if is_apple_mobile: + NO_ARG_FUNCTIONS.append("getgroups") + for name in NO_ARG_FUNCTIONS: @@ -1916,196 +1597,223 @@ index 701543bb6a..8c5a6636aa 100644 @unittest.skipUnless(hasattr(posix, 'getresuid'), 'test needs posix.getresuid()') -@@ -766,9 +772,10 @@ +@@ -766,9 +775,10 @@ 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') and sys.platform not in ('ios', 'tvos', 'watchos'): ++ if hasattr(os, 'getgroups') and not is_apple_mobile: + 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) -@@ -1045,6 +1052,7 @@ - os.chdir(curdir) - os_helper.rmtree(base_path) - -+ - @unittest.skipUnless(hasattr(posix, 'getgrouplist'), "test needs posix.getgrouplist()") - @unittest.skipUnless(hasattr(pwd, 'getpwuid'), "test needs pwd.getpwuid()") - @unittest.skipUnless(hasattr(os, 'getuid'), "test needs os.getuid()") -@@ -1056,6 +1064,7 @@ +@@ -1056,6 +1066,7 @@ @unittest.skipUnless(hasattr(os, 'getegid'), "test needs os.getegid()") @unittest.skipUnless(hasattr(os, 'popen'), "test needs os.popen()") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_getgroups(self): with os.popen('id -G 2>/dev/null') as idg: groups = idg.read().strip() -@@ -1114,7 +1123,7 @@ +@@ -1114,7 +1125,7 @@ self.assertIsInstance(hi, int) self.assertGreaterEqual(hi, lo) # OSX evidently just returns 15 without checking the argument. - if sys.platform != "darwin": -+ if sys.platform not in ('darwin', 'ios', 'tvos', 'watchos'): ++ if sys.platform != 'darwin' and not is_apple_mobile: self.assertRaises(OSError, posix.sched_get_priority_min, -23) self.assertRaises(OSError, posix.sched_get_priority_max, -23) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py -index 0243b8a30b..ca78db1663 100644 +index 0243b8a30b..217534eaf1 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py +@@ -1,4 +1,4 @@ +-from test.support import verbose, reap_children ++from test.support import verbose, reap_children, has_subprocess_support + from test.support.import_helper import import_module + + # Skip these tests if termios is not available @@ -210,6 +210,7 @@ s2 = _readline(master_fd) self.assertEqual(b'For my pet fish, Eric.\n', normalize_output(s2)) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_fork(self): debug("calling pty.fork()") pid, master_fd = pty.fork() diff --git a/Lib/test/test_quopri.py b/Lib/test/test_quopri.py -index 715544c8a9..6e66178816 100644 +index 715544c8a9..0641b2fcef 100644 --- a/Lib/test/test_quopri.py +++ b/Lib/test/test_quopri.py -@@ -180,6 +180,7 @@ +@@ -1,4 +1,5 @@ + import unittest ++from test.support import has_subprocess_support + + import sys, io, subprocess + import quopri +@@ -180,6 +181,7 @@ for p, e in self.HSTRINGS: self.assertEqual(quopri.decodestring(e, header=True), p) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_scriptencode(self): (p, e) = self.STRINGS[-1] process = subprocess.Popen([sys.executable, "-mquopri"], -@@ -196,6 +197,7 @@ +@@ -196,6 +198,7 @@ self.assertEqual(cout[i], e[i]) self.assertEqual(cout, e) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_scriptdecode(self): (p, e) = self.STRINGS[-1] process = subprocess.Popen([sys.executable, "-mquopri", "-d"], diff --git a/Lib/test/test_script_helper.py b/Lib/test/test_script_helper.py -index 4ade2cbc0d..83ce283a04 100644 +index 4ade2cbc0d..63175fcc13 100644 --- a/Lib/test/test_script_helper.py +++ b/Lib/test/test_script_helper.py -@@ -1,5 +1,6 @@ - """Unittests for test.support.script_helper. Who tests the test helper?""" - -+import os +@@ -3,7 +3,7 @@ import subprocess import sys import os -@@ -35,6 +36,7 @@ +-from test.support import script_helper ++from test.support import script_helper, has_subprocess_support + import unittest + from unittest import mock + +@@ -35,6 +35,7 @@ self.assertIn('import sys; sys.exit(0)', error_msg, msg='unexpected command line.') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @mock.patch('subprocess.Popen') def test_assert_python_isolated_when_env_not_required(self, mock_popen): with mock.patch.object(script_helper, -@@ -53,6 +55,7 @@ +@@ -53,6 +54,7 @@ self.assertIn('-I', popen_command) self.assertNotIn('-E', popen_command) # -I overrides this -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @mock.patch('subprocess.Popen') def test_assert_python_not_isolated_when_env_is_required(self, mock_popen): """Ensure that -I is not passed when the environment is required.""" -@@ -82,6 +85,7 @@ +@@ -82,6 +84,7 @@ # Reset the private cached state. script_helper.__dict__['__cached_interp_requires_environment'] = None -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @mock.patch('subprocess.check_call') def test_interpreter_requires_environment_true(self, mock_check_call): with mock.patch.dict(os.environ): -@@ -91,6 +95,7 @@ +@@ -91,6 +94,7 @@ self.assertTrue(script_helper.interpreter_requires_environment()) self.assertEqual(1, mock_check_call.call_count) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @mock.patch('subprocess.check_call') def test_interpreter_requires_environment_false(self, mock_check_call): with mock.patch.dict(os.environ): -@@ -100,6 +105,7 @@ +@@ -100,6 +104,7 @@ self.assertFalse(script_helper.interpreter_requires_environment()) self.assertEqual(1, mock_check_call.call_count) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @mock.patch('subprocess.check_call') def test_interpreter_requires_environment_details(self, mock_check_call): with mock.patch.dict(os.environ): diff --git a/Lib/test/test_select.py b/Lib/test/test_select.py -index cf32cf2f6a..1615b9ee68 100644 +index cf32cf2f6a..e28b80b766 100644 --- a/Lib/test/test_select.py +++ b/Lib/test/test_select.py -@@ -7,7 +7,8 @@ +@@ -6,6 +6,7 @@ + import textwrap import unittest from test import support ++from test.support import has_subprocess_support --@unittest.skipIf((sys.platform[:3]=='win'), -+ -+@unittest.skipIf((sys.platform[:3] == 'win'), + @unittest.skipIf((sys.platform[:3]=='win'), "can't easily test on this system") - class SelectTestCase(unittest.TestCase): - @@ -47,6 +48,7 @@ self.assertIsNot(w, x) @unittest.skipUnless(hasattr(os, 'popen'), "need os.popen()") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_select(self): code = textwrap.dedent(''' import time diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py -index 62e9180375..0214670421 100644 +index 62e9180375..7b94fa3355 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py -@@ -1756,6 +1756,8 @@ +@@ -30,7 +30,7 @@ + posix = None + + from test import support +-from test.support import os_helper ++from test.support import os_helper, has_subprocess_support + from test.support.os_helper import TESTFN, FakePath + + TESTFN2 = TESTFN + "2" +@@ -1756,6 +1756,7 @@ check_chown(dirname, uid, gid) -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't support other executable." % sys.platform) ++@unittest.skipIf(support.has_subprocess_support, 'Test requires support for subprocesses.') class TestWhich(BaseTest, unittest.TestCase): def setUp(self): -@@ -2623,6 +2625,7 @@ +@@ -2623,6 +2624,7 @@ self.assertGreaterEqual(size.lines, 0) @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @unittest.skipUnless(hasattr(os, 'get_terminal_size'), 'need os.get_terminal_size()') def test_stty_match(self): diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py -index 93349ed8bb..430b1d7d95 100644 +index 93349ed8bb..5d207fa720 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py -@@ -210,6 +210,7 @@ +@@ -7,6 +7,7 @@ + import unittest + import test.support + from test import support ++from test.support import has_subprocess_support + from test.support import os_helper + from test.support import socket_helper + from test.support import captured_stderr +@@ -210,6 +211,7 @@ @unittest.skipUnless(site.ENABLE_USER_SITE, "requires access to PEP 370 " "user-site (site.ENABLE_USER_SITE)") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_s_option(self): # (ncoghlan) Change this to use script_helper... usersite = os.path.normpath(site.USER_SITE) -@@ -495,6 +496,7 @@ +@@ -495,6 +497,7 @@ class StartupImportTests(unittest.TestCase): -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_startup_imports(self): # Get sys.path in isolated mode (python3 -I) popen = subprocess.Popen([sys.executable, '-I', '-c', diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 9c5f6d3dc9..4629b35f06 100644 +index e0e0f2437b..2e0b4a234d 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py -@@ -1030,6 +1030,12 @@ +@@ -1,5 +1,6 @@ + import unittest + from test import support ++from test.support import is_apple_mobile + from test.support import os_helper + from test.support import socket_helper + from test.support import threading_helper +@@ -1030,6 +1031,12 @@ with self.assertRaises(OSError, msg=explanation): socket.gethostbyaddr(addr) @@ -2118,109 +1826,99 @@ index 9c5f6d3dc9..4629b35f06 100644 @unittest.skipUnless(hasattr(socket, 'sethostname'), "test needs socket.sethostname()") @unittest.skipUnless(hasattr(socket, 'gethostname'), "test needs socket.gethostname()") def test_sethostname(self): -@@ -1142,7 +1148,7 @@ +@@ -1142,7 +1149,7 @@ # 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')): -+ or sys.platform in ('linux', 'darwin', 'ios', 'tvos', 'watchos')): ++ or sys.platform in ('linux', 'darwin') or is_apple_mobile): # avoid the 'echo' service on this platform, as there is an # assumption breaking non-standard port/protocol entry services = ('daytime', 'qotd', 'domain') -@@ -3498,7 +3504,8 @@ +@@ -3509,7 +3516,7 @@ def _testFDPassCMSG_LEN(self): self.createAndSendFDs(1) - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparate(self): -@@ -3509,7 +3516,8 @@ +@@ -3520,7 +3527,7 @@ maxcmsgs=2) @testFDPassSeparate.client_skip - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparate(self): fd0, fd1 = self.newFDs(2) -@@ -3522,7 +3530,8 @@ +@@ -3533,7 +3540,7 @@ array.array("i", [fd1]))]), len(MSG)) - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparateMinSpace(self): -@@ -3536,7 +3545,8 @@ +@@ -3547,7 +3554,7 @@ maxcmsgs=2, ignoreflags=socket.MSG_CTRUNC) @testFDPassSeparateMinSpace.client_skip - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparateMinSpace(self): fd0, fd1 = self.newFDs(2) -@@ -3560,7 +3570,8 @@ +@@ -3571,7 +3578,7 @@ nbytes = self.sendmsgToServer([msg]) self.assertEqual(nbytes, len(msg)) - @unittest.skipIf(sys.platform == "darwin", "see issue #24725") -+ @unittest.skipIf(sys.platform in ("darwin", 'iOS', 'tvos', 'watchos'), -+ "skipping, see issue #12958") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") def testFDPassEmpty(self): # Try to pass an empty FD array. Can receive either no array # or an empty array. -@@ -4380,28 +4391,38 @@ +@@ -4391,28 +4398,33 @@ pass @requireAttrs(socket.socket, "sendmsg") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class SendmsgUnixStreamTest(SendmsgStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "recvmsg") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class RecvmsgUnixStreamTest(RecvmsgTests, RecvmsgGenericStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "recvmsg_into") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class RecvmsgIntoUnixStreamTest(RecvmsgIntoTests, RecvmsgGenericStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "sendmsg", "recvmsg") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") class RecvmsgSCMRightsStreamTest(SCMRightsTest, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "sendmsg", "recvmsg_into") -+@unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") class RecvmsgIntoSCMRightsStreamTest(RecvmsgIntoMixin, SCMRightsTest, SendrecvmsgUnixStreamTestBase): diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py -index 211321f376..14bf0b70bd 100644 +index 211321f376..07f45e6749 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py -@@ -8,6 +8,7 @@ +@@ -8,13 +8,14 @@ import select import signal import socket @@ -2228,53 +1926,90 @@ index 211321f376..14bf0b70bd 100644 import tempfile import threading import unittest + import socketserver + + import test.support +-from test.support import reap_children, verbose ++from test.support import reap_children, verbose, has_subprocess_support, is_apple_mobile + from test.support import os_helper + from test.support import socket_helper + from test.support import threading_helper @@ -28,7 +29,7 @@ HAVE_UNIX_SOCKETS = hasattr(socket, "AF_UNIX") requires_unix_sockets = unittest.skipUnless(HAVE_UNIX_SOCKETS, 'requires Unix sockets') -HAVE_FORKING = hasattr(os, "fork") -+HAVE_FORKING = hasattr(os, "fork") and os.allows_subprocesses ++HAVE_FORKING = hasattr(os, "fork") and has_subprocess_support requires_forking = unittest.skipUnless(HAVE_FORKING, 'requires forking') def signal_alarm(n): -@@ -196,12 +197,16 @@ +@@ -196,12 +197,14 @@ self.stream_examine) @requires_unix_sockets -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_UnixStreamServer(self): self.run_server(socketserver.UnixStreamServer, socketserver.StreamRequestHandler, self.stream_examine) @requires_unix_sockets -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't fully support UNIX sockets." % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_ThreadingUnixStreamServer(self): self.run_server(socketserver.ThreadingUnixStreamServer, socketserver.StreamRequestHandler, diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py -index a0cb605c16..7aa460d560 100644 +index a0cb605c16..838641f649 100644 --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py -@@ -65,6 +65,7 @@ +@@ -1,7 +1,7 @@ + # -*- coding: koi8-r -*- + + import unittest +-from test.support import script_helper, captured_stdout ++from test.support import script_helper, captured_stdout, has_subprocess_support + from test.support.os_helper import TESTFN, unlink, rmtree + from test.support.import_helper import unload + import importlib +@@ -14,11 +14,11 @@ + + def test_pep263(self): + self.assertEqual( +- "ðÉÔÏÎ".encode("utf-8"), ++ "�����".encode("utf-8"), + b'\xd0\x9f\xd0\xb8\xd1\x82\xd0\xbe\xd0\xbd' + ) + self.assertEqual( +- "\ð".encode("utf-8"), ++ "\�".encode("utf-8"), + b'\\\xd0\x9f' + ) + +@@ -65,6 +65,7 @@ # two bytes in common with the UTF-8 BOM self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_20731(self): sub = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py -index b91791a02a..9fb3d41249 100644 +index b91791a02a..618a843390 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py -@@ -50,6 +50,9 @@ +@@ -24,6 +24,7 @@ + import textwrap + import json + import pathlib ++from test.support import has_subprocess_support + from test.support.os_helper import FakePath + + try: +@@ -50,6 +51,9 @@ mswindows = (sys.platform == "win32") -+if not os.allows_subprocesses: ++if not has_subprocess_support: + raise unittest.SkipTest('Test requires support for subprocesses.') + # @@ -2295,53 +2030,62 @@ index 007d68817c..b49f38fe2d 100644 import distutils.text_file import distutils.unixccompiler diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py -index 3c362485d9..1c43514b74 100644 +index 1094d40849..ebeee34cfa 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py -@@ -110,6 +110,7 @@ - # Python/pythonrun.c::PyErr_PrintEx() is tricky. - - -+ - class SysModuleTest(unittest.TestCase): - - def tearDown(self): -@@ -627,6 +628,7 @@ +@@ -10,6 +10,7 @@ + import sysconfig + import test.support + from test import support ++from test.support import has_subprocess_support + from test.support import os_helper + from test.support.script_helper import assert_python_ok, assert_python_failure + from test.support import threading_helper +@@ -633,6 +634,7 @@ def test_clear_type_cache(self): sys._clear_type_cache() -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_ioencoding(self): env = dict(os.environ) -@@ -674,6 +676,7 @@ +@@ -680,6 +682,7 @@ 'requires OS support of non-ASCII encodings') @unittest.skipUnless(sys.getfilesystemencoding() == locale.getpreferredencoding(False), 'requires FS encoding to match locale') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_ioencoding_nonascii(self): env = dict(os.environ) -@@ -686,6 +689,7 @@ +@@ -692,6 +695,7 @@ @unittest.skipIf(sys.base_prefix != sys.prefix, 'Test is not venv-compatible') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_executable(self): # sys.executable should be absolute self.assertEqual(os.path.abspath(sys.executable), sys.executable) -@@ -720,6 +724,7 @@ +@@ -726,6 +730,7 @@ expected = None self.check_fsencoding(fs_encoding, expected) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def c_locale_get_error_handler(self, locale, isolated=False, encoding=None): # Force the POSIX locale env = os.environ.copy() diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py -index 5ee9839c04..cd8573ad51 100644 +index 5ee9839c04..204a5c8a0f 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py +@@ -5,7 +5,7 @@ + import shutil + from copy import copy + +-from test.support import (captured_stdout, PythonSymlink) ++from test.support import (captured_stdout, PythonSymlink, has_subprocess_support) + from test.support.import_helper import import_module + from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink, + change_cwd) @@ -263,12 +263,13 @@ self.assertTrue(os.path.isfile(config_h), config_h) @@ -2353,84 +2097,98 @@ index 5ee9839c04..cd8573ad51 100644 self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) @skip_unless_symlink -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_symlink(self): # Issue 7880 with PythonSymlink() as py: cmd = "-c", "import sysconfig; print(sysconfig.get_platform())" diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py -index 1946b043d0..95da63a4d4 100644 +index 1946b043d0..c3c21762cc 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py -@@ -21,7 +21,7 @@ +@@ -15,13 +15,14 @@ + + import unittest + from test import support ++from test_support import has_subprocess_support + from test.support import os_helper + from test.support import script_helper + from test.support import warnings_helper has_textmode = (tempfile._text_openflags != tempfile._bin_openflags) -has_spawnl = hasattr(os, 'spawnl') -+has_spawnl = hasattr(os, 'spawnl') and os.allows_subprocesses ++has_spawnl = hasattr(os, 'spawnl') and has_subprocess_support # TEST_FILES may need to be tweaked for systems depending on the maximum # number of files that can be opened at one time (see ulimit -n) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py -index c54806e594..6884bb0f38 100644 +index c54806e594..fc7cf8e1b2 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py -@@ -1142,6 +1142,8 @@ +@@ -3,7 +3,7 @@ + """ + + import test.support +-from test.support import threading_helper ++from test.support import threading_helper, is_apple_mobile + from test.support import verbose, cpython_only, os_helper + from test.support.import_helper import import_module + from test.support.script_helper import assert_python_ok, assert_python_failure +@@ -1142,6 +1142,7 @@ os.set_blocking(r, False) return (r, w) -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't have os.pipe" % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't have os.pipe" % sys.platform) def test_threads_join(self): # Non-daemon threads should be joined at subinterpreter shutdown # (issue #18808) -@@ -1170,6 +1172,8 @@ +@@ -1170,6 +1171,7 @@ # The thread was joined properly. self.assertEqual(os.read(r, 1), b"x") -+ @unittest.skipIf(sys.platform in ('ios', 'tvos', 'watchos'), -+ "%s doesn't have os.pipe" % sys.platform) ++ @unittest.skipIf(is_apple_mobile, "%s doesn't have os.pipe" % sys.platform) def test_threads_join_2(self): # Same as above, but a delay gets introduced after the thread's # Python code returned but before the thread state is deleted. diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py -index 18cd4aba24..c1a2812894 100644 +index 18cd4aba24..d8c782de31 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py -@@ -6,7 +6,9 @@ - import sys - import inspect +@@ -8,7 +8,7 @@ import unittest -+import os import re -+import subprocess from test import support - from test.support import Error, captured_output, cpython_only, ALWAYS_EQ +-from test.support import Error, captured_output, cpython_only, ALWAYS_EQ ++from test.support import Error, captured_output, cpython_only, ALWAYS_EQ, has_subprocess_support from test.support.os_helper import TESTFN, unlink -@@ -130,6 +132,7 @@ + from test.support.script_helper import assert_python_ok + import textwrap +@@ -130,6 +130,7 @@ str_name = '.'.join([X.__module__, X.__qualname__]) self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value)) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_encoded_file(self): # Test that tracebacks are correctly printed for encoded source files: # - correct line number (Issue2384) diff --git a/Lib/test/test_unicodedata.py b/Lib/test/test_unicodedata.py -index 213b3cf252..874a572d56 100644 +index 213b3cf252..9ffb25c012 100644 --- a/Lib/test/test_unicodedata.py +++ b/Lib/test/test_unicodedata.py -@@ -8,6 +8,7 @@ - - import hashlib - from http.client import HTTPException -+import os - import sys +@@ -12,7 +12,7 @@ import unicodedata import unittest -@@ -231,6 +232,7 @@ + from test.support import (open_urlresource, requires_resource, script_helper, +- cpython_only, check_disallow_instantiation) ++ cpython_only, check_disallow_instantiation, has_subprocess_support) + + + class UnicodeMethodsTest(unittest.TestCase): +@@ -231,6 +231,7 @@ # Ensure that the type disallows instantiation (bpo-43916) check_disallow_instantiation(self, unicodedata.UCD) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_failed_import_during_compiling(self): # Issue 4367 # Decoding \N escapes requires the unicodedata module. If it can't be @@ -2466,14 +2224,22 @@ index a7e7c9f0b9..cbe41898a0 100644 redirect_url_with_frag = "http://www.pythontest.net/redir/with_frag/" with socket_helper.transient_internet(redirect_url_with_frag): diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py -index d6a8333427..2064a563ac 100755 +index d6a8333427..a1ea672802 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py +@@ -1,6 +1,6 @@ + import unittest + from test import support +-from test.support import import_helper ++from test.support import import_helper, has_subprocess_support + import builtins + import contextlib + import copy @@ -640,6 +640,7 @@ equal(str(u), v) @unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def testIssue8621(self): # On at least some versions of OSX self.uuid.uuid4 generates # the same sequence of UUIDs in the parent and any @@ -2481,7 +2247,7 @@ index d6a8333427..2064a563ac 100755 @unittest.skipUnless(_uuid._ifconfig_getnode in _uuid._GETTERS, "ifconfig is not used for introspection on this platform") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_ifconfig_getnode(self): node = self.uuid._ifconfig_getnode() self.check_node(node, 'ifconfig') @@ -2489,7 +2255,7 @@ index d6a8333427..2064a563ac 100755 @unittest.skipUnless(_uuid._arp_getnode in _uuid._GETTERS, "arp is not used for introspection on this platform") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_arp_getnode(self): node = self.uuid._arp_getnode() self.check_node(node, 'arp') @@ -2497,19 +2263,28 @@ index d6a8333427..2064a563ac 100755 @unittest.skipUnless(_uuid._netstat_getnode in _uuid._GETTERS, "netstat is not used for introspection on this platform") -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_netstat_getnode(self): node = self.uuid._netstat_getnode() self.check_node(node, 'netstat') diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py -index eca35ec4bf..ef494c479c 100644 +index eca35ec4bf..84863d1d51 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py +@@ -16,7 +16,7 @@ + import sys + import tempfile + from test.support import (captured_stdout, captured_stderr, requires_zlib, +- skip_if_broken_multiprocessing_synchronize) ++ skip_if_broken_multiprocessing_synchronize, has_subprocess_support) + from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) + import unittest + import venv @@ -179,6 +179,7 @@ builder.upgrade_dependencies(fake_context) @requireVenvCreate -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_prefixes(self): """ Test that the prefix values are as expected. @@ -2517,7 +2292,7 @@ index eca35ec4bf..ef494c479c 100644 # point to the venv being used to run the test, and we lose the link # to the source build - so Python can't initialise properly. @requireVenvCreate -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_executable(self): """ Test that the sys.executable value is as expected. @@ -2525,7 +2300,7 @@ index eca35ec4bf..ef494c479c 100644 self.assertEqual(out.strip(), envpy.encode()) @unittest.skipUnless(can_symlink(), 'Needs symlinks') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_executable_symlinks(self): """ Test that the sys.executable value is as expected. @@ -2533,7 +2308,7 @@ index eca35ec4bf..ef494c479c 100644 @requireVenvCreate class EnsurePipTest(BaseTest): """Test venv module installation of pip.""" -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def assert_pip_not_installed(self): envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe) @@ -2541,19 +2316,23 @@ index eca35ec4bf..ef494c479c 100644 # Issue #26610: pip/pep425tags.py requires ctypes @unittest.skipUnless(ctypes, 'pip requires ctypes') -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') @requires_zlib() def test_with_pip(self): self.do_test_with_pip(False) diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py -index 673cc995d3..76870d0111 100644 +index 673cc995d3..bd8a48e694 100644 --- a/Lib/test/test_webbrowser.py +++ b/Lib/test/test_webbrowser.py -@@ -9,6 +9,10 @@ +@@ -6,9 +6,14 @@ + from unittest import mock + from test import support + from test.support import import_helper ++from test.support import is_apple_mobile from test.support import os_helper -+if sys.platform in ('ios', 'tvos', 'watchos'): ++if is_apple_mobile: + raise unittest.SkipTest("Can't run webbrowser tests on %s" % sys.platform) + + @@ -2581,14 +2360,22 @@ index e557d569a1..2be2a96b73 100644 import test packagedir = os.path.dirname(test.__file__) diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py -index 453e6c3d11..7f4ece1ace 100644 +index 453e6c3d11..dd9880f1d8 100644 --- a/Lib/unittest/test/test_runner.py +++ b/Lib/unittest/test/test_runner.py -@@ -1135,6 +1135,7 @@ +@@ -9,6 +9,7 @@ + + from unittest.test.support import (LoggingResult, + ResultWithNoStartTestRunStopTestRun) ++from test.support import has_subprocess_support + + + def resultFactory(*_): +@@ -1135,6 +1136,7 @@ expectedresult = (runner.stream, DESCRIPTIONS, VERBOSITY) self.assertEqual(runner._makeResult(), expectedresult) -+ @unittest.skipUnless(os.allows_subprocesses, 'Test requires support for subprocesses.') ++ @unittest.skipUnless(has_subprocess_support, 'Test requires support for subprocesses.') def test_warnings(self): """ Check that warnings argument of TextTestRunner correctly affects the @@ -2723,50 +2510,6 @@ index 4af8702068..98825e5156 100644 /* Using an alternative stack requires sigaltstack() and sigaction() SA_ONSTACK */ #if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) -diff --git a/Modules/makesetup b/Modules/makesetup -index 1a767838c9..708c65b25f 100755 ---- a/Modules/makesetup -+++ b/Modules/makesetup -@@ -133,7 +133,7 @@ - - # Output DEFS in reverse order so first definition overrides - case $line in -- *=*) DEFS="$line$NL$DEFS"; continue;; -+ [A-Z]*=*) DEFS="$line$NL$DEFS"; continue;; - 'include '*) DEFS="$line$NL$DEFS"; continue;; - '*noobjects*') - case $noobjects in -@@ -162,9 +162,12 @@ - esac - case $arg in - -framework) libs="$libs $arg"; skip=libs; -- # OSX/OSXS/Darwin framework link cmd -+ # OSX/iOS/Darwin framework - ;; -- -[IDUCfF]*) cpps="$cpps $arg";; -+ -F*) libs="$libs $arg"; skip=libs; -+ # OSX/iOS/Darwin framework directory -+ ;; -+ -[IDUCf]*) cpps="$cpps $arg";; - -Xcompiler) skip=cpps;; - -Xlinker) libs="$libs $arg"; skip=libs;; - -rpath) libs="$libs $arg"; skip=libs;; -@@ -182,6 +185,7 @@ - *.c++) srcs="$srcs $arg";; - *.cxx) srcs="$srcs $arg";; - *.cpp) srcs="$srcs $arg";; -+ *.S) srcs="$srcs $arg";; - \$*) libs="$libs $arg" - cpps="$cpps $arg";; - *.*) echo 1>&2 "bad word $arg in $line" -@@ -219,6 +223,7 @@ - *.C) obj=`basename $src .C`.o; cc='$(CXX)';; - *.cxx) obj=`basename $src .cxx`.o; cc='$(CXX)';; - *.cpp) obj=`basename $src .cpp`.o; cc='$(CXX)';; -+ *.S) obj=`basename $src .S`.o; cc='$(CC)';; # Assembly - *.m) obj=`basename $src .m`.o; cc='$(CC)';; # Obj-C - *) continue;; - esac diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 4534176adc..0255bcfd72 100644 --- a/Modules/mathmodule.c @@ -3106,1621 +2849,6 @@ index 4125240606..317a576566 100644 #endif #define TYPE_NULL '0' ---- /dev/null -+++ b/Tools/iOS-test/app/iOS-test/main.py -@@ -0,0 +1,12 @@ -+from datetime import datetime -+import platform -+from test import regrtest -+ -+regrtest.start = datetime.now() -+print("Testing on %s" % platform.machine()) -+print("START:", regrtest.start) -+regrtest.main_in_temp_cwd() -+regrtest.end = datetime.now() -+print("END:", regrtest.end) -+print("Duration:", regrtest.end - regrtest.start) -+ ---- /dev/null -+++ b/Tools/iOS-test/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test.xcodeproj/project.pbxproj -@@ -0,0 +1,369 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+ 60EAF0931C26F7310003B8F5 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */; }; -+ 60EAF0951C26F73D0003B8F5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 60EAF0941C26F73D0003B8F5 /* libz.tbd */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* iOS-test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS-test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* iOS-test-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iOS-test-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* iOS-test-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "iOS-test-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60DBD4B01B47DEF700068095 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; -+ 60EAF0941C26F73D0003B8F5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60EAF0951C26F73D0003B8F5 /* libz.tbd in Frameworks */, -+ 60EAF0931C26F7310003B8F5 /* libsqlite3.tbd in Frameworks */, -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* iOS-test */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* iOS-test.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60EAF0941C26F73D0003B8F5 /* libz.tbd */, -+ 60EAF0921C26F7310003B8F5 /* libsqlite3.tbd */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* iOS-test */ = { -+ isa = PBXGroup; -+ children = ( -+ 60DBD4B01B47DEF700068095 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = "iOS-test"; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* iOS-test-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* iOS-test-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* iOS-test */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "iOS-test" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = "iOS-test"; -+ productName = "iOS-test"; -+ productReference = 60796EE219190F4100A9926B /* iOS-test.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0720; -+ ORGANIZATIONNAME = "Python Software Foundation"; -+ TargetAttributes = { -+ 60796EE119190F4100A9926B = { -+ DevelopmentTeam = 383DLEZ2K4; -+ }; -+ }; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "iOS-test" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* iOS-test */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ ENABLE_BITCODE = NO; -+ ENABLE_TESTABILITY = YES; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_BITCODE = NO; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ CODE_SIGN_IDENTITY = "iPhone Developer"; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "iOS-test/iOS-test-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "iOS-test/iOS-test-Info.plist"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.$(PRODUCT_NAME:rfc1034identifier)"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ PROVISIONING_PROFILE = ""; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ CODE_SIGN_IDENTITY = "iPhone Developer"; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "iOS-test/iOS-test-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "iOS-test/iOS-test-Info.plist"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.$(PRODUCT_NAME:rfc1034identifier)"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ PROVISIONING_PROFILE = ""; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "iOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "iOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,58 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "3x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/iOS-test-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ $(PRODUCT_BUNDLE_IDENTIFIER) -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/iOS-test-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file ---- /dev/null -+++ b/Tools/iOS-test/iOS-test/main.m -@@ -0,0 +1,149 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *exe; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = Py_DecodeLocale([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ // Since iOS doesn't allow dynamic linking, we have to know -+ // the name of the executable so that we can find the ctypes -+ // test objects. However, sys.argv[0] will be updated to -+ // reflect the script name; the TEST_EXECUTABLE environment -+ // variable provides the mechanism for specifying the filename. -+ exe = [NSString stringWithFormat:@"TEST_EXECUTABLE=%s", argv[0], nil]; -+ putenv((char *)[exe UTF8String]); -+ -+ NSLog(@"Initializing Python runtime..."); -+ Py_Initialize(); -+ -+ /******************************************************* -+ To tell lldb not to stop on signals, use the following commands: -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ *******************************************************/ -+ -+ // Arguments to pass to test runner -+ char *test_args[] = { -+ "-j", "1", -+ "-u", "all,-audio,-curses,-largefile,-subprocess,-gui", -+// "-v", // Verbose test output -+ "-W", // Display test output on failure -+ -+ "-x", // Arguments are tests to *exclude* -+// Simulator failures -+// "test_coroutines", // docstring not being populated -+// "test_module", // docstring not being populated -+ -+// ARM64 failures -+// "test_coroutines", // docstring not being populated -+// "test_ctypes", // DL loading? -+// "test_module" // docstring not being populated -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+// "test_unicode", // encoding problem -+ -+// ARMv7 failures -+// "test_cmath", // math domain error -+// "test_ctypes", // DL loading? -+// "test_float", // rounding? -+// "test_math", // math domain error -+// "test_numeric_tower", // -+// "test_strtod", // -+// "test_importlib", // Thread locking problem -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+ -+// COMMON FAILURES -+ "test_bytes" // HARD CRASH ctypes related; PyBytes_FromFormat -+ -+ }; -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.iOS-test/app/iOS-test/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/iOS-test/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ int n_test_args = sizeof(test_args) / sizeof (*test_args) + 1; -+ -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * n_test_args); -+ python_argv[0] = Py_DecodeLocale(main_script, NULL); -+ for (i = 1; i < n_test_args; i++) { -+ python_argv[i] = Py_DecodeLocale(test_args[i-1], NULL); -+ } -+ -+ PySys_SetArgv(n_test_args, python_argv); -+ -+ // If other modules are using thread, we need to initialize them before. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/app/README -@@ -0,0 +1,3 @@ -+Your application code should be placed in this directory. -+ -+The native code will be looking for a tvOS-test/__main__.py file as the entry point. -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/app/tvOS-test/main.py -@@ -0,0 +1,14 @@ -+from __future__ import print_function -+ -+from datetime import datetime -+import platform -+from test import regrtest -+ -+regrtest.start = datetime.now() -+print("Testing on %s" % platform.machine()) -+print("START:", regrtest.start) -+regrtest.main_in_temp_cwd() -+regrtest.end = datetime.now() -+print("END:", regrtest.end) -+print("Duration:", regrtest.end - regrtest.start) -+ ---- /dev/null -+++ b/Tools/tvOS-test/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test.xcodeproj/project.pbxproj -@@ -0,0 +1,356 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 6023B2AE1C28BA7A006F2562 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6023B2AD1C28BA7A006F2562 /* main.m */; }; -+ 6023B2B71C28BA7A006F2562 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2B51C28BA7A006F2562 /* Main.storyboard */; }; -+ 6023B2B91C28BA7A006F2562 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2B81C28BA7A006F2562 /* Assets.xcassets */; }; -+ 6023B2C71C28BD44006F2562 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */; }; -+ 6023B2C81C28BD44006F2562 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */; }; -+ 6023B2C91C28BD44006F2562 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C31C28BD44006F2562 /* Foundation.framework */; }; -+ 6023B2CA1C28BD44006F2562 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */; }; -+ 6023B2CB1C28BD44006F2562 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C51C28BD44006F2562 /* libz.tbd */; }; -+ 6023B2CC1C28BD44006F2562 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2C61C28BD44006F2562 /* UIKit.framework */; }; -+ 6023B2D01C28BDA3006F2562 /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6023B2CE1C28BDA3006F2562 /* Python.framework */; }; -+ 6023B2D31C28BDB7006F2562 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2D11C28BDB7006F2562 /* app */; }; -+ 6023B2D41C28BDB7006F2562 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = 6023B2D21C28BDB7006F2562 /* app_packages */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 6023B2A91C28BA7A006F2562 /* tvOS-test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "tvOS-test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 6023B2AD1C28BA7A006F2562 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 6023B2B61C28BA7A006F2562 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; -+ 6023B2B81C28BA7A006F2562 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; -+ 6023B2BA1C28BA7A006F2562 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -+ 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 6023B2C31C28BD44006F2562 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; -+ 6023B2C51C28BD44006F2562 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; -+ 6023B2C61C28BD44006F2562 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 6023B2CD1C28BDA3006F2562 /* OpenSSL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OpenSSL.framework; sourceTree = ""; }; -+ 6023B2CE1C28BDA3006F2562 /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 6023B2D11C28BDB7006F2562 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; name = app; path = ../app; sourceTree = ""; }; -+ 6023B2D21C28BDB7006F2562 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; name = app_packages; path = ../app_packages; sourceTree = ""; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 6023B2A61C28BA7A006F2562 /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 6023B2CB1C28BD44006F2562 /* libz.tbd in Frameworks */, -+ 6023B2CA1C28BD44006F2562 /* libsqlite3.tbd in Frameworks */, -+ 6023B2D01C28BDA3006F2562 /* Python.framework in Frameworks */, -+ 6023B2C71C28BD44006F2562 /* CoreFoundation.framework in Frameworks */, -+ 6023B2C81C28BD44006F2562 /* CoreGraphics.framework in Frameworks */, -+ 6023B2C91C28BD44006F2562 /* Foundation.framework in Frameworks */, -+ 6023B2CC1C28BD44006F2562 /* UIKit.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 6023B2A01C28BA7A006F2562 = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2AB1C28BA7A006F2562 /* tvOS-test */, -+ 6023B2C01C28BD23006F2562 /* Frameworks */, -+ 6023B2AA1C28BA7A006F2562 /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 6023B2AA1C28BA7A006F2562 /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2A91C28BA7A006F2562 /* tvOS-test.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 6023B2AB1C28BA7A006F2562 /* tvOS-test */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2D11C28BDB7006F2562 /* app */, -+ 6023B2D21C28BDB7006F2562 /* app_packages */, -+ 6023B2B81C28BA7A006F2562 /* Assets.xcassets */, -+ 6023B2AC1C28BA7A006F2562 /* Supporting Files */, -+ ); -+ path = "tvOS-test"; -+ sourceTree = ""; -+ }; -+ 6023B2AC1C28BA7A006F2562 /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2B51C28BA7A006F2562 /* Main.storyboard */, -+ 6023B2BA1C28BA7A006F2562 /* Info.plist */, -+ 6023B2AD1C28BA7A006F2562 /* main.m */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+ 6023B2C01C28BD23006F2562 /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 6023B2C41C28BD44006F2562 /* libsqlite3.tbd */, -+ 6023B2C51C28BD44006F2562 /* libz.tbd */, -+ 6023B2CD1C28BDA3006F2562 /* OpenSSL.framework */, -+ 6023B2CE1C28BDA3006F2562 /* Python.framework */, -+ 6023B2C11C28BD44006F2562 /* CoreFoundation.framework */, -+ 6023B2C21C28BD44006F2562 /* CoreGraphics.framework */, -+ 6023B2C31C28BD44006F2562 /* Foundation.framework */, -+ 6023B2C61C28BD44006F2562 /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 6023B2A81C28BA7A006F2562 /* tvOS-test */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 6023B2BD1C28BA7A006F2562 /* Build configuration list for PBXNativeTarget "tvOS-test" */; -+ buildPhases = ( -+ 6023B2D61C28CB97006F2562 /* Refresh Python source */, -+ 6023B2A51C28BA7A006F2562 /* Sources */, -+ 6023B2A61C28BA7A006F2562 /* Frameworks */, -+ 6023B2A71C28BA7A006F2562 /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = "tvOS-test"; -+ productName = "tvOS-test"; -+ productReference = 6023B2A91C28BA7A006F2562 /* tvOS-test.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 6023B2A11C28BA7A006F2562 /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ LastUpgradeCheck = 0720; -+ ORGANIZATIONNAME = "Python Software Foundation"; -+ TargetAttributes = { -+ 6023B2A81C28BA7A006F2562 = { -+ CreatedOnToolsVersion = 7.2; -+ }; -+ }; -+ }; -+ buildConfigurationList = 6023B2A41C28BA7A006F2562 /* Build configuration list for PBXProject "tvOS-test" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ Base, -+ ); -+ mainGroup = 6023B2A01C28BA7A006F2562; -+ productRefGroup = 6023B2AA1C28BA7A006F2562 /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 6023B2A81C28BA7A006F2562 /* tvOS-test */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 6023B2A71C28BA7A006F2562 /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 6023B2D31C28BDB7006F2562 /* app in Resources */, -+ 6023B2B91C28BA7A006F2562 /* Assets.xcassets in Resources */, -+ 6023B2B71C28BA7A006F2562 /* Main.storyboard in Resources */, -+ 6023B2D41C28BDB7006F2562 /* app_packages in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 6023B2D61C28CB97006F2562 /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/org.python.tvOS-test\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/lib\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/Python.framework/Resources/include\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application Support/org.python.tvOS-test\"\nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git \"$PROJECT_DIR/app_packages/\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\"\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 6023B2A51C28BA7A006F2562 /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 6023B2AE1C28BA7A006F2562 /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 6023B2B51C28BA7A006F2562 /* Main.storyboard */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 6023B2B61C28BA7A006F2562 /* Base */, -+ ); -+ name = Main.storyboard; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 6023B2BB1C28BA7A006F2562 /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN_UNREACHABLE_CODE = YES; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ COPY_PHASE_STRIP = NO; -+ DEBUG_INFORMATION_FORMAT = dwarf; -+ ENABLE_STRICT_OBJC_MSGSEND = YES; -+ ENABLE_TESTABILITY = YES; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_NO_COMMON_BLOCKS = YES; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ MTL_ENABLE_DEBUG_INFO = YES; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = appletvos; -+ TARGETED_DEVICE_FAMILY = 3; -+ TVOS_DEPLOYMENT_TARGET = 9.1; -+ }; -+ name = Debug; -+ }; -+ 6023B2BC1C28BA7A006F2562 /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN_UNREACHABLE_CODE = YES; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ COPY_PHASE_STRIP = NO; -+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; -+ ENABLE_NS_ASSERTIONS = NO; -+ ENABLE_STRICT_OBJC_MSGSEND = YES; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_NO_COMMON_BLOCKS = YES; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ MTL_ENABLE_DEBUG_INFO = NO; -+ SDKROOT = appletvos; -+ TARGETED_DEVICE_FAMILY = 3; -+ TVOS_DEPLOYMENT_TARGET = 9.1; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 6023B2BE1C28BA7A006F2562 /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ INFOPLIST_FILE = "tvOS-test/Info.plist"; -+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.tvOS-test"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ }; -+ name = Debug; -+ }; -+ 6023B2BF1C28BA7A006F2562 /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ INFOPLIST_FILE = "tvOS-test/Info.plist"; -+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; -+ PRODUCT_BUNDLE_IDENTIFIER = "org.python.tvOS-test"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 6023B2A41C28BA7A006F2562 /* Build configuration list for PBXProject "tvOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 6023B2BB1C28BA7A006F2562 /* Debug */, -+ 6023B2BC1C28BA7A006F2562 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 6023B2BD1C28BA7A006F2562 /* Build configuration list for PBXNativeTarget "tvOS-test" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 6023B2BE1C28BA7A006F2562 /* Debug */, -+ 6023B2BF1C28BA7A006F2562 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 6023B2A11C28BA7A006F2562 /* Project object */; -+} ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "large.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json -@@ -0,0 +1,17 @@ -+{ -+ "layers" : [ -+ { -+ "filename" : "Front.imagestacklayer" -+ }, -+ { -+ "filename" : "Middle.imagestacklayer" -+ }, -+ { -+ "filename" : "Back.imagestacklayer" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "large.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "large.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "small.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json -@@ -0,0 +1,17 @@ -+{ -+ "layers" : [ -+ { -+ "filename" : "Front.imagestacklayer" -+ }, -+ { -+ "filename" : "Middle.imagestacklayer" -+ }, -+ { -+ "filename" : "Back.imagestacklayer" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "small.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "small.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json -@@ -0,0 +1,26 @@ -+{ -+ "assets" : [ -+ { -+ "size" : "1280x768", -+ "idiom" : "tv", -+ "filename" : "App Icon - Large.imagestack", -+ "role" : "primary-app-icon" -+ }, -+ { -+ "size" : "400x240", -+ "idiom" : "tv", -+ "filename" : "App Icon - Small.imagestack", -+ "role" : "primary-app-icon" -+ }, -+ { -+ "size" : "1920x720", -+ "idiom" : "tv", -+ "filename" : "Top Shelf Image.imageset", -+ "role" : "top-shelf-image" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json -@@ -0,0 +1,13 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "tv", -+ "filename" : "shelf.png", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Assets.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,16 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "landscape", -+ "idiom" : "tv", -+ "filename" : "launch.png", -+ "extent" : "full-screen", -+ "minimum-system-version" : "9.0", -+ "scale" : "1x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Base.lproj/Main.storyboard -@@ -0,0 +1,25 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/Info.plist -@@ -0,0 +1,32 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleExecutable -+ $(EXECUTABLE_NAME) -+ CFBundleIdentifier -+ $(PRODUCT_BUNDLE_IDENTIFIER) -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ $(PRODUCT_NAME) -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1 -+ LSRequiresIPhoneOS -+ -+ UIMainStoryboardFile -+ Main -+ UIRequiredDeviceCapabilities -+ -+ arm64 -+ -+ -+ ---- /dev/null -+++ b/Tools/tvOS-test/tvOS-test/main.m -@@ -0,0 +1,149 @@ -+// -+// main.m -+// A main module for starting Python projects under tvOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *exe; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = Py_DecodeLocale([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // tvOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ // Since tvOS doesn't allow dynamic linking, we have to know -+ // the name of the executable so that we can find the ctypes -+ // test objects. However, sys.argv[0] will be updated to -+ // reflect the script name; the TEST_EXECUTABLE environment -+ // variable provides the mechanism for specifying the filename. -+ exe = [NSString stringWithFormat:@"TEST_EXECUTABLE=%s", argv[0], nil]; -+ putenv((char *)[exe UTF8String]); -+ -+ NSLog(@"Initializing Python runtime..."); -+ Py_Initialize(); -+ -+ /******************************************************* -+ To tell lldb not to stop on signals, use the following commands: -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ *******************************************************/ -+ -+ // Arguments to pass to test runner -+ char *test_args[] = { -+ "-j", "1", -+ "-u", "all,-audio,-curses,-largefile,-subprocess,-gui", -+// "-v", // Verbose test output -+ "-W", // Display test output on failure -+ -+ "-x", // Arguments are tests to *exclude* -+// Simulator failures -+// "test_coroutines", // docstring not being populated -+// "test_module", // docstring not being populated -+ -+// ARM64 failures -+// "test_coroutines", // docstring not being populated -+// "test_ctypes", // DL loading? -+// "test_module" // docstring not being populated -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+// "test_unicode", // encoding problem -+ -+// ARMv7 failures -+// "test_cmath", // math domain error -+// "test_ctypes", // DL loading? -+// "test_float", // rounding? -+// "test_math", // math domain error -+// "test_numeric_tower", // -+// "test_strtod", // -+// "test_importlib", // Thread locking problem -+// "test_threading", // ctypes related; missing symbol PyThreadState_SetAsyncExc -+ -+// COMMON FAILURES -+ "test_bytes" // HARD CRASH ctypes related; PyBytes_FromFormat -+ -+ }; -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.tvOS-test/app/tvOS-test/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/tvOS-test/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ int n_test_args = sizeof(test_args) / sizeof (*test_args) + 1; -+ -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * n_test_args); -+ python_argv[0] = Py_DecodeLocale(main_script, NULL); -+ for (i = 1; i < n_test_args; i++) { -+ python_argv[i] = Py_DecodeLocale(test_args[i-1], NULL); -+ } -+ -+ PySys_SetArgv(n_test_args, python_argv); -+ -+ // If other modules are using thread, we need to initialize them before. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} diff --git a/aclocal.m4 b/aclocal.m4 index 2f1bd37528..a63e6654da 100644 --- a/aclocal.m4 @@ -4906,7 +3034,7 @@ index d74fb6deac..09ebc4287c 100755 # Blank kernel with real OS is always fine. ;; diff --git a/configure b/configure -index 19f1bd59ba..17d27b9c4d 100755 +index bad619963a..93d99872fd 100755 --- a/configure +++ b/configure @@ -660,6 +660,8 @@ @@ -5087,16 +3215,7 @@ index 19f1bd59ba..17d27b9c4d 100755 fi -@@ -7051,8 +7107,6 @@ - # tweak BASECFLAGS based on compiler and platform - case $GCC in - yes) -- CFLAGS_NODIST="$CFLAGS_NODIST -std=c99" -- - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -Wextra" >&5 - $as_echo_n "checking for -Wextra... " >&6; } - ac_save_cc="$CC" -@@ -9787,6 +9841,10 @@ +@@ -9787,6 +9843,10 @@ BLDSHARED="$LDSHARED" fi ;; @@ -5107,7 +3226,7 @@ index 19f1bd59ba..17d27b9c4d 100755 Linux*|GNU*|QNX*|VxWorks*) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; -@@ -10811,23 +10869,35 @@ +@@ -10811,23 +10871,35 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_system_ffi" >&5 $as_echo "$with_system_ffi" >&6; } else @@ -5149,7 +3268,7 @@ index 19f1bd59ba..17d27b9c4d 100755 # Check for use of the system libmpdec library { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-system-libmpdec" >&5 $as_echo_n "checking for --with-system-libmpdec... " >&6; } -@@ -17742,8 +17812,8 @@ +@@ -17742,8 +17814,8 @@ if ! $found; then OPENSSL_INCLUDES= for ssldir in $ssldirs; do @@ -5160,7 +3279,7 @@ index 19f1bd59ba..17d27b9c4d 100755 if test -f "$ssldir/include/openssl/ssl.h"; then OPENSSL_INCLUDES="-I$ssldir/include" OPENSSL_LDFLAGS="-L$ssldir/lib" -@@ -19367,7 +19437,7 @@ +@@ -19367,7 +19439,7 @@ echo "creating Modules/Setup.local" >&6 if test ! -f Modules/Setup.local then @@ -5170,7 +3289,7 @@ index 19f1bd59ba..17d27b9c4d 100755 echo "creating Makefile" >&6 diff --git a/configure.ac b/configure.ac -index 763fc697be..36c28aaa28 100644 +index cc69015b10..d81f17dad6 100644 --- a/configure.ac +++ b/configure.ac @@ -400,6 +400,15 @@ @@ -5290,16 +3409,7 @@ index 763fc697be..36c28aaa28 100644 fi AC_SUBST(READELF) -@@ -1610,8 +1673,6 @@ - # tweak BASECFLAGS based on compiler and platform - case $GCC in - yes) -- CFLAGS_NODIST="$CFLAGS_NODIST -std=c99" -- - AC_MSG_CHECKING(for -Wextra) - ac_save_cc="$CC" - CC="$CC -Wextra -Werror" -@@ -2712,6 +2773,10 @@ +@@ -2712,6 +2775,10 @@ BLDSHARED="$LDSHARED" fi ;; @@ -5310,7 +3420,7 @@ index 763fc697be..36c28aaa28 100644 Linux*|GNU*|QNX*|VxWorks*) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; -@@ -3118,20 +3183,30 @@ +@@ -3118,20 +3185,30 @@ esac AC_MSG_RESULT($with_system_ffi) else @@ -5346,7 +3456,7 @@ index 763fc697be..36c28aaa28 100644 # Check for use of the system libmpdec library AC_MSG_CHECKING(for --with-system-libmpdec) -@@ -6013,7 +6088,7 @@ +@@ -6013,7 +6090,7 @@ echo "creating Modules/Setup.local" >&AS_MESSAGE_FD if test ! -f Modules/Setup.local then @@ -5355,864 +3465,6 @@ index 763fc697be..36c28aaa28 100644 fi echo "creating Makefile" >&AS_MESSAGE_FD ---- /dev/null -+++ b/iOS/Info.plist -@@ -0,0 +1,20 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ English -+ CFBundleExecutable -+ Python -+ CFBundleIdentifier -+ org.python -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ FMWK -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ xxxVERSIONxxx -+ -+ ---- /dev/null -+++ b/iOS/README -@@ -0,0 +1,165 @@ -+==================== -+Python on iOS README -+==================== -+ -+:Authors: -+ Russell Keith-Magee (2015) -+ -+:Version: 3.5.2 -+ -+This document provides a overview of eccentricities of building and using -+Python on iOS. -+ -+Build instructions -+================== -+ -+The iOS build must be run on an Mac with XCode installed. To build the iOS -+framework, unpack the Python sources, move into the iOS subdirectory, and -+run ``make``. There are no configuration options to this build process - -+it will use XCode utilities to identify the location of compilers, -+resource directories, and so on. -+ -+The build process will configure and build Python 6 times, producing: -+ -+ * A "host" version of Python -+ * A version of Python compiled for the x86-64 iOS Simulator -+ * A version of Python compiled for the i386 iOS Simulator -+ * A version of Python compiled for ARM64 iOS devices -+ * A version of Python compiled for ARMv7s iOS devices -+ * A version of Python compiled for ARMv7 iOS devices -+ -+Build products will be "installed" into iOS/build. The built products will -+then be combined into a single "fat" ``Python.framework`` that can be added to -+an XCode project. The resulting framework will be located in the root -+directory of the Python source tree. -+ -+A ``make clean`` target also exists to clean out all build products; -+``make distclean`` will clean out all user-specific files from the test and -+sample projects. -+ -+Test instructions -+----------------- -+ -+The ``Tools`` directory contains an ``iOS-Test`` project that enables you to -+run the Python regression test suite. When you run ``make`` in the iOS -+directory, a copy of ``Python.framework`` will also be installed into this -+test project. -+ -+To run the test project, load the project into XCode, and run (either on a -+device or in the simulator). The test suite takes around 10 minutes to run on -+an iPhone6S. -+ -+.. note:: If you run the test project in debug mode, the XCode debugger will -+ stop whenever a signal is raised. The Python regression test suite checks -+ a number of signal handlers, and the test suite will stop mid-execution -+ when this occurs. -+ -+ To disable this signal handling, set a breakpoint at the start of -+ ``main.c``; when execution stops at the breakpoint, run the following -+ commands in the debugger (at the ``(lldb)`` prompt in the console log -+ window):: -+ -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ -+ Unfortunately, this has to be done every time the test suite is executed. -+ -+iOS-specific details -+==================== -+ -+* ``import sys; sys.platform`` will report as `'ios'`, regardless of whether you -+ are on a simulator or a real platform. -+ -+* ``import platform; platform.machine()`` will return the device identifier. -+ For example, an iPhone 5S will return `'iPhone6,2'` -+ -+* The following modules are not currently supported: -+ -+ - ``bsddb`` -+ - ``bz2`` -+ - ``curses`` -+ - ``dbm`` -+ - ``gdbm`` -+ - ``hotshot`` -+ - ``idlelib`` -+ - ``lzma`` -+ - ``nis`` -+ - ``ossaudiodev`` -+ - ``readline`` -+ - ``spwd`` -+ - ``sqlite3`` -+ - ``ssl`` -+ - ``tkinter`` -+ - ``turtledemo`` -+ - ``wsgiref`` -+ -+* Due to limitations in using dynamic loading on iOS, binary Python modules must be -+ statically-linked into the executable. The framework package produced by the iOS -+ ``make install`` statically links all the supported standard library modules. -+ If you have a third-party Python binary module, you'll need to incorporate the -+ source files for that module into the sources for your own app. -+ -+ If you want to add or remove a binary module from the set that is included in the -+ Python library, you can do so by providing module setup files for each platform. -+ There are three default module configuration files: -+ -+ - ``Modules/Setup.iOS-aarch64`` for ARM64 iOS builds -+ - ``Modules/Setup.iOS-arm`` for ARMv7 iOS builds -+ - ``Modules/Setup.iOS-x86_64`` for x86_64 iOS simulator builds -+ -+ If you copy these files to a ``.local`` version (e.g., -+ ``Modules/Setup.iOS-aarch64.local``), the local version will override the -+ default. You can then make modifications to the modules that will be included -+ in the iOS framework, and the flags passed to the compiler when compiling those -+ modules. -+ -+Adding Python to an iOS project -+=============================== -+ -+The iOS subdirectory contains a sample XCode 6.1 project to demonstrate how -+Python can be added to an iOS project. After building the Python iOS framework, -+copy it into the ``iOS/XCode-sample`` directory. You should end up with a directory -+structure that looks like this:: -+ -+ XCode-sample/ -+ Python.framework/ - Manually copied into the project -+ app/ -+ sample/ -+ __init__.py -+ main.py - The Python script to be executed -+ app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime -+ sample -+ Images.xcassets -+ en.lproj -+ main.c - The main() definition for the iOS application -+ sample-Info.plist -+ sample-Prefix.pch -+ sample.xcodeproj - The XCode project file -+ -+If you open the project file is project and run it, you should get output -+similar to the following:: -+ -+ 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app -+ 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime -+ 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py -+ Hello, World. -+ 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving -+ -+You can now modify the provide Python source code, import and use -+code from the Python standard library, and add third-party modules to -+app_packages. -+ -+The sample app is a console-only app, so it isn't of any real practical use. -+Python can be embedded into any Objective-C project using the normal Python -+APIs for embedding; but if you want to write a full iOS app in Python, or -+you want to access iOS services from within embedded code, you'll need to -+bridge between the Objective-C environment and the Python environment. -+This binding isn't something that Python does out of the box; you'll need -+to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. -+ -+.. _Rubicon ObjC: http://pybee.org/rubicon -+.. _Pyobjus: http://pyobjus.readthedocs.org/ -+.. _PyObjC: https://pythonhosted.org/pyobjc/ ---- /dev/null -+++ b/iOS/XCode-sample/app/sample/main.py -@@ -0,0 +1,3 @@ -+ -+if __name__ == '__main__': -+ print("Hello, World.") ---- /dev/null -+++ b/iOS/XCode-sample/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample.xcodeproj/project.pbxproj -@@ -0,0 +1,353 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* sample */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* sample.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */, -+ 60796F1819190FBB00A9926B /* libz.dylib */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* sample */ = { -+ isa = PBXGroup; -+ children = ( -+ 60F0BABD191FC83F006EC268 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = sample; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* sample-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* sample */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = sample; -+ productName = sample; -+ productReference = 60796EE219190F4100A9926B /* sample.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0630; -+ ORGANIZATIONNAME = "Example Corp"; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* sample */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/iOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,53 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/iOS/XCode-sample/sample/main.m -@@ -0,0 +1,111 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = _Py_char2wchar([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ NSLog(@"Initializing Python runtime"); -+ Py_Initialize(); -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/sample/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc); -+ -+ python_argv[0] = _Py_char2wchar(main_script, NULL); -+ for (i = 1; i < argc; i++) { -+ python_argv[i] = _Py_char2wchar(argv[i], NULL); -+ } -+ -+ PySys_SetArgv(argc, python_argv); -+ -+ // If other modules are using threads, we need to initialize them. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } else { -+ // In a normal iOS application, the following line is what -+ // actually runs the application. It requires that the -+ // Objective-C runtime environment has a class named -+ // "PythonAppDelegate". This project doesn't define -+ // one, because Objective-C bridging isn't something -+ // Python does out of the box. You'll need to use -+ // a library like Rubicon-ObjC [1], Pyobjus [2] or -+ // PyObjC [3] if you want to run an *actual* iOS app. -+ // [1] http://pybee.org/rubicon -+ // [2] http://pyobjus.readthedocs.org/ -+ // [3] https://pythonhosted.org/pyobjc/ -+ -+ UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/sample-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ com.example.${PRODUCT_NAME:rfc1034identifier} -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/iOS/XCode-sample/sample/sample-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file diff --git a/setup.py b/setup.py index 85a2b26357..510a1ac62b 100644 --- a/setup.py @@ -6267,1711 +3519,3 @@ index 85a2b26357..510a1ac62b 100644 if sysconfig.get_config_var('HAVE_LIBDL'): # for dlopen, see bpo-32647 ---- /dev/null -+++ b/tvOS/Info.plist -@@ -0,0 +1,20 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ English -+ CFBundleExecutable -+ Python -+ CFBundleIdentifier -+ org.python -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ FMWK -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ xxxVERSIONxxx -+ -+ ---- /dev/null -+++ b/tvOS/README -@@ -0,0 +1,161 @@ -+===================== -+Python on tvOS README -+===================== -+ -+:Authors: -+ Russell Keith-Magee (2015) -+ -+:Version: 3.5.2 -+ -+This document provides a overview of eccentricities of building and using -+Python on tvOS devices (i.e., AppleTV). -+ -+Build instructions -+================== -+ -+The tvOS build must be run on an Mac with XCode installed. To build the tvOS -+framework, unpack the Python sources, move into the tvOS subdirectory, and -+run ``make``. There are no configuration options to this build process - -+it will use XCode utilities to identify the location of compilers, -+resource directories, and so on. -+ -+The build process will configure and build Python 3 times, producing: -+ -+ * A "host" version of Python -+ * A version of Python compiled for the x86-64 tvOS Simulator -+ * A version of Python compiled for ARM64 tvOS devices -+ -+Build products will be "installed" into tvOS/build. The built products will -+then be combined into a single "fat" ``Python.framework`` that can be added to -+an XCode project. The resulting framework will be located in the root -+directory of the Python source tree. -+ -+A ``make clean`` target also exists to clean out all build products; -+``make distclean`` will clean out all user-specific files from the test and -+sample projects. -+ -+Test instructions -+----------------- -+ -+The ``Tools`` directory contains an ``tvOS-Test`` project that enables you to -+run the Python regression test suite. When you run ``make`` in the tvOS -+directory, a copy of ``Python.framework`` will also be installed into this -+test project. -+ -+To run the test project, load the project into XCode, and run (either on a -+device or in the simulator). The test suite takes around 10 minutes to run on -+an gen 4 AppleTV. -+ -+.. note:: If you run the test project in debug mode, the XCode debugger will -+ stop whenever a signal is raised. The Python regression test suite checks -+ a number of signal handlers, and the test suite will stop mid-execution -+ when this occurs. -+ -+ To disable this signal handling, set a breakpoint at the start of -+ ``main.c``; when execution stops at the breakpoint, run the following -+ commands in the debugger (at the ``(lldb)`` prompt in the console log -+ window):: -+ -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ -+ Unfortunately, this has to be done every time the test suite is executed. -+ -+tvOS-specific details -+==================== -+ -+* ``import sys; sys.platform`` will report as `'tvos'`, regardless of whether you -+ are on a simulator or a real platform. -+ -+* ``import platform; platform.machine()`` will return the device identifier. -+ For example, a Generation 4 Apple TV will return `'AppleTV5,3'` -+ -+* The following modules are not currently supported: -+ -+ - ``bsddb`` -+ - ``bz2`` -+ - ``curses`` -+ - ``dbm`` -+ - ``gdbm`` -+ - ``hotshot`` -+ - ``idlelib`` -+ - ``lzma`` -+ - ``nis`` -+ - ``ossaudiodev`` -+ - ``readline`` -+ - ``spwd`` -+ - ``sqlite3`` -+ - ``ssl`` -+ - ``tkinter`` -+ - ``turtledemo`` -+ - ``wsgiref`` -+ -+* Due to limitations in using dynamic loading on tvOS, binary Python modules must be -+ statically-linked into the executable. The framework package produced by the tvOS -+ ``make install`` statically links all the supported standard library modules. -+ If you have a third-party Python binary module, you'll need to incorporate the -+ source files for that module into the sources for your own app. -+ -+ If you want to add or remove a binary module from the set that is included in the -+ Python library, you can do so by providing module setup files for each platform. -+ There are two default module configuration files: -+ -+ - ``Modules/Setup.tvOS-aarch64`` for ARM64 tvOS builds -+ - ``Modules/Setup.tvOS-x86_64`` for x86_64 tvOS simulator builds -+ -+ If you copy these files to a ``.local`` version (e.g., -+ ``Modules/Setup.tvOS-aarch64.local``), the local version will override the -+ default. You can then make modifications to the modules that will be included -+ in the tvOS framework, and the flags passed to the compiler when compiling those -+ modules. -+ -+Adding Python to an tvOS project -+=============================== -+ -+The tvOS subdirectory contains a sample XCode 6.1 project to demonstrate how -+Python can be added to an tvOS project. After building the Python tvOS framework, -+copy it into the ``tvOS/XCode-sample`` directory. You should end up with a directory -+structure that looks like this:: -+ -+ XCode-sample/ -+ Python.framework/ - Manually copied into the project -+ app/ -+ sample/ -+ __init__.py -+ main.py - The Python script to be executed -+ app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime -+ sample -+ Images.xcassets -+ en.lproj -+ main.c - The main() definition for the tvOS application -+ sample-Info.plist -+ sample-Prefix.pch -+ sample.xcodeproj - The XCode project file -+ -+If you open the project file is project and run it, you should get output -+similar to the following:: -+ -+ 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app -+ 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime -+ 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py -+ Hello, World. -+ 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving -+ -+You can now modify the provide Python source code, import and use -+code from the Python standard library, and add third-party modules to -+app_packages. -+ -+The sample app is a console-only app, so it isn't of any real practical use. -+Python can be embedded into any Objective-C project using the normal Python -+APIs for embedding; but if you want to write a full tvOS app in Python, or -+you want to access tvOS services from within embedded code, you'll need to -+bridge between the Objective-C environment and the Python environment. -+This binding isn't something that Python does out of the box; you'll need -+to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. -+ -+.. _Rubicon ObjC: http://pybee.org/rubicon -+.. _Pyobjus: http://pyobjus.readthedocs.org/ -+.. _PyObjC: https://pythonhosted.org/pyobjc/ ---- /dev/null -+++ b/tvOS/XCode-sample/app/sample/main.py -@@ -0,0 +1,3 @@ -+ -+if __name__ == '__main__': -+ print("Hello, World.") ---- /dev/null -+++ b/tvOS/XCode-sample/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample.xcodeproj/project.pbxproj -@@ -0,0 +1,353 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* sample */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* sample.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */, -+ 60796F1819190FBB00A9926B /* libz.dylib */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* sample */ = { -+ isa = PBXGroup; -+ children = ( -+ 60F0BABD191FC83F006EC268 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = sample; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* sample-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* sample */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = sample; -+ productName = sample; -+ productReference = 60796EE219190F4100A9926B /* sample.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0630; -+ ORGANIZATIONNAME = "Example Corp"; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* sample */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/tvOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,53 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/tvOS/XCode-sample/sample/main.m -@@ -0,0 +1,111 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *python_home; -+ wchar_t *wpython_home; -+ const char* main_script; -+ wchar_t** python_argv; -+ -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = _Py_char2wchar([python_home UTF8String], NULL); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ NSLog(@"Initializing Python runtime"); -+ Py_Initialize(); -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/sample/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc); -+ -+ python_argv[0] = _Py_char2wchar(main_script, NULL); -+ for (i = 1; i < argc; i++) { -+ python_argv[i] = _Py_char2wchar(argv[i], NULL); -+ } -+ -+ PySys_SetArgv(argc, python_argv); -+ -+ // If other modules are using threads, we need to initialize them. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } else { -+ // In a normal iOS application, the following line is what -+ // actually runs the application. It requires that the -+ // Objective-C runtime environment has a class named -+ // "PythonAppDelegate". This project doesn't define -+ // one, because Objective-C bridging isn't something -+ // Python does out of the box. You'll need to use -+ // a library like Rubicon-ObjC [1], Pyobjus [2] or -+ // PyObjC [3] if you want to run an *actual* iOS app. -+ // [1] http://pybee.org/rubicon -+ // [2] http://pyobjus.readthedocs.org/ -+ // [3] https://pythonhosted.org/pyobjc/ -+ -+ UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_RawFree(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_RawFree(python_argv[i]); -+ } -+ PyMem_RawFree(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/sample-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ com.example.${PRODUCT_NAME:rfc1034identifier} -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/tvOS/XCode-sample/sample/sample-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file ---- /dev/null -+++ b/watchOS/Info.plist -@@ -0,0 +1,20 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ English -+ CFBundleExecutable -+ Python -+ CFBundleIdentifier -+ org.python -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ FMWK -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ xxxVERSIONxxx -+ -+ ---- /dev/null -+++ b/watchOS/README -@@ -0,0 +1,161 @@ -+======================== -+Python on watchOS README -+======================== -+ -+:Authors: -+ Russell Keith-Magee (2015) -+ -+:Version: 3.5.2 -+ -+This document provides a overview of eccentricities of building and using -+Python on watchOS devices (i.e., AppleTV). -+ -+Build instructions -+================== -+ -+The watchOS build must be run on an Mac with XCode installed. To build the watchOS -+framework, unpack the Python sources, move into the watchOS subdirectory, and -+run ``make``. There are no configuration options to this build process - -+it will use XCode utilities to identify the location of compilers, -+resource directories, and so on. -+ -+The build process will configure and build Python 3 times, producing: -+ -+ * A "host" version of Python -+ * A version of Python compiled for the i386 watchOS Simulator -+ * A version of Python compiled for ARMv7k watchOS devices -+ -+Build products will be "installed" into watchOS/build. The built products will -+then be combined into a single "fat" ``Python.framework`` that can be added to -+an XCode project. The resulting framework will be located in the root -+directory of the Python source tree. -+ -+A ``make clean`` target also exists to clean out all build products; -+``make distclean`` will clean out all user-specific files from the test and -+sample projects. -+ -+Test instructions -+----------------- -+ -+The ``Tools`` directory contains an ``watchOS-Test`` project that enables you to -+run the Python regression test suite. When you run ``make`` in the watchOS -+directory, a copy of ``Python.framework`` will also be installed into this -+test project. -+ -+To run the test project, load the project into XCode, and run (either on a -+device or in the simulator). The test suite takes around 10 minutes to run on -+an Apple Watch. -+ -+.. note:: If you run the test project in debug mode, the XCode debugger will -+ stop whenever a signal is raised. The Python regression test suite checks -+ a number of signal handlers, and the test suite will stop mid-execution -+ when this occurs. -+ -+ To disable this signal handling, set a breakpoint at the start of -+ ``main.c``; when execution stops at the breakpoint, run the following -+ commands in the debugger (at the ``(lldb)`` prompt in the console log -+ window):: -+ -+ process handle SIGPIPE -n true -p true -s false -+ process handle SIGINT -n true -p true -s false -+ process handle SIGXFSZ -n true -p true -s false -+ process handle SIGUSR1 -n true -p true -s false -+ process handle SIGUSR2 -n true -p true -s false -+ -+ Unfortunately, this has to be done every time the test suite is executed. -+ -+watchOS-specific details -+==================== -+ -+* ``import sys; sys.platform`` will report as `'watchos'`, regardless of whether you -+ are on a simulator or a real platform. -+ -+* ``import platform; platform.machine()`` will return the device identifier. -+ For example, an origianl Apple Watch will return `'Watch1,1'` -+ -+* The following modules are not currently supported: -+ -+ - ``bsddb`` -+ - ``bz2`` -+ - ``curses`` -+ - ``dbm`` -+ - ``gdbm`` -+ - ``hotshot`` -+ - ``idlelib`` -+ - ``lzma`` -+ - ``nis`` -+ - ``ossaudiodev`` -+ - ``readline`` -+ - ``spwd`` -+ - ``sqlite3`` -+ - ``ssl`` -+ - ``tkinter`` -+ - ``turtledemo`` -+ - ``wsgiref`` -+ -+* Due to limitations in using dynamic loading on watchOS, binary Python modules must be -+ statically-linked into the executable. The framework package produced by the watchOS -+ ``make install`` statically links all the supported standard library modules. -+ If you have a third-party Python binary module, you'll need to incorporate the -+ source files for that module into the sources for your own app. -+ -+ If you want to add or remove a binary module from the set that is included in the -+ Python library, you can do so by providing module setup files for each platform. -+ There are two default module configuration files: -+ -+ - ``Modules/Setup.watchOS-aarch64`` for ARM64 watchOS builds -+ - ``Modules/Setup.watchOS-x86_64`` for x86_64 watchOS simulator builds -+ -+ If you copy these files to a ``.local`` version (e.g., -+ ``Modules/Setup.watchOS-aarch64.local``), the local version will override the -+ default. You can then make modifications to the modules that will be included -+ in the watchOS framework, and the flags passed to the compiler when compiling those -+ modules. -+ -+Adding Python to an watchOS project -+=============================== -+ -+The watchOS subdirectory contains a sample XCode 6.1 project to demonstrate how -+Python can be added to an watchOS project. After building the Python watchOS framework, -+copy it into the ``watchOS/XCode-sample`` directory. You should end up with a directory -+structure that looks like this:: -+ -+ XCode-sample/ -+ Python.framework/ - Manually copied into the project -+ app/ -+ sample/ -+ __init__.py -+ main.py - The Python script to be executed -+ app_packages/ - A directory that will be added to the `PYTHONPATH` at runtime -+ sample -+ Images.xcassets -+ en.lproj -+ main.c - The main() definition for the watchOS application -+ sample-Info.plist -+ sample-Prefix.pch -+ sample.xcodeproj - The XCode project file -+ -+If you open the project file is project and run it, you should get output -+similar to the following:: -+ -+ 2015-03-14 22:15:19.595 sample[84454:22100187] PythonHome is: /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app -+ 2015-03-14 22:15:19.597 sample[84454:22100187] Initializing Python runtime -+ 2015-03-14 22:15:19.758 sample[84454:22100187] Running /Users/rkm/Library/Developer/CoreSimulator/Devices/19FE988F-E5C3-4A6C-8752-C12DE9BF079D/data/Containers/Bundle/Application/A949B323-FD20-4C76-B370-99AFF294E9D5/sample.app/app/sample/main.py -+ Hello, World. -+ 2015-03-14 22:15:19.792 sample[84454:22100187] Leaving -+ -+You can now modify the provide Python source code, import and use -+code from the Python standard library, and add third-party modules to -+app_packages. -+ -+The sample app is a console-only app, so it isn't of any real practical use. -+Python can be embedded into any Objective-C project using the normal Python -+APIs for embedding; but if you want to write a full watchOS app in Python, or -+you want to access watchOS services from within embedded code, you'll need to -+bridge between the Objective-C environment and the Python environment. -+This binding isn't something that Python does out of the box; you'll need -+to use a third-party library like `Rubicon ObjC`_, `Pyobjus`_ or `PyObjC`_. -+ -+.. _Rubicon ObjC: http://pybee.org/rubicon -+.. _Pyobjus: http://pyobjus.readthedocs.org/ -+.. _PyObjC: https://pythonhosted.org/pyobjc/ ---- /dev/null -+++ b/watchOS/XCode-sample/app/sample/main.py -@@ -0,0 +1,3 @@ -+ -+if __name__ == '__main__': -+ print("Hello, World.") ---- /dev/null -+++ b/watchOS/XCode-sample/app_packages/README -@@ -0,0 +1 @@ -+This directory exists so that 3rd party packages can be installed here. -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample.xcodeproj/project.pbxproj -@@ -0,0 +1,353 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 46; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE519190F4100A9926B /* Foundation.framework */; }; -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE719190F4100A9926B /* CoreGraphics.framework */; }; -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796EE919190F4100A9926B /* UIKit.framework */; }; -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 60796EEE19190F4100A9926B /* InfoPlist.strings */; }; -+ 60796EF219190F4100A9926B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60796EF119190F4100A9926B /* main.m */; }; -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60796EF719190F4100A9926B /* Images.xcassets */; }; -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1819190FBB00A9926B /* libz.dylib */; }; -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F1F1919174D00A9926B /* libsqlite3.dylib */; }; -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F2B1919C70800A9926B /* Python.framework */; }; -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60796F38191CDBBA00A9926B /* CoreFoundation.framework */; }; -+/* End PBXBuildFile section */ -+ -+/* Begin PBXFileReference section */ -+ 60796EE219190F4100A9926B /* sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 60796EE519190F4100A9926B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; -+ 60796EE919190F4100A9926B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; -+ 60796EED19190F4100A9926B /* sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "sample-Info.plist"; sourceTree = ""; }; -+ 60796EEF19190F4100A9926B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; -+ 60796EF119190F4100A9926B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "sample-Prefix.pch"; sourceTree = ""; }; -+ 60796EF719190F4100A9926B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; -+ 60796F1819190FBB00A9926B /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; -+ 60796F2B1919C70800A9926B /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Python.framework; sourceTree = ""; }; -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; -+ 60F0BABD191FC83F006EC268 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = SOURCE_ROOT; }; -+ 60F0BABF191FC868006EC268 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = SOURCE_ROOT; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 60796EDF19190F4100A9926B /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796F39191CDBBA00A9926B /* CoreFoundation.framework in Frameworks */, -+ 60796F2C1919C70800A9926B /* Python.framework in Frameworks */, -+ 60796F201919174D00A9926B /* libsqlite3.dylib in Frameworks */, -+ 60796F1919190FBB00A9926B /* libz.dylib in Frameworks */, -+ 60796EE819190F4100A9926B /* CoreGraphics.framework in Frameworks */, -+ 60796EEA19190F4100A9926B /* UIKit.framework in Frameworks */, -+ 60796EE619190F4100A9926B /* Foundation.framework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 60796ED919190F4100A9926B = { -+ isa = PBXGroup; -+ children = ( -+ 60796EEB19190F4100A9926B /* sample */, -+ 60796EE419190F4100A9926B /* Frameworks */, -+ 60796EE319190F4100A9926B /* Products */, -+ ); -+ sourceTree = ""; -+ }; -+ 60796EE319190F4100A9926B /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EE219190F4100A9926B /* sample.app */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 60796EE419190F4100A9926B /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796F1F1919174D00A9926B /* libsqlite3.dylib */, -+ 60796F1819190FBB00A9926B /* libz.dylib */, -+ 60796F38191CDBBA00A9926B /* CoreFoundation.framework */, -+ 60796EE719190F4100A9926B /* CoreGraphics.framework */, -+ 60796EE519190F4100A9926B /* Foundation.framework */, -+ 60796F2B1919C70800A9926B /* Python.framework */, -+ 60796EE919190F4100A9926B /* UIKit.framework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+ 60796EEB19190F4100A9926B /* sample */ = { -+ isa = PBXGroup; -+ children = ( -+ 60F0BABD191FC83F006EC268 /* app */, -+ 60F0BABF191FC868006EC268 /* app_packages */, -+ 60796EF719190F4100A9926B /* Images.xcassets */, -+ 60796EEC19190F4100A9926B /* Supporting Files */, -+ ); -+ path = sample; -+ sourceTree = ""; -+ }; -+ 60796EEC19190F4100A9926B /* Supporting Files */ = { -+ isa = PBXGroup; -+ children = ( -+ 60796EED19190F4100A9926B /* sample-Info.plist */, -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */, -+ 60796EF119190F4100A9926B /* main.m */, -+ 60796EF319190F4100A9926B /* sample-Prefix.pch */, -+ ); -+ name = "Supporting Files"; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 60796EE119190F4100A9926B /* sample */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */; -+ buildPhases = ( -+ 60796F2F1919C7E700A9926B /* Refresh Python source */, -+ 60796EDE19190F4100A9926B /* Sources */, -+ 60796EDF19190F4100A9926B /* Frameworks */, -+ 60796EE019190F4100A9926B /* Resources */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = sample; -+ productName = sample; -+ productReference = 60796EE219190F4100A9926B /* sample.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+/* End PBXNativeTarget section */ -+ -+/* Begin PBXProject section */ -+ 60796EDA19190F4100A9926B /* Project object */ = { -+ isa = PBXProject; -+ attributes = { -+ CLASSPREFIX = Py; -+ LastUpgradeCheck = 0630; -+ ORGANIZATIONNAME = "Example Corp"; -+ }; -+ buildConfigurationList = 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */; -+ compatibilityVersion = "Xcode 3.2"; -+ developmentRegion = English; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ ); -+ mainGroup = 60796ED919190F4100A9926B; -+ productRefGroup = 60796EE319190F4100A9926B /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 60796EE119190F4100A9926B /* sample */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 60796EE019190F4100A9926B /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF019190F4100A9926B /* InfoPlist.strings in Resources */, -+ 60796EF819190F4100A9926B /* Images.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 60796F2F1919C7E700A9926B /* Refresh Python source */ = { -+ isa = PBXShellScriptBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Refresh Python source"; -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nmkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/lib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/Python.framework/Resources/include $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Application\\ Support/org.python.$PROJECT_NAME\nrsync -pvtrL --exclude .hg --exclude .svn --exclude .git $PROJECT_DIR/app_packages/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/Python.framework/Resources/lib/python`readlink $PROJECT_DIR/Python.framework/Versions/Current`/site-packages/\n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 60796EDE19190F4100A9926B /* Sources */ = { -+ isa = PBXSourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 60796EF219190F4100A9926B /* main.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXVariantGroup section */ -+ 60796EEE19190F4100A9926B /* InfoPlist.strings */ = { -+ isa = PBXVariantGroup; -+ children = ( -+ 60796EEF19190F4100A9926B /* en */, -+ ); -+ name = InfoPlist.strings; -+ sourceTree = ""; -+ }; -+/* End PBXVariantGroup section */ -+ -+/* Begin XCBuildConfiguration section */ -+ 60796F0C19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_DYNAMIC_NO_PIC = NO; -+ GCC_OPTIMIZATION_LEVEL = 0; -+ GCC_PREPROCESSOR_DEFINITIONS = ( -+ "DEBUG=1", -+ "$(inherited)", -+ ); -+ GCC_SYMBOLS_PRIVATE_EXTERN = NO; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ name = Debug; -+ }; -+ 60796F0D19190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ALWAYS_SEARCH_USER_PATHS = NO; -+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; -+ CLANG_CXX_LIBRARY = "libc++"; -+ CLANG_ENABLE_MODULES = YES; -+ CLANG_ENABLE_OBJC_ARC = YES; -+ CLANG_WARN_BOOL_CONVERSION = YES; -+ CLANG_WARN_CONSTANT_CONVERSION = YES; -+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; -+ CLANG_WARN_EMPTY_BODY = YES; -+ CLANG_WARN_ENUM_CONVERSION = YES; -+ CLANG_WARN_INT_CONVERSION = YES; -+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; -+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; -+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; -+ COPY_PHASE_STRIP = YES; -+ ENABLE_NS_ASSERTIONS = NO; -+ GCC_C_LANGUAGE_STANDARD = gnu99; -+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; -+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; -+ GCC_WARN_UNDECLARED_SELECTOR = YES; -+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -+ GCC_WARN_UNUSED_FUNCTION = YES; -+ GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 7.1; -+ SDKROOT = iphoneos; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ VALIDATE_PRODUCT = YES; -+ }; -+ name = Release; -+ }; -+ 60796F0F19190F4100A9926B /* Debug */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Debug; -+ }; -+ 60796F1019190F4100A9926B /* Release */ = { -+ isa = XCBuildConfiguration; -+ buildSettings = { -+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; -+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "$(PROJECT_DIR)", -+ ); -+ GCC_PRECOMPILE_PREFIX_HEADER = YES; -+ GCC_PREFIX_HEADER = "sample/sample-Prefix.pch"; -+ HEADER_SEARCH_PATHS = ( -+ "$(inherited)", -+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, -+ "\"$(PROJECT_DIR)/Python.framework/Resources/include/python2.7\"", -+ ); -+ INFOPLIST_FILE = "sample/sample-Info.plist"; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ USER_HEADER_SEARCH_PATHS = include/python2.7; -+ WRAPPER_EXTENSION = app; -+ }; -+ name = Release; -+ }; -+/* End XCBuildConfiguration section */ -+ -+/* Begin XCConfigurationList section */ -+ 60796EDD19190F4100A9926B /* Build configuration list for PBXProject "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0C19190F4100A9926B /* Debug */, -+ 60796F0D19190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 60796F0E19190F4100A9926B /* Build configuration list for PBXNativeTarget "sample" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 60796F0F19190F4100A9926B /* Debug */, -+ 60796F1019190F4100A9926B /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 60796EDA19190F4100A9926B /* Project object */; -+} ---- /dev/null -+++ b/watchOS/XCode-sample/sample/Images.xcassets/AppIcon.appiconset/Contents.json -@@ -0,0 +1,53 @@ -+{ -+ "images" : [ -+ { -+ "idiom" : "iphone", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "iphone", -+ "size" : "60x60", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "29x29", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "40x40", -+ "scale" : "2x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "1x" -+ }, -+ { -+ "idiom" : "ipad", -+ "size" : "76x76", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/Images.xcassets/LaunchImage.launchimage/Contents.json -@@ -0,0 +1,51 @@ -+{ -+ "images" : [ -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "iphone", -+ "subtype" : "retina4", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "1x" -+ }, -+ { -+ "orientation" : "portrait", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ }, -+ { -+ "orientation" : "landscape", -+ "idiom" : "ipad", -+ "extent" : "full-screen", -+ "minimum-system-version" : "7.0", -+ "scale" : "2x" -+ } -+ ], -+ "info" : { -+ "version" : 1, -+ "author" : "xcode" -+ } -+} -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/en.lproj/InfoPlist.strings -@@ -0,0 +1 @@ -+/* Localized versions of Info.plist keys */ ---- /dev/null -+++ b/watchOS/XCode-sample/sample/main.m -@@ -0,0 +1,111 @@ -+// -+// main.m -+// A main module for starting Python projects under iOS. -+// -+ -+#import -+#import -+#include -+#include -+ -+int main(int argc, char *argv[]) { -+ int ret = 0; -+ unsigned int i; -+ NSString *tmp_path; -+ NSString *python_home; -+ char *wpython_home; -+ const char* main_script; -+ char** python_argv; -+ @autoreleasepool { -+ -+ NSString * resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Special environment to avoid writing bytecode because -+ // the process will not have write attribute on the device. -+ putenv("PYTHONDONTWRITEBYTECODE=1"); -+ -+ python_home = [NSString stringWithFormat:@"%@/Library/Python.framework/Resources", resourcePath, nil]; -+ NSLog(@"PythonHome is: %@", python_home); -+ wpython_home = strdup([python_home UTF8String]); -+ Py_SetPythonHome(wpython_home); -+ -+ // iOS provides a specific directory for temp files. -+ tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil]; -+ putenv((char *)[tmp_path UTF8String]); -+ -+ NSLog(@"Initializing Python runtime"); -+ Py_Initialize(); -+ -+ // Set the name of the main script -+ main_script = [ -+ [[NSBundle mainBundle] pathForResource:@"Library/Application Support/org.python.sample/app/sample/main" -+ ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding]; -+ -+ if (main_script == NULL) { -+ NSLog(@"Unable to locate app/sample/main.py file"); -+ exit(-1); -+ } -+ -+ // Construct argv for the interpreter -+ python_argv = PyMem_Malloc(sizeof(char *) * argc); -+ -+ -+ python_argv[0] = strdup(main_script); -+ for (i = 1; i < argc; i++) { -+ python_argv[i] = argv[i]; -+ } -+ -+ PySys_SetArgv(argc, python_argv); -+ -+ // If other modules are using threads, we need to initialize them. -+ PyEval_InitThreads(); -+ -+ // Start the main.py script -+ NSLog(@"Running %s", main_script); -+ -+ @try { -+ FILE* fd = fopen(main_script, "r"); -+ if (fd == NULL) { -+ ret = 1; -+ NSLog(@"Unable to open main.py, abort."); -+ } else { -+ ret = PyRun_SimpleFileEx(fd, main_script, 1); -+ if (ret != 0) { -+ NSLog(@"Application quit abnormally!"); -+ } else { -+ // In a normal iOS application, the following line is what -+ // actually runs the application. It requires that the -+ // Objective-C runtime environment has a class named -+ // "PythonAppDelegate". This project doesn't define -+ // one, because Objective-C bridging isn't something -+ // Python does out of the box. You'll need to use -+ // a library like Rubicon-ObjC [1], Pyobjus [2] or -+ // PyObjC [3] if you want to run an *actual* iOS app. -+ // [1] http://pybee.org/rubicon -+ // [2] http://pyobjus.readthedocs.org/ -+ // [3] https://pythonhosted.org/pyobjc/ -+ -+ UIApplicationMain(argc, argv, nil, @"PythonAppDelegate"); -+ } -+ } -+ } -+ @catch (NSException *exception) { -+ NSLog(@"Python runtime error: %@", [exception reason]); -+ } -+ @finally { -+ Py_Finalize(); -+ } -+ -+ PyMem_Free(wpython_home); -+ if (python_argv) { -+ for (i = 0; i < argc; i++) { -+ PyMem_Free(python_argv[i]); -+ } -+ PyMem_Free(python_argv); -+ } -+ NSLog(@"Leaving"); -+ } -+ -+ exit(ret); -+ return ret; -+} -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/sample-Info.plist -@@ -0,0 +1,45 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ com.example.${PRODUCT_NAME:rfc1034identifier} -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1.0 -+ LSRequiresIPhoneOS -+ -+ UIRequiredDeviceCapabilities -+ -+ armv7 -+ -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/watchOS/XCode-sample/sample/sample-Prefix.pch -@@ -0,0 +1,16 @@ -+// -+// Prefix header -+// -+// The contents of this file are implicitly included at the beginning of every source file. -+// -+ -+#import -+ -+#ifndef __IPHONE_3_0 -+#warning "This project uses features only available in iOS SDK 3.0 and later." -+#endif -+ -+#ifdef __OBJC__ -+ #import -+ #import -+#endif -\ No newline at end of file From 92089b7f6ec01fab7c752fe0b2675765f6a1be20 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 12 Sep 2022 10:05:15 +0800 Subject: [PATCH 34/37] Add site patch to disable distutils. --- patch/Python/sitecustomize.py | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/patch/Python/sitecustomize.py b/patch/Python/sitecustomize.py index 58dd105d..d7d86e36 100644 --- a/patch/Python/sitecustomize.py +++ b/patch/Python/sitecustomize.py @@ -2,10 +2,13 @@ # packages cross-platform. If the folder containing this file is on # your PYTHONPATH when you invoke pip, pip will behave as if it were # running on {{os}}. +import distutils.ccompiler +import distutils.unixccompiler import os import platform import sys import sysconfig +import types # Make platform.system() return "{{os}}" def custom_system(): @@ -19,6 +22,71 @@ def custom_get_platform(): sysconfig.get_platform = custom_get_platform +# Make distutils raise errors if you try to use it to build modules. +DISABLED_COMPILER_ERROR = "Cannot compile native modules" + +distutils.ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" +distutils.ccompiler.compiler_class["disabled"] = ( + "disabledcompiler", + "DisabledCompiler", + "Compiler disabled ({})".format(DISABLED_COMPILER_ERROR), +) + + +def disabled_compiler(prefix): + # No need to give any more advice here: that will come from the higher-level code in pip. + from distutils.errors import DistutilsPlatformError + + raise DistutilsPlatformError("{}: {}".format(prefix, DISABLED_COMPILER_ERROR)) + + +class DisabledCompiler(distutils.ccompiler.CCompiler): + compiler_type = "disabled" + + def preprocess(*args, **kwargs): + disabled_compiler("CCompiler.preprocess") + + def compile(*args, **kwargs): + disabled_compiler("CCompiler.compile") + + def create_static_lib(*args, **kwargs): + disabled_compiler("CCompiler.create_static_lib") + + def link(*args, **kwargs): + disabled_compiler("CCompiler.link") + + +# To maximize the chance of the build getting as far as actually calling compile(), make +# sure the class has all of the expected attributes. +for name in [ + "src_extensions", + "obj_extension", + "static_lib_extension", + "shared_lib_extension", + "static_lib_format", + "shared_lib_format", + "exe_extension", +]: + setattr( + DisabledCompiler, name, getattr(distutils.unixccompiler.UnixCCompiler, name) + ) + +DisabledCompiler.executables = { + name: [DISABLED_COMPILER_ERROR.replace(" ", "_")] + for name in distutils.unixccompiler.UnixCCompiler.executables +} + +disabled_mod = types.ModuleType("distutils.disabledcompiler") +disabled_mod.DisabledCompiler = DisabledCompiler +sys.modules["distutils.disabledcompiler"] = disabled_mod + + +# Try to disable native builds for packages which don't use the distutils native build +# system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). +for tool in ["ar", "as", "cc", "cxx", "ld"]: + os.environ[tool.upper()] = DISABLED_COMPILER_ERROR.replace(" ", "_") + + # Call the next sitecustomize script if there is one # (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). del sys.modules["sitecustomize"] From 895e6607da02648412e4e90f1068dc9da63f4947 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 16 Sep 2022 12:37:16 +0800 Subject: [PATCH 35/37] Add the ability to generate wheels for build dependencies. --- .gitignore | 1 + Makefile | 236 +++++++++++++++++++++++++++++++++++++++++++++++++---- README.rst | 16 ++++ 3 files changed, 237 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index ffdfbbf9..0fb0445a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ local/* merge/* src/* support/* +wheels/* *.dist-info __pycache__ tests/testbed/macOS diff --git a/Makefile b/Makefile index 3c669aab..b73de0b1 100644 --- a/Makefile +++ b/Makefile @@ -47,12 +47,18 @@ BZIP2_VERSION=1.0.8 XZ_VERSION=5.2.6 +# Preference is to use OpenSSL 3; however, Cryptography 3.4.8 (and +# probably some other packages as well) only works with 1.1.1, so +# we need to preserve the ability to build the older OpenSSL (for now...) OPENSSL_VERSION=3.0.5 +# OPENSSL_VERSION_NUMBER=1.1.1 +# OPENSSL_REVISION=q +# OPENSSL_VERSION=$(OPENSSL_VERSION_NUMBER)$(OPENSSL_REVISION) LIBFFI_VERSION=3.4.2 -# Supported OS and products -PRODUCTS=BZip2 XZ OpenSSL libFFI Python +# Supported OS and dependencies +DEPENDENCIES=BZip2 XZ OpenSSL libFFI OS_LIST=macOS iOS tvOS watchOS CURL_FLAGS=--fail --location --create-dirs --progress-bar @@ -81,6 +87,9 @@ PYTHON_CONFIGURE-watchOS=ac_cv_func_sigaltstack=no # The architecture of the machine doing the build HOST_ARCH=$(shell uname -m) +HOST_PYTHON=install/macOS/macosx/python-$(PYTHON_VERSION) +HOST_PYTHON_EXE=$(HOST_PYTHON)/bin/python3 +BDIST_WHEEL=$(HOST_PYTHON)/lib/python$(PYTHON_VER)/site-packages/wheel/bdist_wheel.py # Force the path to be minimal. This ensures that anything in the user environment # (in particular, homebrew and user-provided Python installs) aren't inadvertently @@ -91,9 +100,10 @@ PATH=/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin all: $(OS_LIST) .PHONY: \ - all clean distclean update-patch vars \ - $(foreach product,$(PRODUCTS),$(foreach os,$(OS_LIST),$(product) $(product)-$(os) clean-$(product) clean-$(product)-$(os))) \ - $(foreach os,$(OS_LIST),$(os) clean-$(os) vars-$(os)) + all clean distclean update-patch vars wheels \ + $(foreach product,$(PRODUCTS),$(product) $(foreach os,$(OS_LIST),$(product)-$(os) clean-$(product) clean-$(product)-$(os))) \ + $(foreach product,$(PRODUCTS),$(product)-wheels $(foreach os,$(OS_LIST),$(product)-wheels-$(os) clean-$(product)-wheels-$(os))) \ + $(foreach os,$(OS_LIST),$(os) wheels-$(os) clean-$(os) clean-wheels-$(os) vars-$(os)) # Clean all builds clean: @@ -194,6 +204,7 @@ target=$1 os=$2 OS_LOWER-$(target)=$(shell echo $(os) | tr '[:upper:]' '[:lower:]') +WHEEL_TAG-$(target)=py3-none-$$(OS_LOWER-$(target))_$$(shell echo $$(VERSION_MIN-$(os))_$(target) | sed "s/\./_/g") # $(target) can be broken up into is composed of $(SDK).$(ARCH) SDK-$(target)=$$(basename $(target)) @@ -227,6 +238,8 @@ LDFLAGS-$(target)=\ BZIP2_SRCDIR-$(target)=build/$(os)/$(target)/bzip2-$(BZIP2_VERSION) BZIP2_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/bzip2-$(BZIP2_VERSION) BZIP2_LIB-$(target)=$$(BZIP2_INSTALL-$(target))/lib/libbz2.a +BZIP2_WHEEL-$(target)=wheels/dist/bzip2/bzip2-$(BZIP2_VERSION)-1-$$(WHEEL_TAG-$(target)).whl +BZIP2_WHEEL_DISTINFO-$(target)=$$(BZIP2_INSTALL-$(target))/wheel/bzip2-$(BZIP2_VERSION).dist-info $$(BZIP2_SRCDIR-$(target))/Makefile: downloads/bzip2-$(BZIP2_VERSION).tar.gz @echo ">>> Unpack BZip2 sources for $(target)" @@ -245,6 +258,36 @@ $$(BZIP2_LIB-$(target)): $$(BZIP2_SRCDIR-$(target))/Makefile LDFLAGS="$$(LDFLAGS-$(target))" \ 2>&1 | tee -a ../bzip2-$(BZIP2_VERSION).build.log +$$(BZIP2_WHEEL-$(target)): $$(BZIP2_LIB-$(target)) $$(BDIST_WHEEL) + @echo ">>> Build BZip2 wheel for $(target)" + mkdir -p $$(BZIP2_WHEEL_DISTINFO-$(target)) + mkdir -p $$(BZIP2_INSTALL-$(target))/wheel/opt + + # Copy distributable content + cp -r $$(BZIP2_INSTALL-$(target))/include $$(BZIP2_INSTALL-$(target))/wheel/opt/include + cp -r $$(BZIP2_INSTALL-$(target))/lib $$(BZIP2_INSTALL-$(target))/wheel/opt/lib + + # Copy LICENSE file + cp $$(BZIP2_SRCDIR-$(target))/LICENSE $$(BZIP2_WHEEL_DISTINFO-$(target)) + + # Write package metadata + echo "Metadata-Version: 1.2" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA + echo "Name: bzip2" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA + echo "Version: $(BZIP2_VERSION)" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA + echo "Summary: " >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA + echo "Download-URL: " >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA + + # Write wheel metadata + echo "Wheel-Version: 1.0" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL + echo "Root-Is-Purelib: false" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL + echo "Generator: Python-Apple-support.BeeWare" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL + echo "Build: 1" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL + echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL + + # Pack the wheel + mkdir -p wheels/dist/bzip2 + $(HOST_PYTHON_EXE) -m wheel pack $$(BZIP2_INSTALL-$(target))/wheel --dest-dir wheels/dist/bzip2 --build-number 1 + ########################################################################### # Target: XZ (LZMA) ########################################################################### @@ -252,6 +295,8 @@ $$(BZIP2_LIB-$(target)): $$(BZIP2_SRCDIR-$(target))/Makefile XZ_SRCDIR-$(target)=build/$(os)/$(target)/xz-$(XZ_VERSION) XZ_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/xz-$(XZ_VERSION) XZ_LIB-$(target)=$$(XZ_INSTALL-$(target))/lib/liblzma.a +XZ_WHEEL-$(target)=wheels/dist/xz/xz-$(XZ_VERSION)-1-$$(WHEEL_TAG-$(target)).whl +XZ_WHEEL_DISTINFO-$(target)=$$(XZ_INSTALL-$(target))/wheel/xz-$(XZ_VERSION).dist-info $$(XZ_SRCDIR-$(target))/Makefile: downloads/xz-$(XZ_VERSION).tar.gz @echo ">>> Unpack XZ sources for $(target)" @@ -277,6 +322,37 @@ $$(XZ_LIB-$(target)): $$(XZ_SRCDIR-$(target))/Makefile make install \ 2>&1 | tee -a ../xz-$(XZ_VERSION).build.log +$$(XZ_WHEEL-$(target)): $$(XZ_LIB-$(target)) $$(BDIST_WHEEL) + @echo ">>> Build XZ wheel for $(target)" + mkdir -p $$(XZ_WHEEL_DISTINFO-$(target)) + mkdir -p $$(XZ_INSTALL-$(target))/wheel/opt + + # Copy distributable content + cp -r $$(XZ_INSTALL-$(target))/include $$(XZ_INSTALL-$(target))/wheel/opt/include + cp -r $$(XZ_INSTALL-$(target))/lib $$(XZ_INSTALL-$(target))/wheel/opt/lib + + # Copy license files + cp $$(XZ_SRCDIR-$(target))/COPYING $$(XZ_WHEEL_DISTINFO-$(target))/LICENSE + cp $$(XZ_SRCDIR-$(target))/COPYING.* $$(XZ_WHEEL_DISTINFO-$(target)) + + # Write package metadata + echo "Metadata-Version: 1.2" >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA + echo "Name: xz" >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA + echo "Version: $(XZ_VERSION)" >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA + echo "Summary: " >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA + echo "Download-URL: " >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA + + # Write wheel metadata + echo "Wheel-Version: 1.0" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL + echo "Root-Is-Purelib: false" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL + echo "Generator: Python-Apple-support.BeeWare" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL + echo "Build: 1" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL + echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL + + # Pack the wheel + mkdir -p wheels/dist/xz + $(HOST_PYTHON_EXE) -m wheel pack $$(XZ_INSTALL-$(target))/wheel --dest-dir wheels/dist/xz --build-number 1 + ########################################################################### # Target: OpenSSL ########################################################################### @@ -285,6 +361,8 @@ OPENSSL_SRCDIR-$(target)=build/$(os)/$(target)/openssl-$(OPENSSL_VERSION) OPENSSL_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/openssl-$(OPENSSL_VERSION) OPENSSL_SSL_LIB-$(target)=$$(OPENSSL_INSTALL-$(target))/lib/libssl.a OPENSSL_CRYPTO_LIB-$(target)=$$(OPENSSL_INSTALL-$(target))/lib/libcrypto.a +OPENSSL_WHEEL-$(target)=wheels/dist/openssl/openssl-$(OPENSSL_VERSION)-1-$$(WHEEL_TAG-$(target)).whl +OPENSSL_WHEEL_DISTINFO-$(target)=$$(OPENSSL_INSTALL-$(target))/wheel/openssl-$(OPENSSL_VERSION).dist-info $$(OPENSSL_SRCDIR-$(target))/is_configured: downloads/openssl-$(OPENSSL_VERSION).tar.gz @echo ">>> Unpack and configure OpenSSL sources for $(target)" @@ -293,8 +371,13 @@ $$(OPENSSL_SRCDIR-$(target))/is_configured: downloads/openssl-$(OPENSSL_VERSION) ifneq ($(os),macOS) # Patch code to disable the use of fork as it's not available on $(os) +ifeq ($(OPENSSL_VERSION_NUMBER),1.1.1) + sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/speed.c + sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/ocsp.c +else sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/include/http_server.h sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/speed.c +endif endif # Configure the OpenSSL build @@ -343,6 +426,41 @@ $$(OPENSSL_SSL_LIB-$(target)): $$(OPENSSL_SRCDIR-$(target))/libssl.a make install_sw \ 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).install.log +$$(OPENSSL_WHEEL-$(target)): $$(OPENSSL_LIB-$(target)) $$(BDIST_WHEEL) + @echo ">>> Build OpenSSL wheel for $(target)" + mkdir -p $$(OPENSSL_WHEEL_DISTINFO-$(target)) + mkdir -p $$(OPENSSL_INSTALL-$(target))/wheel/opt + + # Copy distributable content + cp -r $$(OPENSSL_INSTALL-$(target))/include $$(OPENSSL_INSTALL-$(target))/wheel/opt/include + cp -r $$(OPENSSL_INSTALL-$(target))/lib $$(OPENSSL_INSTALL-$(target))/wheel/opt/lib + + # Copy LICENSE file + # OpenSSL 1.1.1 uses LICENSE; OpenSSL 3 uses LICENSE.txt + if [ -f "$$(OPENSSL_SRCDIR-$(target))/LICENSE.txt" ]; then \ + cp $$(OPENSSL_SRCDIR-$(target))/LICENSE.txt $$(OPENSSL_WHEEL_DISTINFO-$(target))/LICENSE; \ + else \ + cp $$(OPENSSL_SRCDIR-$(target))/LICENSE $$(OPENSSL_WHEEL_DISTINFO-$(target))/LICENSE; \ + fi + + # Write package metadata + echo "Metadata-Version: 1.2" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA + echo "Name: openssl" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA + echo "Version: $(OPENSSL_VERSION)" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA + echo "Summary: " >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA + echo "Download-URL: " >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA + + # Write wheel metadata + echo "Wheel-Version: 1.0" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL + echo "Root-Is-Purelib: false" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL + echo "Generator: Python-Apple-support.BeeWare" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL + echo "Build: 1" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL + echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL + + # Pack the wheel + mkdir -p wheels/dist/openssl + $(HOST_PYTHON_EXE) -m wheel pack $$(OPENSSL_INSTALL-$(target))/wheel --dest-dir wheels/dist/openssl --build-number 1 + ########################################################################### # Target: libFFI ########################################################################### @@ -355,6 +473,8 @@ ifneq ($(os),macOS) LIBFFI_SRCDIR-$(os)=build/$(os)/libffi-$(LIBFFI_VERSION) LIBFFI_SRCDIR-$(target)=$$(LIBFFI_SRCDIR-$(os))/build_$$(SDK-$(target))-$$(ARCH-$(target)) LIBFFI_LIB-$(target)=$$(LIBFFI_SRCDIR-$(target))/.libs/libffi.a +LIBFFI_WHEEL-$(target)=wheels/dist/libffi/libffi-$(LIBFFI_VERSION)-1-$$(WHEEL_TAG-$(target)).whl +LIBFFI_WHEEL_DISTINFO-$(target)=$$(LIBFFI_SRCDIR-$(target))/wheel/libffi-$(LIBFFI_VERSION).dist-info $$(LIBFFI_LIB-$(target)): $$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h @echo ">>> Build libFFI for $(target)" @@ -362,6 +482,36 @@ $$(LIBFFI_LIB-$(target)): $$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h make \ 2>&1 | tee -a ../../libffi-$(LIBFFI_VERSION).build.log +$$(LIBFFI_WHEEL-$(target)): $$(LIBFFI_LIB-$(target)) $$(BDIST_WHEEL) + @echo ">>> Build libFFI wheel for $(target)" + mkdir -p $$(LIBFFI_WHEEL_DISTINFO-$(target)) + mkdir -p $$(LIBFFI_SRCDIR-$(target))/wheel/opt + + # Copy distributable content + cp -r $$(LIBFFI_SRCDIR-$(target))/include $$(LIBFFI_SRCDIR-$(target))/wheel/opt/include + cp -r $$(LIBFFI_SRCDIR-$(target))/.libs $$(LIBFFI_SRCDIR-$(target))/wheel/opt/lib + + # Copy LICENSE file + cp $$(LIBFFI_SRCDIR-$(os))/LICENSE $$(LIBFFI_WHEEL_DISTINFO-$(target)) + + # Write package metadata + echo "Metadata-Version: 1.2" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA + echo "Name: libffi" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA + echo "Version: $(LIBFFI_VERSION)" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA + echo "Summary: " >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA + echo "Download-URL: " >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA + + # Write wheel metadata + echo "Wheel-Version: 1.0" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL + echo "Root-Is-Purelib: false" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL + echo "Generator: Python-Apple-support.BeeWare" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL + echo "Build: 1" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL + echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL + + # Pack the wheel + mkdir -p wheels/dist/libffi + $(HOST_PYTHON_EXE) -m wheel pack $$(LIBFFI_SRCDIR-$(target))/wheel --dest-dir wheels/dist/libffi --build-number 1 + endif ########################################################################### @@ -447,15 +597,23 @@ vars-$(target): @echo "BZIP2_SRCDIR-$(target): $$(BZIP2_SRCDIR-$(target))" @echo "BZIP2_INSTALL-$(target): $$(BZIP2_INSTALL-$(target))" @echo "BZIP2_LIB-$(target): $$(BZIP2_LIB-$(target))" + @echo "BZIP2_WHEEL-$(target): $$(BZIP2_WHEEL-$(target))" + @echo "BZIP2_WHEEL_DISTINFO-$(target): $$(BZIP2_WHEEL_DISTINFO-$(target))" @echo "XZ_SRCDIR-$(target): $$(XZ_SRCDIR-$(target))" @echo "XZ_INSTALL-$(target): $$(XZ_INSTALL-$(target))" @echo "XZ_LIB-$(target): $$(XZ_LIB-$(target))" + @echo "XZ_WHEEL-$(target): $$(XZ_WHEEL-$(target))" + @echo "XZ_WHEEL_DISTINFO-$(target): $$(XZ_WHEEL_DISTINFO-$(target))" @echo "OPENSSL_SRCDIR-$(target): $$(OPENSSL_SRCDIR-$(target))" @echo "OPENSSL_INSTALL-$(target): $$(OPENSSL_INSTALL-$(target))" @echo "OPENSSL_SSL_LIB-$(target): $$(OPENSSL_SSL_LIB-$(target))" @echo "OPENSSL_CRYPTO_LIB-$(target): $$(OPENSSL_CRYPTO_LIB-$(target))" + @echo "OPENSSL_WHEEL-$(target): $$(OPENSSL_WHEEL-$(target))" + @echo "OPENSSL_WHEEL_DISTINFO-$(target): $$(OPENSSL_WHEEL_DISTINFO-$(target))" @echo "LIBFFI_SRCDIR-$(target): $$(LIBFFI_SRCDIR-$(target))" @echo "LIBFFI_LIB-$(target): $$(LIBFFI_LIB-$(target))" + @echo "LIBFFI_WHEEL-$(target): $$(LIBFFI_WHEEL-$(target))" + @echo "LIBFFI_WHEEL_DISTINFO-$(target): $$(LIBFFI_WHEEL_DISTINFO-$(target))" @echo "PYTHON_SRCDIR-$(target): $$(PYTHON_SRCDIR-$(target))" @echo "PYTHON_INSTALL-$(target): $$(PYTHON_INSTALL-$(target))" @echo "PYTHON_LIB-$(target): $$(PYTHON_LIB-$(target))" @@ -628,7 +786,7 @@ $$(PYTHON_SRCDIR-$(sdk))/python.exe: \ make all \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).build.log -$$(PYTHON_LIB-$(sdk)): $$(PYTHON_SRCDIR-$(sdk))/python.exe +$(HOST_PYTHON_EXE) $$(PYTHON_LIB-$(sdk)): $$(PYTHON_SRCDIR-$(sdk))/python.exe @echo ">>> Install Python for $(sdk)" cd $$(PYTHON_SRCDIR-$(sdk)) && \ make install \ @@ -761,6 +919,7 @@ $$(foreach sdk,$$(SDKS-$(os)),$$(eval $$(call build-sdk,$$(sdk),$(os)))) ########################################################################### BZip2-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(BZIP2_FATLIB-$$(sdk))) +BZip2-wheels-$(os): $$(foreach target,$$(TARGETS-$(os)),$$(BZIP2_WHEEL-$$(target))) clean-BZip2-$(os): @echo ">>> Clean BZip2 build products on $(os)" @@ -771,12 +930,19 @@ clean-BZip2-$(os): install/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ merge/$(os)/*/bzip2-$(BZIP2_VERSION) \ merge/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ + wheels/dist/bzip2 + +clean-BZip2-wheels-$(os): + rm -rf \ + install/$(os)/*/bzip2-$(BZIP2_VERSION)/wheel \ + wheels/dist/bzip2 ########################################################################### # Build: XZ (LZMA) ########################################################################### XZ-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(XZ_FATLIB-$$(sdk))) +XZ-wheels-$(os): $$(foreach target,$$(TARGETS-$(os)),$$(XZ_WHEEL-$$(target))) clean-XZ-$(os): @echo ">>> Clean XZ build products on $(os)" @@ -787,12 +953,19 @@ clean-XZ-$(os): install/$(os)/*/xz-$(XZ_VERSION).*.log \ merge/$(os)/*/xz-$(XZ_VERSION) \ merge/$(os)/*/xz-$(XZ_VERSION).*.log \ + wheels/dist/xz + +clean-XZ-wheels-$(os): + rm -rf \ + install/$(os)/*/xz-$(XZ_VERSION)/wheel \ + wheels/dist/xz ########################################################################### # Build: OpenSSL ########################################################################### OpenSSL-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(OPENSSL_FATINCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk))) +OpenSSL-wheels-$(os): $$(foreach target,$$(TARGETS-$(os)),$$(OPENSSL_WHEEL-$$(target))) clean-OpenSSL-$(os): @echo ">>> Clean OpenSSL build products on $(os)" @@ -803,6 +976,12 @@ clean-OpenSSL-$(os): install/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ merge/$(os)/*/openssl-$(OPENSSL_VERSION) \ merge/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ + wheels/dist/openssl + +clean-OpenSSL-wheels-$(os): + rm -rf \ + install/$(os)/*/openssl-$(OPENSSL_VERSION)/wheel \ + wheels/dist/openssl ########################################################################### # Build: libFFI @@ -812,7 +991,7 @@ clean-OpenSSL-$(os): # a libFFI framework for macOS. ifneq ($(os),macOS) -$$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h: downloads/libffi-$(LIBFFI_VERSION).tar.gz $$(PYTHON_LIB-macosx) +$$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h: downloads/libffi-$(LIBFFI_VERSION).tar.gz $(HOST_PYTHON_EXE) @echo ">>> Unpack and configure libFFI sources on $(os)" mkdir -p $$(LIBFFI_SRCDIR-$(os)) tar zxf $$< --strip-components 1 -C $$(LIBFFI_SRCDIR-$(os)) @@ -820,22 +999,27 @@ $$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h: downloads/libffi-$(LIBFFI_V cd $$(LIBFFI_SRCDIR-$(os)) && patch -p1 < $(PROJECT_DIR)/patch/libffi-$(LIBFFI_VERSION).patch # Configure the build cd $$(LIBFFI_SRCDIR-$(os)) && \ - PATH="$(PYTHON_INSTALL-macosx)/bin:$(PATH)" \ - python$(PYTHON_VER) generate-darwin-source-and-headers.py --only-$(shell echo $(os) | tr '[:upper:]' '[:lower:]') \ + $(PROJECT_DIR)/$(HOST_PYTHON_EXE) generate-darwin-source-and-headers.py --only-$(shell echo $(os) | tr '[:upper:]' '[:lower:]') \ 2>&1 | tee -a ../libffi-$(LIBFFI_VERSION).config.log endif libFFI-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(LIBFFI_FATLIB-$$(sdk))) +libFFI-wheels-$(os): $$(foreach target,$$(TARGETS-$(os)),$$(LIBFFI_WHEEL-$$(target))) clean-libFFI-$(os): @echo ">>> Clean libFFI build products on $(os)" rm -rf \ - build/$(os)/*/libffi-$(LIBFFI_VERSION) \ - build/$(os)/*/libffi-$(LIBFFI_VERSION).*.log \ - merge/$(os)/*/libffi-$(LIBFFI_VERSION) \ - merge/$(os)/*/libffi-$(LIBFFI_VERSION).*.log \ + build/$(os)/libffi-$(LIBFFI_VERSION) \ + build/$(os)/libffi-$(LIBFFI_VERSION).*.log \ + merge/$(os)/libffi-$(LIBFFI_VERSION) \ + merge/$(os)/libffi-$(LIBFFI_VERSION).*.log \ + wheels/dist/libffi +clean-libFFI-wheels-$(os): + rm -rf \ + build/$(os)/libffi-$(LIBFFI_VERSION)/build_*/wheel \ + wheels/dist/libffi ########################################################################### # Build: Python @@ -944,6 +1128,7 @@ merge-clean-Python-$(os): ########################################################################### $(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz +$(os)-wheels: $(foreach dep,$(DEPENDENCIES),$(dep)-wheels-$(os)) clean-$(os): @echo ">>> Clean $(os) build products" @@ -954,6 +1139,8 @@ clean-$(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz \ dist/Python-$(PYTHON_VER)-$(os)-support.test-$(BUILD_NUMBER).tar.gz \ +clean-wheels-$(os): $(foreach dep,$(DEPENDENCIES),clean-$(dep)-wheels-$(os)) + ########################################################################### # Build: Debug ########################################################################### @@ -968,21 +1155,38 @@ vars-$(os): $$(foreach target,$$(TARGETS-$(os)),vars-$$(target)) $$(foreach sdk, endef # build +$(BDIST_WHEEL): $(HOST_PYTHON_EXE) + @echo ">>> Ensure the macOS python install has pip and wheel" + $(HOST_PYTHON_EXE) -m ensurepip + PIP_REQUIRE_VIRTUALENV=false $(HOST_PYTHON_EXE) -m pip install wheel + +# Binary support wheels +wheels: $(foreach dep,$(DEPENDENCIES),$(dep)-wheels) +clean-wheels: $(foreach dep,$(DEPENDENCIES),clean-wheels-$(dep)) + # Dump environment variables (for debugging purposes) vars: $(foreach os,$(OS_LIST),vars-$(os)) # Expand cross-platform build and clean targets for each output product -XZ: $(foreach os,$(OS_LIST),XZ-$(os)) -clean-XZ: $(foreach os,$(OS_LIST),clean-XZ-$(os)) - BZip2: $(foreach os,$(OS_LIST),BZip2-$(os)) +BZip2-wheels: $(foreach os,$(OS_LIST),BZip2-wheels-$(os)) clean-BZip2: $(foreach os,$(OS_LIST),clean-BZip2-$(os)) +clean-BZip2-wheels: $(foreach os,$(OS_LIST),clean-BZip2-wheels-$(os)) + +XZ: $(foreach os,$(OS_LIST),XZ-$(os)) +XZ-wheels: $(foreach os,$(OS_LIST),XZ-wheels-$(os)) +clean-XZ: $(foreach os,$(OS_LIST),clean-XZ-$(os)) +clean-XZ-wheels: $(foreach os,$(OS_LIST),clean-XZ-wheels-$(os)) OpenSSL: $(foreach os,$(OS_LIST),OpenSSL-$(os)) +OpenSSL-wheels: $(foreach os,$(OS_LIST),OpenSSL-wheels-$(os)) clean-OpenSSL: $(foreach os,$(OS_LIST),clean-OpenSSL-$(os)) +clean-OpenSSL-wheels: $(foreach os,$(OS_LIST),clean-OpenSSL-wheels-$(os)) libFFI: $(foreach os,$(OS_LIST),libFFI-$(os)) +libFFI-wheels: $(foreach os,$(OS_LIST),libFFI-wheels-$(os)) clean-libFFI: $(foreach os,$(OS_LIST),clean-libFFI-$(os)) +clean-libFFI-wheels: $(foreach os,$(OS_LIST),clean-libFFI-wheels-$(os)) Python: $(foreach os,$(OS_LIST),Python-$(os)) clean-Python: $(foreach os,$(OS_LIST),clean-Python-$(os)) diff --git a/README.rst b/README.rst index 3189881d..0188c2a4 100644 --- a/README.rst +++ b/README.rst @@ -130,6 +130,22 @@ to a device simulator. However, to deploy to a physical device (including your own), you will require a Development or Distribution certificate, which requires a paid Apple Developer subscription. +Building binary wheels +---------------------- + +When building binary wheels, you may need to use the libraries built by this +project as inputs (e.g., the `cffi` module uses `libffi`). To support this, this +project is able to package these dependencies as "wheels" that can be added to +the `server/pypi/dist` directory of the [binary dependency builder +project](https://github.com/freakboy3742/chaquopy). + +To build these wheels, run: + +* `make wheels` to make all wheels for all mobile platforms +* `make wheels-iOS` to build all the iOS wheels +* `make wheels-tvOS` to build all the tvOS wheels +* `make wheels-watchOS` to build all the watchOS wheels + .. _for macOS: https://briefcase-support.org/python?platform=macOS&version=3.11 .. _for iOS: https://briefcase-support.org/python?platform=iOS&version=3.11 .. _for tvOS: https://briefcase-support.org/python?platform=tvOS&version=3.11 From cff598c5226ef45cb4bdb1165c048f744caf26bf Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 21 Sep 2022 14:46:12 +0800 Subject: [PATCH 36/37] Purge dynamic libraries from wheels. --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index b73de0b1..8d379752 100644 --- a/Makefile +++ b/Makefile @@ -435,6 +435,9 @@ $$(OPENSSL_WHEEL-$(target)): $$(OPENSSL_LIB-$(target)) $$(BDIST_WHEEL) cp -r $$(OPENSSL_INSTALL-$(target))/include $$(OPENSSL_INSTALL-$(target))/wheel/opt/include cp -r $$(OPENSSL_INSTALL-$(target))/lib $$(OPENSSL_INSTALL-$(target))/wheel/opt/lib + # Remove dynamic library content + rm -f $$(OPENSSL_INSTALL-$(target))/wheel/opt/lib/*.dylib + # Copy LICENSE file # OpenSSL 1.1.1 uses LICENSE; OpenSSL 3 uses LICENSE.txt if [ -f "$$(OPENSSL_SRCDIR-$(target))/LICENSE.txt" ]; then \ @@ -491,6 +494,9 @@ $$(LIBFFI_WHEEL-$(target)): $$(LIBFFI_LIB-$(target)) $$(BDIST_WHEEL) cp -r $$(LIBFFI_SRCDIR-$(target))/include $$(LIBFFI_SRCDIR-$(target))/wheel/opt/include cp -r $$(LIBFFI_SRCDIR-$(target))/.libs $$(LIBFFI_SRCDIR-$(target))/wheel/opt/lib + # Remove dynamic library content + rm -f $$(LIBFFI_SRCDIR-$(target))/wheel/opt/lib/*.dylib + # Copy LICENSE file cp $$(LIBFFI_SRCDIR-$(os))/LICENSE $$(LIBFFI_WHEEL_DISTINFO-$(target)) From 86b83b1516a0beb33982fcaf31f673f830c6a5f0 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 23 Sep 2022 06:27:36 +0800 Subject: [PATCH 37/37] Remove briefcase-support.org from download URLs. --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 0188c2a4..aeb0630a 100644 --- a/README.rst +++ b/README.rst @@ -146,7 +146,7 @@ To build these wheels, run: * `make wheels-tvOS` to build all the tvOS wheels * `make wheels-watchOS` to build all the watchOS wheels -.. _for macOS: https://briefcase-support.org/python?platform=macOS&version=3.11 -.. _for iOS: https://briefcase-support.org/python?platform=iOS&version=3.11 -.. _for tvOS: https://briefcase-support.org/python?platform=tvOS&version=3.11 -.. _for watchOS: https://briefcase-support.org/python?platform=watchOS&version=3.11 +.. _for macOS: https://briefcase-support.s3.amazonaws.com/python/3.11/macOS/Python-3.11-macOS-support.b1.tar.gz +.. _for iOS: https://briefcase-support.s3.amazonaws.com/python/3.11/iOS/Python-3.11-iOS-support.b1.tar.gz +.. _for tvOS: https://briefcase-support.s3.amazonaws.com/python/3.11/tvOS/Python-3.11-tvOS-support.b1.tar.gz +.. _for watchOS: https://briefcase-support.s3.amazonaws.com/python/3.11/watchOS/Python-3.11-watchOS-support.b1.tar.gz