| import unittest | |
| from collections import OrderedDict | |
| from test import support | |
| from test.support import import_helper | |
| _testcapi = import_helper.import_module('_testcapi') | |
| _testlimitedcapi = import_helper.import_module('_testlimitedcapi') | |
| from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX | |
| NULL = None | |
| class StrSubclass(str): | |
| pass | |
| class BytesSubclass(bytes): | |
| pass | |
| class WithStr: | |
| def __init__(self, value): | |
| self.value = value | |
| def __str__(self): | |
| return self.value | |
| class WithRepr: | |
| def __init__(self, value): | |
| self.value = value | |
| def __repr__(self): | |
| return self.value | |
| class WithBytes: | |
| def __init__(self, value): | |
| self.value = value | |
| def __bytes__(self): | |
| return self.value | |
| class TestObject: | |
| @property | |
| def evil(self): | |
| raise RuntimeError('do not get evil') | |
| @evil.setter | |
| def evil(self, value): | |
| raise RuntimeError('do not set evil') | |
| @evil.deleter | |
| def evil(self): | |
| raise RuntimeError('do not del evil') | |
| class ProxyGetItem: | |
| def __init__(self, obj): | |
| self.obj = obj | |
| def __getitem__(self, key): | |
| return self.obj[key] | |
| class ProxySetItem: | |
| def __init__(self, obj): | |
| self.obj = obj | |
| def __setitem__(self, key, value): | |
| self.obj[key] = value | |
| class ProxyDelItem: | |
| def __init__(self, obj): | |
| self.obj = obj | |
| def __delitem__(self, key): | |
| del self.obj[key] | |
| def gen(): | |
| yield 'a' | |
| yield 'b' | |
| yield 'c' | |
| class CAPITest(unittest.TestCase): | |
| def assertTypedEqual(self, actual, expected): | |
| self.assertIs(type(actual), type(expected)) | |
| self.assertEqual(actual, expected) | |
| def test_object_str(self): | |
| # Test PyObject_Str() | |
| object_str = _testlimitedcapi.object_str | |
| self.assertTypedEqual(object_str(''), '') | |
| self.assertTypedEqual(object_str('abc'), 'abc') | |
| self.assertTypedEqual(object_str('\U0001f40d'), '\U0001f40d') | |
| self.assertTypedEqual(object_str(StrSubclass('abc')), 'abc') | |
| self.assertTypedEqual(object_str(WithStr('abc')), 'abc') | |
| self.assertTypedEqual(object_str(WithStr(StrSubclass('abc'))), StrSubclass('abc')) | |
| self.assertTypedEqual(object_str(WithRepr('<abc>')), '<abc>') | |
| self.assertTypedEqual(object_str(WithRepr(StrSubclass('<abc>'))), StrSubclass('<abc>')) | |
| self.assertTypedEqual(object_str(NULL), '<NULL>') | |
| def test_object_repr(self): | |
| # Test PyObject_Repr() | |
| object_repr = _testlimitedcapi.object_repr | |
| self.assertTypedEqual(object_repr(''), "''") | |
| self.assertTypedEqual(object_repr('abc'), "'abc'") | |
| self.assertTypedEqual(object_repr('\U0001f40d'), "'\U0001f40d'") | |
| self.assertTypedEqual(object_repr(StrSubclass('abc')), "'abc'") | |
| self.assertTypedEqual(object_repr(WithRepr('<abc>')), '<abc>') | |
| self.assertTypedEqual(object_repr(WithRepr(StrSubclass('<abc>'))), StrSubclass('<abc>')) | |
| self.assertTypedEqual(object_repr(WithRepr('<\U0001f40d>')), '<\U0001f40d>') | |
| self.assertTypedEqual(object_repr(WithRepr(StrSubclass('<\U0001f40d>'))), StrSubclass('<\U0001f40d>')) | |
| self.assertTypedEqual(object_repr(NULL), '<NULL>') | |
| def test_object_ascii(self): | |
| # Test PyObject_ASCII() | |
| object_ascii = _testlimitedcapi.object_ascii | |
| self.assertTypedEqual(object_ascii(''), "''") | |
| self.assertTypedEqual(object_ascii('abc'), "'abc'") | |
| self.assertTypedEqual(object_ascii('\U0001f40d'), r"'\U0001f40d'") | |
| self.assertTypedEqual(object_ascii(StrSubclass('abc')), "'abc'") | |
| self.assertTypedEqual(object_ascii(WithRepr('<abc>')), '<abc>') | |
| self.assertTypedEqual(object_ascii(WithRepr(StrSubclass('<abc>'))), StrSubclass('<abc>')) | |
| self.assertTypedEqual(object_ascii(WithRepr('<\U0001f40d>')), r'<\U0001f40d>') | |
| self.assertTypedEqual(object_ascii(WithRepr(StrSubclass('<\U0001f40d>'))), r'<\U0001f40d>') | |
| self.assertTypedEqual(object_ascii(NULL), '<NULL>') | |
| def test_object_bytes(self): | |
| # Test PyObject_Bytes() | |
| object_bytes = _testlimitedcapi.object_bytes | |
| self.assertTypedEqual(object_bytes(b''), b'') | |
| self.assertTypedEqual(object_bytes(b'abc'), b'abc') | |
| self.assertTypedEqual(object_bytes(BytesSubclass(b'abc')), b'abc') | |
| self.assertTypedEqual(object_bytes(WithBytes(b'abc')), b'abc') | |
| self.assertTypedEqual(object_bytes(WithBytes(BytesSubclass(b'abc'))), BytesSubclass(b'abc')) | |
| self.assertTypedEqual(object_bytes(bytearray(b'abc')), b'abc') | |
| self.assertTypedEqual(object_bytes(memoryview(b'abc')), b'abc') | |
| self.assertTypedEqual(object_bytes([97, 98, 99]), b'abc') | |
| self.assertTypedEqual(object_bytes((97, 98, 99)), b'abc') | |
| self.assertTypedEqual(object_bytes(iter([97, 98, 99])), b'abc') | |
| self.assertRaises(TypeError, object_bytes, WithBytes(bytearray(b'abc'))) | |
| self.assertRaises(TypeError, object_bytes, WithBytes([97, 98, 99])) | |
| self.assertRaises(TypeError, object_bytes, 3) | |
| self.assertRaises(TypeError, object_bytes, 'abc') | |
| self.assertRaises(TypeError, object_bytes, object()) | |
| self.assertTypedEqual(object_bytes(NULL), b'<NULL>') | |
| def test_object_getattr(self): | |
| xgetattr = _testlimitedcapi.object_getattr | |
| obj = TestObject() | |
| obj.a = 11 | |
| setattr(obj, '\U0001f40d', 22) | |
| self.assertEqual(xgetattr(obj, 'a'), 11) | |
| self.assertRaises(AttributeError, xgetattr, obj, 'b') | |
| self.assertEqual(xgetattr(obj, '\U0001f40d'), 22) | |
| self.assertRaises(RuntimeError, xgetattr, obj, 'evil') | |
| self.assertRaises(TypeError, xgetattr, obj, 1) | |
| # CRASHES xgetattr(obj, NULL) | |
| # CRASHES xgetattr(NULL, 'a') | |
| def test_object_getattrstring(self): | |
| getattrstring = _testlimitedcapi.object_getattrstring | |
| obj = TestObject() | |
| obj.a = 11 | |
| setattr(obj, '\U0001f40d', 22) | |
| self.assertEqual(getattrstring(obj, b'a'), 11) | |
| self.assertRaises(AttributeError, getattrstring, obj, b'b') | |
| self.assertEqual(getattrstring(obj, '\U0001f40d'.encode()), 22) | |
| self.assertRaises(RuntimeError, getattrstring, obj, b'evil') | |
| self.assertRaises(UnicodeDecodeError, getattrstring, obj, b'\xff') | |
| # CRASHES getattrstring(obj, NULL) | |
| # CRASHES getattrstring(NULL, b'a') | |
| def test_object_getoptionalattr(self): | |
| getoptionalattr = _testcapi.object_getoptionalattr | |
| obj = TestObject() | |
| obj.a = 11 | |
| setattr(obj, '\U0001f40d', 22) | |
| self.assertEqual(getoptionalattr(obj, 'a'), 11) | |
| self.assertIs(getoptionalattr(obj, 'b'), AttributeError) | |
| self.assertEqual(getoptionalattr(obj, '\U0001f40d'), 22) | |
| self.assertRaises(RuntimeError, getoptionalattr, obj, 'evil') | |
| self.assertRaises(TypeError, getoptionalattr, obj, 1) | |
| # CRASHES getoptionalattr(obj, NULL) | |
| # CRASHES getoptionalattr(NULL, 'a') | |
| def test_object_getoptionalattrstring(self): | |
| getoptionalattrstring = _testcapi.object_getoptionalattrstring | |
| obj = TestObject() | |
| obj.a = 11 | |
| setattr(obj, '\U0001f40d', 22) | |
| self.assertEqual(getoptionalattrstring(obj, b'a'), 11) | |
| self.assertIs(getoptionalattrstring(obj, b'b'), AttributeError) | |
| self.assertEqual(getoptionalattrstring(obj, '\U0001f40d'.encode()), 22) | |
| self.assertRaises(RuntimeError, getoptionalattrstring, obj, b'evil') | |
| self.assertRaises(UnicodeDecodeError, getoptionalattrstring, obj, b'\xff') | |
| # CRASHES getoptionalattrstring(obj, NULL) | |
| # CRASHES getoptionalattrstring(NULL, b'a') | |
| def test_object_hasattr(self): | |
| xhasattr = _testlimitedcapi.object_hasattr | |
| obj = TestObject() | |
| obj.a = 1 | |
| setattr(obj, '\U0001f40d', 2) | |
| self.assertTrue(xhasattr(obj, 'a')) | |
| self.assertFalse(xhasattr(obj, 'b')) | |
| self.assertTrue(xhasattr(obj, '\U0001f40d')) | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(xhasattr(obj, 'evil')) | |
| self.assertEqual(cm.unraisable.exc_type, RuntimeError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| 'do not get evil') | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(xhasattr(obj, 1)) | |
| self.assertEqual(cm.unraisable.exc_type, TypeError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| "attribute name must be string, not 'int'") | |
| # CRASHES xhasattr(obj, NULL) | |
| # CRASHES xhasattr(NULL, 'a') | |
| def test_object_hasattrstring(self): | |
| hasattrstring = _testlimitedcapi.object_hasattrstring | |
| obj = TestObject() | |
| obj.a = 1 | |
| setattr(obj, '\U0001f40d', 2) | |
| self.assertTrue(hasattrstring(obj, b'a')) | |
| self.assertFalse(hasattrstring(obj, b'b')) | |
| self.assertTrue(hasattrstring(obj, '\U0001f40d'.encode())) | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(hasattrstring(obj, b'evil')) | |
| self.assertEqual(cm.unraisable.exc_type, RuntimeError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| 'do not get evil') | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(hasattrstring(obj, b'\xff')) | |
| self.assertEqual(cm.unraisable.exc_type, UnicodeDecodeError) | |
| self.assertRegex(str(cm.unraisable.exc_value), | |
| "'utf-8' codec can't decode") | |
| # CRASHES hasattrstring(obj, NULL) | |
| # CRASHES hasattrstring(NULL, b'a') | |
| def test_object_hasattrwitherror(self): | |
| xhasattr = _testcapi.object_hasattrwitherror | |
| obj = TestObject() | |
| obj.a = 1 | |
| setattr(obj, '\U0001f40d', 2) | |
| self.assertTrue(xhasattr(obj, 'a')) | |
| self.assertFalse(xhasattr(obj, 'b')) | |
| self.assertTrue(xhasattr(obj, '\U0001f40d')) | |
| self.assertRaises(RuntimeError, xhasattr, obj, 'evil') | |
| self.assertRaises(TypeError, xhasattr, obj, 1) | |
| # CRASHES xhasattr(obj, NULL) | |
| # CRASHES xhasattr(NULL, 'a') | |
| def test_object_hasattrstringwitherror(self): | |
| hasattrstring = _testcapi.object_hasattrstringwitherror | |
| obj = TestObject() | |
| obj.a = 1 | |
| setattr(obj, '\U0001f40d', 2) | |
| self.assertTrue(hasattrstring(obj, b'a')) | |
| self.assertFalse(hasattrstring(obj, b'b')) | |
| self.assertTrue(hasattrstring(obj, '\U0001f40d'.encode())) | |
| self.assertRaises(RuntimeError, hasattrstring, obj, b'evil') | |
| self.assertRaises(UnicodeDecodeError, hasattrstring, obj, b'\xff') | |
| # CRASHES hasattrstring(obj, NULL) | |
| # CRASHES hasattrstring(NULL, b'a') | |
| def test_object_setattr(self): | |
| xsetattr = _testlimitedcapi.object_setattr | |
| obj = TestObject() | |
| xsetattr(obj, 'a', 5) | |
| self.assertEqual(obj.a, 5) | |
| xsetattr(obj, '\U0001f40d', 8) | |
| self.assertEqual(getattr(obj, '\U0001f40d'), 8) | |
| # PyObject_SetAttr(obj, attr_name, NULL) removes the attribute | |
| xsetattr(obj, 'a', NULL) | |
| self.assertNotHasAttr(obj, 'a') | |
| self.assertRaises(AttributeError, xsetattr, obj, 'b', NULL) | |
| self.assertRaises(RuntimeError, xsetattr, obj, 'evil', NULL) | |
| self.assertRaises(RuntimeError, xsetattr, obj, 'evil', 'good') | |
| self.assertRaises(AttributeError, xsetattr, 42, 'a', 5) | |
| self.assertRaises(TypeError, xsetattr, obj, 1, 5) | |
| # CRASHES xsetattr(obj, NULL, 5) | |
| # CRASHES xsetattr(NULL, 'a', 5) | |
| def test_object_setattrstring(self): | |
| setattrstring = _testlimitedcapi.object_setattrstring | |
| obj = TestObject() | |
| setattrstring(obj, b'a', 5) | |
| self.assertEqual(obj.a, 5) | |
| setattrstring(obj, '\U0001f40d'.encode(), 8) | |
| self.assertEqual(getattr(obj, '\U0001f40d'), 8) | |
| # PyObject_SetAttrString(obj, attr_name, NULL) removes the attribute | |
| setattrstring(obj, b'a', NULL) | |
| self.assertNotHasAttr(obj, 'a') | |
| self.assertRaises(AttributeError, setattrstring, obj, b'b', NULL) | |
| self.assertRaises(RuntimeError, setattrstring, obj, b'evil', NULL) | |
| self.assertRaises(RuntimeError, setattrstring, obj, b'evil', 'good') | |
| self.assertRaises(AttributeError, setattrstring, 42, b'a', 5) | |
| self.assertRaises(TypeError, setattrstring, obj, 1, 5) | |
| self.assertRaises(UnicodeDecodeError, setattrstring, obj, b'\xff', 5) | |
| # CRASHES setattrstring(obj, NULL, 5) | |
| # CRASHES setattrstring(NULL, b'a', 5) | |
| def test_object_delattr(self): | |
| xdelattr = _testlimitedcapi.object_delattr | |
| obj = TestObject() | |
| obj.a = 1 | |
| setattr(obj, '\U0001f40d', 2) | |
| xdelattr(obj, 'a') | |
| self.assertNotHasAttr(obj, 'a') | |
| self.assertRaises(AttributeError, xdelattr, obj, 'b') | |
| xdelattr(obj, '\U0001f40d') | |
| self.assertNotHasAttr(obj, '\U0001f40d') | |
| self.assertRaises(AttributeError, xdelattr, 42, 'numerator') | |
| self.assertRaises(RuntimeError, xdelattr, obj, 'evil') | |
| self.assertRaises(TypeError, xdelattr, obj, 1) | |
| # CRASHES xdelattr(obj, NULL) | |
| # CRASHES xdelattr(NULL, 'a') | |
| def test_object_delattrstring(self): | |
| delattrstring = _testlimitedcapi.object_delattrstring | |
| obj = TestObject() | |
| obj.a = 1 | |
| setattr(obj, '\U0001f40d', 2) | |
| delattrstring(obj, b'a') | |
| self.assertNotHasAttr(obj, 'a') | |
| self.assertRaises(AttributeError, delattrstring, obj, b'b') | |
| delattrstring(obj, '\U0001f40d'.encode()) | |
| self.assertNotHasAttr(obj, '\U0001f40d') | |
| self.assertRaises(AttributeError, delattrstring, 42, b'numerator') | |
| self.assertRaises(RuntimeError, delattrstring, obj, b'evil') | |
| self.assertRaises(UnicodeDecodeError, delattrstring, obj, b'\xff') | |
| # CRASHES delattrstring(obj, NULL) | |
| # CRASHES delattrstring(NULL, b'a') | |
| def test_mapping_check(self): | |
| check = _testlimitedcapi.mapping_check | |
| self.assertTrue(check({1: 2})) | |
| self.assertTrue(check([1, 2])) | |
| self.assertTrue(check((1, 2))) | |
| self.assertTrue(check('abc')) | |
| self.assertTrue(check(b'abc')) | |
| self.assertFalse(check(42)) | |
| self.assertFalse(check(object())) | |
| self.assertFalse(check(NULL)) | |
| def test_mapping_size(self): | |
| for size in _testlimitedcapi.mapping_size, _testlimitedcapi.mapping_length: | |
| self.assertEqual(size({1: 2}), 1) | |
| self.assertEqual(size([1, 2]), 2) | |
| self.assertEqual(size((1, 2)), 2) | |
| self.assertEqual(size('abc'), 3) | |
| self.assertEqual(size(b'abc'), 3) | |
| self.assertRaises(TypeError, size, 42) | |
| self.assertRaises(TypeError, size, object()) | |
| self.assertRaises(SystemError, size, NULL) | |
| def test_object_getitem(self): | |
| getitem = _testlimitedcapi.object_getitem | |
| dct = {'a': 1, '\U0001f40d': 2} | |
| self.assertEqual(getitem(dct, 'a'), 1) | |
| self.assertRaises(KeyError, getitem, dct, 'b') | |
| self.assertEqual(getitem(dct, '\U0001f40d'), 2) | |
| dct2 = ProxyGetItem(dct) | |
| self.assertEqual(getitem(dct2, 'a'), 1) | |
| self.assertRaises(KeyError, getitem, dct2, 'b') | |
| self.assertEqual(getitem(['a', 'b', 'c'], 1), 'b') | |
| self.assertRaises(TypeError, getitem, 42, 'a') | |
| self.assertRaises(TypeError, getitem, {}, []) # unhashable | |
| self.assertRaises(SystemError, getitem, {}, NULL) | |
| self.assertRaises(IndexError, getitem, [], 1) | |
| self.assertRaises(TypeError, getitem, [], 'a') | |
| self.assertRaises(SystemError, getitem, NULL, 'a') | |
| def test_mapping_getitemstring(self): | |
| getitemstring = _testlimitedcapi.mapping_getitemstring | |
| dct = {'a': 1, '\U0001f40d': 2} | |
| self.assertEqual(getitemstring(dct, b'a'), 1) | |
| self.assertRaises(KeyError, getitemstring, dct, b'b') | |
| self.assertEqual(getitemstring(dct, '\U0001f40d'.encode()), 2) | |
| dct2 = ProxyGetItem(dct) | |
| self.assertEqual(getitemstring(dct2, b'a'), 1) | |
| self.assertRaises(KeyError, getitemstring, dct2, b'b') | |
| self.assertRaises(TypeError, getitemstring, 42, b'a') | |
| self.assertRaises(UnicodeDecodeError, getitemstring, {}, b'\xff') | |
| self.assertRaises(SystemError, getitemstring, {}, NULL) | |
| self.assertRaises(TypeError, getitemstring, [], b'a') | |
| self.assertRaises(SystemError, getitemstring, NULL, b'a') | |
| def test_mapping_getoptionalitem(self): | |
| getitem = _testcapi.mapping_getoptionalitem | |
| dct = {'a': 1, '\U0001f40d': 2} | |
| self.assertEqual(getitem(dct, 'a'), 1) | |
| self.assertEqual(getitem(dct, 'b'), KeyError) | |
| self.assertEqual(getitem(dct, '\U0001f40d'), 2) | |
| dct2 = ProxyGetItem(dct) | |
| self.assertEqual(getitem(dct2, 'a'), 1) | |
| self.assertEqual(getitem(dct2, 'b'), KeyError) | |
| frozendct = frozendict(dct) | |
| self.assertEqual(getitem(frozendct, 'a'), 1) | |
| self.assertEqual(getitem(frozendct, 'b'), KeyError) | |
| self.assertEqual(getitem(frozendct, '\U0001f40d'), 2) | |
| self.assertEqual(getitem(['a', 'b', 'c'], 1), 'b') | |
| self.assertRaises(TypeError, getitem, 42, 'a') | |
| self.assertRaises(TypeError, getitem, {}, []) # unhashable | |
| self.assertRaises(IndexError, getitem, [], 1) | |
| self.assertRaises(TypeError, getitem, [], 'a') | |
| # CRASHES getitem({}, NULL) | |
| # CRASHES getitem(NULL, 'a') | |
| def test_mapping_getoptionalitemstring(self): | |
| getitemstring = _testcapi.mapping_getoptionalitemstring | |
| dct = {'a': 1, '\U0001f40d': 2} | |
| self.assertEqual(getitemstring(dct, b'a'), 1) | |
| self.assertEqual(getitemstring(dct, b'b'), KeyError) | |
| self.assertEqual(getitemstring(dct, '\U0001f40d'.encode()), 2) | |
| dct2 = ProxyGetItem(dct) | |
| self.assertEqual(getitemstring(dct2, b'a'), 1) | |
| self.assertEqual(getitemstring(dct2, b'b'), KeyError) | |
| frozendct = frozendict(dct) | |
| self.assertEqual(getitemstring(frozendct, 'a'), 1) | |
| self.assertEqual(getitemstring(frozendct, 'b'), KeyError) | |
| self.assertEqual(getitemstring(frozendct, '\U0001f40d'.encode()), 2) | |
| self.assertRaises(TypeError, getitemstring, 42, b'a') | |
| self.assertRaises(UnicodeDecodeError, getitemstring, {}, b'\xff') | |
| self.assertRaises(SystemError, getitemstring, {}, NULL) | |
| self.assertRaises(TypeError, getitemstring, [], b'a') | |
| # CRASHES getitemstring(NULL, b'a') | |
| def test_mapping_haskey(self): | |
| haskey = _testlimitedcapi.mapping_haskey | |
| dct = {'a': 1, '\U0001f40d': 2} | |
| self.assertTrue(haskey(dct, 'a')) | |
| self.assertFalse(haskey(dct, 'b')) | |
| self.assertTrue(haskey(dct, '\U0001f40d')) | |
| dct2 = ProxyGetItem(dct) | |
| self.assertTrue(haskey(dct2, 'a')) | |
| self.assertFalse(haskey(dct2, 'b')) | |
| self.assertTrue(haskey(['a', 'b', 'c'], 1)) | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskey(42, 'a')) | |
| self.assertEqual(cm.unraisable.exc_type, TypeError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| "'int' object is not subscriptable") | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskey({}, [])) | |
| self.assertEqual(cm.unraisable.exc_type, TypeError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| "cannot use 'list' as a dict key " | |
| "(unhashable type: 'list')") | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskey([], 1)) | |
| self.assertEqual(cm.unraisable.exc_type, IndexError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| 'list index out of range') | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskey([], 'a')) | |
| self.assertEqual(cm.unraisable.exc_type, TypeError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| 'list indices must be integers or slices, not str') | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskey({}, NULL)) | |
| self.assertEqual(cm.unraisable.exc_type, SystemError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| 'null argument to internal routine') | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskey(NULL, 'a')) | |
| self.assertEqual(cm.unraisable.exc_type, SystemError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| 'null argument to internal routine') | |
| def test_mapping_haskeystring(self): | |
| haskeystring = _testlimitedcapi.mapping_haskeystring | |
| dct = {'a': 1, '\U0001f40d': 2} | |
| self.assertTrue(haskeystring(dct, b'a')) | |
| self.assertFalse(haskeystring(dct, b'b')) | |
| self.assertTrue(haskeystring(dct, '\U0001f40d'.encode())) | |
| dct2 = ProxyGetItem(dct) | |
| self.assertTrue(haskeystring(dct2, b'a')) | |
| self.assertFalse(haskeystring(dct2, b'b')) | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskeystring(42, b'a')) | |
| self.assertEqual(cm.unraisable.exc_type, TypeError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| "'int' object is not subscriptable") | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskeystring({}, b'\xff')) | |
| self.assertEqual(cm.unraisable.exc_type, UnicodeDecodeError) | |
| self.assertRegex(str(cm.unraisable.exc_value), | |
| "'utf-8' codec can't decode") | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskeystring({}, NULL)) | |
| self.assertEqual(cm.unraisable.exc_type, SystemError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| "null argument to internal routine") | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskeystring([], b'a')) | |
| self.assertEqual(cm.unraisable.exc_type, TypeError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| 'list indices must be integers or slices, not str') | |
| with support.catch_unraisable_exception() as cm: | |
| self.assertFalse(haskeystring(NULL, b'a')) | |
| self.assertEqual(cm.unraisable.exc_type, SystemError) | |
| self.assertEqual(str(cm.unraisable.exc_value), | |
| "null argument to internal routine") | |
| def test_mapping_haskeywitherror(self): | |
| haskey = _testlimitedcapi.mapping_haskeywitherror | |
| dct = {'a': 1, '\U0001f40d': 2} | |
| self.assertTrue(haskey(dct, 'a')) | |
| self.assertFalse(haskey(dct, 'b')) | |
| self.assertTrue(haskey(dct, '\U0001f40d')) | |
| dct2 = ProxyGetItem(dct) | |
| self.assertTrue(haskey(dct2, 'a')) | |
| self.assertFalse(haskey(dct2, 'b')) | |
| self.assertTrue(haskey(['a', 'b', 'c'], 1)) | |
| self.assertRaises(TypeError, haskey, 42, 'a') | |
| self.assertRaises(TypeError, haskey, {}, []) # unhashable | |
| self.assertRaises(IndexError, haskey, [], 1) | |
| self.assertRaises(TypeError, haskey, [], 'a') | |
| # CRASHES haskey({}, NULL)) | |
| # CRASHES haskey(NULL, 'a')) | |
| def test_mapping_haskeystringwitherror(self): | |
| haskeystring = _testlimitedcapi.mapping_haskeystringwitherror | |
| dct = {'a': 1, '\U0001f40d': 2} | |
| self.assertTrue(haskeystring(dct, b'a')) | |
| self.assertFalse(haskeystring(dct, b'b')) | |
| self.assertTrue(haskeystring(dct, '\U0001f40d'.encode())) | |
| dct2 = ProxyGetItem(dct) | |
| self.assertTrue(haskeystring(dct2, b'a')) | |
| self.assertFalse(haskeystring(dct2, b'b')) | |
| self.assertRaises(TypeError, haskeystring, 42, b'a') | |
| self.assertRaises(UnicodeDecodeError, haskeystring, {}, b'\xff') | |
| self.assertRaises(SystemError, haskeystring, {}, NULL) | |
| self.assertRaises(TypeError, haskeystring, [], b'a') | |
| # CRASHES haskeystring(NULL, b'a') | |
| def test_object_setitem(self): | |
| setitem = _testlimitedcapi.object_setitem | |
| dct = {} | |
| setitem(dct, 'a', 5) | |
| self.assertEqual(dct, {'a': 5}) | |
| setitem(dct, '\U0001f40d', 8) | |
| self.assertEqual(dct, {'a': 5, '\U0001f40d': 8}) | |
| dct = {} | |
| dct2 = ProxySetItem(dct) | |
| setitem(dct2, 'a', 5) | |
| self.assertEqual(dct, {'a': 5}) | |
| lst = ['a', 'b', 'c'] | |
| setitem(lst, 1, 'x') | |
| self.assertEqual(lst, ['a', 'x', 'c']) | |
| self.assertRaises(TypeError, setitem, 42, 'a', 5) | |
| self.assertRaises(TypeError, setitem, {}, [], 5) # unhashable | |
| self.assertRaises(SystemError, setitem, {}, NULL, 5) | |
| self.assertRaises(SystemError, setitem, {}, 'a', NULL) | |
| self.assertRaises(IndexError, setitem, [], 1, 5) | |
| self.assertRaises(TypeError, setitem, [], 'a', 5) | |
| self.assertRaises(TypeError, setitem, (), 1, 5) | |
| self.assertRaises(SystemError, setitem, NULL, 'a', 5) | |
| def test_mapping_setitemstring(self): | |
| setitemstring = _testlimitedcapi.mapping_setitemstring | |
| dct = {} | |
| setitemstring(dct, b'a', 5) | |
| self.assertEqual(dct, {'a': 5}) | |
| setitemstring(dct, '\U0001f40d'.encode(), 8) | |
| self.assertEqual(dct, {'a': 5, '\U0001f40d': 8}) | |
| dct = {} | |
| dct2 = ProxySetItem(dct) | |
| setitemstring(dct2, b'a', 5) | |
| self.assertEqual(dct, {'a': 5}) | |
| self.assertRaises(TypeError, setitemstring, 42, b'a', 5) | |
| self.assertRaises(UnicodeDecodeError, setitemstring, {}, b'\xff', 5) | |
| self.assertRaises(SystemError, setitemstring, {}, NULL, 5) | |
| self.assertRaises(SystemError, setitemstring, {}, b'a', NULL) | |
| self.assertRaises(TypeError, setitemstring, [], b'a', 5) | |
| self.assertRaises(SystemError, setitemstring, NULL, b'a', 5) | |
| def test_object_delitem(self): | |
| for delitem in _testlimitedcapi.object_delitem, _testlimitedcapi.mapping_delitem: | |
| dct = {'a': 1, 'c': 2, '\U0001f40d': 3} | |
| delitem(dct, 'a') | |
| self.assertEqual(dct, {'c': 2, '\U0001f40d': 3}) | |
| self.assertRaises(KeyError, delitem, dct, 'b') | |
| delitem(dct, '\U0001f40d') | |
| self.assertEqual(dct, {'c': 2}) | |
| dct = {'a': 1, 'c': 2} | |
| dct2 = ProxyDelItem(dct) | |
| delitem(dct2, 'a') | |
| self.assertEqual(dct, {'c': 2}) | |
| self.assertRaises(KeyError, delitem, dct2, 'b') | |
| lst = ['a', 'b', 'c'] | |
| delitem(lst, 1) | |
| self.assertEqual(lst, ['a', 'c']) | |
| self.assertRaises(TypeError, delitem, 42, 'a') | |
| self.assertRaises(TypeError, delitem, {}, []) # unhashable | |
| self.assertRaises(SystemError, delitem, {}, NULL) | |
| self.assertRaises(IndexError, delitem, [], 1) | |
| self.assertRaises(TypeError, delitem, [], 'a') | |
| self.assertRaises(SystemError, delitem, NULL, 'a') | |
| def test_mapping_delitemstring(self): | |
| delitemstring = _testlimitedcapi.mapping_delitemstring | |
| dct = {'a': 1, 'c': 2, '\U0001f40d': 3} | |
| delitemstring(dct, b'a') | |
| self.assertEqual(dct, {'c': 2, '\U0001f40d': 3}) | |
| self.assertRaises(KeyError, delitemstring, dct, b'b') | |
| delitemstring(dct, '\U0001f40d'.encode()) | |
| self.assertEqual(dct, {'c': 2}) | |
| dct = {'a': 1, 'c': 2} | |
| dct2 = ProxyDelItem(dct) | |
| delitemstring(dct2, b'a') | |
| self.assertEqual(dct, {'c': 2}) | |
| self.assertRaises(KeyError, delitemstring, dct2, b'b') | |
| self.assertRaises(TypeError, delitemstring, 42, b'a') | |
| self.assertRaises(UnicodeDecodeError, delitemstring, {}, b'\xff') | |
| self.assertRaises(SystemError, delitemstring, {}, NULL) | |
| self.assertRaises(TypeError, delitemstring, [], b'a') | |
| self.assertRaises(SystemError, delitemstring, NULL, b'a') | |
| def test_mapping_keys_valuesitems(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(), | |
| frozendict(), | |
| dict_obj, OrderedDict(dict_obj), | |
| Mapping1(dict_obj), Mapping2(dict_obj), | |
| frozendict(dict_obj)]: | |
| self.assertListEqual(_testlimitedcapi.mapping_keys(mapping), | |
| list(mapping.keys())) | |
| self.assertListEqual(_testlimitedcapi.mapping_values(mapping), | |
| list(mapping.values())) | |
| self.assertListEqual(_testlimitedcapi.mapping_items(mapping), | |
| list(mapping.items())) | |
| def test_mapping_keys_valuesitems_bad_arg(self): | |
| self.assertRaises(AttributeError, _testlimitedcapi.mapping_keys, object()) | |
| self.assertRaises(AttributeError, _testlimitedcapi.mapping_values, object()) | |
| self.assertRaises(AttributeError, _testlimitedcapi.mapping_items, object()) | |
| self.assertRaises(AttributeError, _testlimitedcapi.mapping_keys, []) | |
| self.assertRaises(AttributeError, _testlimitedcapi.mapping_values, []) | |
| self.assertRaises(AttributeError, _testlimitedcapi.mapping_items, []) | |
| self.assertRaises(SystemError, _testlimitedcapi.mapping_keys, NULL) | |
| self.assertRaises(SystemError, _testlimitedcapi.mapping_values, NULL) | |
| self.assertRaises(SystemError, _testlimitedcapi.mapping_items, NULL) | |
| class BadMapping: | |
| def keys(self): | |
| return None | |
| def values(self): | |
| return None | |
| def items(self): | |
| return None | |
| bad_mapping = BadMapping() | |
| self.assertRaises(TypeError, _testlimitedcapi.mapping_keys, bad_mapping) | |
| self.assertRaises(TypeError, _testlimitedcapi.mapping_values, bad_mapping) | |
| self.assertRaises(TypeError, _testlimitedcapi.mapping_items, bad_mapping) | |
| def test_sequence_check(self): | |
| check = _testlimitedcapi.sequence_check | |
| self.assertFalse(check({1: 2})) | |
| self.assertTrue(check([1, 2])) | |
| self.assertTrue(check((1, 2))) | |
| self.assertTrue(check('abc')) | |
| self.assertTrue(check(b'abc')) | |
| self.assertFalse(check(42)) | |
| self.assertFalse(check(object())) | |
| # CRASHES check(NULL) | |
| def test_sequence_size(self): | |
| for size in _testlimitedcapi.sequence_size, _testlimitedcapi.sequence_length: | |
| self.assertEqual(size([1, 2]), 2) | |
| self.assertEqual(size((1, 2)), 2) | |
| self.assertEqual(size('abc'), 3) | |
| self.assertEqual(size(b'abc'), 3) | |
| self.assertRaises(TypeError, size, {}) | |
| self.assertRaises(TypeError, size, 42) | |
| self.assertRaises(TypeError, size, object()) | |
| self.assertRaises(SystemError, size, NULL) | |
| def test_sequence_getitem(self): | |
| getitem = _testlimitedcapi.sequence_getitem | |
| lst = ['a', 'b', 'c'] | |
| self.assertEqual(getitem(lst, 1), 'b') | |
| self.assertEqual(getitem(lst, -1), 'c') | |
| self.assertRaises(IndexError, getitem, lst, 3) | |
| self.assertRaises(IndexError, getitem, lst, PY_SSIZE_T_MAX) | |
| self.assertRaises(IndexError, getitem, lst, PY_SSIZE_T_MIN) | |
| self.assertRaises(TypeError, getitem, 42, 1) | |
| self.assertRaises(TypeError, getitem, {}, 1) | |
| self.assertRaises(SystemError, getitem, NULL, 1) | |
| def test_sequence_concat(self): | |
| concat = _testlimitedcapi.sequence_concat | |
| self.assertEqual(concat(['a', 'b'], [1, 2]), ['a', 'b', 1, 2]) | |
| self.assertEqual(concat(('a', 'b'), (1, 2)), ('a', 'b', 1, 2)) | |
| self.assertRaises(TypeError, concat, [], ()) | |
| self.assertRaises(TypeError, concat, (), []) | |
| self.assertRaises(TypeError, concat, [], 42) | |
| self.assertRaises(TypeError, concat, 42, []) | |
| self.assertRaises(TypeError, concat, 42, 43) | |
| self.assertRaises(SystemError, concat, [], NULL) | |
| self.assertRaises(SystemError, concat, NULL, []) | |
| def test_sequence_repeat(self): | |
| repeat = _testlimitedcapi.sequence_repeat | |
| self.assertEqual(repeat(['a', 'b'], 2), ['a', 'b', 'a', 'b']) | |
| self.assertEqual(repeat(('a', 'b'), 2), ('a', 'b', 'a', 'b')) | |
| self.assertEqual(repeat(['a', 'b'], 0), []) | |
| self.assertEqual(repeat(['a', 'b'], -1), []) | |
| self.assertEqual(repeat(['a', 'b'], PY_SSIZE_T_MIN), []) | |
| self.assertEqual(repeat([], PY_SSIZE_T_MAX), []) | |
| self.assertRaises(MemoryError, repeat, ['a', 'b'], PY_SSIZE_T_MAX) | |
| self.assertRaises(TypeError, repeat, set(), 2) | |
| self.assertRaises(TypeError, repeat, 42, 2) | |
| self.assertRaises(SystemError, repeat, NULL, 2) | |
| def test_sequence_inplaceconcat(self): | |
| inplaceconcat = _testlimitedcapi.sequence_inplaceconcat | |
| lst = ['a', 'b'] | |
| res = inplaceconcat(lst, [1, 2]) | |
| self.assertEqual(res, ['a', 'b', 1, 2]) | |
| self.assertIs(res, lst) | |
| lst = ['a', 'b'] | |
| res = inplaceconcat(lst, (1, 2)) | |
| self.assertEqual(res, ['a', 'b', 1, 2]) | |
| self.assertIs(res, lst) | |
| self.assertEqual(inplaceconcat(('a', 'b'), (1, 2)), ('a', 'b', 1, 2)) | |
| self.assertRaises(TypeError, inplaceconcat, (), []) | |
| self.assertRaises(TypeError, inplaceconcat, [], 42) | |
| self.assertRaises(TypeError, inplaceconcat, 42, []) | |
| self.assertRaises(TypeError, inplaceconcat, 42, 43) | |
| self.assertRaises(SystemError, inplaceconcat, [], NULL) | |
| self.assertRaises(SystemError, inplaceconcat, NULL, []) | |
| def test_sequence_inplacerepeat(self): | |
| inplacerepeat = _testlimitedcapi.sequence_inplacerepeat | |
| lst = ['a', 'b'] | |
| res = inplacerepeat(lst, 2) | |
| self.assertEqual(res, ['a', 'b', 'a', 'b']) | |
| self.assertIs(res, lst) | |
| self.assertEqual(inplacerepeat(('a', 'b'), 2), ('a', 'b', 'a', 'b')) | |
| self.assertEqual(inplacerepeat(['a', 'b'], 0), []) | |
| self.assertEqual(inplacerepeat(['a', 'b'], -1), []) | |
| self.assertEqual(inplacerepeat(['a', 'b'], PY_SSIZE_T_MIN), []) | |
| self.assertEqual(inplacerepeat([], PY_SSIZE_T_MAX), []) | |
| self.assertRaises(MemoryError, inplacerepeat, ['a', 'b'], PY_SSIZE_T_MAX) | |
| self.assertRaises(TypeError, inplacerepeat, set(), 2) | |
| self.assertRaises(TypeError, inplacerepeat, 42, 2) | |
| self.assertRaises(SystemError, inplacerepeat, NULL, 2) | |
| def test_sequence_setitem(self): | |
| setitem = _testlimitedcapi.sequence_setitem | |
| lst = ['a', 'b', 'c'] | |
| setitem(lst, 1, 'x') | |
| self.assertEqual(lst, ['a', 'x', 'c']) | |
| setitem(lst, -1, 'y') | |
| self.assertEqual(lst, ['a', 'x', 'y']) | |
| setitem(lst, 0, NULL) | |
| self.assertEqual(lst, ['x', 'y']) | |
| self.assertRaises(IndexError, setitem, lst, 3, 'x') | |
| self.assertRaises(IndexError, setitem, lst, PY_SSIZE_T_MAX, 'x') | |
| self.assertRaises(IndexError, setitem, lst, PY_SSIZE_T_MIN, 'x') | |
| self.assertRaises(TypeError, setitem, 42, 1, 'x') | |
| self.assertRaises(TypeError, setitem, {}, 1, 'x') | |
| self.assertRaises(SystemError, setitem, NULL, 1, 'x') | |
| def test_sequence_delitem(self): | |
| delitem = _testlimitedcapi.sequence_delitem | |
| lst = ['a', 'b', 'c'] | |
| delitem(lst, 1) | |
| self.assertEqual(lst, ['a', 'c']) | |
| delitem(lst, -1) | |
| self.assertEqual(lst, ['a']) | |
| self.assertRaises(IndexError, delitem, lst, 3) | |
| self.assertRaises(IndexError, delitem, lst, PY_SSIZE_T_MAX) | |
| self.assertRaises(IndexError, delitem, lst, PY_SSIZE_T_MIN) | |
| self.assertRaises(TypeError, delitem, 42, 1) | |
| self.assertRaises(TypeError, delitem, {}, 1) | |
| self.assertRaises(SystemError, delitem, NULL, 1) | |
| def test_sequence_setslice(self): | |
| setslice = _testlimitedcapi.sequence_setslice | |
| # Correct case: | |
| for start in [*range(-6, 7), PY_SSIZE_T_MIN, PY_SSIZE_T_MAX]: | |
| for stop in [*range(-6, 7), PY_SSIZE_T_MIN, PY_SSIZE_T_MAX]: | |
| data = [1, 2, 3, 4, 5] | |
| data_copy = [1, 2, 3, 4, 5] | |
| setslice(data, start, stop, [8, 9]) | |
| data_copy[start:stop] = [8, 9] | |
| self.assertEqual(data, data_copy) | |
| data = [1, 2, 3, 4, 5] | |
| data_copy = [1, 2, 3, 4, 5] | |
| setslice(data, start, stop, NULL) | |
| del data_copy[start:stop] | |
| self.assertEqual(data, data_copy) | |
| # Custom class: | |
| class Custom: | |
| def __setitem__(self, index, value): | |
| self.index = index | |
| self.value = value | |
| c = Custom() | |
| setslice(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) | |
| self.assertRaises(TypeError, setslice, bad_seq1, 1, 3, (8, 9)) | |
| self.assertEqual(bad_seq1, (1, 2, 3, 4)) | |
| bad_seq2 = 'abcd' | |
| self.assertRaises(TypeError, setslice, bad_seq2, 1, 3, 'xy') | |
| self.assertEqual(bad_seq2, 'abcd') | |
| # Not a sequence: | |
| self.assertRaises(TypeError, setslice, object(), 1, 3, 'xy') | |
| self.assertRaises(SystemError, setslice, NULL, 1, 3, 'xy') | |
| def test_sequence_delslice(self): | |
| delslice = _testlimitedcapi.sequence_delslice | |
| # Correct case: | |
| for start in [*range(-6, 7), PY_SSIZE_T_MIN, PY_SSIZE_T_MAX]: | |
| for stop in [*range(-6, 7), PY_SSIZE_T_MIN, PY_SSIZE_T_MAX]: | |
| data = [1, 2, 3, 4, 5] | |
| data_copy = [1, 2, 3, 4, 5] | |
| delslice(data, start, stop) | |
| del data_copy[start:stop] | |
| self.assertEqual(data, data_copy) | |
| # Custom class: | |
| class Custom: | |
| def __delitem__(self, index): | |
| self.index = index | |
| c = Custom() | |
| delslice(c, 0, 5) | |
| self.assertEqual(c.index, slice(0, 5)) | |
| # Immutable sequences must raise: | |
| bad_seq1 = (1, 2, 3, 4) | |
| self.assertRaises(TypeError, delslice, bad_seq1, 1, 3) | |
| self.assertEqual(bad_seq1, (1, 2, 3, 4)) | |
| bad_seq2 = 'abcd' | |
| self.assertRaises(TypeError, delslice, bad_seq2, 1, 3) | |
| self.assertEqual(bad_seq2, 'abcd') | |
| # Not a sequence: | |
| self.assertRaises(TypeError, delslice, object(), 1, 3) | |
| self.assertRaises(SystemError, delslice, NULL, 1, 3) | |
| mapping = {1: 'a', 2: 'b', 3: 'c'} | |
| self.assertRaises(KeyError, delslice, mapping, 1, 3) | |
| self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'}) | |
| def test_sequence_count(self): | |
| count = _testlimitedcapi.sequence_count | |
| lst = ['a', 'b', 'a'] | |
| self.assertEqual(count(lst, 'a'), 2) | |
| self.assertEqual(count(lst, 'c'), 0) | |
| self.assertEqual(count(iter(lst), 'a'), 2) | |
| self.assertEqual(count(iter(lst), 'c'), 0) | |
| self.assertEqual(count({'a': 2}, 'a'), 1) | |
| self.assertRaises(TypeError, count, 42, 'a') | |
| self.assertRaises(SystemError, count, [], NULL) | |
| self.assertRaises(SystemError, count, [1], NULL) | |
| self.assertRaises(SystemError, count, NULL, 'a') | |
| def test_sequence_contains(self): | |
| contains = _testlimitedcapi.sequence_contains | |
| lst = ['a', 'b', 'a'] | |
| self.assertEqual(contains(lst, 'a'), 1) | |
| self.assertEqual(contains(lst, 'c'), 0) | |
| self.assertEqual(contains(iter(lst), 'a'), 1) | |
| self.assertEqual(contains(iter(lst), 'c'), 0) | |
| self.assertEqual(contains({'a': 2}, 'a'), 1) | |
| # XXX Only for empty sequences. Should be SystemError? | |
| self.assertEqual(contains([], NULL), 0) | |
| self.assertRaises(TypeError, contains, 42, 'a') | |
| self.assertRaises(SystemError, contains, [1], NULL) | |
| # CRASHES contains({}, NULL) | |
| # CRASHES contains(set(), NULL) | |
| # CRASHES contains(NULL, 'a') | |
| def test_sequence_index(self): | |
| index = _testlimitedcapi.sequence_index | |
| lst = ['a', 'b', 'a'] | |
| self.assertEqual(index(lst, 'a'), 0) | |
| self.assertEqual(index(lst, 'b'), 1) | |
| self.assertRaises(ValueError, index, lst, 'c') | |
| self.assertEqual(index(iter(lst), 'a'), 0) | |
| self.assertEqual(index(iter(lst), 'b'), 1) | |
| self.assertRaises(ValueError, index, iter(lst), 'c') | |
| dct = {'a': 2, 'b': 3} | |
| self.assertEqual(index(dct, 'a'), 0) | |
| self.assertEqual(index(dct, 'b'), 1) | |
| self.assertRaises(ValueError, index, dct, 'c') | |
| self.assertRaises(TypeError, index, 42, 'a') | |
| self.assertRaises(SystemError, index, [], NULL) | |
| self.assertRaises(SystemError, index, [1], NULL) | |
| self.assertRaises(SystemError, index, NULL, 'a') | |
| def test_sequence_list(self): | |
| xlist = _testlimitedcapi.sequence_list | |
| self.assertEqual(xlist(['a', 'b', 'c']), ['a', 'b', 'c']) | |
| self.assertEqual(xlist(('a', 'b', 'c')), ['a', 'b', 'c']) | |
| self.assertEqual(xlist(iter(['a', 'b', 'c'])), ['a', 'b', 'c']) | |
| self.assertEqual(xlist(gen()), ['a', 'b', 'c']) | |
| self.assertRaises(TypeError, xlist, 42) | |
| self.assertRaises(SystemError, xlist, NULL) | |
| def test_sequence_tuple(self): | |
| xtuple = _testlimitedcapi.sequence_tuple | |
| self.assertEqual(xtuple(['a', 'b', 'c']), ('a', 'b', 'c')) | |
| self.assertEqual(xtuple(('a', 'b', 'c')), ('a', 'b', 'c')) | |
| self.assertEqual(xtuple(iter(['a', 'b', 'c'])), ('a', 'b', 'c')) | |
| self.assertEqual(xtuple(gen()), ('a', 'b', 'c')) | |
| self.assertRaises(TypeError, xtuple, 42) | |
| self.assertRaises(SystemError, xtuple, NULL) | |
| def test_sequence_fast(self): | |
| # Test PySequence_Fast() | |
| sequence_fast = _testlimitedcapi.sequence_fast | |
| sequence_fast_get_size = _testcapi.sequence_fast_get_size | |
| sequence_fast_get_item = _testcapi.sequence_fast_get_item | |
| tpl = ('a', 'b', 'c') | |
| fast = sequence_fast(tpl, "err_msg") | |
| self.assertIs(fast, tpl) | |
| self.assertEqual(sequence_fast_get_size(fast), 3) | |
| self.assertEqual(sequence_fast_get_item(fast, 2), 'c') | |
| lst = ['a', 'b', 'c'] | |
| fast = sequence_fast(lst, "err_msg") | |
| self.assertIs(fast, lst) | |
| self.assertEqual(sequence_fast_get_size(fast), 3) | |
| self.assertEqual(sequence_fast_get_item(fast, 2), 'c') | |
| it = iter(['A', 'B']) | |
| fast = sequence_fast(it, "err_msg") | |
| self.assertEqual(fast, ['A', 'B']) | |
| self.assertEqual(sequence_fast_get_size(fast), 2) | |
| self.assertEqual(sequence_fast_get_item(fast, 1), 'B') | |
| text = 'fast' | |
| fast = sequence_fast(text, "err_msg") | |
| self.assertEqual(fast, ['f', 'a', 's', 't']) | |
| self.assertEqual(sequence_fast_get_size(fast), 4) | |
| self.assertEqual(sequence_fast_get_item(fast, 0), 'f') | |
| self.assertRaises(TypeError, sequence_fast, 42, "err_msg") | |
| self.assertRaises(SystemError, sequence_fast, NULL, "err_msg") | |
| # CRASHES sequence_fast_get_size(NULL) | |
| # CRASHES sequence_fast_get_item(NULL, 0) | |
| def test_object_generichash(self): | |
| # Test PyObject_GenericHash() | |
| generichash = _testcapi.object_generichash | |
| for obj in object(), 1, 'string', []: | |
| self.assertEqual(generichash(obj), object.__hash__(obj)) | |
| def run_iter_api_test(self, next_func): | |
| for data in (), [], (1, 2, 3), [1 , 2, 3], "123": | |
| with self.subTest(data=data): | |
| items = [] | |
| it = iter(data) | |
| while (item := next_func(it)) is not None: | |
| items.append(item) | |
| self.assertEqual(items, list(data)) | |
| class Broken: | |
| def __init__(self): | |
| self.count = 0 | |
| def __next__(self): | |
| if self.count < 3: | |
| self.count += 1 | |
| return self.count | |
| else: | |
| raise TypeError('bad type') | |
| it = Broken() | |
| self.assertEqual(next_func(it), 1) | |
| self.assertEqual(next_func(it), 2) | |
| self.assertEqual(next_func(it), 3) | |
| with self.assertRaisesRegex(TypeError, 'bad type'): | |
| next_func(it) | |
| def test_iter_next(self): | |
| from _testcapi import PyIter_Next | |
| self.run_iter_api_test(PyIter_Next) | |
| # CRASHES PyIter_Next(10) | |
| def test_iter_nextitem(self): | |
| from _testcapi import PyIter_NextItem | |
| self.run_iter_api_test(PyIter_NextItem) | |
| regex = "expected.*iterator.*got.*'int'" | |
| with self.assertRaisesRegex(TypeError, regex): | |
| PyIter_NextItem(10) | |
| def test_object_setattr_null_exc(self): | |
| class Obj: | |
| pass | |
| obj = Obj() | |
| obj.attr = 123 | |
| exc = ValueError("error") | |
| with self.assertRaises(SystemError) as cm: | |
| _testcapi.object_setattr_null_exc(obj, 'attr', exc) | |
| self.assertIs(cm.exception.__context__, exc) | |
| self.assertIsNone(cm.exception.__cause__) | |
| self.assertHasAttr(obj, 'attr') | |
| with self.assertRaises(SystemError) as cm: | |
| _testcapi.object_setattrstring_null_exc(obj, 'attr', exc) | |
| self.assertIs(cm.exception.__context__, exc) | |
| self.assertIsNone(cm.exception.__cause__) | |
| self.assertHasAttr(obj, 'attr') | |
| with self.assertRaises(SystemError) as cm: | |
| # undecodable name | |
| _testcapi.object_setattrstring_null_exc(obj, b'\xff', exc) | |
| self.assertIs(cm.exception.__context__, exc) | |
| self.assertIsNone(cm.exception.__cause__) | |
| if __name__ == "__main__": | |
| unittest.main() |