Patch CPython to use a type reflection trampoline if possible (#3964)

This commit is contained in:
Hood Chatham 2023-07-08 08:35:11 -07:00
parent 3085cb9d5e
commit c931b06ab6
9 changed files with 405 additions and 768 deletions

View File

@ -1,7 +1,7 @@
From 8dc93243eb0cdf27a81e9baf156df621cc8e2eb9 Mon Sep 17 00:00:00 2001
From a64272682c47c7317faebdc4f5545cb05bfedcb5 Mon Sep 17 00:00:00 2001
From: Hood Chatham <roberthoodchatham@gmail.com>
Date: Sun, 17 Jul 2022 14:40:39 +0100
Subject: [PATCH 1/9] Public pymain_run_python
Subject: [PATCH 1/8] Public pymain_run_python
---
Modules/main.c | 2 +-

View File

@ -1,7 +1,7 @@
From 90812b6dc97860ef164a2810ebf5f3121dfc919e Mon Sep 17 00:00:00 2001
From c5a5b530159a991c7c5936a75e6448a7916b72d1 Mon Sep 17 00:00:00 2001
From: Hood Chatham <roberthoodchatham@gmail.com>
Date: Wed, 16 Nov 2022 14:02:53 -0800
Subject: [PATCH 2/9] Patch importlib to allow modifications to
Subject: [PATCH 2/8] Patch importlib to allow modifications to
ModuleNotFoundError
---

View File

@ -1,7 +1,7 @@
From bb33672157df771613c9d6c61756c5294f13333d Mon Sep 17 00:00:00 2001
From 08cc579925825da3388a6b4967868d86ea2c488f Mon Sep 17 00:00:00 2001
From: ryanking13 <def6488@gmail.com>
Date: Fri, 2 Dec 2022 11:36:44 +0000
Subject: [PATCH 3/9] Add emscripten platform support to
Subject: [PATCH 3/8] Add emscripten platform support to
ctypes.util.find_library
---
@ -44,5 +44,5 @@ index 0c2510e161..c8875ec009 100644
# AIX has two styles of storing shared libraries
# GNU auto_tools refer to these as svr4 and aix
--
2.29.2.windows.2
2.25.1

View File

@ -1,14 +1,14 @@
From becbcc43e3e09a9c065ea7856a2d3069061fd570 Mon Sep 17 00:00:00 2001
From f8970422b395b24ca58cef3dca6954bb07d4974d Mon Sep 17 00:00:00 2001
From: Hood Chatham <roberthoodchatham@gmail.com>
Date: Mon, 19 Dec 2022 09:09:14 -0800
Subject: [PATCH 4/9] Allow multiprocessing.connection top level import
Subject: [PATCH 4/8] Allow multiprocessing.connection top level import
---
Lib/multiprocessing/connection.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
index 510e4b5aba..372f9ced37 100644
index b08144f7a1..6a98829169 100644
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -18,7 +18,10 @@

View File

@ -1,7 +1,7 @@
From d82e0bfe8b98a122ca443b356d81998c804b686e Mon Sep 17 00:00:00 2001
From b08715ecc79e6398f5480bfb3b1b197f07d8a741 Mon Sep 17 00:00:00 2001
From: Victor Stinner <vstinner@python.org>
Date: Tue, 21 Jun 2022 10:24:33 +0200
Subject: [PATCH 5/9] gh-93839: Move Lib/ctypes/test/ to Lib/test/test_ctypes/
Subject: [PATCH 5/8] gh-93839: Move Lib/ctypes/test/ to Lib/test/test_ctypes/
(#94041)
* Move Lib/ctypes/test/ to Lib/test/test_ctypes/
@ -64,11 +64,10 @@ Subject: [PATCH 5/9] gh-93839: Move Lib/ctypes/test/ to Lib/test/test_ctypes/
.../test_ctypes}/test_varsize_struct.py | 0
.../test => test/test_ctypes}/test_win32.py | 0
.../test_ctypes}/test_wintypes.py | 0
Makefile.pre.in | 4 +-
...2-06-20-23-04-52.gh-issue-93839.OE3Ybk.rst | 2 +
PCbuild/lib.pyproj | 109 +++++++++---------
Tools/wasm/wasm_assets.py | 1 -
60 files changed, 83 insertions(+), 93 deletions(-)
59 files changed, 81 insertions(+), 91 deletions(-)
delete mode 100644 Lib/ctypes/test/__main__.py
delete mode 100644 Lib/test/test_ctypes.py
rename Lib/{ctypes/test => test/test_ctypes}/__init__.py (100%)
@ -205,7 +204,7 @@ diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/test/test_ctypes/test_as
similarity index 99%
rename from Lib/ctypes/test/test_as_parameter.py
rename to Lib/test/test_ctypes/test_as_parameter.py
index f9d27cb89d..b35defb158 100644
index 9c39179d2a..e9ec9ad847 100644
--- a/Lib/ctypes/test/test_as_parameter.py
+++ b/Lib/test/test_ctypes/test_as_parameter.py
@@ -1,6 +1,6 @@
@ -256,7 +255,7 @@ diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/test/test_ctypes/test_callb
similarity index 99%
rename from Lib/ctypes/test/test_callbacks.py
rename to Lib/test/test_ctypes/test_callbacks.py
index 1099cf9a69..2758720d4a 100644
index 8f95a24443..b5bef04e14 100644
--- a/Lib/ctypes/test/test_callbacks.py
+++ b/Lib/test/test_ctypes/test_callbacks.py
@@ -3,7 +3,7 @@
@ -286,7 +285,7 @@ diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/test/test_ctypes/test_cfuncs.p
similarity index 99%
rename from Lib/ctypes/test/test_cfuncs.py
rename to Lib/test/test_ctypes/test_cfuncs.py
index ac2240fa19..0a9394bf31 100644
index 09b06840bf..7cba4b0e52 100644
--- a/Lib/ctypes/test/test_cfuncs.py
+++ b/Lib/test/test_ctypes/test_cfuncs.py
@@ -3,7 +3,7 @@
@ -338,7 +337,7 @@ diff --git a/Lib/ctypes/test/test_functions.py b/Lib/test/test_ctypes/test_funct
similarity index 99%
rename from Lib/ctypes/test/test_functions.py
rename to Lib/test/test_ctypes/test_functions.py
index f9e92e1cc6..4a784c8d79 100644
index fc571700ce..a3db003353 100644
--- a/Lib/ctypes/test/test_functions.py
+++ b/Lib/test/test_ctypes/test_functions.py
@@ -6,7 +6,7 @@
@ -434,7 +433,7 @@ diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/test/test_ctypes/test_para
similarity index 99%
rename from Lib/ctypes/test/test_parameters.py
rename to Lib/test/test_ctypes/test_parameters.py
index 38af7ac13d..2f755a6d09 100644
index 3fdc994e90..662a17e094 100644
--- a/Lib/ctypes/test/test_parameters.py
+++ b/Lib/test/test_ctypes/test_parameters.py
@@ -1,5 +1,5 @@
@ -540,7 +539,7 @@ diff --git a/Lib/ctypes/test/test_structures.py b/Lib/test/test_ctypes/test_stru
similarity index 99%
rename from Lib/ctypes/test/test_structures.py
rename to Lib/test/test_ctypes/test_structures.py
index 97ad2b8ed8..13c0470ba2 100644
index f95d5a99a3..df39dc7f50 100644
--- a/Lib/ctypes/test/test_structures.py
+++ b/Lib/test/test_ctypes/test_structures.py
@@ -2,7 +2,7 @@
@ -737,10 +736,10 @@ index 43c570f1da..692b083349 100644
<Folder Include="test\test_email\data" />
<Folder Include="test\test_import" />
diff --git a/Tools/wasm/wasm_assets.py b/Tools/wasm/wasm_assets.py
index b7e83517ca..d0a0570840 100755
index 6557e3f37a..aa09d2dda8 100755
--- a/Tools/wasm/wasm_assets.py
+++ b/Tools/wasm/wasm_assets.py
@@ -111,7 +111,6 @@
@@ -112,7 +112,6 @@
# regression test sub directories
OMIT_SUBDIRS = (
@ -749,5 +748,5 @@ index b7e83517ca..d0a0570840 100755
"unittest/test/",
)
--
2.29.2.windows.2
2.25.1

