mirror of
https://github.com/python/cpython.git
synced 2026-06-05 01:10:53 +00:00
314 lines
12 KiB
Python
314 lines
12 KiB
Python
from test.support import import_helper, subTests
|
|
import contextlib
|
|
import unittest
|
|
import types
|
|
import sys
|
|
|
|
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
|
|
_testcapi = import_helper.import_module('_testcapi')
|
|
|
|
class FakeSpec:
|
|
name = 'module'
|
|
|
|
|
|
# See Modules/_testlimitedcapi/slots.c for the definitions.
|
|
# This module is full of "magic constants" which simply need to match
|
|
# between the C and Python part of the tests.
|
|
|
|
class TypeSlotsTests(unittest.TestCase):
|
|
def test_basic_type_slots(self):
|
|
cls = _testlimitedcapi.type_from_slots("basic")
|
|
self.assertEqual(cls.__name__, "MyType")
|
|
|
|
# Py_TPFLAGS_IMMUTABLETYPE is *not* set
|
|
cls.attr = 123
|
|
|
|
# Py_TPFLAGS_BASETYPE is *not* set
|
|
with self.assertRaises(TypeError):
|
|
class Sub(cls):
|
|
pass
|
|
|
|
def test_mod_slot_in_type(self):
|
|
with self.assertRaisesRegex(SystemError, "invalid.* 100 .*Py_mod_name"):
|
|
_testlimitedcapi.type_from_slots("foreign_slot")
|
|
|
|
def test_size_slots(self):
|
|
cls = _testlimitedcapi.type_from_slots("basicsize")
|
|
self.assertGreaterEqual(cls.__basicsize__, 256)
|
|
|
|
cls = _testlimitedcapi.type_from_slots("extra_basicsize")
|
|
self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
|
|
|
|
cls = _testlimitedcapi.type_from_slots("itemsize")
|
|
self.assertGreaterEqual(cls.__itemsize__, 16)
|
|
|
|
def test_flag_slots(self):
|
|
cls = _testlimitedcapi.type_from_slots("flags")
|
|
with self.assertRaises(TypeError):
|
|
# Py_TPFLAGS_IMMUTABLETYPE is set
|
|
cls.attr = 123
|
|
class Sub(cls):
|
|
# Py_TPFLAGS_BASETYPE is set
|
|
pass
|
|
|
|
def test_func_slots(self):
|
|
cls = _testlimitedcapi.type_from_slots("matmul_123")
|
|
self.assertEqual(cls() @ None, 123)
|
|
|
|
def test_optional_end(self):
|
|
with self.assertRaisesRegex(SystemError, "invalid flags.*Py_slot_end"):
|
|
cls = _testlimitedcapi.type_from_slots("optional_end")
|
|
|
|
def test_invalid(self):
|
|
with self.assertRaisesRegex(SystemError, "Py_slot_invalid"):
|
|
cls = _testlimitedcapi.type_from_slots("invalid")
|
|
with self.assertRaisesRegex(SystemError, f"slot ID {0xfbad}"):
|
|
cls = _testlimitedcapi.type_from_slots("invalid_fbad")
|
|
|
|
cls = _testlimitedcapi.type_from_slots("optional_invalid")
|
|
self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
|
|
|
|
cls = _testlimitedcapi.type_from_slots("optional_invalid_fbad")
|
|
self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
|
|
|
|
@subTests("case_name", ["old_slot_numbers", "new_slot_numbers"])
|
|
def test_compat_slot_numbers(self, case_name):
|
|
cls = _testlimitedcapi.type_from_slots(case_name)
|
|
obj = cls()
|
|
|
|
# Py_bf_getbuffer (1), Py_bf_releasebuffer (2)
|
|
self.assertEqual(obj.buf_counter, 0)
|
|
mem = memoryview(obj)
|
|
self.assertEqual(bytes(mem), b"buf\0")
|
|
self.assertEqual(obj.buf_counter, 1)
|
|
mem.release()
|
|
self.assertEqual(obj.buf_counter, 0)
|
|
|
|
# Py_mp_ass_subscript (3)
|
|
with self.assertRaises(KeyError):
|
|
obj["key"] = "value"
|
|
|
|
# Py_mp_length (4)
|
|
self.assertEqual(len(obj), 456)
|
|
|
|
def test_nonstatic_tp_members(self):
|
|
with self.assertRaisesRegex(SystemError, "Py_tp_members.*STATIC"):
|
|
_testlimitedcapi.type_from_slots("nonstatic_tp_members")
|
|
|
|
def test_intptr_flags(self):
|
|
cls = _testlimitedcapi.type_from_slots("intptr_flags_macro")
|
|
with self.assertRaises(TypeError):
|
|
# Py_TPFLAGS_IMMUTABLETYPE is set
|
|
cls.attr = 123
|
|
|
|
cls = _testlimitedcapi.type_from_slots("intptr_flags_struct")
|
|
with self.assertRaises(TypeError):
|
|
# Py_TPFLAGS_IMMUTABLETYPE is set
|
|
cls.attr = 123
|
|
|
|
cls = _testlimitedcapi.type_from_slots("intptr_static")
|
|
cls.attribute = 123
|
|
|
|
def test_nested(self):
|
|
cls = _testlimitedcapi.type_from_slots("nested")
|
|
self.assertEqual(cls() + 1, 123)
|
|
self.assertEqual(cls() - 1, 234)
|
|
|
|
cls = _testlimitedcapi.type_from_slots("nested_max")
|
|
self.assertEqual(cls() + 1, 123)
|
|
self.assertEqual(cls() - 1, 234)
|
|
self.assertEqual(cls() * 1, 345)
|
|
self.assertEqual(cls() / 1, 456)
|
|
self.assertEqual(cls() % 1, 567)
|
|
|
|
with self.assertRaisesRegex(SystemError, "too many levels"):
|
|
_testlimitedcapi.type_from_slots("nested_over_limit")
|
|
|
|
cls = _testlimitedcapi.type_from_slots("nested_old")
|
|
self.assertEqual(cls() + 1, 123)
|
|
self.assertEqual(cls() - 1, 234)
|
|
|
|
cls = _testlimitedcapi.type_from_slots("nested_old_max")
|
|
self.assertEqual(cls() + 1, 123)
|
|
self.assertEqual(cls() - 1, 234)
|
|
self.assertEqual(cls() * 1, 345)
|
|
self.assertEqual(cls() / 1, 456)
|
|
self.assertEqual(cls() % 1, 567)
|
|
|
|
with self.assertRaisesRegex(SystemError, "too many levels"):
|
|
_testlimitedcapi.type_from_slots("nested_old_over_limit")
|
|
|
|
cls = _testlimitedcapi.type_from_slots("nested_pingpong")
|
|
self.assertEqual(cls() + 1, 123)
|
|
self.assertEqual(cls() - 1, 234)
|
|
self.assertEqual(cls() * 1, 345)
|
|
self.assertEqual(cls() / 1, 456)
|
|
self.assertEqual(cls() % 1, 567)
|
|
|
|
# Slot names aren't exposed to Python yet; see Include/slots_generated.h
|
|
# for the definitions.
|
|
|
|
@subTests("slot_number", [
|
|
*range(1, 83), # Original slots
|
|
*range(88, 92), # New compat slot values
|
|
*range(95, 99), # Slots for PyType_Spec fields
|
|
*range(107, 109), # Slots for PyType_FromMetaclass args
|
|
])
|
|
def test_null_slot_handling(self, slot_number):
|
|
if slot_number == 56:
|
|
# Py_tp_doc
|
|
return
|
|
elif slot_number == 72 or slot_number >= 95:
|
|
# Py_tp_members; all new slots
|
|
ctx = self.assertRaisesRegex(
|
|
SystemError, "NULL not allowed|must be positive")
|
|
ctx_old = ctx
|
|
else:
|
|
ctx = self.assertWarnsRegex(DeprecationWarning, "NULL")
|
|
ctx_old = contextlib.nullcontext()
|
|
with ctx:
|
|
_testlimitedcapi.type_from_null_slot(slot_number)
|
|
if slot_number < 95:
|
|
with ctx_old:
|
|
_testlimitedcapi.type_from_null_spec_slot(slot_number)
|
|
|
|
def test_repeat_warning(self):
|
|
with self.assertWarnsRegex(DeprecationWarning, "multiple"):
|
|
cls = _testlimitedcapi.type_from_slots("repeat_add")
|
|
self.assertEqual(cls() + 1, 456)
|
|
|
|
def test_repeat_error(self):
|
|
with self.assertRaisesRegex(SystemError, "multiple"):
|
|
cls = _testlimitedcapi.type_from_slots("repeat_module")
|
|
|
|
class ModuleSlotsTests(unittest.TestCase):
|
|
def test_basic_module_slots(self):
|
|
mod = _testlimitedcapi.module_from_slots("basic", FakeSpec())
|
|
self.assertIsInstance(mod, types.ModuleType)
|
|
|
|
def test_type_slot_in_module(self):
|
|
with self.assertRaisesRegex(SystemError, "invalid.* 95 .*Py_tp_name"):
|
|
_testlimitedcapi.module_from_slots("foreign_slot", FakeSpec())
|
|
|
|
def test_size_slots(self):
|
|
mod = _testlimitedcapi.module_from_slots("state_size", FakeSpec())
|
|
self.assertEqual(mod.state_size, 42)
|
|
|
|
def test_flag_slots(self):
|
|
mod = _testlimitedcapi.module_from_slots("multi_interp", FakeSpec())
|
|
|
|
def test_exec_slot(self):
|
|
mod = _testlimitedcapi.module_from_slots("exec", FakeSpec())
|
|
self.assertEqual(mod.exec_done, "yes")
|
|
|
|
def test_optional_end(self):
|
|
with self.assertRaisesRegex(SystemError, "invalid flags.*Py_slot_end"):
|
|
_testlimitedcapi.module_from_slots("optional_end", FakeSpec())
|
|
|
|
def test_invalid(self):
|
|
with self.assertRaisesRegex(SystemError, "Py_slot_invalid"):
|
|
_testlimitedcapi.module_from_slots("invalid", FakeSpec())
|
|
with self.assertRaisesRegex(SystemError, f"slot ID {0xfbad}"):
|
|
_testlimitedcapi.module_from_slots("invalid_fbad", FakeSpec())
|
|
|
|
mod = _testlimitedcapi.module_from_slots("optional_invalid", FakeSpec())
|
|
self.assertEqual(mod.exec_done, "yes")
|
|
|
|
mod = _testlimitedcapi.module_from_slots("optional_invalid_fbad", FakeSpec())
|
|
self.assertEqual(mod.exec_done, "yes")
|
|
|
|
@subTests("case_name", ["old_slot_numbers", "new_slot_numbers"])
|
|
def test_compat_slot_numbers(self, case_name):
|
|
mod = _testlimitedcapi.module_from_slots(case_name, FakeSpec())
|
|
self.assertEqual(mod.exec_done, "yes")
|
|
|
|
@subTests("case_name", ["old_slot_number_create", "new_slot_number_create"])
|
|
def test_compat_slot_number_create(self, case_name):
|
|
spec = FakeSpec()
|
|
mod = _testlimitedcapi.module_from_slots(case_name, spec)
|
|
self.assertIs(mod, spec)
|
|
|
|
@subTests("slot_number", [4, 87])
|
|
def test_compat_slot_number_gil(self, slot_number):
|
|
spec = FakeSpec()
|
|
gil_enabled = sys._is_gil_enabled()
|
|
mod = _testlimitedcapi.module_from_gil_slot(slot_number, spec)
|
|
self.assertEqual(gil_enabled, sys._is_gil_enabled())
|
|
|
|
def test_nonstatic_mod_methods(self):
|
|
with self.assertRaisesRegex(SystemError, "Py_mod_methods.*STATIC"):
|
|
_testlimitedcapi.module_from_slots("nonstatic_mod_methods",
|
|
FakeSpec())
|
|
|
|
def test_intptr_methods(self):
|
|
mod = _testlimitedcapi.module_from_slots("intptr_methods",
|
|
FakeSpec())
|
|
self.assertEqual(mod.type_from_slots.__name__, "type_from_slots")
|
|
|
|
def test_nested(self):
|
|
mod = _testlimitedcapi.module_from_slots("nested", FakeSpec())
|
|
self.assertEqual(mod.exec_done, "yes")
|
|
self.assertEqual(mod.__doc__, "doc")
|
|
|
|
mod = _testlimitedcapi.module_from_slots("nested_max", FakeSpec())
|
|
self.assertEqual(mod.exec_done, "yes")
|
|
self.assertEqual(mod.state_size, 53)
|
|
self.assertEqual(mod.__doc__, "doc")
|
|
|
|
with self.assertRaisesRegex(SystemError, "too many levels"):
|
|
_testlimitedcapi.module_from_slots("nested_over_limit", FakeSpec())
|
|
|
|
mod = _testlimitedcapi.module_from_slots("nested_old", FakeSpec())
|
|
self.assertEqual(mod.exec_done, "yes")
|
|
self.assertEqual(mod.__doc__, "doc")
|
|
|
|
mod = _testlimitedcapi.module_from_slots("nested_old_max", FakeSpec())
|
|
self.assertEqual(mod.exec_done, "yes")
|
|
self.assertEqual(mod.state_size, 53)
|
|
self.assertEqual(mod.__doc__, "doc")
|
|
|
|
with self.assertRaisesRegex(SystemError, "too many levels"):
|
|
_testlimitedcapi.module_from_slots("nested_old_over_limit", FakeSpec())
|
|
|
|
mod = _testlimitedcapi.module_from_slots("nested_pingpong", FakeSpec())
|
|
self.assertEqual(mod.exec_done, "yes")
|
|
self.assertEqual(mod.state_size, 53)
|
|
self.assertEqual(mod.__doc__, "doc")
|
|
|
|
def test_nested_nonstatic_from_def(self):
|
|
with self.assertRaisesRegex(SystemError, "must be static"):
|
|
_testcapi.module_from_def_nonstatic_nested(FakeSpec())
|
|
|
|
# Slot names aren't exposed to Python yet; see Include/slots_generated.h
|
|
# for the definitions.
|
|
|
|
@subTests("slot_number", [
|
|
*range(1, 5), # Old compat slot values
|
|
*range(84, 88), # New compat slot values
|
|
*range(100, 107), # Slots for PyModuleDef fields
|
|
*range(109, 111), # Slots new in 3.15
|
|
])
|
|
def test_null_slot_handling(self, slot_number):
|
|
if slot_number in {3, 86, 4, 87, 102}:
|
|
# Py_mod_mult.interp., Py_mod_gil, Py_mod_state_size
|
|
return
|
|
elif slot_number in {2, 85} or slot_number > 85:
|
|
# Py_mod_exec, new slots
|
|
ctx = self.assertRaisesRegex(SystemError, "NULL not allowed")
|
|
ctx_old = ctx
|
|
else:
|
|
ctx = self.assertWarnsRegex(DeprecationWarning, "NULL")
|
|
ctx_old = contextlib.nullcontext()
|
|
with ctx:
|
|
_testlimitedcapi.module_from_null_slot(slot_number, FakeSpec())
|
|
with ctx_old:
|
|
_testcapi.module_from_null_def_slot(slot_number,
|
|
FakeSpec())
|
|
|
|
def test_repeat_error(self):
|
|
with self.assertRaisesRegex(SystemError, "multiple"):
|
|
_testlimitedcapi.module_from_slots("repeat_create", FakeSpec())
|
|
with self.assertRaisesRegex(SystemError, "multiple"):
|
|
_testlimitedcapi.module_from_slots("repeat_exec", FakeSpec())
|
|
with self.assertRaisesRegex(SystemError, "multiple"):
|
|
_testlimitedcapi.module_from_slots("repeat_gil", FakeSpec())
|