Skip to content

Commit

Permalink
pythongh-107178: Add the C API tests for the Abstract Objects Layer
Browse files Browse the repository at this point in the history
Cover all the Mapping Protocol, almost all the Sequence Protocol
(except PySequence_Fast) and a part of the Object Protocol.

Move existing tests to Lib/test/test_capi/test_abstract.py and
Modules/_testcapi/abstract.c.
  • Loading branch information
serhiy-storchaka committed Jul 24, 2023
1 parent 1e50112 commit d553110
Show file tree
Hide file tree
Showing 10 changed files with 1,376 additions and 296 deletions.
722 changes: 722 additions & 0 deletions Lib/test/test_capi/test_abstract.py

Large diffs are not rendered by default.

131 changes: 0 additions & 131 deletions Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,137 +299,6 @@ def test_getitem_with_error(self):
def test_buildvalue_N(self):
_testcapi.test_buildvalue_N()

def test_mapping_keys_values_items(self):
class Mapping1(dict):
def keys(self):
return list(super().keys())
def values(self):
return list(super().values())
def items(self):
return list(super().items())
class Mapping2(dict):
def keys(self):
return tuple(super().keys())
def values(self):
return tuple(super().values())
def items(self):
return tuple(super().items())
dict_obj = {'foo': 1, 'bar': 2, 'spam': 3}

for mapping in [{}, OrderedDict(), Mapping1(), Mapping2(),
dict_obj, OrderedDict(dict_obj),
Mapping1(dict_obj), Mapping2(dict_obj)]:
self.assertListEqual(_testcapi.get_mapping_keys(mapping),
list(mapping.keys()))
self.assertListEqual(_testcapi.get_mapping_values(mapping),
list(mapping.values()))
self.assertListEqual(_testcapi.get_mapping_items(mapping),
list(mapping.items()))

def test_mapping_keys_values_items_bad_arg(self):
self.assertRaises(AttributeError, _testcapi.get_mapping_keys, None)
self.assertRaises(AttributeError, _testcapi.get_mapping_values, None)
self.assertRaises(AttributeError, _testcapi.get_mapping_items, None)

class BadMapping:
def keys(self):
return None
def values(self):
return None
def items(self):
return None
bad_mapping = BadMapping()
self.assertRaises(TypeError, _testcapi.get_mapping_keys, bad_mapping)
self.assertRaises(TypeError, _testcapi.get_mapping_values, bad_mapping)
self.assertRaises(TypeError, _testcapi.get_mapping_items, bad_mapping)

def test_mapping_has_key(self):
dct = {'a': 1}
self.assertTrue(_testcapi.mapping_has_key(dct, 'a'))
self.assertFalse(_testcapi.mapping_has_key(dct, 'b'))

class SubDict(dict):
pass

dct2 = SubDict({'a': 1})
self.assertTrue(_testcapi.mapping_has_key(dct2, 'a'))
self.assertFalse(_testcapi.mapping_has_key(dct2, 'b'))

def test_sequence_set_slice(self):
# Correct case:
data = [1, 2, 3, 4, 5]
data_copy = data.copy()

_testcapi.sequence_set_slice(data, 1, 3, [8, 9])
data_copy[1:3] = [8, 9]
self.assertEqual(data, data_copy)
self.assertEqual(data, [1, 8, 9, 4, 5])

# Custom class:
class Custom:
def __setitem__(self, index, value):
self.index = index
self.value = value

c = Custom()
_testcapi.sequence_set_slice(c, 0, 5, 'abc')
self.assertEqual(c.index, slice(0, 5))
self.assertEqual(c.value, 'abc')

# Immutable sequences must raise:
bad_seq1 = (1, 2, 3, 4)
with self.assertRaises(TypeError):
_testcapi.sequence_set_slice(bad_seq1, 1, 3, (8, 9))
self.assertEqual(bad_seq1, (1, 2, 3, 4))

bad_seq2 = 'abcd'
with self.assertRaises(TypeError):
_testcapi.sequence_set_slice(bad_seq2, 1, 3, 'xy')
self.assertEqual(bad_seq2, 'abcd')

# Not a sequence:
with self.assertRaises(TypeError):
_testcapi.sequence_set_slice(None, 1, 3, 'xy')

def test_sequence_del_slice(self):
# Correct case:
data = [1, 2, 3, 4, 5]
data_copy = data.copy()

_testcapi.sequence_del_slice(data, 1, 3)
del data_copy[1:3]
self.assertEqual(data, data_copy)
self.assertEqual(data, [1, 4, 5])

# Custom class:
class Custom:
def __delitem__(self, index):
self.index = index

c = Custom()
_testcapi.sequence_del_slice(c, 0, 5)
self.assertEqual(c.index, slice(0, 5))

# Immutable sequences must raise:
bad_seq1 = (1, 2, 3, 4)
with self.assertRaises(TypeError):
_testcapi.sequence_del_slice(bad_seq1, 1, 3)
self.assertEqual(bad_seq1, (1, 2, 3, 4))

bad_seq2 = 'abcd'
with self.assertRaises(TypeError):
_testcapi.sequence_del_slice(bad_seq2, 1, 3)
self.assertEqual(bad_seq2, 'abcd')

# Not a sequence:
with self.assertRaises(TypeError):
_testcapi.sequence_del_slice(None, 1, 3)

mapping = {1: 'a', 2: 'b', 3: 'c'}
with self.assertRaises(KeyError):
_testcapi.sequence_del_slice(mapping, 1, 3)
self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'})

@unittest.skipUnless(hasattr(_testcapi, 'negative_refcount'),
'need _testcapi.negative_refcount')
def test_negative_refcount(self):
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,8 +455,8 @@ def __init__(self):
self.attr = 1

a = A()
self.assertEqual(_testcapi.hasattr_string(a, "attr"), True)
self.assertEqual(_testcapi.hasattr_string(a, "noattr"), False)
self.assertEqual(_testcapi.object_hasattrstring(a, b"attr"), 1)
self.assertEqual(_testcapi.object_hasattrstring(a, b"noattr"), 0)
self.assertIsNone(sys.exception())

def testDel(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add the C API test for functions in the Mapping Protocol, the Sequence
Protocol and some functions in the Object Protocol.
2 changes: 1 addition & 1 deletion Modules/Setup.stdlib.in
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c

# Some testing modules MUST be built as shared libraries.
Expand Down
Loading

0 comments on commit d553110

Please sign in to comment.