View File

@ -1,8 +1,8 @@
From c735d545343c3ab002c62596b2fb2cfa4488b0af Mon Sep 17 00:00:00 2001
From bfc38df65896e34c6d7d995a044ce75974757436 Mon Sep 17 00:00:00 2001
From: Victor Stinner <vstinner@python.org>
Date: Tue, 21 Jun 2022 10:27:59 +0200
Subject: [PATCH 6/9] gh-93839: Move Lib/unttest/test/ to Lib/test/test_unittest/
(#94043)
Subject: [PATCH 6/8] gh-93839: Move Lib/unttest/test/ to
Lib/test/test_unittest/ (#94043)
* Move Lib/unittest/test/ to Lib/test/test_unittest/
* Remove Lib/test/test_unittest.py
@ -11,714 +11,21 @@ Subject: [PATCH 6/9] gh-93839: Move Lib/unttest/test/ to Lib/test/test_unittest/
* Rewrite unittest __init__.py and __main__.py
* Update build system, CODEOWNERS, and wasm_assets.py
---
.github/CODEOWNERS | 2 +-
Lib/test/test_unittest.py | 16 -----
Lib/test/test_unittest/__init__.py | 6 ++
Lib/test/test_unittest/__main__.py | 4 ++
.../test_unittest}/_test_warnings.py | 0
.../test => test/test_unittest}/dummy.py | 0
.../test => test/test_unittest}/support.py | 0
.../test_unittest}/test_assertions.py | 0
.../test_unittest}/test_async_case.py | 0
.../test => test/test_unittest}/test_break.py | 0
.../test => test/test_unittest}/test_case.py | 2 +-
.../test_unittest}/test_discovery.py | 6 +-
.../test_unittest}/test_functiontestcase.py | 2 +-
.../test_unittest}/test_loader.py | 6 +-
.../test_unittest}/test_program.py | 16 ++---
.../test_unittest}/test_result.py | 0
.../test_unittest}/test_runner.py | 2 +-
.../test_unittest}/test_setups.py | 0
.../test_unittest}/test_skipping.py | 2 +-
.../test => test/test_unittest}/test_suite.py | 2 +-
.../test_unittest}/testmock/__init__.py | 2 +-
.../test_unittest}/testmock/__main__.py | 2 +-
.../test_unittest}/testmock/support.py | 0
.../test_unittest}/testmock/testasync.py | 0
.../test_unittest}/testmock/testcallable.py | 2 +-
.../test_unittest}/testmock/testhelpers.py | 0
.../testmock/testmagicmethods.py | 0
.../test_unittest}/testmock/testmock.py | 2 +-
.../test_unittest}/testmock/testpatch.py | 22 +++----
.../test_unittest}/testmock/testsealable.py | 0
.../test_unittest}/testmock/testsentinel.py | 0
.../test_unittest}/testmock/testwith.py | 2 +-
Lib/unittest/__init__.py | 10 ----
Lib/unittest/test/__init__.py | 25 --------
Lib/unittest/test/__main__.py | 18 ------
Makefile.pre.in | 4 +-
PCbuild/lib.pyproj | 58 +++++++++----------
Tools/wasm/wasm_assets.py | 1 -
38 files changed, 77 insertions(+), 137 deletions(-)
delete mode 100644 Lib/test/test_unittest.py
create mode 100644 Lib/test/test_unittest/__init__.py
create mode 100644 Lib/test/test_unittest/__main__.py
rename Lib/{unittest/test => test/test_unittest}/_test_warnings.py (100%)
rename Lib/{unittest/test => test/test_unittest}/dummy.py (100%)
rename Lib/{unittest/test => test/test_unittest}/support.py (100%)
rename Lib/{unittest/test => test/test_unittest}/test_assertions.py (100%)
rename Lib/{unittest/test => test/test_unittest}/test_async_case.py (100%)
rename Lib/{unittest/test => test/test_unittest}/test_break.py (100%)
rename Lib/{unittest/test => test/test_unittest}/test_case.py (99%)
rename Lib/{unittest/test => test/test_unittest}/test_discovery.py (99%)
rename Lib/{unittest/test => test/test_unittest}/test_functiontestcase.py (99%)
rename Lib/{unittest/test => test/test_unittest}/test_loader.py (99%)
rename Lib/{unittest/test => test/test_unittest}/test_program.py (96%)
rename Lib/{unittest/test => test/test_unittest}/test_result.py (100%)
rename Lib/{unittest/test => test/test_unittest}/test_runner.py (99%)
rename Lib/{unittest/test => test/test_unittest}/test_setups.py (100%)
rename Lib/{unittest/test => test/test_unittest}/test_skipping.py (99%)
rename Lib/{unittest/test => test/test_unittest}/test_suite.py (99%)
rename Lib/{unittest/test => test/test_unittest}/testmock/__init__.py (86%)
rename Lib/{unittest/test => test/test_unittest}/testmock/__main__.py (86%)
rename Lib/{unittest/test => test/test_unittest}/testmock/support.py (100%)
rename Lib/{unittest/test => test/test_unittest}/testmock/testasync.py (100%)
rename Lib/{unittest/test => test/test_unittest}/testmock/testcallable.py (98%)
rename Lib/{unittest/test => test/test_unittest}/testmock/testhelpers.py (100%)
rename Lib/{unittest/test => test/test_unittest}/testmock/testmagicmethods.py (100%)
rename Lib/{unittest/test => test/test_unittest}/testmock/testmock.py (99%)
rename Lib/{unittest/test => test/test_unittest}/testmock/testpatch.py (98%)
rename Lib/{unittest/test => test/test_unittest}/testmock/testsealable.py (100%)
rename Lib/{unittest/test => test/test_unittest}/testmock/testsentinel.py (100%)
rename Lib/{unittest/test => test/test_unittest}/testmock/testwith.py (99%)
delete mode 100644 Lib/unittest/test/__init__.py
delete mode 100644 Lib/unittest/test/__main__.py
Tools/wasm/wasm_assets.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py
deleted file mode 100644
index 1079c7df2e..0000000000
--- a/Lib/test/test_unittest.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import unittest.test
-
-from test import support
-
-
-def load_tests(*_):
- # used by unittest
- return unittest.test.suite()
-
-
-def tearDownModule():
- support.reap_children()
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/Lib/test/test_unittest/__init__.py b/Lib/test/test_unittest/__init__.py
new file mode 100644
index 0000000000..bc502ef32d
--- /dev/null
+++ b/Lib/test/test_unittest/__init__.py
@@ -0,0 +1,6 @@
+import os.path
+from test.support import load_package_tests
+
+
+def load_tests(*args):
+ return load_package_tests(os.path.dirname(__file__), *args)
diff --git a/Lib/test/test_unittest/__main__.py b/Lib/test/test_unittest/__main__.py
new file mode 100644
index 0000000000..40a23a297e
--- /dev/null
+++ b/Lib/test/test_unittest/__main__.py
@@ -0,0 +1,4 @@
+from . import load_tests
+import unittest
+
+unittest.main()
diff --git a/Lib/unittest/test/_test_warnings.py b/Lib/test/test_unittest/_test_warnings.py
similarity index 100%
rename from Lib/unittest/test/_test_warnings.py
rename to Lib/test/test_unittest/_test_warnings.py
diff --git a/Lib/unittest/test/dummy.py b/Lib/test/test_unittest/dummy.py
similarity index 100%
rename from Lib/unittest/test/dummy.py
rename to Lib/test/test_unittest/dummy.py
diff --git a/Lib/unittest/test/support.py b/Lib/test/test_unittest/support.py
similarity index 100%
rename from Lib/unittest/test/support.py
rename to Lib/test/test_unittest/support.py
diff --git a/Lib/unittest/test/test_assertions.py b/Lib/test/test_unittest/test_assertions.py
similarity index 100%
rename from Lib/unittest/test/test_assertions.py
rename to Lib/test/test_unittest/test_assertions.py
diff --git a/Lib/unittest/test/test_async_case.py b/Lib/test/test_unittest/test_async_case.py
similarity index 100%
rename from Lib/unittest/test/test_async_case.py
rename to Lib/test/test_unittest/test_async_case.py
diff --git a/Lib/unittest/test/test_break.py b/Lib/test/test_unittest/test_break.py
similarity index 100%
rename from Lib/unittest/test/test_break.py
rename to Lib/test/test_unittest/test_break.py
diff --git a/Lib/unittest/test/test_case.py b/Lib/test/test_unittest/test_case.py
similarity index 99%
rename from Lib/unittest/test/test_case.py
rename to Lib/test/test_unittest/test_case.py
index 374a255255..e000fe4f07 100644
--- a/Lib/unittest/test/test_case.py
+++ b/Lib/test/test_unittest/test_case.py
@@ -15,7 +15,7 @@
import unittest
-from unittest.test.support import (
+from test.test_unittest.support import (
TestEquality, TestHashing, LoggingResult, LegacyLoggingResult,
ResultWithNoStartTestRunStopTestRun
)
diff --git a/Lib/unittest/test/test_discovery.py b/Lib/test/test_unittest/test_discovery.py
similarity index 99%
rename from Lib/unittest/test/test_discovery.py
rename to Lib/test/test_unittest/test_discovery.py
index 3b58786ec1..946fa1258e 100644
--- a/Lib/unittest/test/test_discovery.py
+++ b/Lib/test/test_unittest/test_discovery.py
@@ -10,7 +10,7 @@
import unittest
import unittest.mock
-import unittest.test
+import test.test_unittest
class TestableTestProgram(unittest.TestProgram):
@@ -789,7 +789,7 @@ def test_discovery_from_dotted_path(self):
loader = unittest.TestLoader()
tests = [self]
- expectedPath = os.path.abspath(os.path.dirname(unittest.test.__file__))
+ expectedPath = os.path.abspath(os.path.dirname(test.test_unittest.__file__))
self.wasRun = False
def _find_tests(start_dir, pattern):
@@ -797,7 +797,7 @@ def _find_tests(start_dir, pattern):
self.assertEqual(start_dir, expectedPath)
return tests
loader._find_tests = _find_tests
- suite = loader.discover('unittest.test')
+ suite = loader.discover('test.test_unittest')
self.assertTrue(self.wasRun)
self.assertEqual(suite._tests, tests)
diff --git a/Lib/unittest/test/test_functiontestcase.py b/Lib/test/test_unittest/test_functiontestcase.py
similarity index 99%
rename from Lib/unittest/test/test_functiontestcase.py
rename to Lib/test/test_unittest/test_functiontestcase.py
index 4971729880..2ebed9564a 100644
--- a/Lib/unittest/test/test_functiontestcase.py
+++ b/Lib/test/test_unittest/test_functiontestcase.py
@@ -1,6 +1,6 @@
import unittest
-from unittest.test.support import LoggingResult
+from test.test_unittest.support import LoggingResult
class Test_FunctionTestCase(unittest.TestCase):
diff --git a/Lib/unittest/test/test_loader.py b/Lib/test/test_unittest/test_loader.py
similarity index 99%
rename from Lib/unittest/test/test_loader.py
rename to Lib/test/test_unittest/test_loader.py
index de2268cda9..c06ebb658d 100644
--- a/Lib/unittest/test/test_loader.py
+++ b/Lib/test/test_unittest/test_loader.py
@@ -716,7 +716,7 @@ def test_loadTestsFromName__module_not_loaded(self):
# We're going to try to load this module as a side-effect, so it
# better not be loaded before we try.
#
- module_name = 'unittest.test.dummy'
+ module_name = 'test.test_unittest.dummy'
sys.modules.pop(module_name, None)
loader = unittest.TestLoader()
@@ -844,7 +844,7 @@ def test_loadTestsFromNames__unknown_attr_name(self):
loader = unittest.TestLoader()
suite = loader.loadTestsFromNames(
- ['unittest.loader.sdasfasfasdf', 'unittest.test.dummy'])
+ ['unittest.loader.sdasfasfasdf', 'test.test_unittest.dummy'])
error, test = self.check_deferred_error(loader, list(suite)[0])
expected = "module 'unittest.loader' has no attribute 'sdasfasfasdf'"
self.assertIn(
@@ -1141,7 +1141,7 @@ def test_loadTestsFromNames__module_not_loaded(self):
# We're going to try to load this module as a side-effect, so it
# better not be loaded before we try.
#
- module_name = 'unittest.test.dummy'
+ module_name = 'test.test_unittest.dummy'
sys.modules.pop(module_name, None)
loader = unittest.TestLoader()
diff --git a/Lib/unittest/test/test_program.py b/Lib/test/test_unittest/test_program.py
similarity index 96%
rename from Lib/unittest/test/test_program.py
rename to Lib/test/test_unittest/test_program.py
index 26a8550af8..169fc4ed94 100644
--- a/Lib/unittest/test/test_program.py
+++ b/Lib/test/test_unittest/test_program.py
@@ -5,8 +5,8 @@
import subprocess
from test import support
import unittest
-import unittest.test
-from unittest.test.test_result import BufferedWriter
+import test.test_unittest
+from test.test_unittest.test_result import BufferedWriter
class Test_TestProgram(unittest.TestCase):
@@ -15,7 +15,7 @@ def test_discovery_from_dotted_path(self):
loader = unittest.TestLoader()
tests = [self]
- expectedPath = os.path.abspath(os.path.dirname(unittest.test.__file__))
+ expectedPath = os.path.abspath(os.path.dirname(test.test_unittest.__file__))
self.wasRun = False
def _find_tests(start_dir, pattern):
@@ -23,7 +23,7 @@ def _find_tests(start_dir, pattern):
self.assertEqual(start_dir, expectedPath)
return tests
loader._find_tests = _find_tests
- suite = loader.discover('unittest.test')
+ suite = loader.discover('test.test_unittest')
self.assertTrue(self.wasRun)
self.assertEqual(suite._tests, tests)
@@ -93,10 +93,10 @@ def run(self, test):
sys.argv = ['faketest']
runner = FakeRunner()
program = unittest.TestProgram(testRunner=runner, exit=False,
- defaultTest='unittest.test',
+ defaultTest='test.test_unittest',
testLoader=self.FooBarLoader())
sys.argv = old_argv
- self.assertEqual(('unittest.test',), program.testNames)
+ self.assertEqual(('test.test_unittest',), program.testNames)
def test_defaultTest_with_iterable(self):
class FakeRunner(object):
@@ -109,10 +109,10 @@ def run(self, test):
runner = FakeRunner()
program = unittest.TestProgram(
testRunner=runner, exit=False,
- defaultTest=['unittest.test', 'unittest.test2'],
+ defaultTest=['test.test_unittest', 'test.test_unittest2'],
testLoader=self.FooBarLoader())
sys.argv = old_argv
- self.assertEqual(['unittest.test', 'unittest.test2'],
+ self.assertEqual(['test.test_unittest', 'test.test_unittest2'],
program.testNames)
def test_NonExit(self):
diff --git a/Lib/unittest/test/test_result.py b/Lib/test/test_unittest/test_result.py
similarity index 100%
rename from Lib/unittest/test/test_result.py
rename to Lib/test/test_unittest/test_result.py
diff --git a/Lib/unittest/test/test_runner.py b/Lib/test/test_unittest/test_runner.py
similarity index 99%
rename from Lib/unittest/test/test_runner.py
rename to Lib/test/test_unittest/test_runner.py
index d3488b40e8..9e3a0a9ca0 100644
--- a/Lib/unittest/test/test_runner.py
+++ b/Lib/test/test_unittest/test_runner.py
@@ -8,7 +8,7 @@
import unittest
from unittest.case import _Outcome
-from unittest.test.support import (LoggingResult,
+from test.test_unittest.support import (LoggingResult,
ResultWithNoStartTestRunStopTestRun)
diff --git a/Lib/unittest/test/test_setups.py b/Lib/test/test_unittest/test_setups.py
similarity index 100%
rename from Lib/unittest/test/test_setups.py
rename to Lib/test/test_unittest/test_setups.py
diff --git a/Lib/unittest/test/test_skipping.py b/Lib/test/test_unittest/test_skipping.py
similarity index 99%
rename from Lib/unittest/test/test_skipping.py
rename to Lib/test/test_unittest/test_skipping.py
index 64ceeae37e..f146dcac18 100644
--- a/Lib/unittest/test/test_skipping.py
+++ b/Lib/test/test_unittest/test_skipping.py
@@ -1,6 +1,6 @@
import unittest
-from unittest.test.support import LoggingResult
+from test.test_unittest.support import LoggingResult
class Test_TestSkipping(unittest.TestCase):
diff --git a/Lib/unittest/test/test_suite.py b/Lib/test/test_unittest/test_suite.py
similarity index 99%
rename from Lib/unittest/test/test_suite.py
rename to Lib/test/test_unittest/test_suite.py
index 0551a16996..ca52ee9d9c 100644
--- a/Lib/unittest/test/test_suite.py
+++ b/Lib/test/test_unittest/test_suite.py
@@ -3,7 +3,7 @@
import gc
import sys
import weakref
-from unittest.test.support import LoggingResult, TestEquality
+from test.test_unittest.support import LoggingResult, TestEquality
### Support code for Test_TestSuite
diff --git a/Lib/unittest/test/testmock/__init__.py b/Lib/test/test_unittest/testmock/__init__.py
similarity index 86%
rename from Lib/unittest/test/testmock/__init__.py
rename to Lib/test/test_unittest/testmock/__init__.py
index 87d7ae994d..6ee60b2376 100644
--- a/Lib/unittest/test/testmock/__init__.py
+++ b/Lib/test/test_unittest/testmock/__init__.py
@@ -10,7 +10,7 @@ def load_tests(*args):
suite = unittest.TestSuite()
for fn in os.listdir(here):
if fn.startswith("test") and fn.endswith(".py"):
- modname = "unittest.test.testmock." + fn[:-3]
+ modname = "test.test_unittest.testmock." + fn[:-3]
__import__(modname)
module = sys.modules[modname]
suite.addTest(loader.loadTestsFromModule(module))
diff --git a/Lib/unittest/test/testmock/__main__.py b/Lib/test/test_unittest/testmock/__main__.py
similarity index 86%
rename from Lib/unittest/test/testmock/__main__.py
rename to Lib/test/test_unittest/testmock/__main__.py
index 45c633a4ee..1e3068b0dd 100644
--- a/Lib/unittest/test/testmock/__main__.py
+++ b/Lib/test/test_unittest/testmock/__main__.py
@@ -6,7 +6,7 @@ def load_tests(loader, standard_tests, pattern):
# top level directory cached on loader instance
this_dir = os.path.dirname(__file__)
pattern = pattern or "test*.py"
- # We are inside unittest.test.testmock, so the top-level is three notches up
+ # We are inside test.test_unittest.testmock, so the top-level is three notches up
top_level_dir = os.path.dirname(os.path.dirname(os.path.dirname(this_dir)))
package_tests = loader.discover(start_dir=this_dir, pattern=pattern,
top_level_dir=top_level_dir)
diff --git a/Lib/unittest/test/testmock/support.py b/Lib/test/test_unittest/testmock/support.py
similarity index 100%
rename from Lib/unittest/test/testmock/support.py
rename to Lib/test/test_unittest/testmock/support.py
diff --git a/Lib/unittest/test/testmock/testasync.py b/Lib/test/test_unittest/testmock/testasync.py
similarity index 100%
rename from Lib/unittest/test/testmock/testasync.py
rename to Lib/test/test_unittest/testmock/testasync.py
diff --git a/Lib/unittest/test/testmock/testcallable.py b/Lib/test/test_unittest/testmock/testcallable.py
similarity index 98%
rename from Lib/unittest/test/testmock/testcallable.py
rename to Lib/test/test_unittest/testmock/testcallable.py
index 5eadc00704..ca88511f63 100644
--- a/Lib/unittest/test/testmock/testcallable.py
+++ b/Lib/test/test_unittest/testmock/testcallable.py
@@ -3,7 +3,7 @@
# http://www.voidspace.org.uk/python/mock/
import unittest
-from unittest.test.testmock.support import is_instance, X, SomeClass
+from test.test_unittest.testmock.support import is_instance, X, SomeClass
from unittest.mock import (
Mock, MagicMock, NonCallableMagicMock,
diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/test/test_unittest/testmock/testhelpers.py
similarity index 100%
rename from Lib/unittest/test/testmock/testhelpers.py
rename to Lib/test/test_unittest/testmock/testhelpers.py
diff --git a/Lib/unittest/test/testmock/testmagicmethods.py b/Lib/test/test_unittest/testmock/testmagicmethods.py
similarity index 100%
rename from Lib/unittest/test/testmock/testmagicmethods.py
rename to Lib/test/test_unittest/testmock/testmagicmethods.py
diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py
similarity index 99%
rename from Lib/unittest/test/testmock/testmock.py
rename to Lib/test/test_unittest/testmock/testmock.py
index c99098dc4e..8a92490137 100644
--- a/Lib/unittest/test/testmock/testmock.py
+++ b/Lib/test/test_unittest/testmock/testmock.py
@@ -5,7 +5,7 @@
from test.support import ALWAYS_EQ
import unittest
-from unittest.test.testmock.support import is_instance
+from test.test_unittest.testmock.support import is_instance
from unittest import mock
from unittest.mock import (
call, DEFAULT, patch, sentinel,
diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/test/test_unittest/testmock/testpatch.py
similarity index 98%
rename from Lib/unittest/test/testmock/testpatch.py
rename to Lib/test/test_unittest/testmock/testpatch.py
index 8ab63a1317..93ec0ca4be 100644
--- a/Lib/unittest/test/testmock/testpatch.py
+++ b/Lib/test/test_unittest/testmock/testpatch.py
@@ -7,8 +7,8 @@
from collections import OrderedDict
import unittest
-from unittest.test.testmock import support
-from unittest.test.testmock.support import SomeClass, is_instance
+from test.test_unittest.testmock import support
+from test.test_unittest.testmock.support import SomeClass, is_instance
from test.test_importlib.util import uncache
from unittest.mock import (
@@ -669,7 +669,7 @@ def test_patch_dict_decorator_resolution(self):
# the new dictionary during function call
original = support.target.copy()
- @patch.dict('unittest.test.testmock.support.target', {'bar': 'BAR'})
+ @patch.dict('test.test_unittest.testmock.support.target', {'bar': 'BAR'})
def test():
self.assertEqual(support.target, {'foo': 'BAZ', 'bar': 'BAR'})
@@ -1614,7 +1614,7 @@ def test_patch_with_spec_mock_repr(self):
def test_patch_nested_autospec_repr(self):
- with patch('unittest.test.testmock.support', autospec=True) as m:
+ with patch('test.test_unittest.testmock.support', autospec=True) as m:
self.assertIn(" name='support.SomeClass.wibble()'",
repr(m.SomeClass.wibble()))
self.assertIn(" name='support.SomeClass().wibble()'",
@@ -1882,7 +1882,7 @@ def foo(x=0):
with patch.object(foo, '__module__', "testpatch2"):
self.assertEqual(foo.__module__, "testpatch2")
- self.assertEqual(foo.__module__, 'unittest.test.testmock.testpatch')
+ self.assertEqual(foo.__module__, 'test.test_unittest.testmock.testpatch')
with patch.object(foo, '__annotations__', dict([('s', 1, )])):
self.assertEqual(foo.__annotations__, dict([('s', 1, )]))
@@ -1917,16 +1917,16 @@ def test_dotted_but_module_not_loaded(self):
# This exercises the AttributeError branch of _dot_lookup.
# make sure it's there
- import unittest.test.testmock.support
+ import test.test_unittest.testmock.support
# now make sure it's not:
with patch.dict('sys.modules'):
- del sys.modules['unittest.test.testmock.support']
- del sys.modules['unittest.test.testmock']
- del sys.modules['unittest.test']
+ del sys.modules['test.test_unittest.testmock.support']
+ del sys.modules['test.test_unittest.testmock']
+ del sys.modules['test.test_unittest']
del sys.modules['unittest']
# now make sure we can patch based on a dotted path:
- @patch('unittest.test.testmock.support.X')
+ @patch('test.test_unittest.testmock.support.X')
def test(mock):
pass
test()
@@ -1943,7 +1943,7 @@ class Foo:
def test_cant_set_kwargs_when_passing_a_mock(self):
- @patch('unittest.test.testmock.support.X', new=object(), x=1)
+ @patch('test.test_unittest.testmock.support.X', new=object(), x=1)
def test(): pass
with self.assertRaises(TypeError):
test()
diff --git a/Lib/unittest/test/testmock/testsealable.py b/Lib/test/test_unittest/testmock/testsealable.py
similarity index 100%
rename from Lib/unittest/test/testmock/testsealable.py
rename to Lib/test/test_unittest/testmock/testsealable.py
diff --git a/Lib/unittest/test/testmock/testsentinel.py b/Lib/test/test_unittest/testmock/testsentinel.py
similarity index 100%
rename from Lib/unittest/test/testmock/testsentinel.py
rename to Lib/test/test_unittest/testmock/testsentinel.py
diff --git a/Lib/unittest/test/testmock/testwith.py b/Lib/test/test_unittest/testmock/testwith.py
similarity index 99%
rename from Lib/unittest/test/testmock/testwith.py
rename to Lib/test/test_unittest/testmock/testwith.py
index c74d49a63c..8dc8eb1137 100644
--- a/Lib/unittest/test/testmock/testwith.py
+++ b/Lib/test/test_unittest/testmock/testwith.py
@@ -1,7 +1,7 @@
import unittest
from warnings import catch_warnings
-from unittest.test.testmock.support import is_instance
+from test.test_unittest.testmock.support import is_instance
from unittest.mock import MagicMock, Mock, patch, sentinel, mock_open, call
diff --git a/Lib/unittest/__init__.py b/Lib/unittest/__init__.py
index 005d23f6d0..b8de8c95d6 100644
--- a/Lib/unittest/__init__.py
+++ b/Lib/unittest/__init__.py
@@ -73,16 +73,6 @@ def testMultiply(self):
_TextTestResult = TextTestResult
-# There are no tests here, so don't try to run anything discovered from
-# introspecting the symbols (e.g. FunctionTestCase). Instead, all our
-# tests come from within unittest.test.
-def load_tests(loader, tests, pattern):
- import os.path
- # top level directory cached on loader instance
- this_dir = os.path.dirname(__file__)
- return loader.discover(start_dir=this_dir, pattern=pattern)
-
-
# Lazy import of IsolatedAsyncioTestCase from .async_case
# It imports asyncio, which is relatively heavy, but most tests
# do not need it.
diff --git a/Lib/unittest/test/__init__.py b/Lib/unittest/test/__init__.py
deleted file mode 100644
index 143f4ab5a3..0000000000
--- a/Lib/unittest/test/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import os
-import sys
-import unittest
-
-
-here = os.path.dirname(__file__)
-loader = unittest.defaultTestLoader
-
-def suite():
- suite = unittest.TestSuite()
- for fn in os.listdir(here):
- if fn.startswith("test") and fn.endswith(".py"):
- modname = "unittest.test." + fn[:-3]
- try:
- __import__(modname)
- except unittest.SkipTest:
- continue
- module = sys.modules[modname]
- suite.addTest(loader.loadTestsFromModule(module))
- suite.addTest(loader.loadTestsFromName('unittest.test.testmock'))
- return suite
-
-
-if __name__ == "__main__":
- unittest.main(defaultTest="suite")
diff --git a/Lib/unittest/test/__main__.py b/Lib/unittest/test/__main__.py
deleted file mode 100644
index 44d0591e84..0000000000
--- a/Lib/unittest/test/__main__.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import os
-import unittest
-
-
-def load_tests(loader, standard_tests, pattern):
- # top level directory cached on loader instance
- this_dir = os.path.dirname(__file__)
- pattern = pattern or "test_*.py"
- # We are inside unittest.test, so the top-level is two notches up
- top_level_dir = os.path.dirname(os.path.dirname(this_dir))
- package_tests = loader.discover(start_dir=this_dir, pattern=pattern,
- top_level_dir=top_level_dir)
- standard_tests.addTests(package_tests)
- return standard_tests
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/PCbuild/lib.pyproj b/PCbuild/lib.pyproj
index 692b083349..f3f44d1d8f 100644
--- a/PCbuild/lib.pyproj
+++ b/PCbuild/lib.pyproj
@@ -1491,33 +1491,33 @@
<Compile Include="unittest\runner.py" />
<Compile Include="unittest\signals.py" />
<Compile Include="unittest\suite.py" />
- <Compile Include="unittest\test\dummy.py" />
- <Compile Include="unittest\test\support.py" />
- <Compile Include="unittest\test\testmock\support.py" />
- <Compile Include="unittest\test\testmock\testcallable.py" />
- <Compile Include="unittest\test\testmock\testhelpers.py" />
- <Compile Include="unittest\test\testmock\testmagicmethods.py" />
- <Compile Include="unittest\test\testmock\testmock.py" />
- <Compile Include="unittest\test\testmock\testpatch.py" />
- <Compile Include="unittest\test\testmock\testsentinel.py" />
- <Compile Include="unittest\test\testmock\testwith.py" />
- <Compile Include="unittest\test\testmock\__init__.py" />
- <Compile Include="unittest\test\testmock\__main__.py" />
- <Compile Include="unittest\test\test_assertions.py" />
- <Compile Include="unittest\test\test_break.py" />
- <Compile Include="unittest\test\test_case.py" />
- <Compile Include="unittest\test\test_discovery.py" />
- <Compile Include="unittest\test\test_functiontestcase.py" />
- <Compile Include="unittest\test\test_loader.py" />
- <Compile Include="unittest\test\test_program.py" />
- <Compile Include="unittest\test\test_result.py" />
- <Compile Include="unittest\test\test_runner.py" />
- <Compile Include="unittest\test\test_setups.py" />
- <Compile Include="unittest\test\test_skipping.py" />
- <Compile Include="unittest\test\test_suite.py" />
- <Compile Include="unittest\test\_test_warnings.py" />
- <Compile Include="unittest\test\__init__.py" />
- <Compile Include="unittest\test\__main__.py" />
+ <Compile Include="test\test_unittest\dummy.py" />
+ <Compile Include="test\test_unittest\support.py" />
+ <Compile Include="test\test_unittest\testmock\support.py" />
+ <Compile Include="test\test_unittest\testmock\testcallable.py" />
+ <Compile Include="test\test_unittest\testmock\testhelpers.py" />
+ <Compile Include="test\test_unittest\testmock\testmagicmethods.py" />
+ <Compile Include="test\test_unittest\testmock\testmock.py" />
+ <Compile Include="test\test_unittest\testmock\testpatch.py" />
+ <Compile Include="test\test_unittest\testmock\testsentinel.py" />
+ <Compile Include="test\test_unittest\testmock\testwith.py" />
+ <Compile Include="test\test_unittest\testmock\__init__.py" />
+ <Compile Include="test\test_unittest\testmock\__main__.py" />
+ <Compile Include="test\test_unittest\test_assertions.py" />
+ <Compile Include="test\test_unittest\test_break.py" />
+ <Compile Include="test\test_unittest\test_case.py" />
+ <Compile Include="test\test_unittest\test_discovery.py" />
+ <Compile Include="test\test_unittest\test_functiontestcase.py" />
+ <Compile Include="test\test_unittest\test_loader.py" />
+ <Compile Include="test\test_unittest\test_program.py" />
+ <Compile Include="test\test_unittest\test_result.py" />
+ <Compile Include="test\test_unittest\test_runner.py" />
+ <Compile Include="test\test_unittest\test_setups.py" />
+ <Compile Include="test\test_unittest\test_skipping.py" />
+ <Compile Include="test\test_unittest\test_suite.py" />
+ <Compile Include="test\test_unittest\_test_warnings.py" />
+ <Compile Include="test\test_unittest\__init__.py" />
+ <Compile Include="test\test_unittest\__main__.py" />
<Compile Include="unittest\util.py" />
<Compile Include="unittest\__init__.py" />
<Compile Include="unittest\__main__.py" />
@@ -1804,6 +1804,8 @@
<Folder Include="test\test_json" />
<Folder Include="test\test_peg_generator" />
<Folder Include="test\test_tools" />
+ <Folder Include="test\test_unittest" />
+ <Folder Include="test\test_unittest\testmock" />
<Folder Include="test\test_warnings" />
<Folder Include="test\test_warnings\data" />
<Folder Include="test\tracedmodules" />
@@ -1813,8 +1815,6 @@
<Folder Include="tkinter\test\test_ttk" />
<Folder Include="turtledemo" />
<Folder Include="unittest" />
- <Folder Include="unittest\test" />
- <Folder Include="unittest\test\testmock" />
<Folder Include="urllib" />
<Folder Include="venv" />
<Folder Include="wsgiref" />
diff --git a/Tools/wasm/wasm_assets.py b/Tools/wasm/wasm_assets.py
index d0a0570840..67afde60f0 100755
index aa09d2dda8..30aad35760 100755
--- a/Tools/wasm/wasm_assets.py
+++ b/Tools/wasm/wasm_assets.py
@@ -112,7 +112,6 @@
@@ -113,7 +113,6 @@
# regression test sub directories
OMIT_SUBDIRS = (
"tkinter/test/",
- "unittest/test/",
)
def get_builddir(args: argparse.Namespace) -> pathlib.Path:
SYSCONFIG_NAMES = (
--
2.29.2.windows.2
2.25.1

View File

@ -1,7 +1,7 @@
From 4c71c808cc65ed6003b1e29d583c71586ebb36e1 Mon Sep 17 00:00:00 2001
From 19171eafd8a07b9df74014b757e51ece237a2943 Mon Sep 17 00:00:00 2001
From: ryanking13 <def6488@gmail.com>
Date: Wed, 25 Jan 2023 15:54:16 +0900
Subject: [PATCH 8/9] Move test directories
Subject: [PATCH 7/8] Move test directories
---
Makefile.pre.in | 6 +++---
@ -32,5 +32,5 @@ index b356f6293e..68c55a356a 100644
TEST_MODULES=@TEST_MODULES@
libinstall: all $(srcdir)/Modules/xxmodule.c
--
2.29.2.windows.2
2.25.1

View File

@ -1,40 +0,0 @@
From 50ebd72fb0e69c78f95cea3d4a47589beb91ac37 Mon Sep 17 00:00:00 2001
From: Christian Heimes <christian@python.org>
Date: Tue, 21 Jun 2022 14:51:39 +0200
Subject: [PATCH 7/9] gh-93839: Use load_package_tests() for testmock (GH-94055)
Fixes failing tests on WebAssembly platforms.
Automerge-Triggered-By: GH:tiran
---
Lib/test/test_unittest/testmock/__init__.py | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/Lib/test/test_unittest/testmock/__init__.py b/Lib/test/test_unittest/testmock/__init__.py
index 6ee60b2376..bc502ef32d 100644
--- a/Lib/test/test_unittest/testmock/__init__.py
+++ b/Lib/test/test_unittest/testmock/__init__.py
@@ -1,17 +1,6 @@
-import os
-import sys
-import unittest
+import os.path
+from test.support import load_package_tests
-here = os.path.dirname(__file__)
-loader = unittest.defaultTestLoader
-
def load_tests(*args):
- suite = unittest.TestSuite()
- for fn in os.listdir(here):
- if fn.startswith("test") and fn.endswith(".py"):
- modname = "test.test_unittest.testmock." + fn[:-3]
- __import__(modname)
- module = sys.modules[modname]
- suite.addTest(loader.loadTestsFromModule(module))
- return suite
+ return load_package_tests(os.path.dirname(__file__), *args)
--
2.29.2.windows.2

View File

@ -0,0 +1,371 @@
From 68a77d4dab1c27231fed2a541095929bdb2f34ab Mon Sep 17 00:00:00 2001
From: Hood Chatham <roberthoodchatham@gmail.com>
Date: Wed, 28 Jun 2023 10:46:19 -0700
Subject: [PATCH 8/8] Make Emscripten trampolines work with JSPI
There is a WIP proposal to enable webassembly stack switching which have been
implemented in v8:
https://github.com/WebAssembly/js-promise-integration
It is not possible to switch stacks that contain JS frames so the Emscripten JS
trampolines that allow calling functions with the wrong number of arguments
don't work in this case. However, the js-promise-integration proposal requires
the [type reflection for Wasm/JS API](https://github.com/WebAssembly/js-types)
proposal, which allows us to actually count the number of arguments a function
expects.
For better compatibility with stack switching, this PR checks if type reflection
is available, and if so we use a switch block to decide the appropriate
signature. If type reflection is unavailable, we should use the current EMJS
trampoline.
We cache the function argument counts since when I didn't cache them performance
was negatively affected.
---
.../internal/pycore_emscripten_trampoline.h | 55 +++++++++++++
Include/internal/pycore_object.h | 28 +------
Include/internal/pycore_runtime.h | 5 ++
Objects/descrobject.c | 22 ++++--
Objects/methodobject.c | 7 --
Python/emscripten_trampoline.c | 79 +++++++++++++++++++
Python/pylifecycle.c | 3 +
Python/pystate.c | 2 +
configure | 4 +-
configure.ac | 4 +-
10 files changed, 163 insertions(+), 46 deletions(-)
create mode 100644 Include/internal/pycore_emscripten_trampoline.h
create mode 100644 Python/emscripten_trampoline.c
diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h
new file mode 100644
index 0000000000..e5031c8362
--- /dev/null
+++ b/Include/internal/pycore_emscripten_trampoline.h
@@ -0,0 +1,55 @@
+#ifndef Py_EMSCRIPTEN_TRAMPOLINE_H
+#define Py_EMSCRIPTEN_TRAMPOLINE_H
+
+#include "pycore_runtime.h" // _PyRuntimeState
+
+/**
+ * C function call trampolines to mitigate bad function pointer casts.
+ *
+ * Section 6.3.2.3, paragraph 8 reads:
+ *
+ * A pointer to a function of one type may be converted to a pointer to a
+ * function of another type and back again; the result shall compare equal to
+ * the original pointer. If a converted pointer is used to call a function
+ * whose type is not compatible with the pointed-to type, the behavior is
+ * undefined.
+ *
+ * Typical native ABIs ignore additional arguments or fill in missing values
+ * with 0/NULL in function pointer cast. Compilers do not show warnings when a
+ * function pointer is explicitly casted to an incompatible type.
+ *
+ * Bad fpcasts are an issue in WebAssembly. WASM's indirect_call has strict
+ * function signature checks. Argument count, types, and return type must match.
+ *
+ * Third party code unintentionally rely on problematic fpcasts. The call
+ * trampoline mitigates common occurrences of bad fpcasts on Emscripten.
+ */
+
+#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
+
+void _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime);
+
+PyObject*
+_PyEM_TrampolineCall(PyCFunctionWithKeywords func,
+ PyObject* self,
+ PyObject* args,
+ PyObject* kw);
+
+#define _PyCFunction_TrampolineCall(meth, self, args) \
+ _PyEM_TrampolineCall( \
+ (*(PyCFunctionWithKeywords)(void(*)(void))(meth)), (self), (args), NULL)
+
+#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \
+ _PyEM_TrampolineCall((meth), (self), (args), (kw))
+
+#else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
+
+#define _Py_EmscriptenTrampoline_Init(runtime)
+
+#define _PyCFunction_TrampolineCall(meth, self, args) \
+ (meth)((self), (args))
+#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \
+ (meth)((self), (args), (kw))
+
+#endif // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
+#endif // ndef Py_EMSCRIPTEN_SIGNAL_H
diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index f022f82469..8bf4ebac38 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -10,6 +10,7 @@ extern "C" {
#include <stdbool.h>
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
+#include "pycore_emscripten_trampoline.h" // _PyCFunction_TrampolineCall()
#include "pycore_interp.h" // PyInterpreterState.gc
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_runtime.h" // _PyRuntime
@@ -277,33 +278,6 @@ extern PyObject* _PyType_GetSubclasses(PyTypeObject *);
PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, PyObject *);
-/* C function call trampolines to mitigate bad function pointer casts.
- *
- * Typical native ABIs ignore additional arguments or fill in missing
- * values with 0/NULL in function pointer cast. Compilers do not show
- * warnings when a function pointer is explicitly casted to an
- * incompatible type.
- *
- * Bad fpcasts are an issue in WebAssembly. WASM's indirect_call has strict
- * function signature checks. Argument count, types, and return type must
- * match.
- *
- * Third party code unintentionally rely on problematic fpcasts. The call
- * trampoline mitigates common occurences of bad fpcasts on Emscripten.
- */
-#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
-#define _PyCFunction_TrampolineCall(meth, self, args) \
- _PyCFunctionWithKeywords_TrampolineCall( \
- (*(PyCFunctionWithKeywords)(void(*)(void))meth), self, args, NULL)
-extern PyObject* _PyCFunctionWithKeywords_TrampolineCall(
- PyCFunctionWithKeywords meth, PyObject *, PyObject *, PyObject *);
-#else
-#define _PyCFunction_TrampolineCall(meth, self, args) \
- (meth)((self), (args))
-#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \
- (meth)((self), (args), (kw))
-#endif // __EMSCRIPTEN__ && PY_CALL_TRAMPOLINE
-
#ifdef __cplusplus
}
#endif
diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h
index ae63ae74af..521a1e87a4 100644
--- a/Include/internal/pycore_runtime.h
+++ b/Include/internal/pycore_runtime.h
@@ -75,6 +75,11 @@ typedef struct pyruntimestate {
/* Is Python fully initialized? Set to 1 by Py_Initialize() */
int initialized;
+#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
+ /* Choose between trampoline based on type reflection vs based on EM_JS */
+ int wasm_type_reflection_available;
+#endif
+
/* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize()
is called again.
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index 4d8b83758b..f9d4c00399 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -15,15 +15,21 @@ class property "propertyobject *" "&PyProperty_Type"
// see pycore_object.h
#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
-#include <emscripten.h>
-EM_JS(int, descr_set_trampoline_call, (setter set, PyObject *obj, PyObject *value, void *closure), {
- return wasmTable.get(set)(obj, value, closure);
-});
-
-EM_JS(PyObject*, descr_get_trampoline_call, (getter get, PyObject *obj, void *closure), {
- return wasmTable.get(get)(obj, closure);
-});
+extern PyObject*
+_PyEM_TrampolineCall(PyCFunctionWithKeywords func,
+ PyObject* self,
+ PyObject* args,
+ PyObject* kw);
+
+#define descr_set_trampoline_call(set, obj, value, closure) \
+ ((int)_PyEM_TrampolineCall((PyCFunctionWithKeywords)(set), (obj), (value), (PyObject*)(closure)))
+
+
+#define descr_get_trampoline_call(get, obj, closure) \
+ _PyEM_TrampolineCall((PyCFunctionWithKeywords)(get), (obj), (PyObject*)(closure), NULL)
+
#else
+
#define descr_set_trampoline_call(set, obj, value, closure) \
(set)((obj), (value), (closure))
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index 953cf4666d..d344bfa234 100644
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -555,10 +555,3 @@ cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs)
return _Py_CheckFunctionResult(tstate, func, result, NULL);
}
-#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
-#include <emscripten.h>
-
-EM_JS(PyObject*, _PyCFunctionWithKeywords_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *self, PyObject *args, PyObject *kw), {
- return wasmTable.get(func)(self, args, kw);
-});
-#endif
diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c
new file mode 100644
index 0000000000..33c9d25d2e
--- /dev/null
+++ b/Python/emscripten_trampoline.c
@@ -0,0 +1,79 @@
+#if defined(PY_CALL_TRAMPOLINE)
+
+#include <emscripten.h> // EM_JS, EM_JS_DEPS
+#include <Python.h>
+#include "pycore_runtime.h" // _PyRuntime
+
+
+/**
+ * This is the GoogleChromeLabs approved way to feature detect type-reflection:
+ * https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/type-reflection/index.js
+ */
+EM_JS(int, _PyEM_detect_type_reflection, (), {
+ return "Function" in WebAssembly;
+});
+
+void
+_Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime)
+{
+ runtime->wasm_type_reflection_available = _PyEM_detect_type_reflection();
+}
+
+/**
+ * Backwards compatible trampoline works with all JS runtimes
+ */
+EM_JS_DEPS(_PyEMJS_TrampolineCall, "$getWasmTableEntry")
+EM_JS(PyObject*, _PyEMJS_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), {
+ return getWasmTableEntry(func)(arg1, arg2, arg3);
+}
+);
+
+/**
+ * In runtimes with WebAssembly type reflection, count the number of parameters
+ * and cast to the appropriate signature
+ */
+EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func), {
+ let n = _PyEM_CountFuncParams.cache.get(func);
+ if (n !== undefined) {
+ return n;
+ }
+ n = WebAssembly.Function.type(getWasmTableEntry(func)).parameters.length;
+ _PyEM_CountFuncParams.cache.set(func, n);
+ return n;
+}
+_PyEM_CountFuncParams.cache = new Map();
+)
+
+
+typedef PyObject* (*zero_arg)(void);
+typedef PyObject* (*one_arg)(PyObject*);
+typedef PyObject* (*two_arg)(PyObject*, PyObject*);
+typedef PyObject* (*three_arg)(PyObject*, PyObject*, PyObject*);
+
+
+PyObject*
+_PyEM_TrampolineCall(PyCFunctionWithKeywords func,
+ PyObject* self,
+ PyObject* args,
+ PyObject* kw)
+{
+ if (!_PyRuntime.wasm_type_reflection_available) {
+ return _PyEMJS_TrampolineCall(func, self, args, kw);
+ } else {
+ switch (_PyEM_CountFuncParams(func)) {
+ case 0:
+ return ((zero_arg)func)();
+ case 1:
+ return ((one_arg)func)(self);
+ case 2:
+ return ((two_arg)func)(self, args);
+ case 3:
+ return ((three_arg)func)(self, args, kw);
+ default:
+ PyErr_SetString(PyExc_SystemError, "Handler takes too many arguments");
+ return NULL;
+ }
+ }
+}
+
+#endif
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 9248e971d9..05fd28b2df 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -5,6 +5,7 @@
#include "pycore_bytesobject.h" // _PyBytes_InitTypes()
#include "pycore_ceval.h" // _PyEval_FiniGIL()
#include "pycore_context.h" // _PyContext_Init()
+#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init()
#include "pycore_exceptions.h" // _PyExc_InitTypes()
#include "pycore_dict.h" // _PyDict_Fini()
#include "pycore_fileutils.h" // _Py_ResetForceASCII()
@@ -606,7 +607,9 @@ pycore_init_runtime(_PyRuntimeState *runtime,
if (_PyStatus_EXCEPTION(status)) {
return status;
}
+
return _PyStatus_OK();
+
}
diff --git a/Python/pystate.c b/Python/pystate.c
index dfca3f5fd7..0dd2e7733e 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -2,6 +2,7 @@
/* Thread and interpreter state structures and their interfaces */
#include "Python.h"
+#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init()
#include "pycore_ceval.h"
#include "pycore_code.h" // stats
#include "pycore_frame.h"
@@ -126,6 +127,7 @@ init_runtime(_PyRuntimeState *runtime,
runtime->unicode_ids.lock = unicode_ids_mutex;
runtime->_initialized = 1;
+ _Py_EmscriptenTrampoline_Init(runtime);
}
PyStatus
diff --git a/configure b/configure
index 9e76287725..630ba49c0b 100755
--- a/configure
+++ b/configure
@@ -15083,8 +15083,8 @@ PLATFORM_OBJS=
case $ac_sys_system in #(
Emscripten) :
- as_fn_append PLATFORM_OBJS ' Python/emscripten_signal.o'
- as_fn_append PLATFORM_HEADERS ' $(srcdir)/Include/internal/pycore_emscripten_signal.h'
+ as_fn_append PLATFORM_OBJS ' Python/emscripten_signal.o Python/emscripten_trampoline.o'
+ as_fn_append PLATFORM_HEADERS ' $(srcdir)/Include/internal/pycore_emscripten_signal.h $(srcdir)/Include/internal/pycore_emscripten_trampoline.h'
;; #(
*) :
;;
diff --git a/configure.ac b/configure.ac
index c62a565eb6..9b488da03b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4523,8 +4523,8 @@ PLATFORM_OBJS=
AS_CASE([$ac_sys_system],
[Emscripten], [
- AS_VAR_APPEND([PLATFORM_OBJS], [' Python/emscripten_signal.o'])
- AS_VAR_APPEND([PLATFORM_HEADERS], [' $(srcdir)/Include/internal/pycore_emscripten_signal.h'])
+ AS_VAR_APPEND([PLATFORM_OBJS], [' Python/emscripten_signal.o Python/emscripten_trampoline.o'])
+ AS_VAR_APPEND([PLATFORM_HEADERS], [' $(srcdir)/Include/internal/pycore_emscripten_signal.h $(srcdir)/Include/internal/pycore_emscripten_trampoline.h'])
],
)
AC_SUBST([PLATFORM_HEADERS])
--
2.25.1