mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-137210: Add a struct, slot & function for checking an extension's ABI (GH-137212)
Co-authored-by: Steve Dower <steve.dower@microsoft.com>
This commit is contained in:
parent
c1a9c23195
commit
0c74fc8af0
24 changed files with 654 additions and 8 deletions
154
Lib/test/test_capi/test_modsupport.py
Normal file
154
Lib/test/test_capi/test_modsupport.py
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
import sys
|
||||
import unittest
|
||||
import sysconfig
|
||||
|
||||
from test.support import subTests
|
||||
from test.support import import_helper
|
||||
|
||||
_testcapi = import_helper.import_module('_testcapi')
|
||||
|
||||
|
||||
class Test_ABIInfo_Check(unittest.TestCase):
|
||||
@subTests('modname', (None, 'test_mod'))
|
||||
def test_zero(self, modname):
|
||||
_testcapi.pyabiinfo_check(modname, 0, 0, 0, 0, 0)
|
||||
_testcapi.pyabiinfo_check(modname, 1, 0, 0, 0, 0)
|
||||
|
||||
def test_large_major_version(self):
|
||||
with self.assertRaisesRegex(ImportError,
|
||||
'^PyABIInfo version too high$'):
|
||||
_testcapi.pyabiinfo_check(None, 2, 0, 0, 0, 0)
|
||||
with self.assertRaisesRegex(ImportError,
|
||||
'^test_mod: PyABIInfo version too high$'):
|
||||
_testcapi.pyabiinfo_check("test_mod", 2, 0, 0, 0, 0)
|
||||
|
||||
@subTests('modname', (None, 'test_mod'))
|
||||
def test_large_minor_version(self, modname):
|
||||
_testcapi.pyabiinfo_check(modname, 1, 2, 0, 0, 0)
|
||||
|
||||
@subTests('modname', (None, 'test_mod'))
|
||||
@subTests('major', (0, 1))
|
||||
@subTests('minor', (0, 1, 9))
|
||||
@subTests('build', (0, sys.hexversion))
|
||||
def test_positive_regular(self, modname, major, minor, build):
|
||||
ver = sys.hexversion
|
||||
truncated = ver & 0xffff0000
|
||||
filled = truncated | 0x12b8
|
||||
maxed = truncated | 0xffff
|
||||
for abi_version in (0, ver, truncated, filled, maxed):
|
||||
with self.subTest(abi_version=abi_version):
|
||||
_testcapi.pyabiinfo_check(modname, major, minor, 0,
|
||||
build, abi_version)
|
||||
|
||||
@subTests('modname', (None, 'test_mod'))
|
||||
@subTests('minor', (0, 1, 9))
|
||||
@subTests('build', (0, sys.hexversion))
|
||||
@subTests('offset', (+0x00010000, -0x00010000))
|
||||
def test_negative_regular(self, modname, minor, build, offset):
|
||||
ver = sys.hexversion + offset
|
||||
truncated = ver & 0xffff0000
|
||||
filled = truncated | 0x12b8
|
||||
maxed = truncated | 0xffff
|
||||
for abi_version in (ver, truncated, filled, maxed):
|
||||
with self.subTest(abi_version=abi_version):
|
||||
with self.assertRaisesRegex(
|
||||
ImportError,
|
||||
r'incompatible ABI version \(3\.\d+\)$'):
|
||||
_testcapi.pyabiinfo_check(modname, 1, minor, 0,
|
||||
build,
|
||||
abi_version)
|
||||
|
||||
@subTests('modname', (None, 'test_mod'))
|
||||
@subTests('major', (0, 1))
|
||||
@subTests('minor', (0, 1, 9))
|
||||
@subTests('build', (0, sys.hexversion))
|
||||
@subTests('abi_version', (
|
||||
0,
|
||||
0x03020000,
|
||||
sys.hexversion,
|
||||
sys.hexversion & 0xffff0000,
|
||||
sys.hexversion - 0x00010000,
|
||||
))
|
||||
def test_positive_stable(self, modname, major, minor, build, abi_version):
|
||||
_testcapi.pyabiinfo_check(modname, major, minor,
|
||||
_testcapi.PyABIInfo_STABLE,
|
||||
build,
|
||||
abi_version)
|
||||
|
||||
@subTests('modname', (None, 'test_mod'))
|
||||
@subTests('minor', (0, 1, 9))
|
||||
@subTests('build', (0, sys.hexversion))
|
||||
@subTests('abi_version_and_msg', (
|
||||
(1, 'invalid'),
|
||||
(3, 'invalid'),
|
||||
(0x0301ffff, 'invalid'),
|
||||
((sys.hexversion & 0xffff0000) + 0x00010000, 'incompatible future'),
|
||||
(sys.hexversion + 0x00010000, 'incompatible future'),
|
||||
(0x04000000, 'incompatible future'),
|
||||
))
|
||||
def test_negative_stable(self, modname, minor, build, abi_version_and_msg):
|
||||
abi_version, msg = abi_version_and_msg
|
||||
with self.assertRaisesRegex(
|
||||
ImportError,
|
||||
rf'{msg} stable ABI version \(\d+\.\d+\)$'):
|
||||
_testcapi.pyabiinfo_check(modname, 1, minor,
|
||||
_testcapi.PyABIInfo_STABLE,
|
||||
build,
|
||||
abi_version)
|
||||
|
||||
@subTests('modname', (None, 'test_mod'))
|
||||
@subTests('major', (0, 1))
|
||||
@subTests('minor', (0, 1, 9))
|
||||
@subTests('build', (0, sys.hexversion))
|
||||
@subTests('abi_version', (0, sys.hexversion))
|
||||
def test_positive_internal(self, modname, major, minor, build, abi_version):
|
||||
_testcapi.pyabiinfo_check(modname, major, minor,
|
||||
_testcapi.PyABIInfo_INTERNAL,
|
||||
build,
|
||||
abi_version)
|
||||
|
||||
@subTests('modname', (None, 'test_mod'))
|
||||
@subTests('minor', (0, 1, 9))
|
||||
@subTests('build', (0, sys.hexversion))
|
||||
@subTests('abi_version', (
|
||||
sys.hexversion - 0x00010000,
|
||||
sys.hexversion - 1,
|
||||
sys.hexversion + 1,
|
||||
sys.hexversion + 0x00010000,
|
||||
))
|
||||
def test_negative_internal(self, modname, minor, build, abi_version):
|
||||
with self.assertRaisesRegex(
|
||||
ImportError,
|
||||
r'incompatible internal ABI \(0x[\da-f]+ != 0x[\da-f]+\)$'):
|
||||
_testcapi.pyabiinfo_check(modname, 1, minor,
|
||||
_testcapi.PyABIInfo_INTERNAL,
|
||||
build,
|
||||
abi_version)
|
||||
|
||||
@subTests('modname', (None, 'test_mod'))
|
||||
@subTests('minor', (0, 1, 9))
|
||||
@subTests('build', (0, sys.hexversion))
|
||||
@subTests('ft_flag', (
|
||||
0,
|
||||
(_testcapi.PyABIInfo_FREETHREADED
|
||||
if sysconfig.get_config_var("Py_GIL_DISABLED")
|
||||
else _testcapi.PyABIInfo_GIL),
|
||||
_testcapi.PyABIInfo_FREETHREADING_AGNOSTIC,
|
||||
))
|
||||
def test_positive_freethreading(self, modname, minor, build, ft_flag):
|
||||
self.assertEqual(ft_flag & _testcapi.PyABIInfo_FREETHREADING_AGNOSTIC,
|
||||
ft_flag)
|
||||
_testcapi.pyabiinfo_check(modname, 1, minor, ft_flag, build, 0)
|
||||
|
||||
@subTests('modname', (None, 'test_mod'))
|
||||
@subTests('minor', (0, 1, 9))
|
||||
@subTests('build', (0, sys.hexversion))
|
||||
def test_negative_freethreading(self, modname, minor, build):
|
||||
if sysconfig.get_config_var("Py_GIL_DISABLED"):
|
||||
ft_flag = _testcapi.PyABIInfo_GIL
|
||||
msg = "incompatible with free-threaded CPython"
|
||||
else:
|
||||
ft_flag = _testcapi.PyABIInfo_FREETHREADED
|
||||
msg = "only compatible with free-threaded CPython"
|
||||
with self.assertRaisesRegex(ImportError, msg):
|
||||
_testcapi.pyabiinfo_check(modname, 1, minor, ft_flag, build, 0)
|
||||
Loading…
Add table
Add a link
Reference in a new issue