Removed the Requirement to Install Python and NodeJS (Now Bundled with Borealis)

This commit is contained in:
2025-04-24 00:42:19 -06:00
parent 785265d3e7
commit 9c68cdea84
7786 changed files with 2386458 additions and 217 deletions

View File

@ -0,0 +1,12 @@
import os
import unittest
from test.support import load_package_tests
from test.support import TEST_MODULES_ENABLED
if not TEST_MODULES_ENABLED:
raise unittest.SkipTest("requires test modules")
def load_tests(*args):
return load_package_tests(os.path.dirname(__file__), *args)

View File

@ -0,0 +1,3 @@
import unittest
unittest.main('test.test_capi')

View File

@ -0,0 +1,77 @@
# This script is used by test_misc.
import _imp
import _testinternalcapi
import json
import os
import sys
def import_singlephase():
assert '_testsinglephase' not in sys.modules
try:
import _testsinglephase
except ImportError:
sys.modules.pop('_testsinglephase', None)
return False
else:
del sys.modules['_testsinglephase']
return True
def check_singlephase(override):
# Check using the default setting.
settings_initial = _testinternalcapi.get_interp_settings()
allowed_initial = import_singlephase()
assert(_testinternalcapi.get_interp_settings() == settings_initial)
# Apply the override and check.
override_initial = _imp._override_multi_interp_extensions_check(override)
settings_after = _testinternalcapi.get_interp_settings()
allowed_after = import_singlephase()
# Apply the override again and check.
noop = {}
override_after = _imp._override_multi_interp_extensions_check(override)
settings_noop = _testinternalcapi.get_interp_settings()
if settings_noop != settings_after:
noop['settings_noop'] = settings_noop
allowed_noop = import_singlephase()
if allowed_noop != allowed_after:
noop['allowed_noop'] = allowed_noop
# Restore the original setting and check.
override_noop = _imp._override_multi_interp_extensions_check(override_initial)
if override_noop != override_after:
noop['override_noop'] = override_noop
settings_restored = _testinternalcapi.get_interp_settings()
allowed_restored = import_singlephase()
# Restore the original setting again.
override_restored = _imp._override_multi_interp_extensions_check(override_initial)
assert(_testinternalcapi.get_interp_settings() == settings_restored)
return dict({
'requested': override,
'override__initial': override_initial,
'override_after': override_after,
'override_restored': override_restored,
'settings__initial': settings_initial,
'settings_after': settings_after,
'settings_restored': settings_restored,
'allowed__initial': allowed_initial,
'allowed_after': allowed_after,
'allowed_restored': allowed_restored,
}, **noop)
def run_singlephase_check(override, outfd):
with os.fdopen(outfd, 'w') as outfile:
sys.stdout = outfile
sys.stderr = outfile
try:
results = check_singlephase(override)
json.dump(results, outfile)
finally:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,167 @@
import unittest
from test.support import import_helper
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX
NULL = None
class ByteArraySubclass(bytearray):
pass
class BytesLike:
def __init__(self, value):
self.value = value
def __bytes__(self):
return self.value
class CAPITest(unittest.TestCase):
def test_check(self):
# Test PyByteArray_Check()
check = _testlimitedcapi.bytearray_check
self.assertTrue(check(bytearray(b'')))
self.assertTrue(check(bytearray(b'abc')))
self.assertFalse(check(b'abc'))
self.assertTrue(check(ByteArraySubclass(b'abc')))
self.assertFalse(check(BytesLike(b'abc')))
self.assertFalse(check(3))
self.assertFalse(check([]))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_checkexact(self):
# Test PyByteArray_CheckExact()
check = _testlimitedcapi.bytearray_checkexact
self.assertTrue(check(bytearray(b'')))
self.assertTrue(check(bytearray(b'abc')))
self.assertFalse(check(b'abc'))
self.assertFalse(check(ByteArraySubclass(b'abc')))
self.assertFalse(check(BytesLike(b'abc')))
self.assertFalse(check(3))
self.assertFalse(check([]))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_fromstringandsize(self):
# Test PyByteArray_FromStringAndSize()
fromstringandsize = _testlimitedcapi.bytearray_fromstringandsize
self.assertEqual(fromstringandsize(b'abc'), bytearray(b'abc'))
self.assertEqual(fromstringandsize(b'abc', 2), bytearray(b'ab'))
self.assertEqual(fromstringandsize(b'abc\0def'), bytearray(b'abc\0def'))
self.assertEqual(fromstringandsize(b'', 0), bytearray())
self.assertEqual(fromstringandsize(NULL, 0), bytearray())
self.assertEqual(len(fromstringandsize(NULL, 3)), 3)
self.assertRaises(MemoryError, fromstringandsize, NULL, PY_SSIZE_T_MAX)
self.assertRaises(SystemError, fromstringandsize, b'abc', -1)
self.assertRaises(SystemError, fromstringandsize, b'abc', PY_SSIZE_T_MIN)
self.assertRaises(SystemError, fromstringandsize, NULL, -1)
self.assertRaises(SystemError, fromstringandsize, NULL, PY_SSIZE_T_MIN)
def test_fromobject(self):
# Test PyByteArray_FromObject()
fromobject = _testlimitedcapi.bytearray_fromobject
self.assertEqual(fromobject(b'abc'), bytearray(b'abc'))
self.assertEqual(fromobject(bytearray(b'abc')), bytearray(b'abc'))
self.assertEqual(fromobject(ByteArraySubclass(b'abc')), bytearray(b'abc'))
self.assertEqual(fromobject([97, 98, 99]), bytearray(b'abc'))
self.assertEqual(fromobject(3), bytearray(b'\0\0\0'))
self.assertRaises(TypeError, fromobject, BytesLike(b'abc'))
self.assertRaises(TypeError, fromobject, 'abc')
self.assertRaises(TypeError, fromobject, object())
# CRASHES fromobject(NULL)
def test_size(self):
# Test PyByteArray_Size()
size = _testlimitedcapi.bytearray_size
self.assertEqual(size(bytearray(b'')), 0)
self.assertEqual(size(bytearray(b'abc')), 3)
self.assertEqual(size(ByteArraySubclass(b'abc')), 3)
# CRASHES size(b'abc')
# CRASHES size(object())
# CRASHES size(NULL)
def test_asstring(self):
"""Test PyByteArray_AsString()"""
asstring = _testlimitedcapi.bytearray_asstring
self.assertEqual(asstring(bytearray(b''), 1), b'\0')
self.assertEqual(asstring(bytearray(b'abc'), 4), b'abc\0')
self.assertEqual(asstring(ByteArraySubclass(b'abc'), 4), b'abc\0')
self.assertEqual(asstring(bytearray(b'abc\0def'), 8), b'abc\0def\0')
# CRASHES asstring(b'abc', 0)
# CRASHES asstring(object()', 0)
# CRASHES asstring(NULL, 0)
def test_concat(self):
"""Test PyByteArray_Concat()"""
concat = _testlimitedcapi.bytearray_concat
ba = bytearray(b'abc')
self.assertEqual(concat(ba, b'def'), bytearray(b'abcdef'))
self.assertEqual(ba, b'abc')
self.assertEqual(concat(ba, ba), bytearray(b'abcabc'))
self.assertEqual(concat(b'abc', b'def'), bytearray(b'abcdef'))
self.assertEqual(concat(b'a\0b', b'c\0d'), bytearray(b'a\0bc\0d'))
self.assertEqual(concat(bytearray(b'abc'), b'def'), bytearray(b'abcdef'))
self.assertEqual(concat(b'abc', bytearray(b'def')), bytearray(b'abcdef'))
self.assertEqual(concat(bytearray(b'abc'), b''), bytearray(b'abc'))
self.assertEqual(concat(b'', bytearray(b'def')), bytearray(b'def'))
self.assertEqual(concat(memoryview(b'xabcy')[1:4], b'def'),
bytearray(b'abcdef'))
self.assertEqual(concat(b'abc', memoryview(b'xdefy')[1:4]),
bytearray(b'abcdef'))
self.assertRaises(TypeError, concat, memoryview(b'axbycz')[::2], b'def')
self.assertRaises(TypeError, concat, b'abc', memoryview(b'dxeyfz')[::2])
self.assertRaises(TypeError, concat, b'abc', 'def')
self.assertRaises(TypeError, concat, 'abc', b'def')
self.assertRaises(TypeError, concat, 'abc', 'def')
self.assertRaises(TypeError, concat, [], b'def')
self.assertRaises(TypeError, concat, b'abc', [])
self.assertRaises(TypeError, concat, [], [])
# CRASHES concat(NULL, bytearray(b'def'))
# CRASHES concat(bytearray(b'abc'), NULL)
# CRASHES concat(NULL, object())
# CRASHES concat(object(), NULL)
def test_resize(self):
"""Test PyByteArray_Resize()"""
resize = _testlimitedcapi.bytearray_resize
ba = bytearray(b'abcdef')
self.assertEqual(resize(ba, 3), 0)
self.assertEqual(ba, bytearray(b'abc'))
self.assertEqual(resize(ba, 10), 0)
self.assertEqual(len(ba), 10)
self.assertEqual(ba[:3], bytearray(b'abc'))
self.assertEqual(resize(ba, 2**20), 0)
self.assertEqual(len(ba), 2**20)
self.assertEqual(ba[:3], bytearray(b'abc'))
self.assertEqual(resize(ba, 0), 0)
self.assertEqual(ba, bytearray())
ba = ByteArraySubclass(b'abcdef')
self.assertEqual(resize(ba, 3), 0)
self.assertEqual(ba, bytearray(b'abc'))
self.assertRaises(MemoryError, resize, bytearray(), PY_SSIZE_T_MAX)
self.assertRaises(MemoryError, resize, bytearray(1000), PY_SSIZE_T_MAX)
# CRASHES resize(bytearray(b'abc'), -1)
# CRASHES resize(b'abc', 0)
# CRASHES resize(object(), 0)
# CRASHES resize(NULL, 0)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,255 @@
import unittest
from test.support import import_helper
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
_testcapi = import_helper.import_module('_testcapi')
from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX
NULL = None
class BytesSubclass(bytes):
pass
class BytesLike:
def __init__(self, value):
self.value = value
def __bytes__(self):
return self.value
class CAPITest(unittest.TestCase):
def test_check(self):
# Test PyBytes_Check()
check = _testlimitedcapi.bytes_check
self.assertTrue(check(b'abc'))
self.assertFalse(check('abc'))
self.assertFalse(check(bytearray(b'abc')))
self.assertTrue(check(BytesSubclass(b'abc')))
self.assertFalse(check(BytesLike(b'abc')))
self.assertFalse(check(3))
self.assertFalse(check([]))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_checkexact(self):
# Test PyBytes_CheckExact()
check = _testlimitedcapi.bytes_checkexact
self.assertTrue(check(b'abc'))
self.assertFalse(check('abc'))
self.assertFalse(check(bytearray(b'abc')))
self.assertFalse(check(BytesSubclass(b'abc')))
self.assertFalse(check(BytesLike(b'abc')))
self.assertFalse(check(3))
self.assertFalse(check([]))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_fromstringandsize(self):
# Test PyBytes_FromStringAndSize()
fromstringandsize = _testlimitedcapi.bytes_fromstringandsize
self.assertEqual(fromstringandsize(b'abc'), b'abc')
self.assertEqual(fromstringandsize(b'abc', 2), b'ab')
self.assertEqual(fromstringandsize(b'abc\0def'), b'abc\0def')
self.assertEqual(fromstringandsize(b'a'), b'a')
self.assertEqual(fromstringandsize(b'a', 1), b'a')
self.assertEqual(fromstringandsize(b'', 0), b'')
self.assertEqual(fromstringandsize(NULL, 0), b'')
self.assertEqual(len(fromstringandsize(NULL, 3)), 3)
self.assertRaises((MemoryError, OverflowError),
fromstringandsize, NULL, PY_SSIZE_T_MAX)
self.assertRaises(SystemError, fromstringandsize, b'abc', -1)
self.assertRaises(SystemError, fromstringandsize, b'abc', PY_SSIZE_T_MIN)
self.assertRaises(SystemError, fromstringandsize, NULL, -1)
self.assertRaises(SystemError, fromstringandsize, NULL, PY_SSIZE_T_MIN)
def test_fromstring(self):
# Test PyBytes_FromString()
fromstring = _testlimitedcapi.bytes_fromstring
self.assertEqual(fromstring(b'abc\0def'), b'abc')
self.assertEqual(fromstring(b''), b'')
# CRASHES fromstring(NULL)
def test_fromobject(self):
# Test PyBytes_FromObject()
fromobject = _testlimitedcapi.bytes_fromobject
self.assertEqual(fromobject(b'abc'), b'abc')
self.assertEqual(fromobject(bytearray(b'abc')), b'abc')
self.assertEqual(fromobject(BytesSubclass(b'abc')), b'abc')
self.assertEqual(fromobject([97, 98, 99]), b'abc')
self.assertRaises(TypeError, fromobject, 3)
self.assertRaises(TypeError, fromobject, BytesLike(b'abc'))
self.assertRaises(TypeError, fromobject, 'abc')
self.assertRaises(TypeError, fromobject, object())
self.assertRaises(SystemError, fromobject, NULL)
def test_size(self):
# Test PyBytes_Size()
size = _testlimitedcapi.bytes_size
self.assertEqual(size(b''), 0)
self.assertEqual(size(b'abc'), 3)
self.assertEqual(size(BytesSubclass(b'abc')), 3)
self.assertRaises(TypeError, size, bytearray(b'abc'))
self.assertRaises(TypeError, size, 'abc')
self.assertRaises(TypeError, size, object())
# CRASHES size(NULL)
def test_asstring(self):
"""Test PyBytes_AsString()"""
asstring = _testlimitedcapi.bytes_asstring
self.assertEqual(asstring(b'abc', 4), b'abc\0')
self.assertEqual(asstring(b'abc\0def', 8), b'abc\0def\0')
self.assertRaises(TypeError, asstring, 'abc', 0)
self.assertRaises(TypeError, asstring, object(), 0)
# CRASHES asstring(NULL, 0)
def test_asstringandsize(self):
"""Test PyBytes_AsStringAndSize()"""
asstringandsize = _testlimitedcapi.bytes_asstringandsize
asstringandsize_null = _testlimitedcapi.bytes_asstringandsize_null
self.assertEqual(asstringandsize(b'abc', 4), (b'abc\0', 3))
self.assertEqual(asstringandsize(b'abc\0def', 8), (b'abc\0def\0', 7))
self.assertEqual(asstringandsize_null(b'abc', 4), b'abc\0')
self.assertRaises(ValueError, asstringandsize_null, b'abc\0def', 8)
self.assertRaises(TypeError, asstringandsize, 'abc', 0)
self.assertRaises(TypeError, asstringandsize_null, 'abc', 0)
self.assertRaises(TypeError, asstringandsize, object(), 0)
self.assertRaises(TypeError, asstringandsize_null, object(), 0)
# CRASHES asstringandsize(NULL, 0)
# CRASHES asstringandsize_null(NULL, 0)
def test_repr(self):
# Test PyBytes_Repr()
bytes_repr = _testlimitedcapi.bytes_repr
self.assertEqual(bytes_repr(b'''abc''', 0), r"""b'abc'""")
self.assertEqual(bytes_repr(b'''abc''', 1), r"""b'abc'""")
self.assertEqual(bytes_repr(b'''a'b"c"d''', 0), r"""b'a\'b"c"d'""")
self.assertEqual(bytes_repr(b'''a'b"c"d''', 1), r"""b'a\'b"c"d'""")
self.assertEqual(bytes_repr(b'''a'b"c''', 0), r"""b'a\'b"c'""")
self.assertEqual(bytes_repr(b'''a'b"c''', 1), r"""b'a\'b"c'""")
self.assertEqual(bytes_repr(b'''a'b'c"d''', 0), r"""b'a\'b\'c"d'""")
self.assertEqual(bytes_repr(b'''a'b'c"d''', 1), r"""b'a\'b\'c"d'""")
self.assertEqual(bytes_repr(b'''a'b'c'd''', 0), r"""b'a\'b\'c\'d'""")
self.assertEqual(bytes_repr(b'''a'b'c'd''', 1), r'''b"a'b'c'd"''')
self.assertEqual(bytes_repr(BytesSubclass(b'abc'), 0), r"""b'abc'""")
# UDEFINED bytes_repr(object(), 0)
# CRASHES bytes_repr(NULL, 0)
def test_concat(self, concat=None):
"""Test PyBytes_Concat()"""
if concat is None:
concat = _testlimitedcapi.bytes_concat
self.assertEqual(concat(b'abc', b'def'), b'abcdef')
self.assertEqual(concat(b'a\0b', b'c\0d'), b'a\0bc\0d')
self.assertEqual(concat(bytearray(b'abc'), b'def'), b'abcdef')
self.assertEqual(concat(b'abc', bytearray(b'def')), b'abcdef')
self.assertEqual(concat(bytearray(b'abc'), b''), b'abc')
self.assertEqual(concat(b'', bytearray(b'def')), b'def')
self.assertEqual(concat(memoryview(b'xabcy')[1:4], b'def'), b'abcdef')
self.assertEqual(concat(b'abc', memoryview(b'xdefy')[1:4]), b'abcdef')
self.assertEqual(concat(b'abc', b'def', True), b'abcdef')
self.assertEqual(concat(b'abc', bytearray(b'def'), True), b'abcdef')
# Check that it does not change the singleton
self.assertEqual(concat(bytes(), b'def', True), b'def')
self.assertEqual(len(bytes()), 0)
self.assertRaises(TypeError, concat, memoryview(b'axbycz')[::2], b'def')
self.assertRaises(TypeError, concat, b'abc', memoryview(b'dxeyfz')[::2])
self.assertRaises(TypeError, concat, b'abc', 'def')
self.assertRaises(TypeError, concat, 'abc', b'def')
self.assertRaises(TypeError, concat, 'abc', 'def')
self.assertRaises(TypeError, concat, [], b'def')
self.assertRaises(TypeError, concat, b'abc', [])
self.assertRaises(TypeError, concat, [], [])
self.assertEqual(concat(NULL, b'def'), NULL)
self.assertEqual(concat(b'abc', NULL), NULL)
self.assertEqual(concat(NULL, object()), NULL)
self.assertEqual(concat(object(), NULL), NULL)
def test_concatanddel(self):
"""Test PyBytes_ConcatAndDel()"""
self.test_concat(_testlimitedcapi.bytes_concatanddel)
def test_decodeescape(self):
"""Test PyBytes_DecodeEscape()"""
decodeescape = _testlimitedcapi.bytes_decodeescape
self.assertEqual(decodeescape(b'abc'), b'abc')
self.assertEqual(decodeescape(br'\t\n\r\x0b\x0c\x00\\\'\"'),
b'''\t\n\r\v\f\0\\'"''')
self.assertEqual(decodeescape(b'\t\n\r\x0b\x0c\x00'), b'\t\n\r\v\f\0')
self.assertEqual(decodeescape(br'\xa1\xa2'), b'\xa1\xa2')
self.assertEqual(decodeescape(br'\2\24\241'), b'\x02\x14\xa1')
self.assertEqual(decodeescape(b'\xa1\xa2'), b'\xa1\xa2')
with self.assertWarns(DeprecationWarning):
self.assertEqual(decodeescape(br'\u4f60'), br'\u4f60')
with self.assertWarns(DeprecationWarning):
self.assertEqual(decodeescape(br'\z'), br'\z')
with self.assertWarns(DeprecationWarning):
self.assertEqual(decodeescape(br'\541'), b'a')
for b in b'\\', br'\x', br'\xa', br'\xz', br'\xaz':
self.assertRaises(ValueError, decodeescape, b)
self.assertRaises(ValueError, decodeescape, b, 'strict')
self.assertEqual(decodeescape(br'x\xa', 'replace'), b'x?')
self.assertEqual(decodeescape(br'x\xay', 'replace'), b'x?y')
self.assertEqual(decodeescape(br'x\xa\xy', 'replace'), b'x??y')
self.assertEqual(decodeescape(br'x\xa\xy', 'ignore'), b'xy')
self.assertRaises(ValueError, decodeescape, b'\\', 'spam')
self.assertEqual(decodeescape(NULL), b'')
self.assertRaises(OverflowError, decodeescape, b'abc', NULL, PY_SSIZE_T_MAX)
self.assertRaises(OverflowError, decodeescape, NULL, NULL, PY_SSIZE_T_MAX)
# CRASHES decodeescape(b'abc', NULL, -1)
# CRASHES decodeescape(NULL, NULL, 1)
def test_resize(self):
"""Test _PyBytes_Resize()"""
resize = _testcapi.bytes_resize
for new in True, False:
self.assertEqual(resize(b'abc', 0, new), b'')
self.assertEqual(resize(b'abc', 1, new), b'a')
self.assertEqual(resize(b'abc', 2, new), b'ab')
self.assertEqual(resize(b'abc', 3, new), b'abc')
b = resize(b'abc', 4, new)
self.assertEqual(len(b), 4)
self.assertEqual(b[:3], b'abc')
self.assertEqual(resize(b'a', 0, new), b'')
self.assertEqual(resize(b'a', 1, new), b'a')
b = resize(b'a', 2, new)
self.assertEqual(len(b), 2)
self.assertEqual(b[:1], b'a')
self.assertEqual(resize(b'', 0, new), b'')
self.assertEqual(len(resize(b'', 1, new)), 1)
self.assertEqual(len(resize(b'', 2, new)), 2)
self.assertRaises(SystemError, resize, b'abc', -1, False)
self.assertRaises(SystemError, resize, bytearray(b'abc'), 3, False)
# CRASHES resize(NULL, 0, False)
# CRASHES resize(NULL, 3, False)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,520 @@
import unittest
import sys
from test.support import import_helper
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
NULL = None
class CAPITest(unittest.TestCase):
# TODO: Test the following functions:
#
# PyUnicode_BuildEncodingMap
# PyUnicode_FSConverter
# PyUnicode_FSDecoder
# PyUnicode_DecodeMBCS
# PyUnicode_DecodeMBCSStateful
# PyUnicode_DecodeCodePageStateful
# PyUnicode_AsMBCSString
# PyUnicode_EncodeCodePage
# PyUnicode_DecodeLocaleAndSize
# PyUnicode_DecodeLocale
# PyUnicode_EncodeLocale
# PyUnicode_DecodeFSDefault
# PyUnicode_DecodeFSDefaultAndSize
# PyUnicode_EncodeFSDefault
def test_fromencodedobject(self):
"""Test PyUnicode_FromEncodedObject()"""
fromencodedobject = _testlimitedcapi.unicode_fromencodedobject
self.assertEqual(fromencodedobject(b'abc', NULL), 'abc')
self.assertEqual(fromencodedobject(b'abc', 'ascii'), 'abc')
b = b'a\xc2\xa1\xe4\xbd\xa0\xf0\x9f\x98\x80'
s = 'a\xa1\u4f60\U0001f600'
self.assertEqual(fromencodedobject(b, NULL), s)
self.assertEqual(fromencodedobject(b, 'utf-8'), s)
self.assertEqual(fromencodedobject(b, 'latin1'), b.decode('latin1'))
self.assertRaises(UnicodeDecodeError, fromencodedobject, b, 'ascii')
self.assertEqual(fromencodedobject(b, 'ascii', 'replace'),
'a' + '\ufffd'*9)
self.assertEqual(fromencodedobject(bytearray(b), NULL), s)
self.assertEqual(fromencodedobject(bytearray(b), 'utf-8'), s)
self.assertRaises(LookupError, fromencodedobject, b'abc', 'foo')
self.assertRaises(LookupError, fromencodedobject, b, 'ascii', 'foo')
self.assertRaises(TypeError, fromencodedobject, 'abc', NULL)
self.assertRaises(TypeError, fromencodedobject, 'abc', 'ascii')
self.assertRaises(TypeError, fromencodedobject, [], NULL)
self.assertRaises(TypeError, fromencodedobject, [], 'ascii')
self.assertRaises(SystemError, fromencodedobject, NULL, NULL)
self.assertRaises(SystemError, fromencodedobject, NULL, 'ascii')
def test_decode(self):
"""Test PyUnicode_Decode()"""
decode = _testlimitedcapi.unicode_decode
self.assertEqual(decode(b'[\xe2\x82\xac]', 'utf-8'), '[\u20ac]')
self.assertEqual(decode(b'[\xa4]', 'iso8859-15'), '[\u20ac]')
self.assertEqual(decode(b'[\xa4]', 'iso8859-15', 'strict'), '[\u20ac]')
self.assertRaises(UnicodeDecodeError, decode, b'[\xa4]', 'utf-8')
self.assertEqual(decode(b'[\xa4]', 'utf-8', 'replace'), '[\ufffd]')
self.assertEqual(decode(b'[\xe2\x82\xac]', NULL), '[\u20ac]')
self.assertEqual(decode(b'[\xa4]', NULL, 'replace'), '[\ufffd]')
self.assertRaises(LookupError, decode, b'\xa4', 'foo')
self.assertRaises(LookupError, decode, b'\xa4', 'utf-8', 'foo')
# TODO: Test PyUnicode_Decode() with NULL as data and
# negative size.
def test_asencodedstring(self):
"""Test PyUnicode_AsEncodedString()"""
asencodedstring = _testlimitedcapi.unicode_asencodedstring
self.assertEqual(asencodedstring('abc', NULL), b'abc')
self.assertEqual(asencodedstring('abc', 'ascii'), b'abc')
s = 'a\xa1\u4f60\U0001f600'
b = b'a\xc2\xa1\xe4\xbd\xa0\xf0\x9f\x98\x80'
self.assertEqual(asencodedstring(s, NULL), b)
self.assertEqual(asencodedstring(s, 'utf-8'), b)
self.assertEqual(asencodedstring('\xa1\xa2', 'latin1'), b'\xa1\xa2')
self.assertRaises(UnicodeEncodeError, asencodedstring, '\xa1\xa2', 'ascii')
self.assertEqual(asencodedstring(s, 'ascii', 'replace'), b'a???')
self.assertRaises(LookupError, asencodedstring, 'abc', 'foo')
self.assertRaises(LookupError, asencodedstring, s, 'ascii', 'foo')
self.assertRaises(TypeError, asencodedstring, b'abc', NULL)
self.assertRaises(TypeError, asencodedstring, b'abc', 'ascii')
self.assertRaises(TypeError, asencodedstring, [], NULL)
self.assertRaises(TypeError, asencodedstring, [], 'ascii')
# CRASHES asencodedstring(NULL, NULL)
# CRASHES asencodedstring(NULL, 'ascii')
def test_decodeutf8(self):
"""Test PyUnicode_DecodeUTF8()"""
decodeutf8 = _testlimitedcapi.unicode_decodeutf8
for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']:
b = s.encode('utf-8')
self.assertEqual(decodeutf8(b), s)
self.assertEqual(decodeutf8(b, 'strict'), s)
self.assertRaises(UnicodeDecodeError, decodeutf8, b'\x80')
self.assertRaises(UnicodeDecodeError, decodeutf8, b'\xc0')
self.assertRaises(UnicodeDecodeError, decodeutf8, b'\xff')
self.assertRaises(UnicodeDecodeError, decodeutf8, b'a\xf0\x9f')
self.assertEqual(decodeutf8(b'a\xf0\x9f', 'replace'), 'a\ufffd')
self.assertEqual(decodeutf8(b'a\xf0\x9fb', 'replace'), 'a\ufffdb')
self.assertRaises(LookupError, decodeutf8, b'a\x80', 'foo')
# TODO: Test PyUnicode_DecodeUTF8() with NULL as data and
# negative size.
def test_decodeutf8stateful(self):
"""Test PyUnicode_DecodeUTF8Stateful()"""
decodeutf8stateful = _testlimitedcapi.unicode_decodeutf8stateful
for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']:
b = s.encode('utf-8')
self.assertEqual(decodeutf8stateful(b), (s, len(b)))
self.assertEqual(decodeutf8stateful(b, 'strict'), (s, len(b)))
self.assertRaises(UnicodeDecodeError, decodeutf8stateful, b'\x80')
self.assertRaises(UnicodeDecodeError, decodeutf8stateful, b'\xc0')
self.assertRaises(UnicodeDecodeError, decodeutf8stateful, b'\xff')
self.assertEqual(decodeutf8stateful(b'a\xf0\x9f'), ('a', 1))
self.assertEqual(decodeutf8stateful(b'a\xf0\x9f', 'replace'), ('a', 1))
self.assertRaises(UnicodeDecodeError, decodeutf8stateful, b'a\xf0\x9fb')
self.assertEqual(decodeutf8stateful(b'a\xf0\x9fb', 'replace'), ('a\ufffdb', 4))
self.assertRaises(LookupError, decodeutf8stateful, b'a\x80', 'foo')
# TODO: Test PyUnicode_DecodeUTF8Stateful() with NULL as data and
# negative size.
# TODO: Test PyUnicode_DecodeUTF8Stateful() with NULL as the address of
# "consumed".
def test_asutf8string(self):
"""Test PyUnicode_AsUTF8String()"""
asutf8string = _testlimitedcapi.unicode_asutf8string
for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']:
self.assertEqual(asutf8string(s), s.encode('utf-8'))
self.assertRaises(UnicodeEncodeError, asutf8string, '\ud8ff')
self.assertRaises(TypeError, asutf8string, b'abc')
self.assertRaises(TypeError, asutf8string, [])
# CRASHES asutf8string(NULL)
def test_decodeutf16(self):
"""Test PyUnicode_DecodeUTF16()"""
decodeutf16 = _testlimitedcapi.unicode_decodeutf16
naturalbyteorder = -1 if sys.byteorder == 'little' else 1
for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']:
b = s.encode('utf-16')
self.assertEqual(decodeutf16(0, b), (naturalbyteorder, s))
b = s.encode('utf-16le')
self.assertEqual(decodeutf16(-1, b), (-1, s))
self.assertEqual(decodeutf16(0, b'\xff\xfe'+b), (-1, s))
b = s.encode('utf-16be')
self.assertEqual(decodeutf16(1, b), (1, s))
self.assertEqual(decodeutf16(0, b'\xfe\xff'+b), (1, s))
self.assertRaises(UnicodeDecodeError, decodeutf16, -1, b'a')
self.assertRaises(UnicodeDecodeError, decodeutf16, 1, b'a')
self.assertRaises(UnicodeDecodeError, decodeutf16, 0, b'\xff\xfea')
self.assertRaises(UnicodeDecodeError, decodeutf16, 0, b'\xfe\xffa')
self.assertRaises(UnicodeDecodeError, decodeutf16, -1, b'\x00\xde')
self.assertRaises(UnicodeDecodeError, decodeutf16, 1, b'\xde\x00')
self.assertRaises(UnicodeDecodeError, decodeutf16, 0, b'\xde\xde')
self.assertEqual(decodeutf16(-1, b'\x00\xde', 'replace'), (-1, '\ufffd'))
self.assertEqual(decodeutf16(1, b'\xde\x00', 'replace'), (1, '\ufffd'))
self.assertEqual(decodeutf16(0, b'\xde\xde', 'replace'), (0, '\ufffd'))
self.assertEqual(decodeutf16(0, b'\xff\xfe\x00\xde', 'replace'), (-1, '\ufffd'))
self.assertEqual(decodeutf16(0, b'\xfe\xff\xde\x00', 'replace'), (1, '\ufffd'))
self.assertRaises(UnicodeDecodeError, decodeutf16, -1, b'\x3d\xd8')
self.assertRaises(UnicodeDecodeError, decodeutf16, 1, b'\xd8\x3d')
self.assertRaises(UnicodeDecodeError, decodeutf16, 0, b'\xd8\xd8')
self.assertEqual(decodeutf16(-1, b'\x3d\xd8', 'replace'), (-1, '\ufffd'))
self.assertEqual(decodeutf16(1, b'\xd8\x3d', 'replace'), (1, '\ufffd'))
self.assertEqual(decodeutf16(0, b'\xd8\xd8', 'replace'), (0, '\ufffd'))
self.assertEqual(decodeutf16(0, b'\xff\xfe\x3d\xd8', 'replace'), (-1, '\ufffd'))
self.assertEqual(decodeutf16(0, b'\xfe\xff\xd8\x3d', 'replace'), (1, '\ufffd'))
self.assertRaises(LookupError, decodeutf16, -1, b'\x00\xde', 'foo')
self.assertRaises(LookupError, decodeutf16, 1, b'\xde\x00', 'foo')
self.assertRaises(LookupError, decodeutf16, 0, b'\xde\xde', 'foo')
# TODO: Test PyUnicode_DecodeUTF16() with NULL as data and
# negative size.
def test_decodeutf16stateful(self):
"""Test PyUnicode_DecodeUTF16Stateful()"""
decodeutf16stateful = _testlimitedcapi.unicode_decodeutf16stateful
naturalbyteorder = -1 if sys.byteorder == 'little' else 1
for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']:
b = s.encode('utf-16')
self.assertEqual(decodeutf16stateful(0, b), (naturalbyteorder, s, len(b)))
b = s.encode('utf-16le')
self.assertEqual(decodeutf16stateful(-1, b), (-1, s, len(b)))
self.assertEqual(decodeutf16stateful(0, b'\xff\xfe'+b), (-1, s, len(b)+2))
b = s.encode('utf-16be')
self.assertEqual(decodeutf16stateful(1, b), (1, s, len(b)))
self.assertEqual(decodeutf16stateful(0, b'\xfe\xff'+b), (1, s, len(b)+2))
self.assertEqual(decodeutf16stateful(-1, b'\x61\x00\x3d'), (-1, 'a', 2))
self.assertEqual(decodeutf16stateful(-1, b'\x61\x00\x3d\xd8'), (-1, 'a', 2))
self.assertEqual(decodeutf16stateful(-1, b'\x61\x00\x3d\xd8\x00'), (-1, 'a', 2))
self.assertEqual(decodeutf16stateful(1, b'\x00\x61\xd8'), (1, 'a', 2))
self.assertEqual(decodeutf16stateful(1, b'\x00\x61\xd8\x3d'), (1, 'a', 2))
self.assertEqual(decodeutf16stateful(1, b'\x00\x61\xd8\x3d\xde'), (1, 'a', 2))
self.assertEqual(decodeutf16stateful(0, b'\xff\xfe\x61\x00\x3d\xd8\x00'), (-1, 'a', 4))
self.assertEqual(decodeutf16stateful(0, b'\xfe\xff\x00\x61\xd8\x3d\xde'), (1, 'a', 4))
self.assertRaises(UnicodeDecodeError, decodeutf16stateful, -1, b'\x00\xde')
self.assertRaises(UnicodeDecodeError, decodeutf16stateful, 1, b'\xde\x00')
self.assertRaises(UnicodeDecodeError, decodeutf16stateful, 0, b'\xde\xde')
self.assertEqual(decodeutf16stateful(-1, b'\x00\xde', 'replace'), (-1, '\ufffd', 2))
self.assertEqual(decodeutf16stateful(1, b'\xde\x00', 'replace'), (1, '\ufffd', 2))
self.assertEqual(decodeutf16stateful(0, b'\xde\xde', 'replace'), (0, '\ufffd', 2))
self.assertEqual(decodeutf16stateful(0, b'\xff\xfe\x00\xde', 'replace'), (-1, '\ufffd', 4))
self.assertEqual(decodeutf16stateful(0, b'\xfe\xff\xde\x00', 'replace'), (1, '\ufffd', 4))
self.assertRaises(UnicodeDecodeError, decodeutf16stateful, -1, b'\x3d\xd8\x61\x00')
self.assertEqual(decodeutf16stateful(-1, b'\x3d\xd8\x61\x00', 'replace'), (-1, '\ufffda', 4))
self.assertRaises(UnicodeDecodeError, decodeutf16stateful, 1, b'\xd8\x3d\x00\x61')
self.assertEqual(decodeutf16stateful(1, b'\xd8\x3d\x00\x61', 'replace'), (1, '\ufffda', 4))
self.assertRaises(LookupError, decodeutf16stateful, -1, b'\x00\xde', 'foo')
self.assertRaises(LookupError, decodeutf16stateful, 1, b'\xde\x00', 'foo')
self.assertRaises(LookupError, decodeutf16stateful, 0, b'\xde\xde', 'foo')
# TODO: Test PyUnicode_DecodeUTF16Stateful() with NULL as data and
# negative size.
# TODO: Test PyUnicode_DecodeUTF16Stateful() with NULL as the address of
# "consumed".
def test_asutf16string(self):
"""Test PyUnicode_AsUTF16String()"""
asutf16string = _testlimitedcapi.unicode_asutf16string
for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']:
self.assertEqual(asutf16string(s), s.encode('utf-16'))
self.assertRaises(UnicodeEncodeError, asutf16string, '\ud8ff')
self.assertRaises(TypeError, asutf16string, b'abc')
self.assertRaises(TypeError, asutf16string, [])
# CRASHES asutf16string(NULL)
def test_decodeutf32(self):
"""Test PyUnicode_DecodeUTF8()"""
decodeutf32 = _testlimitedcapi.unicode_decodeutf32
naturalbyteorder = -1 if sys.byteorder == 'little' else 1
for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']:
b = s.encode('utf-32')
self.assertEqual(decodeutf32(0, b), (naturalbyteorder, s))
b = s.encode('utf-32le')
self.assertEqual(decodeutf32(-1, b), (-1, s))
self.assertEqual(decodeutf32(0, b'\xff\xfe\x00\x00'+b), (-1, s))
b = s.encode('utf-32be')
self.assertEqual(decodeutf32(1, b), (1, s))
self.assertEqual(decodeutf32(0, b'\x00\x00\xfe\xff'+b), (1, s))
self.assertRaises(UnicodeDecodeError, decodeutf32, -1, b'\x61\x00\x00\x00\x00')
self.assertRaises(UnicodeDecodeError, decodeutf32, 1, b'\x00\x00\x00\x61\x00')
self.assertRaises(UnicodeDecodeError, decodeutf32, 0, b'\xff\xfe\x00\x00\x61\x00\x00\x00\x00')
self.assertRaises(UnicodeDecodeError, decodeutf32, 0, b'\x00\x00\xfe\xff\x00\x00\x00\x61\x00')
self.assertRaises(UnicodeDecodeError, decodeutf32, -1, b'\xff\xff\xff\xff')
self.assertRaises(UnicodeDecodeError, decodeutf32, 1, b'\xff\xff\xff\xff')
self.assertRaises(UnicodeDecodeError, decodeutf32, 0, b'\xff\xff\xff\xff')
self.assertEqual(decodeutf32(-1, b'\xff\xff\xff\xff', 'replace'), (-1, '\ufffd'))
self.assertEqual(decodeutf32(1, b'\xff\xff\xff\xff', 'replace'), (1, '\ufffd'))
self.assertEqual(decodeutf32(0, b'\xff\xff\xff\xff', 'replace'), (0, '\ufffd'))
self.assertEqual(decodeutf32(0, b'\xff\xfe\x00\x00\xff\xff\xff\xff', 'replace'), (-1, '\ufffd'))
self.assertEqual(decodeutf32(0, b'\x00\x00\xfe\xff\xff\xff\xff\xff', 'replace'), (1, '\ufffd'))
self.assertRaises(UnicodeDecodeError, decodeutf32, -1, b'\x3d\xd8\x00\x00')
self.assertEqual(decodeutf32(-1, b'\x3d\xd8\x00\x00', 'replace'), (-1, '\ufffd'))
self.assertRaises(UnicodeDecodeError, decodeutf32, 1, b'\x00\x00\xd8\x3d')
self.assertEqual(decodeutf32(1, b'\x00\x00\xd8\x3d', 'replace'), (1, '\ufffd'))
self.assertRaises(LookupError, decodeutf32, -1, b'\xff\xff\xff\xff', 'foo')
self.assertRaises(LookupError, decodeutf32, 1, b'\xff\xff\xff\xff', 'foo')
self.assertRaises(LookupError, decodeutf32, 0, b'\xff\xff\xff\xff', 'foo')
# TODO: Test PyUnicode_DecodeUTF32() with NULL as data and
# negative size.
def test_decodeutf32stateful(self):
"""Test PyUnicode_DecodeUTF32Stateful()"""
decodeutf32stateful = _testlimitedcapi.unicode_decodeutf32stateful
naturalbyteorder = -1 if sys.byteorder == 'little' else 1
for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']:
b = s.encode('utf-32')
self.assertEqual(decodeutf32stateful(0, b), (naturalbyteorder, s, len(b)))
b = s.encode('utf-32le')
self.assertEqual(decodeutf32stateful(-1, b), (-1, s, len(b)))
self.assertEqual(decodeutf32stateful(0, b'\xff\xfe\x00\x00'+b), (-1, s, len(b)+4))
b = s.encode('utf-32be')
self.assertEqual(decodeutf32stateful(1, b), (1, s, len(b)))
self.assertEqual(decodeutf32stateful(0, b'\x00\x00\xfe\xff'+b), (1, s, len(b)+4))
self.assertEqual(decodeutf32stateful(-1, b'\x61\x00\x00\x00\x00'), (-1, 'a', 4))
self.assertEqual(decodeutf32stateful(-1, b'\x61\x00\x00\x00\x00\xf6'), (-1, 'a', 4))
self.assertEqual(decodeutf32stateful(-1, b'\x61\x00\x00\x00\x00\xf6\x01'), (-1, 'a', 4))
self.assertEqual(decodeutf32stateful(1, b'\x00\x00\x00\x61\x00'), (1, 'a', 4))
self.assertEqual(decodeutf32stateful(1, b'\x00\x00\x00\x61\x00\x01'), (1, 'a', 4))
self.assertEqual(decodeutf32stateful(1, b'\x00\x00\x00\x61\x00\x01\xf6'), (1, 'a', 4))
self.assertEqual(decodeutf32stateful(0, b'\xff\xfe\x00\x00\x61\x00\x00\x00\x00\xf6\x01'), (-1, 'a', 8))
self.assertEqual(decodeutf32stateful(0, b'\x00\x00\xfe\xff\x00\x00\x00\x61\x00\x01\xf6'), (1, 'a', 8))
for b in b'\xff', b'\xff\xff', b'\xff\xff\xff':
self.assertEqual(decodeutf32stateful(-1, b), (-1, '', 0))
self.assertEqual(decodeutf32stateful(1, b), (1, '', 0))
self.assertEqual(decodeutf32stateful(0, b), (0, '', 0))
self.assertEqual(decodeutf32stateful(0, b'\xff\xfe\x00\x00'+b), (-1, '', 4))
self.assertEqual(decodeutf32stateful(0, b'\x00\x00\xfe\xff'+b), (1, '', 4))
self.assertRaises(UnicodeDecodeError, decodeutf32stateful, -1, b'\xff\xff\xff\xff')
self.assertRaises(UnicodeDecodeError, decodeutf32stateful, 1, b'\xff\xff\xff\xff')
self.assertRaises(UnicodeDecodeError, decodeutf32stateful, 0, b'\xff\xff\xff\xff')
self.assertEqual(decodeutf32stateful(-1, b'\xff\xff\xff\xff', 'replace'), (-1, '\ufffd', 4))
self.assertEqual(decodeutf32stateful(1, b'\xff\xff\xff\xff', 'replace'), (1, '\ufffd', 4))
self.assertEqual(decodeutf32stateful(0, b'\xff\xff\xff\xff', 'replace'), (0, '\ufffd', 4))
self.assertEqual(decodeutf32stateful(0, b'\xff\xfe\x00\x00\xff\xff\xff\xff', 'replace'), (-1, '\ufffd', 8))
self.assertEqual(decodeutf32stateful(0, b'\x00\x00\xfe\xff\xff\xff\xff\xff', 'replace'), (1, '\ufffd', 8))
self.assertRaises(UnicodeDecodeError, decodeutf32stateful, -1, b'\x3d\xd8\x00\x00')
self.assertEqual(decodeutf32stateful(-1, b'\x3d\xd8\x00\x00', 'replace'), (-1, '\ufffd', 4))
self.assertRaises(UnicodeDecodeError, decodeutf32stateful, 1, b'\x00\x00\xd8\x3d')
self.assertEqual(decodeutf32stateful(1, b'\x00\x00\xd8\x3d', 'replace'), (1, '\ufffd', 4))
self.assertRaises(LookupError, decodeutf32stateful, -1, b'\xff\xff\xff\xff', 'foo')
self.assertRaises(LookupError, decodeutf32stateful, 1, b'\xff\xff\xff\xff', 'foo')
self.assertRaises(LookupError, decodeutf32stateful, 0, b'\xff\xff\xff\xff', 'foo')
# TODO: Test PyUnicode_DecodeUTF32Stateful() with NULL as data and
# negative size.
# TODO: Test PyUnicode_DecodeUTF32Stateful() with NULL as the address of
# "consumed".
def test_asutf32string(self):
"""Test PyUnicode_AsUTF32String()"""
asutf32string = _testlimitedcapi.unicode_asutf32string
for s in ['abc', '\xa1\xa2', '\u4f60\u597d', 'a\U0001f600']:
self.assertEqual(asutf32string(s), s.encode('utf-32'))
self.assertRaises(UnicodeEncodeError, asutf32string, '\ud8ff')
self.assertRaises(TypeError, asutf32string, b'abc')
self.assertRaises(TypeError, asutf32string, [])
# CRASHES asutf32string(NULL)
def test_decodelatin1(self):
"""Test PyUnicode_DecodeLatin1()"""
decodelatin1 = _testlimitedcapi.unicode_decodelatin1
self.assertEqual(decodelatin1(b'abc'), 'abc')
self.assertEqual(decodelatin1(b'abc', 'strict'), 'abc')
self.assertEqual(decodelatin1(b'\xa1\xa2'), '\xa1\xa2')
self.assertEqual(decodelatin1(b'\xa1\xa2', 'strict'), '\xa1\xa2')
# TODO: Test PyUnicode_DecodeLatin1() with NULL as data and
# negative size.
def test_aslatin1string(self):
"""Test PyUnicode_AsLatin1String()"""
aslatin1string = _testlimitedcapi.unicode_aslatin1string
self.assertEqual(aslatin1string('abc'), b'abc')
self.assertEqual(aslatin1string('\xa1\xa2'), b'\xa1\xa2')
self.assertRaises(UnicodeEncodeError, aslatin1string, '\u4f60')
self.assertRaises(TypeError, aslatin1string, b'abc')
self.assertRaises(TypeError, aslatin1string, [])
# CRASHES aslatin1string(NULL)
def test_decodeascii(self):
"""Test PyUnicode_DecodeASCII()"""
decodeascii = _testlimitedcapi.unicode_decodeascii
self.assertEqual(decodeascii(b'abc'), 'abc')
self.assertEqual(decodeascii(b'abc', 'strict'), 'abc')
self.assertRaises(UnicodeDecodeError, decodeascii, b'\xff')
self.assertEqual(decodeascii(b'a\xff', 'replace'), 'a\ufffd')
self.assertEqual(decodeascii(b'a\xffb', 'replace'), 'a\ufffdb')
self.assertRaises(LookupError, decodeascii, b'a\xff', 'foo')
# TODO: Test PyUnicode_DecodeASCII() with NULL as data and
# negative size.
def test_asasciistring(self):
"""Test PyUnicode_AsASCIIString()"""
asasciistring = _testlimitedcapi.unicode_asasciistring
self.assertEqual(asasciistring('abc'), b'abc')
self.assertRaises(UnicodeEncodeError, asasciistring, '\x80')
self.assertRaises(TypeError, asasciistring, b'abc')
self.assertRaises(TypeError, asasciistring, [])
# CRASHES asasciistring(NULL)
def test_decodecharmap(self):
"""Test PyUnicode_DecodeCharmap()"""
decodecharmap = _testlimitedcapi.unicode_decodecharmap
self.assertEqual(decodecharmap(b'\3\0\7', {0: 'a', 3: 'b', 7: 'c'}), 'bac')
self.assertEqual(decodecharmap(b'\1\0\2', ['a', 'b', 'c']), 'bac')
self.assertEqual(decodecharmap(b'\1\0\2', 'abc'), 'bac')
self.assertEqual(decodecharmap(b'\1\0\2', ['\xa1', '\xa2', '\xa3']), '\xa2\xa1\xa3')
self.assertEqual(decodecharmap(b'\1\0\2', ['\u4f60', '\u597d', '\u4e16']), '\u597d\u4f60\u4e16')
self.assertEqual(decodecharmap(b'\1\0\2', ['\U0001f600', '\U0001f601', '\U0001f602']), '\U0001f601\U0001f600\U0001f602')
self.assertEqual(decodecharmap(b'\1\0\2', [97, 98, 99]), 'bac')
self.assertEqual(decodecharmap(b'\1\0\2', ['', 'b', 'cd']), 'bcd')
self.assertRaises(UnicodeDecodeError, decodecharmap, b'\0', {})
self.assertRaises(UnicodeDecodeError, decodecharmap, b'\0', {0: None})
self.assertEqual(decodecharmap(b'\1\0\2', [None, 'b', 'c'], 'replace'), 'b\ufffdc')
self.assertEqual(decodecharmap(b'\1\0\2\xff', NULL), '\1\0\2\xff')
self.assertRaises(TypeError, decodecharmap, b'\0', 42)
# TODO: Test PyUnicode_DecodeCharmap() with NULL as data and
# negative size.
def test_ascharmapstring(self):
"""Test PyUnicode_AsCharmapString()"""
ascharmapstring = _testlimitedcapi.unicode_ascharmapstring
self.assertEqual(ascharmapstring('abc', {97: 3, 98: 0, 99: 7}), b'\3\0\7')
self.assertEqual(ascharmapstring('\xa1\xa2\xa3', {0xa1: 3, 0xa2: 0, 0xa3: 7}), b'\3\0\7')
self.assertEqual(ascharmapstring('\u4f60\u597d\u4e16', {0x4f60: 3, 0x597d: 0, 0x4e16: 7}), b'\3\0\7')
self.assertEqual(ascharmapstring('\U0001f600\U0001f601\U0001f602', {0x1f600: 3, 0x1f601: 0, 0x1f602: 7}), b'\3\0\7')
self.assertEqual(ascharmapstring('abc', {97: 3, 98: b'', 99: b'spam'}), b'\3spam')
self.assertRaises(UnicodeEncodeError, ascharmapstring, 'a', {})
self.assertRaises(UnicodeEncodeError, ascharmapstring, 'a', {97: None})
self.assertRaises(TypeError, ascharmapstring, b'a', {})
self.assertRaises(TypeError, ascharmapstring, [], {})
self.assertRaises(TypeError, ascharmapstring, 'a', NULL)
# CRASHES ascharmapstring(NULL, {})
def test_decodeunicodeescape(self):
"""Test PyUnicode_DecodeUnicodeEscape()"""
decodeunicodeescape = _testlimitedcapi.unicode_decodeunicodeescape
self.assertEqual(decodeunicodeescape(b'abc'), 'abc')
self.assertEqual(decodeunicodeescape(br'\t\n\r\x0b\x0c\x00\\'), '\t\n\r\v\f\0\\')
self.assertEqual(decodeunicodeescape(b'\t\n\r\x0b\x0c\x00'), '\t\n\r\v\f\0')
self.assertEqual(decodeunicodeescape(br'\xa1\xa2'), '\xa1\xa2')
self.assertEqual(decodeunicodeescape(b'\xa1\xa2'), '\xa1\xa2')
self.assertEqual(decodeunicodeescape(br'\u4f60\u597d'), '\u4f60\u597d')
self.assertEqual(decodeunicodeescape(br'\U0001f600'), '\U0001f600')
with self.assertWarns(DeprecationWarning):
self.assertEqual(decodeunicodeescape(br'\z'), r'\z')
for b in b'\\', br'\xa', br'\u4f6', br'\U0001f60':
self.assertRaises(UnicodeDecodeError, decodeunicodeescape, b)
self.assertRaises(UnicodeDecodeError, decodeunicodeescape, b, 'strict')
self.assertEqual(decodeunicodeescape(br'x\U0001f60', 'replace'), 'x\ufffd')
self.assertEqual(decodeunicodeescape(br'x\U0001f60y', 'replace'), 'x\ufffdy')
self.assertRaises(LookupError, decodeunicodeescape, b'\\', 'foo')
# TODO: Test PyUnicode_DecodeUnicodeEscape() with NULL as data and
# negative size.
def test_asunicodeescapestring(self):
"""Test PyUnicode_AsUnicodeEscapeString()"""
asunicodeescapestring = _testlimitedcapi.unicode_asunicodeescapestring
self.assertEqual(asunicodeescapestring('abc'), b'abc')
self.assertEqual(asunicodeescapestring('\t\n\r\v\f\0\\'), br'\t\n\r\x0b\x0c\x00\\')
self.assertEqual(asunicodeescapestring('\xa1\xa2'), br'\xa1\xa2')
self.assertEqual(asunicodeescapestring('\u4f60\u597d'), br'\u4f60\u597d')
self.assertEqual(asunicodeescapestring('\U0001f600'), br'\U0001f600')
self.assertRaises(TypeError, asunicodeescapestring, b'abc')
self.assertRaises(TypeError, asunicodeescapestring, [])
# CRASHES asunicodeescapestring(NULL)
def test_decoderawunicodeescape(self):
"""Test PyUnicode_DecodeRawUnicodeEscape()"""
decoderawunicodeescape = _testlimitedcapi.unicode_decoderawunicodeescape
self.assertEqual(decoderawunicodeescape(b'abc'), 'abc')
self.assertEqual(decoderawunicodeescape(b'\t\n\r\v\f\0\\'), '\t\n\r\v\f\0\\')
self.assertEqual(decoderawunicodeescape(b'\xa1\xa2'), '\xa1\xa2')
self.assertEqual(decoderawunicodeescape(br'\u4f60\u597d'), '\u4f60\u597d')
self.assertEqual(decoderawunicodeescape(br'\U0001f600'), '\U0001f600')
self.assertEqual(decoderawunicodeescape(br'\xa1\xa2'), r'\xa1\xa2')
self.assertEqual(decoderawunicodeescape(br'\z'), r'\z')
for b in br'\u4f6', br'\U0001f60':
self.assertRaises(UnicodeDecodeError, decoderawunicodeescape, b)
self.assertRaises(UnicodeDecodeError, decoderawunicodeescape, b, 'strict')
self.assertEqual(decoderawunicodeescape(br'x\U0001f60', 'replace'), 'x\ufffd')
self.assertEqual(decoderawunicodeescape(br'x\U0001f60y', 'replace'), 'x\ufffdy')
self.assertRaises(LookupError, decoderawunicodeescape, br'\U0001f60', 'foo')
# TODO: Test PyUnicode_DecodeRawUnicodeEscape() with NULL as data and
# negative size.
def test_asrawunicodeescapestring(self):
"""Test PyUnicode_AsRawUnicodeEscapeString()"""
asrawunicodeescapestring = _testlimitedcapi.unicode_asrawunicodeescapestring
self.assertEqual(asrawunicodeescapestring('abc'), b'abc')
self.assertEqual(asrawunicodeescapestring('\t\n\r\v\f\0\\'), b'\t\n\r\v\f\0\\')
self.assertEqual(asrawunicodeescapestring('\xa1\xa2'), b'\xa1\xa2')
self.assertEqual(asrawunicodeescapestring('\u4f60\u597d'), br'\u4f60\u597d')
self.assertEqual(asrawunicodeescapestring('\U0001f600'), br'\U0001f600')
self.assertRaises(TypeError, asrawunicodeescapestring, b'abc')
self.assertRaises(TypeError, asrawunicodeescapestring, [])
# CRASHES asrawunicodeescapestring(NULL)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,251 @@
from math import isnan
import errno
import unittest
import warnings
from test.test_capi.test_getargs import (BadComplex, BadComplex2, Complex,
FloatSubclass, Float, BadFloat,
BadFloat2, ComplexSubclass)
from test.support import import_helper
_testcapi = import_helper.import_module('_testcapi')
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
NULL = None
INF = float("inf")
NAN = float("nan")
DBL_MAX = _testcapi.DBL_MAX
class BadComplex3:
def __complex__(self):
raise RuntimeError
class CAPIComplexTest(unittest.TestCase):
def test_check(self):
# Test PyComplex_Check()
check = _testlimitedcapi.complex_check
self.assertTrue(check(1+2j))
self.assertTrue(check(ComplexSubclass(1+2j)))
self.assertFalse(check(Complex()))
self.assertFalse(check(3))
self.assertFalse(check(3.0))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_checkexact(self):
# PyComplex_CheckExact()
checkexact = _testlimitedcapi.complex_checkexact
self.assertTrue(checkexact(1+2j))
self.assertFalse(checkexact(ComplexSubclass(1+2j)))
self.assertFalse(checkexact(Complex()))
self.assertFalse(checkexact(3))
self.assertFalse(checkexact(3.0))
self.assertFalse(checkexact(object()))
# CRASHES checkexact(NULL)
def test_fromccomplex(self):
# Test PyComplex_FromCComplex()
fromccomplex = _testcapi.complex_fromccomplex
self.assertEqual(fromccomplex(1+2j), 1.0+2.0j)
def test_fromdoubles(self):
# Test PyComplex_FromDoubles()
fromdoubles = _testlimitedcapi.complex_fromdoubles
self.assertEqual(fromdoubles(1.0, 2.0), 1.0+2.0j)
def test_realasdouble(self):
# Test PyComplex_RealAsDouble()
realasdouble = _testlimitedcapi.complex_realasdouble
self.assertEqual(realasdouble(1+2j), 1.0)
self.assertEqual(realasdouble(-1+0j), -1.0)
self.assertEqual(realasdouble(4.25), 4.25)
self.assertEqual(realasdouble(-1.0), -1.0)
self.assertEqual(realasdouble(42), 42.)
self.assertEqual(realasdouble(-1), -1.0)
# Test subclasses of complex/float
self.assertEqual(realasdouble(ComplexSubclass(1+2j)), 1.0)
self.assertEqual(realasdouble(FloatSubclass(4.25)), 4.25)
# Test types with __complex__ dunder method
self.assertEqual(realasdouble(Complex()), 4.25)
self.assertRaises(TypeError, realasdouble, BadComplex())
with self.assertWarns(DeprecationWarning):
self.assertEqual(realasdouble(BadComplex2()), 4.25)
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
self.assertRaises(DeprecationWarning, realasdouble, BadComplex2())
self.assertRaises(RuntimeError, realasdouble, BadComplex3())
# Test types with __float__ dunder method
self.assertEqual(realasdouble(Float()), 4.25)
self.assertRaises(TypeError, realasdouble, BadFloat())
with self.assertWarns(DeprecationWarning):
self.assertEqual(realasdouble(BadFloat2()), 4.25)
self.assertRaises(TypeError, realasdouble, object())
# CRASHES realasdouble(NULL)
def test_imagasdouble(self):
# Test PyComplex_ImagAsDouble()
imagasdouble = _testlimitedcapi.complex_imagasdouble
self.assertEqual(imagasdouble(1+2j), 2.0)
self.assertEqual(imagasdouble(1-1j), -1.0)
self.assertEqual(imagasdouble(4.25), 0.0)
self.assertEqual(imagasdouble(42), 0.0)
# Test subclasses of complex/float
self.assertEqual(imagasdouble(ComplexSubclass(1+2j)), 2.0)
self.assertEqual(imagasdouble(FloatSubclass(4.25)), 0.0)
# Test types with __complex__ dunder method
self.assertEqual(imagasdouble(Complex()), 0.5)
self.assertRaises(TypeError, imagasdouble, BadComplex())
with self.assertWarns(DeprecationWarning):
self.assertEqual(imagasdouble(BadComplex2()), 0.5)
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
self.assertRaises(DeprecationWarning, imagasdouble, BadComplex2())
self.assertRaises(RuntimeError, imagasdouble, BadComplex3())
# Test types with __float__ dunder method
self.assertEqual(imagasdouble(Float()), 0.0)
self.assertRaises(TypeError, imagasdouble, BadFloat())
with self.assertWarns(DeprecationWarning):
self.assertEqual(imagasdouble(BadFloat2()), 0.0)
self.assertRaises(TypeError, imagasdouble, object())
# CRASHES imagasdouble(NULL)
def test_asccomplex(self):
# Test PyComplex_AsCComplex()
asccomplex = _testcapi.complex_asccomplex
self.assertEqual(asccomplex(1+2j), 1.0+2.0j)
self.assertEqual(asccomplex(-1+2j), -1.0+2.0j)
self.assertEqual(asccomplex(4.25), 4.25+0.0j)
self.assertEqual(asccomplex(-1.0), -1.0+0.0j)
self.assertEqual(asccomplex(42), 42+0j)
self.assertEqual(asccomplex(-1), -1.0+0.0j)
# Test subclasses of complex/float
self.assertEqual(asccomplex(ComplexSubclass(1+2j)), 1.0+2.0j)
self.assertEqual(asccomplex(FloatSubclass(4.25)), 4.25+0.0j)
# Test types with __complex__ dunder method
self.assertEqual(asccomplex(Complex()), 4.25+0.5j)
self.assertRaises(TypeError, asccomplex, BadComplex())
with self.assertWarns(DeprecationWarning):
self.assertEqual(asccomplex(BadComplex2()), 4.25+0.5j)
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
self.assertRaises(DeprecationWarning, asccomplex, BadComplex2())
self.assertRaises(RuntimeError, asccomplex, BadComplex3())
# Test types with __float__ dunder method
self.assertEqual(asccomplex(Float()), 4.25+0.0j)
self.assertRaises(TypeError, asccomplex, BadFloat())
with self.assertWarns(DeprecationWarning):
self.assertEqual(asccomplex(BadFloat2()), 4.25+0.0j)
self.assertRaises(TypeError, asccomplex, object())
# CRASHES asccomplex(NULL)
def test_py_c_sum(self):
# Test _Py_c_sum()
_py_c_sum = _testcapi._py_c_sum
self.assertEqual(_py_c_sum(1, 1j), (1+1j, 0))
def test_py_c_diff(self):
# Test _Py_c_diff()
_py_c_diff = _testcapi._py_c_diff
self.assertEqual(_py_c_diff(1, 1j), (1-1j, 0))
def test_py_c_neg(self):
# Test _Py_c_neg()
_py_c_neg = _testcapi._py_c_neg
self.assertEqual(_py_c_neg(1+1j), -1-1j)
def test_py_c_prod(self):
# Test _Py_c_prod()
_py_c_prod = _testcapi._py_c_prod
self.assertEqual(_py_c_prod(2, 1j), (2j, 0))
def test_py_c_quot(self):
# Test _Py_c_quot()
_py_c_quot = _testcapi._py_c_quot
self.assertEqual(_py_c_quot(1, 1j), (-1j, 0))
self.assertEqual(_py_c_quot(1, -1j), (1j, 0))
self.assertEqual(_py_c_quot(1j, 2), (0.5j, 0))
self.assertEqual(_py_c_quot(1j, -2), (-0.5j, 0))
self.assertEqual(_py_c_quot(1, 2j), (-0.5j, 0))
z, e = _py_c_quot(NAN, 1j)
self.assertTrue(isnan(z.real))
self.assertTrue(isnan(z.imag))
self.assertEqual(e, 0)
z, e = _py_c_quot(1j, NAN)
self.assertTrue(isnan(z.real))
self.assertTrue(isnan(z.imag))
self.assertEqual(e, 0)
self.assertEqual(_py_c_quot(1, 0j)[1], errno.EDOM)
def test_py_c_pow(self):
# Test _Py_c_pow()
_py_c_pow = _testcapi._py_c_pow
self.assertEqual(_py_c_pow(1j, 0j), (1+0j, 0))
self.assertEqual(_py_c_pow(1, 1j), (1+0j, 0))
self.assertEqual(_py_c_pow(0j, 1), (0j, 0))
self.assertAlmostEqual(_py_c_pow(1j, 2)[0], -1.0+0j)
r, e = _py_c_pow(1+1j, -1)
self.assertAlmostEqual(r, 0.5-0.5j)
self.assertEqual(e, 0)
self.assertEqual(_py_c_pow(0j, -1)[1], errno.EDOM)
self.assertEqual(_py_c_pow(0j, 1j)[1], errno.EDOM)
self.assertEqual(_py_c_pow(*[DBL_MAX+1j]*2)[0], complex(*[INF]*2))
def test_py_c_abs(self):
# Test _Py_c_abs()
_py_c_abs = _testcapi._py_c_abs
self.assertEqual(_py_c_abs(-1), (1.0, 0))
self.assertEqual(_py_c_abs(1j), (1.0, 0))
self.assertEqual(_py_c_abs(complex('+inf+1j')), (INF, 0))
self.assertEqual(_py_c_abs(complex('-inf+1j')), (INF, 0))
self.assertEqual(_py_c_abs(complex('1.25+infj')), (INF, 0))
self.assertEqual(_py_c_abs(complex('1.25-infj')), (INF, 0))
self.assertTrue(isnan(_py_c_abs(complex('1.25+nanj'))[0]))
self.assertTrue(isnan(_py_c_abs(complex('nan-1j'))[0]))
self.assertEqual(_py_c_abs(complex(*[DBL_MAX]*2))[1], errno.ERANGE)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,550 @@
import unittest
from collections import OrderedDict, UserDict
from types import MappingProxyType
from test import support
from test.support import import_helper
_testcapi = import_helper.import_module("_testcapi")
_testlimitedcapi = import_helper.import_module("_testlimitedcapi")
NULL = None
INVALID_UTF8 = b'\xff'
class DictSubclass(dict):
def __getitem__(self, key):
raise RuntimeError('do not get evil')
def __setitem__(self, key, value):
raise RuntimeError('do not set evil')
def __delitem__(self, key):
raise RuntimeError('do not del evil')
def gen():
yield 'a'
yield 'b'
yield 'c'
class CAPITest(unittest.TestCase):
def test_dict_check(self):
check = _testlimitedcapi.dict_check
self.assertTrue(check({1: 2}))
self.assertTrue(check(OrderedDict({1: 2})))
self.assertFalse(check(UserDict({1: 2})))
self.assertFalse(check([1, 2]))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_dict_checkexact(self):
check = _testlimitedcapi.dict_checkexact
self.assertTrue(check({1: 2}))
self.assertFalse(check(OrderedDict({1: 2})))
self.assertFalse(check(UserDict({1: 2})))
self.assertFalse(check([1, 2]))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_dict_new(self):
dict_new = _testlimitedcapi.dict_new
dct = dict_new()
self.assertEqual(dct, {})
self.assertIs(type(dct), dict)
dct2 = dict_new()
self.assertIsNot(dct2, dct)
def test_dictproxy_new(self):
dictproxy_new = _testlimitedcapi.dictproxy_new
for dct in {1: 2}, OrderedDict({1: 2}), UserDict({1: 2}):
proxy = dictproxy_new(dct)
self.assertIs(type(proxy), MappingProxyType)
self.assertEqual(proxy, dct)
with self.assertRaises(TypeError):
proxy[1] = 3
self.assertEqual(proxy[1], 2)
dct[1] = 4
self.assertEqual(proxy[1], 4)
self.assertRaises(TypeError, dictproxy_new, [])
self.assertRaises(TypeError, dictproxy_new, 42)
# CRASHES dictproxy_new(NULL)
def test_dict_copy(self):
copy = _testlimitedcapi.dict_copy
for dct in {1: 2}, OrderedDict({1: 2}):
dct_copy = copy(dct)
self.assertIs(type(dct_copy), dict)
self.assertEqual(dct_copy, dct)
self.assertRaises(SystemError, copy, UserDict())
self.assertRaises(SystemError, copy, [])
self.assertRaises(SystemError, copy, 42)
self.assertRaises(SystemError, copy, NULL)
def test_dict_clear(self):
clear = _testlimitedcapi.dict_clear
dct = {1: 2}
clear(dct)
self.assertEqual(dct, {})
# NOTE: It is not safe to call it with OrderedDict.
# Has no effect for non-dicts.
dct = UserDict({1: 2})
clear(dct)
self.assertEqual(dct, {1: 2})
lst = [1, 2]
clear(lst)
self.assertEqual(lst, [1, 2])
clear(object())
# CRASHES? clear(NULL)
def test_dict_size(self):
size = _testlimitedcapi.dict_size
self.assertEqual(size({1: 2}), 1)
self.assertEqual(size(OrderedDict({1: 2})), 1)
self.assertRaises(SystemError, size, UserDict())
self.assertRaises(SystemError, size, [])
self.assertRaises(SystemError, size, 42)
self.assertRaises(SystemError, size, object())
self.assertRaises(SystemError, size, NULL)
def test_dict_getitem(self):
getitem = _testlimitedcapi.dict_getitem
dct = {'a': 1, '\U0001f40d': 2}
self.assertEqual(getitem(dct, 'a'), 1)
self.assertIs(getitem(dct, 'b'), KeyError)
self.assertEqual(getitem(dct, '\U0001f40d'), 2)
dct2 = DictSubclass(dct)
self.assertEqual(getitem(dct2, 'a'), 1)
self.assertIs(getitem(dct2, 'b'), KeyError)
with support.catch_unraisable_exception() as cm:
self.assertIs(getitem({}, []), KeyError) # unhashable
self.assertEqual(cm.unraisable.exc_type, TypeError)
self.assertEqual(str(cm.unraisable.exc_value),
"unhashable type: 'list'")
self.assertIs(getitem(42, 'a'), KeyError)
self.assertIs(getitem([1], 0), KeyError)
# CRASHES getitem({}, NULL)
# CRASHES getitem(NULL, 'a')
def test_dict_getitemstring(self):
getitemstring = _testlimitedcapi.dict_getitemstring
dct = {'a': 1, '\U0001f40d': 2}
self.assertEqual(getitemstring(dct, b'a'), 1)
self.assertIs(getitemstring(dct, b'b'), KeyError)
self.assertEqual(getitemstring(dct, '\U0001f40d'.encode()), 2)
dct2 = DictSubclass(dct)
self.assertEqual(getitemstring(dct2, b'a'), 1)
self.assertIs(getitemstring(dct2, b'b'), KeyError)
with support.catch_unraisable_exception() as cm:
self.assertIs(getitemstring({}, INVALID_UTF8), KeyError)
self.assertEqual(cm.unraisable.exc_type, UnicodeDecodeError)
self.assertRegex(str(cm.unraisable.exc_value),
"'utf-8' codec can't decode")
self.assertIs(getitemstring(42, b'a'), KeyError)
self.assertIs(getitemstring([], b'a'), KeyError)
# CRASHES getitemstring({}, NULL)
# CRASHES getitemstring(NULL, b'a')
def test_dict_getitemref(self):
getitem = _testcapi.dict_getitemref
dct = {'a': 1, '\U0001f40d': 2}
self.assertEqual(getitem(dct, 'a'), 1)
self.assertIs(getitem(dct, 'b'), KeyError)
self.assertEqual(getitem(dct, '\U0001f40d'), 2)
dct2 = DictSubclass(dct)
self.assertEqual(getitem(dct2, 'a'), 1)
self.assertIs(getitem(dct2, 'b'), KeyError)
self.assertRaises(SystemError, getitem, 42, 'a')
self.assertRaises(TypeError, getitem, {}, []) # unhashable
self.assertRaises(SystemError, getitem, [], 1)
self.assertRaises(SystemError, getitem, [], 'a')
# CRASHES getitem({}, NULL)
# CRASHES getitem(NULL, 'a')
def test_dict_getitemstringref(self):
getitemstring = _testcapi.dict_getitemstringref
dct = {'a': 1, '\U0001f40d': 2}
self.assertEqual(getitemstring(dct, b'a'), 1)
self.assertIs(getitemstring(dct, b'b'), KeyError)
self.assertEqual(getitemstring(dct, '\U0001f40d'.encode()), 2)
dct2 = DictSubclass(dct)
self.assertEqual(getitemstring(dct2, b'a'), 1)
self.assertIs(getitemstring(dct2, b'b'), KeyError)
self.assertRaises(SystemError, getitemstring, 42, b'a')
self.assertRaises(UnicodeDecodeError, getitemstring, {}, INVALID_UTF8)
self.assertRaises(SystemError, getitemstring, [], b'a')
# CRASHES getitemstring({}, NULL)
# CRASHES getitemstring(NULL, b'a')
def test_dict_getitemwitherror(self):
getitem = _testlimitedcapi.dict_getitemwitherror
dct = {'a': 1, '\U0001f40d': 2}
self.assertEqual(getitem(dct, 'a'), 1)
self.assertIs(getitem(dct, 'b'), KeyError)
self.assertEqual(getitem(dct, '\U0001f40d'), 2)
dct2 = DictSubclass(dct)
self.assertEqual(getitem(dct2, 'a'), 1)
self.assertIs(getitem(dct2, 'b'), KeyError)
self.assertRaises(SystemError, getitem, 42, 'a')
self.assertRaises(TypeError, getitem, {}, []) # unhashable
self.assertRaises(SystemError, getitem, [], 1)
self.assertRaises(SystemError, getitem, [], 'a')
# CRASHES getitem({}, NULL)
# CRASHES getitem(NULL, 'a')
def test_dict_contains(self):
contains = _testlimitedcapi.dict_contains
dct = {'a': 1, '\U0001f40d': 2}
self.assertTrue(contains(dct, 'a'))
self.assertFalse(contains(dct, 'b'))
self.assertTrue(contains(dct, '\U0001f40d'))
dct2 = DictSubclass(dct)
self.assertTrue(contains(dct2, 'a'))
self.assertFalse(contains(dct2, 'b'))
self.assertRaises(TypeError, contains, {}, []) # unhashable
# CRASHES contains({}, NULL)
# CRASHES contains(UserDict(), 'a')
# CRASHES contains(42, 'a')
# CRASHES contains(NULL, 'a')
def test_dict_contains_string(self):
contains_string = _testcapi.dict_containsstring
dct = {'a': 1, '\U0001f40d': 2}
self.assertTrue(contains_string(dct, b'a'))
self.assertFalse(contains_string(dct, b'b'))
self.assertTrue(contains_string(dct, '\U0001f40d'.encode()))
self.assertRaises(UnicodeDecodeError, contains_string, dct, INVALID_UTF8)
dct2 = DictSubclass(dct)
self.assertTrue(contains_string(dct2, b'a'))
self.assertFalse(contains_string(dct2, b'b'))
# CRASHES contains({}, NULL)
# CRASHES contains(NULL, b'a')
def test_dict_setitem(self):
setitem = _testlimitedcapi.dict_setitem
dct = {}
setitem(dct, 'a', 5)
self.assertEqual(dct, {'a': 5})
setitem(dct, '\U0001f40d', 8)
self.assertEqual(dct, {'a': 5, '\U0001f40d': 8})
dct2 = DictSubclass()
setitem(dct2, 'a', 5)
self.assertEqual(dct2, {'a': 5})
self.assertRaises(TypeError, setitem, {}, [], 5) # unhashable
self.assertRaises(SystemError, setitem, UserDict(), 'a', 5)
self.assertRaises(SystemError, setitem, [1], 0, 5)
self.assertRaises(SystemError, setitem, 42, 'a', 5)
# CRASHES setitem({}, NULL, 5)
# CRASHES setitem({}, 'a', NULL)
# CRASHES setitem(NULL, 'a', 5)
def test_dict_setitemstring(self):
setitemstring = _testlimitedcapi.dict_setitemstring
dct = {}
setitemstring(dct, b'a', 5)
self.assertEqual(dct, {'a': 5})
setitemstring(dct, '\U0001f40d'.encode(), 8)
self.assertEqual(dct, {'a': 5, '\U0001f40d': 8})
dct2 = DictSubclass()
setitemstring(dct2, b'a', 5)
self.assertEqual(dct2, {'a': 5})
self.assertRaises(UnicodeDecodeError, setitemstring, {}, INVALID_UTF8, 5)
self.assertRaises(SystemError, setitemstring, UserDict(), b'a', 5)
self.assertRaises(SystemError, setitemstring, 42, b'a', 5)
# CRASHES setitemstring({}, NULL, 5)
# CRASHES setitemstring({}, b'a', NULL)
# CRASHES setitemstring(NULL, b'a', 5)
def test_dict_delitem(self):
delitem = _testlimitedcapi.dict_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})
dct2 = DictSubclass({'a': 1, 'c': 2})
delitem(dct2, 'a')
self.assertEqual(dct2, {'c': 2})
self.assertRaises(KeyError, delitem, dct2, 'b')
self.assertRaises(TypeError, delitem, {}, []) # unhashable
self.assertRaises(SystemError, delitem, UserDict({'a': 1}), 'a')
self.assertRaises(SystemError, delitem, [1], 0)
self.assertRaises(SystemError, delitem, 42, 'a')
# CRASHES delitem({}, NULL)
# CRASHES delitem(NULL, 'a')
def test_dict_delitemstring(self):
delitemstring = _testlimitedcapi.dict_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})
dct2 = DictSubclass({'a': 1, 'c': 2})
delitemstring(dct2, b'a')
self.assertEqual(dct2, {'c': 2})
self.assertRaises(KeyError, delitemstring, dct2, b'b')
self.assertRaises(UnicodeDecodeError, delitemstring, {}, INVALID_UTF8)
self.assertRaises(SystemError, delitemstring, UserDict({'a': 1}), b'a')
self.assertRaises(SystemError, delitemstring, 42, b'a')
# CRASHES delitemstring({}, NULL)
# CRASHES delitemstring(NULL, b'a')
def test_dict_setdefault(self):
setdefault = _testcapi.dict_setdefault
dct = {}
self.assertEqual(setdefault(dct, 'a', 5), 5)
self.assertEqual(dct, {'a': 5})
self.assertEqual(setdefault(dct, 'a', 8), 5)
self.assertEqual(dct, {'a': 5})
dct2 = DictSubclass()
self.assertEqual(setdefault(dct2, 'a', 5), 5)
self.assertEqual(dct2, {'a': 5})
self.assertEqual(setdefault(dct2, 'a', 8), 5)
self.assertEqual(dct2, {'a': 5})
self.assertRaises(TypeError, setdefault, {}, [], 5) # unhashable
self.assertRaises(SystemError, setdefault, UserDict(), 'a', 5)
self.assertRaises(SystemError, setdefault, [1], 0, 5)
self.assertRaises(SystemError, setdefault, 42, 'a', 5)
# CRASHES setdefault({}, NULL, 5)
# CRASHES setdefault({}, 'a', NULL)
# CRASHES setdefault(NULL, 'a', 5)
def test_dict_setdefaultref(self):
setdefault = _testcapi.dict_setdefaultref
dct = {}
self.assertEqual(setdefault(dct, 'a', 5), 5)
self.assertEqual(dct, {'a': 5})
self.assertEqual(setdefault(dct, 'a', 8), 5)
self.assertEqual(dct, {'a': 5})
dct2 = DictSubclass()
self.assertEqual(setdefault(dct2, 'a', 5), 5)
self.assertEqual(dct2, {'a': 5})
self.assertEqual(setdefault(dct2, 'a', 8), 5)
self.assertEqual(dct2, {'a': 5})
self.assertRaises(TypeError, setdefault, {}, [], 5) # unhashable
self.assertRaises(SystemError, setdefault, UserDict(), 'a', 5)
self.assertRaises(SystemError, setdefault, [1], 0, 5)
self.assertRaises(SystemError, setdefault, 42, 'a', 5)
# CRASHES setdefault({}, NULL, 5)
# CRASHES setdefault({}, 'a', NULL)
# CRASHES setdefault(NULL, 'a', 5)
def test_mapping_keys_valuesitems(self):
class BadMapping(dict):
def keys(self):
return None
def values(self):
return None
def items(self):
return None
dict_obj = {'foo': 1, 'bar': 2, 'spam': 3}
for mapping in [dict_obj, DictSubclass(dict_obj), BadMapping(dict_obj)]:
self.assertListEqual(_testlimitedcapi.dict_keys(mapping),
list(dict_obj.keys()))
self.assertListEqual(_testlimitedcapi.dict_values(mapping),
list(dict_obj.values()))
self.assertListEqual(_testlimitedcapi.dict_items(mapping),
list(dict_obj.items()))
def test_dict_keys_valuesitems_bad_arg(self):
for mapping in UserDict(), [], object():
self.assertRaises(SystemError, _testlimitedcapi.dict_keys, mapping)
self.assertRaises(SystemError, _testlimitedcapi.dict_values, mapping)
self.assertRaises(SystemError, _testlimitedcapi.dict_items, mapping)
def test_dict_next(self):
dict_next = _testlimitedcapi.dict_next
self.assertIsNone(dict_next({}, 0))
dct = {'a': 1, 'b': 2, 'c': 3}
pos = 0
pairs = []
while True:
res = dict_next(dct, pos)
if res is None:
break
rc, pos, key, value = res
self.assertEqual(rc, 1)
pairs.append((key, value))
self.assertEqual(pairs, list(dct.items()))
# CRASHES dict_next(NULL, 0)
def test_dict_update(self):
update = _testlimitedcapi.dict_update
for cls1 in dict, DictSubclass:
for cls2 in dict, DictSubclass, UserDict:
dct = cls1({'a': 1, 'b': 2})
update(dct, cls2({'b': 3, 'c': 4}))
self.assertEqual(dct, {'a': 1, 'b': 3, 'c': 4})
self.assertRaises(AttributeError, update, {}, [])
self.assertRaises(AttributeError, update, {}, 42)
self.assertRaises(SystemError, update, UserDict(), {})
self.assertRaises(SystemError, update, 42, {})
self.assertRaises(SystemError, update, {}, NULL)
self.assertRaises(SystemError, update, NULL, {})
def test_dict_merge(self):
merge = _testlimitedcapi.dict_merge
for cls1 in dict, DictSubclass:
for cls2 in dict, DictSubclass, UserDict:
dct = cls1({'a': 1, 'b': 2})
merge(dct, cls2({'b': 3, 'c': 4}), 0)
self.assertEqual(dct, {'a': 1, 'b': 2, 'c': 4})
dct = cls1({'a': 1, 'b': 2})
merge(dct, cls2({'b': 3, 'c': 4}), 1)
self.assertEqual(dct, {'a': 1, 'b': 3, 'c': 4})
self.assertRaises(AttributeError, merge, {}, [], 0)
self.assertRaises(AttributeError, merge, {}, 42, 0)
self.assertRaises(SystemError, merge, UserDict(), {}, 0)
self.assertRaises(SystemError, merge, 42, {}, 0)
self.assertRaises(SystemError, merge, {}, NULL, 0)
self.assertRaises(SystemError, merge, NULL, {}, 0)
def test_dict_mergefromseq2(self):
mergefromseq2 = _testlimitedcapi.dict_mergefromseq2
for cls1 in dict, DictSubclass:
for cls2 in list, iter:
dct = cls1({'a': 1, 'b': 2})
mergefromseq2(dct, cls2([('b', 3), ('c', 4)]), 0)
self.assertEqual(dct, {'a': 1, 'b': 2, 'c': 4})
dct = cls1({'a': 1, 'b': 2})
mergefromseq2(dct, cls2([('b', 3), ('c', 4)]), 1)
self.assertEqual(dct, {'a': 1, 'b': 3, 'c': 4})
self.assertRaises(ValueError, mergefromseq2, {}, [(1,)], 0)
self.assertRaises(ValueError, mergefromseq2, {}, [(1, 2, 3)], 0)
self.assertRaises(TypeError, mergefromseq2, {}, [1], 0)
self.assertRaises(TypeError, mergefromseq2, {}, 42, 0)
# CRASHES mergefromseq2(UserDict(), [], 0)
# CRASHES mergefromseq2(42, [], 0)
# CRASHES mergefromseq2({}, NULL, 0)
# CRASHES mergefromseq2(NULL, {}, 0)
def test_dict_pop(self):
# Test PyDict_Pop()
dict_pop = _testcapi.dict_pop
dict_pop_null = _testcapi.dict_pop_null
# key present, get removed value
mydict = {"key": "value", "key2": "value2"}
self.assertEqual(dict_pop(mydict, "key"), (1, "value"))
self.assertEqual(mydict, {"key2": "value2"})
self.assertEqual(dict_pop(mydict, "key2"), (1, "value2"))
self.assertEqual(mydict, {})
# key present, ignore removed value
mydict = {"key": "value", "key2": "value2"}
self.assertEqual(dict_pop_null(mydict, "key"), 1)
self.assertEqual(mydict, {"key2": "value2"})
self.assertEqual(dict_pop_null(mydict, "key2"), 1)
self.assertEqual(mydict, {})
# key missing, expect removed value; empty dict has a fast path
self.assertEqual(dict_pop({}, "key"), (0, NULL))
self.assertEqual(dict_pop({"a": 1}, "key"), (0, NULL))
# key missing, ignored removed value; empty dict has a fast path
self.assertEqual(dict_pop_null({}, "key"), 0)
self.assertEqual(dict_pop_null({"a": 1}, "key"), 0)
# dict error
not_dict = UserDict({1: 2})
self.assertRaises(SystemError, dict_pop, not_dict, "key")
self.assertRaises(SystemError, dict_pop_null, not_dict, "key")
# key error; don't hash key if dict is empty
not_hashable_key = ["list"]
self.assertEqual(dict_pop({}, not_hashable_key), (0, NULL))
with self.assertRaises(TypeError):
dict_pop({'key': 1}, not_hashable_key)
dict_pop({}, NULL) # key is not checked if dict is empty
# CRASHES dict_pop(NULL, "key")
# CRASHES dict_pop({"a": 1}, NULL)
def test_dict_popstring(self):
# Test PyDict_PopString()
dict_popstring = _testcapi.dict_popstring
dict_popstring_null = _testcapi.dict_popstring_null
# key present, get removed value
mydict = {"key": "value", "key2": "value2"}
self.assertEqual(dict_popstring(mydict, "key"), (1, "value"))
self.assertEqual(mydict, {"key2": "value2"})
self.assertEqual(dict_popstring(mydict, "key2"), (1, "value2"))
self.assertEqual(mydict, {})
# key present, ignore removed value
mydict = {"key": "value", "key2": "value2"}
self.assertEqual(dict_popstring_null(mydict, "key"), 1)
self.assertEqual(mydict, {"key2": "value2"})
self.assertEqual(dict_popstring_null(mydict, "key2"), 1)
self.assertEqual(mydict, {})
# key missing; empty dict has a fast path
self.assertEqual(dict_popstring({}, "key"), (0, NULL))
self.assertEqual(dict_popstring_null({}, "key"), 0)
self.assertEqual(dict_popstring({"a": 1}, "key"), (0, NULL))
self.assertEqual(dict_popstring_null({"a": 1}, "key"), 0)
# non-ASCII key
non_ascii = '\U0001f40d'
dct = {'\U0001f40d': 123}
self.assertEqual(dict_popstring(dct, '\U0001f40d'.encode()), (1, 123))
dct = {'\U0001f40d': 123}
self.assertEqual(dict_popstring_null(dct, '\U0001f40d'.encode()), 1)
# dict error
not_dict = UserDict({1: 2})
self.assertRaises(SystemError, dict_popstring, not_dict, "key")
self.assertRaises(SystemError, dict_popstring_null, not_dict, "key")
# key error
self.assertRaises(UnicodeDecodeError, dict_popstring, {1: 2}, INVALID_UTF8)
self.assertRaises(UnicodeDecodeError, dict_popstring_null, {1: 2}, INVALID_UTF8)
# CRASHES dict_popstring(NULL, "key")
# CRASHES dict_popstring({}, NULL)
# CRASHES dict_popstring({"a": 1}, NULL)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,103 @@
import sys
import unittest
from test.support import import_helper
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
class Tests(unittest.TestCase):
def test_eval_get_func_name(self):
eval_get_func_name = _testlimitedcapi.eval_get_func_name
def function_example(): ...
class A:
def method_example(self): ...
self.assertEqual(eval_get_func_name(function_example),
"function_example")
self.assertEqual(eval_get_func_name(A.method_example),
"method_example")
self.assertEqual(eval_get_func_name(A().method_example),
"method_example")
self.assertEqual(eval_get_func_name(sum), "sum") # c function
self.assertEqual(eval_get_func_name(A), "type")
def test_eval_get_func_desc(self):
eval_get_func_desc = _testlimitedcapi.eval_get_func_desc
def function_example(): ...
class A:
def method_example(self): ...
self.assertEqual(eval_get_func_desc(function_example),
"()")
self.assertEqual(eval_get_func_desc(A.method_example),
"()")
self.assertEqual(eval_get_func_desc(A().method_example),
"()")
self.assertEqual(eval_get_func_desc(sum), "()") # c function
self.assertEqual(eval_get_func_desc(A), " object")
def test_eval_getlocals(self):
# Test PyEval_GetLocals()
x = 1
self.assertEqual(_testlimitedcapi.eval_getlocals(),
{'self': self,
'x': 1})
y = 2
self.assertEqual(_testlimitedcapi.eval_getlocals(),
{'self': self,
'x': 1,
'y': 2})
def test_eval_getglobals(self):
# Test PyEval_GetGlobals()
self.assertEqual(_testlimitedcapi.eval_getglobals(),
globals())
def test_eval_getbuiltins(self):
# Test PyEval_GetBuiltins()
self.assertEqual(_testlimitedcapi.eval_getbuiltins(),
globals()['__builtins__'])
def test_eval_getframe(self):
# Test PyEval_GetFrame()
self.assertEqual(_testlimitedcapi.eval_getframe(),
sys._getframe())
def test_eval_getframe_builtins(self):
# Test PyEval_GetFrameBuiltins()
self.assertEqual(_testlimitedcapi.eval_getframe_builtins(),
sys._getframe().f_builtins)
def test_eval_getframe_globals(self):
# Test PyEval_GetFrameGlobals()
self.assertEqual(_testlimitedcapi.eval_getframe_globals(),
sys._getframe().f_globals)
def test_eval_getframe_locals(self):
# Test PyEval_GetFrameLocals()
self.assertEqual(_testlimitedcapi.eval_getframe_locals(),
sys._getframe().f_locals)
def test_eval_get_recursion_limit(self):
# Test Py_GetRecursionLimit()
self.assertEqual(_testlimitedcapi.eval_get_recursion_limit(),
sys.getrecursionlimit())
def test_eval_set_recursion_limit(self):
# Test Py_SetRecursionLimit()
old_limit = sys.getrecursionlimit()
try:
limit = old_limit + 123
_testlimitedcapi.eval_set_recursion_limit(limit)
self.assertEqual(sys.getrecursionlimit(), limit)
finally:
sys.setrecursionlimit(old_limit)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,126 @@
import unittest
import builtins
from collections import UserDict
from test.support import import_helper
from test.support import swap_attr
# Skip this test if the _testcapi module isn't available.
_testcapi = import_helper.import_module('_testcapi')
NULL = None
class PyEval_EvalCodeExTests(unittest.TestCase):
def test_simple(self):
def f():
return a
eval_code_ex = _testcapi.eval_code_ex
code = f.__code__
self.assertEqual(eval_code_ex(code, dict(a=1)), 1)
self.assertRaises(NameError, eval_code_ex, code, {})
self.assertRaises(SystemError, eval_code_ex, code, UserDict(a=1))
self.assertRaises(SystemError, eval_code_ex, code, [])
self.assertRaises(SystemError, eval_code_ex, code, 1)
# CRASHES eval_code_ex(code, NULL)
# CRASHES eval_code_ex(1, {})
# CRASHES eval_code_ex(NULL, {})
def test_custom_locals(self):
# Monkey-patch __build_class__ to get a class code object.
code = None
def build_class(func, name, /, *bases, **kwds):
nonlocal code
code = func.__code__
with swap_attr(builtins, '__build_class__', build_class):
class A:
# Uses LOAD_NAME for a
r[:] = [a]
eval_code_ex = _testcapi.eval_code_ex
results = []
g = dict(a=1, r=results)
self.assertIsNone(eval_code_ex(code, g))
self.assertEqual(results, [1])
self.assertIsNone(eval_code_ex(code, g, dict(a=2)))
self.assertEqual(results, [2])
self.assertIsNone(eval_code_ex(code, g, UserDict(a=3)))
self.assertEqual(results, [3])
self.assertIsNone(eval_code_ex(code, g, {}))
self.assertEqual(results, [1])
self.assertIsNone(eval_code_ex(code, g, NULL))
self.assertEqual(results, [1])
self.assertRaises(TypeError, eval_code_ex, code, g, [])
self.assertRaises(TypeError, eval_code_ex, code, g, 1)
self.assertRaises(NameError, eval_code_ex, code, dict(r=results), {})
self.assertRaises(NameError, eval_code_ex, code, dict(r=results), NULL)
self.assertRaises(TypeError, eval_code_ex, code, dict(r=results), [])
self.assertRaises(TypeError, eval_code_ex, code, dict(r=results), 1)
def test_with_args(self):
def f(a, b, c):
return a
eval_code_ex = _testcapi.eval_code_ex
code = f.__code__
self.assertEqual(eval_code_ex(code, {}, {}, (1, 2, 3)), 1)
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (1, 2))
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (1, 2, 3, 4))
def test_with_kwargs(self):
def f(a, b, c):
return a
eval_code_ex = _testcapi.eval_code_ex
code = f.__code__
self.assertEqual(eval_code_ex(code, {}, {}, (), dict(a=1, b=2, c=3)), 1)
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), dict(a=1, b=2))
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), dict(a=1, b=2))
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), dict(a=1, b=2, c=3, d=4))
def test_with_default(self):
def f(a):
return a
eval_code_ex = _testcapi.eval_code_ex
code = f.__code__
self.assertEqual(eval_code_ex(code, {}, {}, (), {}, (1,)), 1)
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), {}, ())
def test_with_kwarg_default(self):
def f(*, a):
return a
eval_code_ex = _testcapi.eval_code_ex
code = f.__code__
self.assertEqual(eval_code_ex(code, {}, {}, (), {}, (), dict(a=1)), 1)
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), {}, (), {})
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), {}, (), NULL)
self.assertRaises(SystemError, eval_code_ex, code, {}, {}, (), {}, (), UserDict(a=1))
self.assertRaises(SystemError, eval_code_ex, code, {}, {}, (), {}, (), [])
self.assertRaises(SystemError, eval_code_ex, code, {}, {}, (), {}, (), 1)
def test_with_closure(self):
a = 1
b = 2
def f():
b
return a
eval_code_ex = _testcapi.eval_code_ex
code = f.__code__
self.assertEqual(eval_code_ex(code, {}, {}, (), {}, (), {}, f.__closure__), 1)
self.assertEqual(eval_code_ex(code, {}, {}, (), {}, (), {}, f.__closure__[::-1]), 2)
# CRASHES eval_code_ex(code, {}, {}, (), {}, (), {}, ()), 1)
# CRASHES eval_code_ex(code, {}, {}, (), {}, (), {}, NULL), 1)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,509 @@
import errno
import os
import re
import sys
import unittest
import textwrap
from test import support
from test.support import import_helper
from test.support.os_helper import TESTFN, TESTFN_UNDECODABLE
from test.support.script_helper import assert_python_failure, assert_python_ok
from test.support.testcase import ExceptionIsLikeMixin
from .test_misc import decode_stderr
# Skip this test if the _testcapi module isn't available.
_testcapi = import_helper.import_module('_testcapi')
NULL = None
class CustomError(Exception):
pass
class Test_Exceptions(unittest.TestCase):
def test_exception(self):
raised_exception = ValueError("5")
new_exc = TypeError("TEST")
try:
raise raised_exception
except ValueError as e:
orig_sys_exception = sys.exception()
orig_exception = _testcapi.set_exception(new_exc)
new_sys_exception = sys.exception()
new_exception = _testcapi.set_exception(orig_exception)
reset_sys_exception = sys.exception()
self.assertEqual(orig_exception, e)
self.assertEqual(orig_exception, raised_exception)
self.assertEqual(orig_sys_exception, orig_exception)
self.assertEqual(reset_sys_exception, orig_exception)
self.assertEqual(new_exception, new_exc)
self.assertEqual(new_sys_exception, new_exception)
else:
self.fail("Exception not raised")
def test_exc_info(self):
raised_exception = ValueError("5")
new_exc = TypeError("TEST")
try:
raise raised_exception
except ValueError as e:
tb = e.__traceback__
orig_sys_exc_info = sys.exc_info()
orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None)
new_sys_exc_info = sys.exc_info()
new_exc_info = _testcapi.set_exc_info(*orig_exc_info)
reset_sys_exc_info = sys.exc_info()
self.assertEqual(orig_exc_info[1], e)
self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb))
self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info)
self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info)
self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None))
self.assertSequenceEqual(new_sys_exc_info, new_exc_info)
else:
self.assertTrue(False)
def test_warn_with_stacklevel(self):
code = textwrap.dedent('''\
import _testcapi
def foo():
_testcapi.function_set_warning()
foo() # line 6
foo() # line 9
''')
proc = assert_python_ok("-c", code)
warnings = proc.err.splitlines()
self.assertEqual(warnings, [
b'<string>:6: RuntimeWarning: Testing PyErr_WarnEx',
b'<string>:9: RuntimeWarning: Testing PyErr_WarnEx',
])
def test_warn_during_finalization(self):
code = textwrap.dedent('''\
import _testcapi
class Foo:
def foo(self):
_testcapi.function_set_warning()
def __del__(self):
self.foo()
ref = Foo()
''')
proc = assert_python_ok("-c", code)
warnings = proc.err.splitlines()
# Due to the finalization of the interpreter, the source will be ommited
# because the ``warnings`` module cannot be imported at this time
self.assertEqual(warnings, [
b'<string>:7: RuntimeWarning: Testing PyErr_WarnEx',
])
class Test_FatalError(unittest.TestCase):
def check_fatal_error(self, code, expected, not_expected=()):
with support.SuppressCrashReport():
rc, out, err = assert_python_failure('-sSI', '-c', code)
err = decode_stderr(err)
self.assertIn('Fatal Python error: _testcapi_fatal_error_impl: MESSAGE\n',
err)
match = re.search(r'^Extension modules:(.*) \(total: ([0-9]+)\)$',
err, re.MULTILINE)
if not match:
self.fail(f"Cannot find 'Extension modules:' in {err!r}")
modules = set(match.group(1).strip().split(', '))
total = int(match.group(2))
for name in expected:
self.assertIn(name, modules)
for name in not_expected:
self.assertNotIn(name, modules)
self.assertEqual(len(modules), total)
@support.requires_subprocess()
def test_fatal_error(self):
# By default, stdlib extension modules are ignored,
# but not test modules.
expected = ('_testcapi',)
not_expected = ('sys',)
code = 'import _testcapi, sys; _testcapi.fatal_error(b"MESSAGE")'
self.check_fatal_error(code, expected, not_expected)
# Mark _testcapi as stdlib module, but not sys
expected = ('sys',)
not_expected = ('_testcapi',)
code = """if True:
import _testcapi, sys
sys.stdlib_module_names = frozenset({"_testcapi"})
_testcapi.fatal_error(b"MESSAGE")
"""
self.check_fatal_error(code, expected)
class Test_ErrSetAndRestore(unittest.TestCase):
def test_err_set_raised(self):
with self.assertRaises(ValueError):
_testcapi.err_set_raised(ValueError())
v = ValueError()
try:
_testcapi.err_set_raised(v)
except ValueError as ex:
self.assertIs(v, ex)
def test_err_restore(self):
with self.assertRaises(ValueError):
_testcapi.err_restore(ValueError)
with self.assertRaises(ValueError):
_testcapi.err_restore(ValueError, 1)
with self.assertRaises(ValueError):
_testcapi.err_restore(ValueError, 1, None)
with self.assertRaises(ValueError):
_testcapi.err_restore(ValueError, ValueError())
try:
_testcapi.err_restore(KeyError, "hi")
except KeyError as k:
self.assertEqual("hi", k.args[0])
try:
1/0
except Exception as e:
tb = e.__traceback__
with self.assertRaises(ValueError):
_testcapi.err_restore(ValueError, 1, tb)
with self.assertRaises(TypeError):
_testcapi.err_restore(ValueError, 1, 0)
try:
_testcapi.err_restore(ValueError, 1, tb)
except ValueError as v:
self.assertEqual(1, v.args[0])
self.assertIs(tb, v.__traceback__.tb_next)
def test_set_object(self):
# new exception as obj is not an exception
with self.assertRaises(ValueError) as e:
_testcapi.exc_set_object(ValueError, 42)
self.assertEqual(e.exception.args, (42,))
# wraps the exception because unrelated types
with self.assertRaises(ValueError) as e:
_testcapi.exc_set_object(ValueError, TypeError(1,2,3))
wrapped = e.exception.args[0]
self.assertIsInstance(wrapped, TypeError)
self.assertEqual(wrapped.args, (1, 2, 3))
# is superclass, so does not wrap
with self.assertRaises(PermissionError) as e:
_testcapi.exc_set_object(OSError, PermissionError(24))
self.assertEqual(e.exception.args, (24,))
class Meta(type):
def __subclasscheck__(cls, sub):
1/0
class Broken(Exception, metaclass=Meta):
pass
with self.assertRaises(ZeroDivisionError) as e:
_testcapi.exc_set_object(Broken, Broken())
def test_set_object_and_fetch(self):
class Broken(Exception):
def __init__(self, *arg):
raise ValueError("Broken __init__")
exc = _testcapi.exc_set_object_fetch(Broken, 'abcd')
self.assertIsInstance(exc, ValueError)
self.assertEqual(exc.__notes__[0],
"Normalization failed: type=Broken args='abcd'")
class BadArg:
def __repr__(self):
raise TypeError('Broken arg type')
exc = _testcapi.exc_set_object_fetch(Broken, BadArg())
self.assertIsInstance(exc, ValueError)
self.assertEqual(exc.__notes__[0],
'Normalization failed: type=Broken args=<unknown>')
def test_set_string(self):
"""Test PyErr_SetString()"""
setstring = _testcapi.err_setstring
with self.assertRaises(ZeroDivisionError) as e:
setstring(ZeroDivisionError, b'error')
self.assertEqual(e.exception.args, ('error',))
with self.assertRaises(ZeroDivisionError) as e:
setstring(ZeroDivisionError, 'помилка'.encode())
self.assertEqual(e.exception.args, ('помилка',))
with self.assertRaises(UnicodeDecodeError):
setstring(ZeroDivisionError, b'\xff')
self.assertRaises(SystemError, setstring, list, b'error')
# CRASHES setstring(ZeroDivisionError, NULL)
# CRASHES setstring(NULL, b'error')
def test_format(self):
"""Test PyErr_Format()"""
import_helper.import_module('ctypes')
from ctypes import pythonapi, py_object, c_char_p, c_int
name = "PyErr_Format"
PyErr_Format = getattr(pythonapi, name)
PyErr_Format.argtypes = (py_object, c_char_p,)
PyErr_Format.restype = py_object
with self.assertRaises(ZeroDivisionError) as e:
PyErr_Format(ZeroDivisionError, b'%s %d', b'error', c_int(42))
self.assertEqual(e.exception.args, ('error 42',))
with self.assertRaises(ZeroDivisionError) as e:
PyErr_Format(ZeroDivisionError, b'%s', 'помилка'.encode())
self.assertEqual(e.exception.args, ('помилка',))
with self.assertRaisesRegex(OverflowError, 'not in range'):
PyErr_Format(ZeroDivisionError, b'%c', c_int(-1))
with self.assertRaisesRegex(ValueError, 'format string'):
PyErr_Format(ZeroDivisionError, b'\xff')
self.assertRaises(SystemError, PyErr_Format, list, b'error')
# CRASHES PyErr_Format(ZeroDivisionError, NULL)
# CRASHES PyErr_Format(py_object(), b'error')
def test_setfromerrnowithfilename(self):
"""Test PyErr_SetFromErrnoWithFilename()"""
setfromerrnowithfilename = _testcapi.err_setfromerrnowithfilename
ENOENT = errno.ENOENT
with self.assertRaises(FileNotFoundError) as e:
setfromerrnowithfilename(ENOENT, OSError, b'file')
self.assertEqual(e.exception.args,
(ENOENT, 'No such file or directory'))
self.assertEqual(e.exception.errno, ENOENT)
self.assertEqual(e.exception.filename, 'file')
with self.assertRaises(FileNotFoundError) as e:
setfromerrnowithfilename(ENOENT, OSError, os.fsencode(TESTFN))
self.assertEqual(e.exception.filename, TESTFN)
if TESTFN_UNDECODABLE:
with self.assertRaises(FileNotFoundError) as e:
setfromerrnowithfilename(ENOENT, OSError, TESTFN_UNDECODABLE)
self.assertEqual(e.exception.filename,
os.fsdecode(TESTFN_UNDECODABLE))
with self.assertRaises(FileNotFoundError) as e:
setfromerrnowithfilename(ENOENT, OSError, NULL)
self.assertIsNone(e.exception.filename)
with self.assertRaises(OSError) as e:
setfromerrnowithfilename(0, OSError, b'file')
self.assertEqual(e.exception.args, (0, 'Error'))
self.assertEqual(e.exception.errno, 0)
self.assertEqual(e.exception.filename, 'file')
with self.assertRaises(ZeroDivisionError) as e:
setfromerrnowithfilename(ENOENT, ZeroDivisionError, b'file')
self.assertEqual(e.exception.args,
(ENOENT, 'No such file or directory', 'file'))
# CRASHES setfromerrnowithfilename(ENOENT, NULL, b'error')
def test_err_writeunraisable(self):
# Test PyErr_WriteUnraisable()
writeunraisable = _testcapi.err_writeunraisable
firstline = self.test_err_writeunraisable.__code__.co_firstlineno
with support.catch_unraisable_exception() as cm:
writeunraisable(CustomError('oops!'), hex)
self.assertEqual(cm.unraisable.exc_type, CustomError)
self.assertEqual(str(cm.unraisable.exc_value), 'oops!')
self.assertEqual(cm.unraisable.exc_traceback.tb_lineno,
firstline + 6)
self.assertIsNone(cm.unraisable.err_msg)
self.assertEqual(cm.unraisable.object, hex)
with support.catch_unraisable_exception() as cm:
writeunraisable(CustomError('oops!'), NULL)
self.assertEqual(cm.unraisable.exc_type, CustomError)
self.assertEqual(str(cm.unraisable.exc_value), 'oops!')
self.assertEqual(cm.unraisable.exc_traceback.tb_lineno,
firstline + 15)
self.assertIsNone(cm.unraisable.err_msg)
self.assertIsNone(cm.unraisable.object)
with (support.swap_attr(sys, 'unraisablehook', None),
support.captured_stderr() as stderr):
writeunraisable(CustomError('oops!'), hex)
lines = stderr.getvalue().splitlines()
self.assertEqual(lines[0], f'Exception ignored in: {hex!r}')
self.assertEqual(lines[1], 'Traceback (most recent call last):')
self.assertEqual(lines[-1], f'{__name__}.CustomError: oops!')
with (support.swap_attr(sys, 'unraisablehook', None),
support.captured_stderr() as stderr):
writeunraisable(CustomError('oops!'), NULL)
lines = stderr.getvalue().splitlines()
self.assertEqual(lines[0], 'Traceback (most recent call last):')
self.assertEqual(lines[-1], f'{__name__}.CustomError: oops!')
# CRASHES writeunraisable(NULL, hex)
# CRASHES writeunraisable(NULL, NULL)
def test_err_formatunraisable(self):
# Test PyErr_FormatUnraisable()
formatunraisable = _testcapi.err_formatunraisable
firstline = self.test_err_formatunraisable.__code__.co_firstlineno
with support.catch_unraisable_exception() as cm:
formatunraisable(CustomError('oops!'), b'Error in %R', [])
self.assertEqual(cm.unraisable.exc_type, CustomError)
self.assertEqual(str(cm.unraisable.exc_value), 'oops!')
self.assertEqual(cm.unraisable.exc_traceback.tb_lineno,
firstline + 6)
self.assertEqual(cm.unraisable.err_msg, 'Error in []')
self.assertIsNone(cm.unraisable.object)
with support.catch_unraisable_exception() as cm:
formatunraisable(CustomError('oops!'), b'undecodable \xff')
self.assertEqual(cm.unraisable.exc_type, CustomError)
self.assertEqual(str(cm.unraisable.exc_value), 'oops!')
self.assertEqual(cm.unraisable.exc_traceback.tb_lineno,
firstline + 15)
self.assertIsNone(cm.unraisable.err_msg)
self.assertIsNone(cm.unraisable.object)
with support.catch_unraisable_exception() as cm:
formatunraisable(CustomError('oops!'), NULL)
self.assertEqual(cm.unraisable.exc_type, CustomError)
self.assertEqual(str(cm.unraisable.exc_value), 'oops!')
self.assertEqual(cm.unraisable.exc_traceback.tb_lineno,
firstline + 24)
self.assertIsNone(cm.unraisable.err_msg)
self.assertIsNone(cm.unraisable.object)
with (support.swap_attr(sys, 'unraisablehook', None),
support.captured_stderr() as stderr):
formatunraisable(CustomError('oops!'), b'Error in %R', [])
lines = stderr.getvalue().splitlines()
self.assertEqual(lines[0], f'Error in []:')
self.assertEqual(lines[1], 'Traceback (most recent call last):')
self.assertEqual(lines[-1], f'{__name__}.CustomError: oops!')
with (support.swap_attr(sys, 'unraisablehook', None),
support.captured_stderr() as stderr):
formatunraisable(CustomError('oops!'), b'undecodable \xff')
lines = stderr.getvalue().splitlines()
self.assertEqual(lines[0], 'Traceback (most recent call last):')
self.assertEqual(lines[-1], f'{__name__}.CustomError: oops!')
with (support.swap_attr(sys, 'unraisablehook', None),
support.captured_stderr() as stderr):
formatunraisable(CustomError('oops!'), NULL)
lines = stderr.getvalue().splitlines()
self.assertEqual(lines[0], 'Traceback (most recent call last):')
self.assertEqual(lines[-1], f'{__name__}.CustomError: oops!')
# CRASHES formatunraisable(NULL, b'Error in %R', [])
# CRASHES formatunraisable(NULL, NULL)
class Test_PyUnstable_Exc_PrepReraiseStar(ExceptionIsLikeMixin, unittest.TestCase):
def setUp(self):
super().setUp()
try:
raise ExceptionGroup("eg", [TypeError('bad type'), ValueError(42)])
except ExceptionGroup as e:
self.orig = e
def test_invalid_args(self):
with self.assertRaisesRegex(TypeError, "orig must be an exception"):
_testcapi.unstable_exc_prep_reraise_star(42, [None])
with self.assertRaisesRegex(TypeError, "excs must be a list"):
_testcapi.unstable_exc_prep_reraise_star(self.orig, 42)
with self.assertRaisesRegex(TypeError, "not an exception"):
_testcapi.unstable_exc_prep_reraise_star(self.orig, [TypeError(42), 42])
with self.assertRaisesRegex(ValueError, "orig must be a raised exception"):
_testcapi.unstable_exc_prep_reraise_star(ValueError(42), [TypeError(42)])
with self.assertRaisesRegex(ValueError, "orig must be a raised exception"):
_testcapi.unstable_exc_prep_reraise_star(ExceptionGroup("eg", [ValueError(42)]),
[TypeError(42)])
def test_nothing_to_reraise(self):
self.assertEqual(
_testcapi.unstable_exc_prep_reraise_star(self.orig, [None]), None)
try:
raise ValueError(42)
except ValueError as e:
orig = e
self.assertEqual(
_testcapi.unstable_exc_prep_reraise_star(orig, [None]), None)
def test_reraise_orig(self):
orig = self.orig
res = _testcapi.unstable_exc_prep_reraise_star(orig, [orig])
self.assertExceptionIsLike(res, orig)
def test_raise_orig_parts(self):
orig = self.orig
match, rest = orig.split(TypeError)
test_cases = [
([match, rest], orig),
([rest, match], orig),
([match], match),
([rest], rest),
([], None),
]
for input, expected in test_cases:
with self.subTest(input=input):
res = _testcapi.unstable_exc_prep_reraise_star(orig, input)
self.assertExceptionIsLike(res, expected)
def test_raise_with_new_exceptions(self):
orig = self.orig
match, rest = orig.split(TypeError)
new1 = OSError('bad file')
new2 = RuntimeError('bad runtime')
test_cases = [
([new1, match, rest], ExceptionGroup("", [new1, orig])),
([match, new1, rest], ExceptionGroup("", [new1, orig])),
([match, rest, new1], ExceptionGroup("", [new1, orig])),
([new1, new2, match, rest], ExceptionGroup("", [new1, new2, orig])),
([new1, match, new2, rest], ExceptionGroup("", [new1, new2, orig])),
([new2, rest, match, new1], ExceptionGroup("", [new2, new1, orig])),
([rest, new2, match, new1], ExceptionGroup("", [new2, new1, orig])),
([new1, new2, rest], ExceptionGroup("", [new1, new2, rest])),
([new1, match, new2], ExceptionGroup("", [new1, new2, match])),
([rest, new2, new1], ExceptionGroup("", [new2, new1, rest])),
([new1, new2], ExceptionGroup("", [new1, new2])),
([new2, new1], ExceptionGroup("", [new2, new1])),
]
for (input, expected) in test_cases:
with self.subTest(input=input):
res = _testcapi.unstable_exc_prep_reraise_star(orig, input)
self.assertExceptionIsLike(res, expected)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,234 @@
import io
import os
import unittest
import warnings
from test import support
from test.support import import_helper, os_helper, warnings_helper
_testcapi = import_helper.import_module('_testcapi')
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
_io = import_helper.import_module('_io')
NULL = None
STDOUT_FD = 1
with open(__file__, 'rb') as fp:
FIRST_LINE = next(fp).decode()
FIRST_LINE_NORM = FIRST_LINE.rstrip() + '\n'
class CAPIFileTest(unittest.TestCase):
def test_pyfile_fromfd(self):
# Test PyFile_FromFd() which is a thin wrapper to _io.open()
pyfile_fromfd = _testlimitedcapi.pyfile_fromfd
filename = __file__
with open(filename, "rb") as fp:
fd = fp.fileno()
# FileIO
fp.seek(0)
obj = pyfile_fromfd(fd, filename, "rb", 0, NULL, NULL, NULL, 0)
try:
self.assertIsInstance(obj, _io.FileIO)
self.assertEqual(obj.readline(), FIRST_LINE.encode())
finally:
obj.close()
# BufferedReader
fp.seek(0)
obj = pyfile_fromfd(fd, filename, "rb", 1024, NULL, NULL, NULL, 0)
try:
self.assertIsInstance(obj, _io.BufferedReader)
self.assertEqual(obj.readline(), FIRST_LINE.encode())
finally:
obj.close()
# TextIOWrapper
fp.seek(0)
obj = pyfile_fromfd(fd, filename, "r", 1,
"utf-8", "replace", NULL, 0)
try:
self.assertIsInstance(obj, _io.TextIOWrapper)
self.assertEqual(obj.encoding, "utf-8")
self.assertEqual(obj.errors, "replace")
self.assertEqual(obj.readline(), FIRST_LINE_NORM)
finally:
obj.close()
def test_pyfile_getline(self):
# Test PyFile_GetLine(file, n): call file.readline()
# and strip "\n" suffix if n < 0.
pyfile_getline = _testlimitedcapi.pyfile_getline
# Test Unicode
with open(__file__, "r") as fp:
fp.seek(0)
self.assertEqual(pyfile_getline(fp, -1),
FIRST_LINE_NORM.rstrip('\n'))
fp.seek(0)
self.assertEqual(pyfile_getline(fp, 0),
FIRST_LINE_NORM)
fp.seek(0)
self.assertEqual(pyfile_getline(fp, 6),
FIRST_LINE_NORM[:6])
# Test bytes
with open(__file__, "rb") as fp:
fp.seek(0)
self.assertEqual(pyfile_getline(fp, -1),
FIRST_LINE.rstrip('\n').encode())
fp.seek(0)
self.assertEqual(pyfile_getline(fp, 0),
FIRST_LINE.encode())
fp.seek(0)
self.assertEqual(pyfile_getline(fp, 6),
FIRST_LINE.encode()[:6])
def test_pyfile_writestring(self):
# Test PyFile_WriteString(str, file): call file.write(str)
writestr = _testlimitedcapi.pyfile_writestring
with io.StringIO() as fp:
self.assertEqual(writestr("a\xe9\u20ac\U0010FFFF".encode(), fp), 0)
with self.assertRaises(UnicodeDecodeError):
writestr(b"\xff", fp)
with self.assertRaises(UnicodeDecodeError):
writestr("\udc80".encode("utf-8", "surrogatepass"), fp)
text = fp.getvalue()
self.assertEqual(text, "a\xe9\u20ac\U0010FFFF")
with self.assertRaises(SystemError):
writestr(b"abc", NULL)
def test_pyfile_writeobject(self):
# Test PyFile_WriteObject(obj, file, flags):
# - Call file.write(str(obj)) if flags equals Py_PRINT_RAW.
# - Call file.write(repr(obj)) otherwise.
writeobject = _testlimitedcapi.pyfile_writeobject
Py_PRINT_RAW = 1
with io.StringIO() as fp:
# Test flags=Py_PRINT_RAW
self.assertEqual(writeobject("raw", fp, Py_PRINT_RAW), 0)
writeobject(NULL, fp, Py_PRINT_RAW)
# Test flags=0
self.assertEqual(writeobject("repr", fp, 0), 0)
writeobject(NULL, fp, 0)
text = fp.getvalue()
self.assertEqual(text, "raw<NULL>'repr'<NULL>")
# invalid file type
for invalid_file in (123, "abc", object()):
with self.subTest(file=invalid_file):
with self.assertRaises(AttributeError):
writeobject("abc", invalid_file, Py_PRINT_RAW)
with self.assertRaises(TypeError):
writeobject("abc", NULL, 0)
def test_pyobject_asfiledescriptor(self):
# Test PyObject_AsFileDescriptor(obj):
# - Return obj if obj is an integer.
# - Return obj.fileno() otherwise.
# File descriptor must be >= 0.
asfd = _testlimitedcapi.pyobject_asfiledescriptor
self.assertEqual(asfd(123), 123)
self.assertEqual(asfd(0), 0)
with open(__file__, "rb") as fp:
self.assertEqual(asfd(fp), fp.fileno())
# bool emits RuntimeWarning
msg = r"bool is used as a file descriptor"
with warnings_helper.check_warnings((msg, RuntimeWarning)):
self.assertEqual(asfd(True), 1)
class FakeFile:
def __init__(self, fd):
self.fd = fd
def fileno(self):
return self.fd
# file descriptor must be positive
with self.assertRaises(ValueError):
asfd(-1)
with self.assertRaises(ValueError):
asfd(FakeFile(-1))
# fileno() result must be an integer
with self.assertRaises(TypeError):
asfd(FakeFile("text"))
# unsupported types
for obj in ("string", ["list"], object()):
with self.subTest(obj=obj):
with self.assertRaises(TypeError):
asfd(obj)
# CRASHES asfd(NULL)
def test_pyfile_newstdprinter(self):
# Test PyFile_NewStdPrinter()
pyfile_newstdprinter = _testcapi.pyfile_newstdprinter
file = pyfile_newstdprinter(STDOUT_FD)
self.assertEqual(file.closed, False)
self.assertIsNone(file.encoding)
self.assertEqual(file.mode, "w")
self.assertEqual(file.fileno(), STDOUT_FD)
self.assertEqual(file.isatty(), os.isatty(STDOUT_FD))
# flush() is a no-op
self.assertIsNone(file.flush())
# close() is a no-op
self.assertIsNone(file.close())
self.assertEqual(file.closed, False)
support.check_disallow_instantiation(self, type(file))
def test_pyfile_newstdprinter_write(self):
# Test the write() method of PyFile_NewStdPrinter()
pyfile_newstdprinter = _testcapi.pyfile_newstdprinter
filename = os_helper.TESTFN
self.addCleanup(os_helper.unlink, filename)
try:
old_stdout = os.dup(STDOUT_FD)
except OSError as exc:
# os.dup(STDOUT_FD) is not supported on WASI
self.skipTest(f"os.dup() failed with {exc!r}")
try:
with open(filename, "wb") as fp:
# PyFile_NewStdPrinter() only accepts fileno(stdout)
# or fileno(stderr) file descriptor.
fd = fp.fileno()
os.dup2(fd, STDOUT_FD)
file = pyfile_newstdprinter(STDOUT_FD)
self.assertEqual(file.write("text"), 4)
# The surrogate character is encoded with
# the "surrogateescape" error handler
self.assertEqual(file.write("[\udc80]"), 8)
finally:
os.dup2(old_stdout, STDOUT_FD)
os.close(old_stdout)
with open(filename, "r") as fp:
self.assertEqual(fp.read(), "text[\\udc80]")
# TODO: Test Py_UniversalNewlineFgets()
# PyFile_SetOpenCodeHook() and PyFile_OpenCode() are tested by
# test_embed.test_open_code_hook()
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,183 @@
import math
import sys
import unittest
import warnings
from test.test_capi.test_getargs import (Float, FloatSubclass, FloatSubclass2,
BadIndex2, BadFloat2, Index, BadIndex,
BadFloat)
from test.support import import_helper
_testcapi = import_helper.import_module('_testcapi')
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
NULL = None
# For PyFloat_Pack/Unpack*
BIG_ENDIAN = 0
LITTLE_ENDIAN = 1
EPSILON = {
2: 2.0 ** -11, # binary16
4: 2.0 ** -24, # binary32
8: 2.0 ** -53, # binary64
}
HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE")
INF = float("inf")
NAN = float("nan")
class CAPIFloatTest(unittest.TestCase):
def test_check(self):
# Test PyFloat_Check()
check = _testlimitedcapi.float_check
self.assertTrue(check(4.25))
self.assertTrue(check(FloatSubclass(4.25)))
self.assertFalse(check(Float()))
self.assertFalse(check(3))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_checkexact(self):
# Test PyFloat_CheckExact()
checkexact = _testlimitedcapi.float_checkexact
self.assertTrue(checkexact(4.25))
self.assertFalse(checkexact(FloatSubclass(4.25)))
self.assertFalse(checkexact(Float()))
self.assertFalse(checkexact(3))
self.assertFalse(checkexact(object()))
# CRASHES checkexact(NULL)
def test_fromstring(self):
# Test PyFloat_FromString()
fromstring = _testlimitedcapi.float_fromstring
self.assertEqual(fromstring("4.25"), 4.25)
self.assertEqual(fromstring(b"4.25"), 4.25)
self.assertRaises(ValueError, fromstring, "4.25\0")
self.assertRaises(ValueError, fromstring, b"4.25\0")
self.assertEqual(fromstring(bytearray(b"4.25")), 4.25)
self.assertEqual(fromstring(memoryview(b"4.25")), 4.25)
self.assertEqual(fromstring(memoryview(b"4.255")[:-1]), 4.25)
self.assertRaises(TypeError, fromstring, memoryview(b"4.25")[::2])
self.assertRaises(TypeError, fromstring, 4.25)
# CRASHES fromstring(NULL)
def test_fromdouble(self):
# Test PyFloat_FromDouble()
fromdouble = _testlimitedcapi.float_fromdouble
self.assertEqual(fromdouble(4.25), 4.25)
def test_asdouble(self):
# Test PyFloat_AsDouble()
asdouble = _testlimitedcapi.float_asdouble
class BadFloat3:
def __float__(self):
raise RuntimeError
self.assertEqual(asdouble(4.25), 4.25)
self.assertEqual(asdouble(-1.0), -1.0)
self.assertEqual(asdouble(42), 42.0)
self.assertEqual(asdouble(-1), -1.0)
self.assertEqual(asdouble(2**1000), float(2**1000))
self.assertEqual(asdouble(FloatSubclass(4.25)), 4.25)
self.assertEqual(asdouble(FloatSubclass2(4.25)), 4.25)
self.assertEqual(asdouble(Index()), 99.)
self.assertRaises(TypeError, asdouble, BadIndex())
self.assertRaises(TypeError, asdouble, BadFloat())
self.assertRaises(RuntimeError, asdouble, BadFloat3())
with self.assertWarns(DeprecationWarning):
self.assertEqual(asdouble(BadIndex2()), 1.)
with self.assertWarns(DeprecationWarning):
self.assertEqual(asdouble(BadFloat2()), 4.25)
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
self.assertRaises(DeprecationWarning, asdouble, BadFloat2())
self.assertRaises(TypeError, asdouble, object())
self.assertRaises(TypeError, asdouble, NULL)
def test_getinfo(self):
# Test PyFloat_GetInfo()
getinfo = _testlimitedcapi.float_getinfo
self.assertEqual(getinfo(), sys.float_info)
def test_getmax(self):
# Test PyFloat_GetMax()
getmax = _testlimitedcapi.float_getmax
self.assertEqual(getmax(), sys.float_info.max)
def test_getmin(self):
# Test PyFloat_GetMax()
getmin = _testlimitedcapi.float_getmin
self.assertEqual(getmin(), sys.float_info.min)
def test_pack(self):
# Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8()
pack = _testcapi.float_pack
self.assertEqual(pack(2, 1.5, BIG_ENDIAN), b'>\x00')
self.assertEqual(pack(4, 1.5, BIG_ENDIAN), b'?\xc0\x00\x00')
self.assertEqual(pack(8, 1.5, BIG_ENDIAN),
b'?\xf8\x00\x00\x00\x00\x00\x00')
self.assertEqual(pack(2, 1.5, LITTLE_ENDIAN), b'\x00>')
self.assertEqual(pack(4, 1.5, LITTLE_ENDIAN), b'\x00\x00\xc0?')
self.assertEqual(pack(8, 1.5, LITTLE_ENDIAN),
b'\x00\x00\x00\x00\x00\x00\xf8?')
def test_unpack(self):
# Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8()
unpack = _testcapi.float_unpack
self.assertEqual(unpack(b'>\x00', BIG_ENDIAN), 1.5)
self.assertEqual(unpack(b'?\xc0\x00\x00', BIG_ENDIAN), 1.5)
self.assertEqual(unpack(b'?\xf8\x00\x00\x00\x00\x00\x00', BIG_ENDIAN),
1.5)
self.assertEqual(unpack(b'\x00>', LITTLE_ENDIAN), 1.5)
self.assertEqual(unpack(b'\x00\x00\xc0?', LITTLE_ENDIAN), 1.5)
self.assertEqual(unpack(b'\x00\x00\x00\x00\x00\x00\xf8?', LITTLE_ENDIAN),
1.5)
def test_pack_unpack_roundtrip(self):
pack = _testcapi.float_pack
unpack = _testcapi.float_unpack
large = 2.0 ** 100
values = [1.0, 1.5, large, 1.0/7, math.pi]
if HAVE_IEEE_754:
values.extend((INF, NAN))
for value in values:
for size in (2, 4, 8,):
if size == 2 and value == large:
# too large for 16-bit float
continue
rel_tol = EPSILON[size]
for endian in (BIG_ENDIAN, LITTLE_ENDIAN):
with self.subTest(value=value, size=size, endian=endian):
data = pack(size, value, endian)
value2 = unpack(data, endian)
if math.isnan(value):
self.assertTrue(math.isnan(value2), (value, value2))
elif size < 8:
self.assertTrue(math.isclose(value2, value, rel_tol=rel_tol),
(value, value2))
else:
self.assertEqual(value2, value)
if __name__ == "__main__":
unittest.main()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
import sys
import unittest
from test.support import import_helper
_testcapi = import_helper.import_module('_testcapi')
SIZEOF_VOID_P = _testcapi.SIZEOF_VOID_P
SIZEOF_PY_HASH_T = SIZEOF_VOID_P
class CAPITest(unittest.TestCase):
def test_hash_getfuncdef(self):
# Test PyHash_GetFuncDef()
hash_getfuncdef = _testcapi.hash_getfuncdef
func_def = hash_getfuncdef()
match func_def.name:
case "fnv":
self.assertEqual(func_def.hash_bits, 8 * SIZEOF_PY_HASH_T)
self.assertEqual(func_def.seed_bits, 16 * SIZEOF_PY_HASH_T)
case "siphash13":
self.assertEqual(func_def.hash_bits, 64)
self.assertEqual(func_def.seed_bits, 128)
case "siphash24":
self.assertEqual(func_def.hash_bits, 64)
self.assertEqual(func_def.seed_bits, 128)
case _:
self.fail(f"unknown function name: {func_def.name!r}")
# compare with sys.hash_info
hash_info = sys.hash_info
self.assertEqual(func_def.name, hash_info.algorithm)
self.assertEqual(func_def.hash_bits, hash_info.hash_bits)
self.assertEqual(func_def.seed_bits, hash_info.seed_bits)
def test_hash_pointer(self):
# Test Py_HashPointer()
hash_pointer = _testcapi.hash_pointer
UHASH_T_MASK = ((2 ** (8 * SIZEOF_PY_HASH_T)) - 1)
HASH_T_MAX = (2 ** (8 * SIZEOF_PY_HASH_T - 1) - 1)
def python_hash_pointer(x):
# Py_HashPointer() rotates the pointer bits by 4 bits to the right
x = (x >> 4) | ((x & 15) << (8 * SIZEOF_VOID_P - 4))
# Convert unsigned uintptr_t (Py_uhash_t) to signed Py_hash_t
if HASH_T_MAX < x:
x = (~x) + 1
x &= UHASH_T_MASK
x = (~x) + 1
return x
if SIZEOF_VOID_P == 8:
values = (
0xABCDEF1234567890,
0x1234567890ABCDEF,
0xFEE4ABEDD1CECA5E,
)
else:
values = (
0x12345678,
0x1234ABCD,
0xDEADCAFE,
)
for value in values:
expected = python_hash_pointer(value)
with self.subTest(value=value):
self.assertEqual(hash_pointer(value), expected,
f"hash_pointer({value:x}) = "
f"{hash_pointer(value):x} != {expected:x}")
# Py_HashPointer(NULL) returns 0
self.assertEqual(hash_pointer(0), 0)
# Py_HashPointer((void*)(uintptr_t)-1) doesn't return -1 but -2
VOID_P_MAX = -1 & (2 ** (8 * SIZEOF_VOID_P) - 1)
self.assertEqual(hash_pointer(VOID_P_MAX), -2)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,16 @@
import unittest
from test.support import import_helper
_testcapi = import_helper.import_module('_testcapi')
class TestCAPI(unittest.TestCase):
def test_immortal_builtins(self):
_testcapi.test_immortal_builtins()
def test_immortal_small_ints(self):
_testcapi.test_immortal_small_ints()
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,322 @@
import importlib.util
import os.path
import sys
import types
import unittest
from test.support import os_helper
from test.support import import_helper
from test.support.warnings_helper import check_warnings
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
NULL = None
class ImportTests(unittest.TestCase):
def test_getmagicnumber(self):
# Test PyImport_GetMagicNumber()
magic = _testlimitedcapi.PyImport_GetMagicNumber()
self.assertEqual(magic,
int.from_bytes(importlib.util.MAGIC_NUMBER, 'little'))
def test_getmagictag(self):
# Test PyImport_GetMagicTag()
tag = _testlimitedcapi.PyImport_GetMagicTag()
self.assertEqual(tag, sys.implementation.cache_tag)
def test_getmoduledict(self):
# Test PyImport_GetModuleDict()
modules = _testlimitedcapi.PyImport_GetModuleDict()
self.assertIs(modules, sys.modules)
def check_import_loaded_module(self, import_module):
for name in ('os', 'sys', 'test', 'unittest'):
with self.subTest(name=name):
self.assertIn(name, sys.modules)
old_module = sys.modules[name]
module = import_module(name)
self.assertIsInstance(module, types.ModuleType)
self.assertIs(module, old_module)
def check_import_fresh_module(self, import_module):
old_modules = dict(sys.modules)
try:
for name in ('colorsys', 'math'):
with self.subTest(name=name):
sys.modules.pop(name, None)
module = import_module(name)
self.assertIsInstance(module, types.ModuleType)
self.assertIs(module, sys.modules[name])
self.assertEqual(module.__name__, name)
finally:
sys.modules.clear()
sys.modules.update(old_modules)
def test_getmodule(self):
# Test PyImport_GetModule()
getmodule = _testlimitedcapi.PyImport_GetModule
self.check_import_loaded_module(getmodule)
nonexistent = 'nonexistent'
self.assertNotIn(nonexistent, sys.modules)
self.assertIs(getmodule(nonexistent), KeyError)
self.assertIs(getmodule(''), KeyError)
self.assertIs(getmodule(object()), KeyError)
self.assertRaises(TypeError, getmodule, []) # unhashable
# CRASHES getmodule(NULL)
def check_addmodule(self, add_module, accept_nonstr=False):
# create a new module
names = ['nonexistent']
if accept_nonstr:
names.append(b'\xff') # non-UTF-8
for name in names:
with self.subTest(name=name):
self.assertNotIn(name, sys.modules)
try:
module = add_module(name)
self.assertIsInstance(module, types.ModuleType)
self.assertEqual(module.__name__, name)
self.assertIs(module, sys.modules[name])
finally:
sys.modules.pop(name, None)
# get an existing module
self.check_import_loaded_module(add_module)
def test_addmoduleobject(self):
# Test PyImport_AddModuleObject()
addmoduleobject = _testlimitedcapi.PyImport_AddModuleObject
self.check_addmodule(addmoduleobject, accept_nonstr=True)
self.assertRaises(TypeError, addmoduleobject, []) # unhashable
# CRASHES addmoduleobject(NULL)
def test_addmodule(self):
# Test PyImport_AddModule()
addmodule = _testlimitedcapi.PyImport_AddModule
self.check_addmodule(addmodule)
self.assertRaises(UnicodeDecodeError, addmodule, b'\xff')
# CRASHES addmodule(NULL)
def test_addmoduleref(self):
# Test PyImport_AddModuleRef()
addmoduleref = _testlimitedcapi.PyImport_AddModuleRef
self.check_addmodule(addmoduleref)
self.assertRaises(UnicodeDecodeError, addmoduleref, b'\xff')
# CRASHES addmoduleref(NULL)
def check_import_func(self, import_module):
self.check_import_loaded_module(import_module)
self.check_import_fresh_module(import_module)
self.assertRaises(ModuleNotFoundError, import_module, 'nonexistent')
self.assertRaises(ValueError, import_module, '')
def test_import(self):
# Test PyImport_Import()
import_ = _testlimitedcapi.PyImport_Import
self.check_import_func(import_)
self.assertRaises(TypeError, import_, b'os')
self.assertRaises(SystemError, import_, NULL)
def test_importmodule(self):
# Test PyImport_ImportModule()
importmodule = _testlimitedcapi.PyImport_ImportModule
self.check_import_func(importmodule)
self.assertRaises(UnicodeDecodeError, importmodule, b'\xff')
# CRASHES importmodule(NULL)
def test_importmodulenoblock(self):
# Test deprecated PyImport_ImportModuleNoBlock()
importmodulenoblock = _testlimitedcapi.PyImport_ImportModuleNoBlock
with check_warnings(('', DeprecationWarning)):
self.check_import_func(importmodulenoblock)
self.assertRaises(UnicodeDecodeError, importmodulenoblock, b'\xff')
# CRASHES importmodulenoblock(NULL)
def check_frozen_import(self, import_frozen_module):
# Importing a frozen module executes its code, so start by unloading
# the module to execute the code in a new (temporary) module.
old_zipimport = sys.modules.pop('zipimport')
try:
self.assertEqual(import_frozen_module('zipimport'), 1)
# import zipimport again
self.assertEqual(import_frozen_module('zipimport'), 1)
finally:
sys.modules['zipimport'] = old_zipimport
# not a frozen module
self.assertEqual(import_frozen_module('sys'), 0)
self.assertEqual(import_frozen_module('nonexistent'), 0)
self.assertEqual(import_frozen_module(''), 0)
def test_importfrozenmodule(self):
# Test PyImport_ImportFrozenModule()
importfrozenmodule = _testlimitedcapi.PyImport_ImportFrozenModule
self.check_frozen_import(importfrozenmodule)
self.assertRaises(UnicodeDecodeError, importfrozenmodule, b'\xff')
# CRASHES importfrozenmodule(NULL)
def test_importfrozenmoduleobject(self):
# Test PyImport_ImportFrozenModuleObject()
importfrozenmoduleobject = _testlimitedcapi.PyImport_ImportFrozenModuleObject
self.check_frozen_import(importfrozenmoduleobject)
self.assertEqual(importfrozenmoduleobject(b'zipimport'), 0)
self.assertEqual(importfrozenmoduleobject(NULL), 0)
def test_importmoduleex(self):
# Test PyImport_ImportModuleEx()
importmoduleex = _testlimitedcapi.PyImport_ImportModuleEx
self.check_import_func(lambda name: importmoduleex(name, NULL, NULL, NULL))
self.assertRaises(ModuleNotFoundError, importmoduleex, 'nonexistent', NULL, NULL, NULL)
self.assertRaises(ValueError, importmoduleex, '', NULL, NULL, NULL)
self.assertRaises(UnicodeDecodeError, importmoduleex, b'\xff', NULL, NULL, NULL)
# CRASHES importmoduleex(NULL, NULL, NULL, NULL)
def check_importmodulelevel(self, importmodulelevel):
self.check_import_func(lambda name: importmodulelevel(name, NULL, NULL, NULL, 0))
self.assertRaises(ModuleNotFoundError, importmodulelevel, 'nonexistent', NULL, NULL, NULL, 0)
self.assertRaises(ValueError, importmodulelevel, '', NULL, NULL, NULL, 0)
if __package__:
self.assertIs(importmodulelevel('test_import', globals(), NULL, NULL, 1),
sys.modules['test.test_capi.test_import'])
self.assertIs(importmodulelevel('test_capi', globals(), NULL, NULL, 2),
sys.modules['test.test_capi'])
self.assertRaises(ValueError, importmodulelevel, 'os', NULL, NULL, NULL, -1)
with self.assertWarns(ImportWarning):
self.assertRaises(KeyError, importmodulelevel, 'test_import', {}, NULL, NULL, 1)
self.assertRaises(TypeError, importmodulelevel, 'test_import', [], NULL, NULL, 1)
def test_importmodulelevel(self):
# Test PyImport_ImportModuleLevel()
importmodulelevel = _testlimitedcapi.PyImport_ImportModuleLevel
self.check_importmodulelevel(importmodulelevel)
self.assertRaises(UnicodeDecodeError, importmodulelevel, b'\xff', NULL, NULL, NULL, 0)
# CRASHES importmodulelevel(NULL, NULL, NULL, NULL, 0)
def test_importmodulelevelobject(self):
# Test PyImport_ImportModuleLevelObject()
importmodulelevel = _testlimitedcapi.PyImport_ImportModuleLevelObject
self.check_importmodulelevel(importmodulelevel)
self.assertRaises(TypeError, importmodulelevel, b'os', NULL, NULL, NULL, 0)
self.assertRaises(ValueError, importmodulelevel, NULL, NULL, NULL, NULL, 0)
def check_executecodemodule(self, execute_code, *args):
name = 'test_import_executecode'
try:
# Create a temporary module where the code will be executed
self.assertNotIn(name, sys.modules)
module = _testlimitedcapi.PyImport_AddModuleRef(name)
self.assertFalse(hasattr(module, 'attr'))
# Execute the code
code = compile('attr = 1', '<test>', 'exec')
module2 = execute_code(name, code, *args)
self.assertIs(module2, module)
# Check the function side effects
self.assertEqual(module.attr, 1)
finally:
sys.modules.pop(name, None)
return module.__spec__.origin
def test_executecodemodule(self):
# Test PyImport_ExecCodeModule()
execcodemodule = _testlimitedcapi.PyImport_ExecCodeModule
self.check_executecodemodule(execcodemodule)
code = compile('attr = 1', '<test>', 'exec')
self.assertRaises(UnicodeDecodeError, execcodemodule, b'\xff', code)
# CRASHES execcodemodule(NULL, code)
# CRASHES execcodemodule(name, NULL)
def test_executecodemoduleex(self):
# Test PyImport_ExecCodeModuleEx()
execcodemoduleex = _testlimitedcapi.PyImport_ExecCodeModuleEx
# Test NULL path (it should not crash)
self.check_executecodemodule(execcodemoduleex, NULL)
# Test non-NULL path
pathname = b'pathname'
origin = self.check_executecodemodule(execcodemoduleex, pathname)
self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname)))
pathname = os_helper.TESTFN_UNDECODABLE
if pathname:
origin = self.check_executecodemodule(execcodemoduleex, pathname)
self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname)))
code = compile('attr = 1', '<test>', 'exec')
self.assertRaises(UnicodeDecodeError, execcodemoduleex, b'\xff', code, NULL)
# CRASHES execcodemoduleex(NULL, code, NULL)
# CRASHES execcodemoduleex(name, NULL, NULL)
def check_executecode_pathnames(self, execute_code_func, object=False):
# Test non-NULL pathname and NULL cpathname
# Test NULL paths (it should not crash)
self.check_executecodemodule(execute_code_func, NULL, NULL)
pathname = 'pathname'
origin = self.check_executecodemodule(execute_code_func, pathname, NULL)
self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname)))
origin = self.check_executecodemodule(execute_code_func, NULL, pathname)
if not object:
self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname)))
pathname = os_helper.TESTFN_UNDECODABLE
if pathname:
if object:
pathname = os.fsdecode(pathname)
origin = self.check_executecodemodule(execute_code_func, pathname, NULL)
self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname)))
self.check_executecodemodule(execute_code_func, NULL, pathname)
# Test NULL pathname and non-NULL cpathname
pyc_filename = importlib.util.cache_from_source(__file__)
py_filename = importlib.util.source_from_cache(pyc_filename)
origin = self.check_executecodemodule(execute_code_func, NULL, pyc_filename)
if not object:
self.assertEqual(origin, py_filename)
def test_executecodemodulewithpathnames(self):
# Test PyImport_ExecCodeModuleWithPathnames()
execute_code_func = _testlimitedcapi.PyImport_ExecCodeModuleWithPathnames
self.check_executecode_pathnames(execute_code_func)
code = compile('attr = 1', '<test>', 'exec')
self.assertRaises(UnicodeDecodeError, execute_code_func, b'\xff', code, NULL, NULL)
# CRASHES execute_code_func(NULL, code, NULL, NULL)
# CRASHES execute_code_func(name, NULL, NULL, NULL)
def test_executecodemoduleobject(self):
# Test PyImport_ExecCodeModuleObject()
execute_code_func = _testlimitedcapi.PyImport_ExecCodeModuleObject
self.check_executecode_pathnames(execute_code_func, object=True)
code = compile('attr = 1', '<test>', 'exec')
self.assertRaises(TypeError, execute_code_func, [], code, NULL, NULL)
# CRASHES execute_code_func(NULL, code, NULL, NULL)
# CRASHES execute_code_func(name, NULL, NULL, NULL)
# TODO: test PyImport_GetImporter()
# TODO: test PyImport_ReloadModule()
# TODO: test PyImport_ExtendInittab()
# PyImport_AppendInittab() is tested by test_embed
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,357 @@
import gc
import weakref
import unittest
from test.support import import_helper
from collections import UserList
_testcapi = import_helper.import_module('_testcapi')
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
NULL = None
PY_SSIZE_T_MIN = _testcapi.PY_SSIZE_T_MIN
PY_SSIZE_T_MAX = _testcapi.PY_SSIZE_T_MAX
class ListSubclass(list):
pass
class DelAppend:
def __init__(self, lst, item):
self.lst = lst
self.item = item
def __del__(self):
self.lst.append(self.item)
class CAPITest(unittest.TestCase):
def test_check(self):
# Test PyList_Check()
check = _testlimitedcapi.list_check
self.assertTrue(check([1, 2]))
self.assertTrue(check([]))
self.assertTrue(check(ListSubclass([1, 2])))
self.assertFalse(check({1: 2}))
self.assertFalse(check((1, 2)))
self.assertFalse(check(42))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_list_check_exact(self):
# Test PyList_CheckExact()
check = _testlimitedcapi.list_check_exact
self.assertTrue(check([1]))
self.assertTrue(check([]))
self.assertFalse(check(ListSubclass([1])))
self.assertFalse(check(UserList([1, 2])))
self.assertFalse(check({1: 2}))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_list_new(self):
# Test PyList_New()
list_new = _testlimitedcapi.list_new
lst = list_new(0)
self.assertEqual(lst, [])
self.assertIs(type(lst), list)
lst2 = list_new(0)
self.assertIsNot(lst2, lst)
self.assertRaises(SystemError, list_new, NULL)
self.assertRaises(SystemError, list_new, -1)
def test_list_size(self):
# Test PyList_Size()
size = _testlimitedcapi.list_size
self.assertEqual(size([]), 0)
self.assertEqual(size([1, 2]), 2)
self.assertEqual(size(ListSubclass([1, 2])), 2)
self.assertRaises(SystemError, size, UserList())
self.assertRaises(SystemError, size, {})
self.assertRaises(SystemError, size, 23)
self.assertRaises(SystemError, size, object())
# CRASHES size(NULL)
def test_list_get_size(self):
# Test PyList_GET_SIZE()
size = _testcapi.list_get_size
self.assertEqual(size([]), 0)
self.assertEqual(size([1, 2]), 2)
self.assertEqual(size(ListSubclass([1, 2])), 2)
# CRASHES size(object())
# CRASHES size(23)
# CRASHES size({})
# CRASHES size(UserList())
# CRASHES size(NULL)
def check_list_get_item(self, getitem, exctype):
# Common test cases for PyList_GetItem() and PyList_GetItemRef()
lst = [1, 2, 3]
self.assertEqual(getitem(lst, 0), 1)
self.assertEqual(getitem(lst, 2), 3)
self.assertRaises(IndexError, getitem, lst, 3)
self.assertRaises(IndexError, getitem, lst, -1)
self.assertRaises(IndexError, getitem, lst, PY_SSIZE_T_MIN)
self.assertRaises(IndexError, getitem, lst, PY_SSIZE_T_MAX)
self.assertRaises(exctype, getitem, 42, 1)
self.assertRaises(exctype, getitem, (1, 2, 3), 1)
self.assertRaises(exctype, getitem, {1: 2}, 1)
# CRASHES getitem(NULL, 1)
def test_list_getitem(self):
# Test PyList_GetItem()
self.check_list_get_item(_testlimitedcapi.list_getitem, SystemError)
def test_list_get_item_ref(self):
# Test PyList_GetItemRef()
self.check_list_get_item(_testlimitedcapi.list_get_item_ref, TypeError)
def test_list_get_item(self):
# Test PyList_GET_ITEM()
get_item = _testcapi.list_get_item
lst = [1, 2, [1, 2, 3]]
self.assertEqual(get_item(lst, 0), 1)
self.assertEqual(get_item(lst, 2), [1, 2, 3])
# CRASHES for out of index: get_item(lst, 3)
# CRASHES for get_item(lst, PY_SSIZE_T_MIN)
# CRASHES for get_item(lst, PY_SSIZE_T_MAX)
# CRASHES get_item(21, 2)
# CRASHES get_item(NULL, 1)
def test_list_setitem(self):
# Test PyList_SetItem()
setitem = _testlimitedcapi.list_setitem
lst = [1, 2, 3]
setitem(lst, 0, 10)
self.assertEqual(lst, [10, 2, 3])
setitem(lst, 2, 12)
self.assertEqual(lst, [10, 2, 12])
self.assertRaises(IndexError, setitem, lst, 3 , 5)
self.assertRaises(IndexError, setitem, lst, -1, 5)
self.assertRaises(IndexError, setitem, lst, PY_SSIZE_T_MIN, 5)
self.assertRaises(IndexError, setitem, lst, PY_SSIZE_T_MAX, 5)
self.assertRaises(SystemError, setitem, (1, 2, 3), 1, 5)
self.assertRaises(SystemError, setitem, {1: 2}, 1, 5)
# CRASHES setitem(NULL, 'a', 5)
def test_list_set_item(self):
# Test PyList_SET_ITEM()
set_item = _testcapi.list_set_item
lst = [1, 2, 3]
set_item(lst, 1, 10)
set_item(lst, 2, [1, 2, 3])
self.assertEqual(lst, [1, 10, [1, 2, 3]])
# CRASHES for set_item([1], -1, 5)
# CRASHES for set_item([1], PY_SSIZE_T_MIN, 5)
# CRASHES for set_item([1], PY_SSIZE_T_MAX, 5)
# CRASHES for set_item([], 0, 1)
# CRASHES for set_item(NULL, 0, 1)
def test_list_insert(self):
# Test PyList_Insert()
insert = _testlimitedcapi.list_insert
lst = [1, 2, 3]
insert(lst, 0, 23)
self.assertEqual(lst, [23, 1, 2, 3])
insert(lst, -1, 22)
self.assertEqual(lst, [23, 1, 2, 22, 3])
insert(lst, PY_SSIZE_T_MIN, 1)
self.assertEqual(lst[0], 1)
insert(lst, len(lst), 123)
self.assertEqual(lst[-1], 123)
insert(lst, len(lst)-1, 124)
self.assertEqual(lst[-2], 124)
insert(lst, PY_SSIZE_T_MAX, 223)
self.assertEqual(lst[-1], 223)
self.assertRaises(SystemError, insert, (1, 2, 3), 1, 5)
self.assertRaises(SystemError, insert, {1: 2}, 1, 5)
# CRASHES insert(NULL, 1, 5)
def test_list_append(self):
# Test PyList_Append()
append = _testlimitedcapi.list_append
lst = [1, 2, 3]
append(lst, 10)
self.assertEqual(lst, [1, 2, 3, 10])
append(lst, [4, 5])
self.assertEqual(lst, [1, 2, 3, 10, [4, 5]])
self.assertRaises(SystemError, append, lst, NULL)
self.assertRaises(SystemError, append, (), 0)
self.assertRaises(SystemError, append, 42, 0)
# CRASHES append(NULL, 0)
def test_list_getslice(self):
# Test PyList_GetSlice()
getslice = _testlimitedcapi.list_getslice
lst = [1, 2, 3]
# empty
self.assertEqual(getslice(lst, PY_SSIZE_T_MIN, 0), [])
self.assertEqual(getslice(lst, -1, 0), [])
self.assertEqual(getslice(lst, 3, PY_SSIZE_T_MAX), [])
# slice
self.assertEqual(getslice(lst, 1, 3), [2, 3])
# whole
self.assertEqual(getslice(lst, 0, len(lst)), lst)
self.assertEqual(getslice(lst, 0, 100), lst)
self.assertEqual(getslice(lst, -100, 100), lst)
self.assertRaises(SystemError, getslice, (1, 2, 3), 0, 0)
self.assertRaises(SystemError, getslice, 'abc', 0, 0)
self.assertRaises(SystemError, getslice, 42, 0, 0)
# CRASHES getslice(NULL, 0, 0)
def test_list_setslice(self):
# Test PyList_SetSlice()
list_setslice = _testlimitedcapi.list_setslice
def set_slice(lst, low, high, value):
lst = lst.copy()
self.assertEqual(list_setslice(lst, low, high, value), 0)
return lst
# insert items
self.assertEqual(set_slice([], 0, 0, list("abc")), list("abc"))
self.assertEqual(set_slice([], PY_SSIZE_T_MIN, PY_SSIZE_T_MIN, list("abc")), list("abc"))
self.assertEqual(set_slice([], PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, list("abc")), list("abc"))
lst = list("abc")
self.assertEqual(set_slice(lst, 0, 0, ["X"]), list("Xabc"))
self.assertEqual(set_slice(lst, 1, 1, list("XY")), list("aXYbc"))
self.assertEqual(set_slice(lst, len(lst), len(lst), ["X"]), list("abcX"))
# self.assertEqual(set_slice(lst, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, ["X"]), list("abcX"))
# replace items
lst = list("abc")
self.assertEqual(set_slice(lst, -100, 1, list("X")), list("Xbc"))
self.assertEqual(set_slice(lst, 1, 2, list("X")), list("aXc"))
self.assertEqual(set_slice(lst, 1, 3, list("XY")), list("aXY"))
self.assertEqual(set_slice(lst, 0, 3, list("XYZ")), list("XYZ"))
# delete items
lst = list("abcdef")
self.assertEqual(set_slice(lst, 0, len(lst), []), [])
self.assertEqual(set_slice(lst, -100, 100, []), [])
self.assertEqual(set_slice(lst, 1, 5, []), list("af"))
self.assertEqual(set_slice(lst, 3, len(lst), []), list("abc"))
# delete items with NULL
lst = list("abcdef")
self.assertEqual(set_slice(lst, 0, len(lst), NULL), [])
self.assertEqual(set_slice(lst, 3, len(lst), NULL), list("abc"))
self.assertRaises(SystemError, list_setslice, (), 0, 0, [])
self.assertRaises(SystemError, list_setslice, 42, 0, 0, [])
# Item finalizer modify the list (clear the list)
lst = []
lst.append(DelAppend(lst, 'zombie'))
self.assertEqual(list_setslice(lst, 0, len(lst), NULL), 0)
self.assertEqual(lst, ['zombie'])
# Item finalizer modify the list (remove an list item)
lst = []
lst.append(DelAppend(lst, 'zombie'))
lst.extend("abc")
self.assertEqual(list_setslice(lst, 0, 1, NULL), 0)
self.assertEqual(lst, ['a', 'b', 'c', 'zombie'])
# CRASHES setslice(NULL, 0, 0, [])
def test_list_sort(self):
# Test PyList_Sort()
sort = _testlimitedcapi.list_sort
lst = [4, 6, 7, 3, 1, 5, 9, 2, 0, 8]
sort(lst)
self.assertEqual(lst, list(range(10)))
lst2 = ListSubclass([4, 6, 7, 3, 1, 5, 9, 2, 0, 8])
sort(lst2)
self.assertEqual(lst2, list(range(10)))
self.assertRaises(SystemError, sort, ())
self.assertRaises(SystemError, sort, object())
self.assertRaises(SystemError, sort, NULL)
def test_list_reverse(self):
# Test PyList_Reverse()
reverse = _testlimitedcapi.list_reverse
def list_reverse(lst):
self.assertEqual(reverse(lst), 0)
return lst
self.assertEqual(list_reverse([]), [])
self.assertEqual(list_reverse([2, 5, 10]), [10, 5, 2])
self.assertEqual(list_reverse(list_reverse([2, 5, 10])), [2, 5, 10])
self.assertRaises(SystemError, reverse, ())
self.assertRaises(SystemError, reverse, object())
self.assertRaises(SystemError, reverse, NULL)
def test_list_astuple(self):
# Test PyList_AsTuple()
astuple = _testlimitedcapi.list_astuple
self.assertEqual(astuple([]), ())
self.assertEqual(astuple([[]]), ([],))
self.assertEqual(astuple([2, 5, 10]), (2, 5, 10))
self.assertRaises(SystemError, astuple, ())
self.assertRaises(SystemError, astuple, object())
self.assertRaises(SystemError, astuple, NULL)
def test_list_clear(self):
# Test PyList_Clear()
list_clear = _testcapi.list_clear
lst = [1, 2, 3]
self.assertEqual(list_clear(lst), 0)
self.assertEqual(lst, [])
lst = []
self.assertEqual(list_clear(lst), 0)
self.assertEqual(lst, [])
self.assertRaises(SystemError, list_clear, ())
self.assertRaises(SystemError, list_clear, object())
# Item finalizer modify the list
lst = []
lst.append(DelAppend(lst, 'zombie'))
list_clear(lst)
self.assertEqual(lst, ['zombie'])
# CRASHES list_clear(NULL)
def test_list_extend(self):
# Test PyList_Extend()
list_extend = _testcapi.list_extend
for other_type in (list, tuple, str, iter):
lst = list("ab")
arg = other_type("def")
self.assertEqual(list_extend(lst, arg), 0)
self.assertEqual(lst, list("abdef"))
# PyList_Extend(lst, lst)
lst = list("abc")
self.assertEqual(list_extend(lst, lst), 0)
self.assertEqual(lst, list("abcabc"))
self.assertRaises(TypeError, list_extend, [], object())
self.assertRaises(SystemError, list_extend, (), list("abc"))
# CRASHES list_extend(NULL, [])
# CRASHES list_extend([], NULL)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,619 @@
import unittest
import sys
import test.support as support
from test.support import import_helper
# Skip this test if the _testcapi and _testlimitedcapi modules isn't available.
_testcapi = import_helper.import_module('_testcapi')
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
NULL = None
class IntSubclass(int):
pass
class Index:
def __init__(self, value):
self.value = value
def __index__(self):
return self.value
# use __index__(), not __int__()
class MyIndexAndInt:
def __index__(self):
return 10
def __int__(self):
return 22
class LongTests(unittest.TestCase):
def test_compact(self):
for n in {
# Edge cases
*(2**n for n in range(66)),
*(-2**n for n in range(66)),
*(2**n - 1 for n in range(66)),
*(-2**n + 1 for n in range(66)),
# Essentially random
*(37**n for n in range(14)),
*(-37**n for n in range(14)),
}:
with self.subTest(n=n):
is_compact, value = _testcapi.call_long_compact_api(n)
if is_compact:
self.assertEqual(n, value)
def test_compact_known(self):
# Sanity-check some implementation details (we don't guarantee
# that these are/aren't compact)
self.assertEqual(_testcapi.call_long_compact_api(-1), (True, -1))
self.assertEqual(_testcapi.call_long_compact_api(0), (True, 0))
self.assertEqual(_testcapi.call_long_compact_api(256), (True, 256))
self.assertEqual(_testcapi.call_long_compact_api(sys.maxsize),
(False, -1))
def test_long_check(self):
# Test PyLong_Check()
check = _testlimitedcapi.pylong_check
self.assertTrue(check(1))
self.assertTrue(check(123456789012345678901234567890))
self.assertTrue(check(-1))
self.assertTrue(check(True))
self.assertTrue(check(IntSubclass(1)))
self.assertFalse(check(1.0))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_long_checkexact(self):
# Test PyLong_CheckExact()
check = _testlimitedcapi.pylong_checkexact
self.assertTrue(check(1))
self.assertTrue(check(123456789012345678901234567890))
self.assertTrue(check(-1))
self.assertFalse(check(True))
self.assertFalse(check(IntSubclass(1)))
self.assertFalse(check(1.0))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_long_fromdouble(self):
# Test PyLong_FromDouble()
fromdouble = _testlimitedcapi.pylong_fromdouble
float_max = sys.float_info.max
for value in (5.0, 5.1, 5.9, -5.1, -5.9, 0.0, -0.0, float_max, -float_max):
with self.subTest(value=value):
self.assertEqual(fromdouble(value), int(value))
self.assertRaises(OverflowError, fromdouble, float('inf'))
self.assertRaises(OverflowError, fromdouble, float('-inf'))
self.assertRaises(ValueError, fromdouble, float('nan'))
def test_long_fromvoidptr(self):
# Test PyLong_FromVoidPtr()
fromvoidptr = _testlimitedcapi.pylong_fromvoidptr
obj = object()
x = fromvoidptr(obj)
y = fromvoidptr(NULL)
self.assertIsInstance(x, int)
self.assertGreaterEqual(x, 0)
self.assertIsInstance(y, int)
self.assertEqual(y, 0)
self.assertNotEqual(x, y)
def test_long_fromstring(self):
# Test PyLong_FromString()
fromstring = _testlimitedcapi.pylong_fromstring
self.assertEqual(fromstring(b'123', 10), (123, 3))
self.assertEqual(fromstring(b'cafe', 16), (0xcafe, 4))
self.assertEqual(fromstring(b'xyz', 36), (44027, 3))
self.assertEqual(fromstring(b'123', 0), (123, 3))
self.assertEqual(fromstring(b'0xcafe', 0), (0xcafe, 6))
self.assertRaises(ValueError, fromstring, b'cafe', 0)
self.assertEqual(fromstring(b'-123', 10), (-123, 4))
self.assertEqual(fromstring(b' -123 ', 10), (-123, 6))
self.assertEqual(fromstring(b'1_23', 10), (123, 4))
self.assertRaises(ValueError, fromstring, b'- 123', 10)
self.assertRaises(ValueError, fromstring, b'', 10)
self.assertRaises(ValueError, fromstring, b'123', 1)
self.assertRaises(ValueError, fromstring, b'123', -1)
self.assertRaises(ValueError, fromstring, b'123', 37)
self.assertRaises(ValueError, fromstring, '١٢٣٤٥٦٧٨٩٠'.encode(), 0)
self.assertRaises(ValueError, fromstring, '١٢٣٤٥٦٧٨٩٠'.encode(), 16)
self.assertEqual(fromstring(b'123\x00', 0), (123, 3))
self.assertEqual(fromstring(b'123\x00456', 0), (123, 3))
self.assertEqual(fromstring(b'123\x00', 16), (0x123, 3))
self.assertEqual(fromstring(b'123\x00456', 16), (0x123, 3))
# CRASHES fromstring(NULL, 0)
# CRASHES fromstring(NULL, 16)
def test_long_fromunicodeobject(self):
# Test PyLong_FromUnicodeObject()
fromunicodeobject = _testcapi.pylong_fromunicodeobject
self.assertEqual(fromunicodeobject('123', 10), 123)
self.assertEqual(fromunicodeobject('cafe', 16), 0xcafe)
self.assertEqual(fromunicodeobject('xyz', 36), 44027)
self.assertEqual(fromunicodeobject('123', 0), 123)
self.assertEqual(fromunicodeobject('0xcafe', 0), 0xcafe)
self.assertRaises(ValueError, fromunicodeobject, 'cafe', 0)
self.assertEqual(fromunicodeobject('-123', 10), -123)
self.assertEqual(fromunicodeobject(' -123 ', 10), -123)
self.assertEqual(fromunicodeobject('1_23', 10), 123)
self.assertRaises(ValueError, fromunicodeobject, '- 123', 10)
self.assertRaises(ValueError, fromunicodeobject, '', 10)
self.assertRaises(ValueError, fromunicodeobject, '123', 1)
self.assertRaises(ValueError, fromunicodeobject, '123', -1)
self.assertRaises(ValueError, fromunicodeobject, '123', 37)
self.assertEqual(fromunicodeobject('١٢٣٤٥٦٧٨٩٠', 0), 1234567890)
self.assertEqual(fromunicodeobject('١٢٣٤٥٦٧٨٩٠', 16), 0x1234567890)
self.assertRaises(ValueError, fromunicodeobject, '123\x00', 0)
self.assertRaises(ValueError, fromunicodeobject, '123\x00456', 0)
self.assertRaises(ValueError, fromunicodeobject, '123\x00', 16)
self.assertRaises(ValueError, fromunicodeobject, '123\x00456', 16)
# CRASHES fromunicodeobject(NULL, 0)
# CRASHES fromunicodeobject(NULL, 16)
def check_long_asint(self, func, min_val, max_val, *,
use_index=True,
mask=False,
negative_value_error=OverflowError):
# round trip (object -> C integer -> object)
values = (0, 1, 1234, max_val)
if min_val < 0:
values += (-1, min_val)
for value in values:
with self.subTest(value=value):
self.assertEqual(func(value), value)
self.assertEqual(func(IntSubclass(value)), value)
if use_index:
self.assertEqual(func(Index(value)), value)
if use_index:
self.assertEqual(func(MyIndexAndInt()), 10)
else:
self.assertRaises(TypeError, func, Index(42))
self.assertRaises(TypeError, func, MyIndexAndInt())
if mask:
self.assertEqual(func(min_val - 1), max_val)
self.assertEqual(func(max_val + 1), min_val)
self.assertEqual(func(-1 << 1000), 0)
self.assertEqual(func(1 << 1000), 0)
else:
self.assertRaises(negative_value_error, func, min_val - 1)
self.assertRaises(negative_value_error, func, -1 << 1000)
self.assertRaises(OverflowError, func, max_val + 1)
self.assertRaises(OverflowError, func, 1 << 1000)
self.assertRaises(TypeError, func, 1.0)
self.assertRaises(TypeError, func, b'2')
self.assertRaises(TypeError, func, '3')
self.assertRaises(SystemError, func, NULL)
def check_long_asintandoverflow(self, func, min_val, max_val):
# round trip (object -> C integer -> object)
for value in (min_val, max_val, -1, 0, 1, 1234):
with self.subTest(value=value):
self.assertEqual(func(value), (value, 0))
self.assertEqual(func(IntSubclass(value)), (value, 0))
self.assertEqual(func(Index(value)), (value, 0))
self.assertEqual(func(MyIndexAndInt()), (10, 0))
self.assertEqual(func(min_val - 1), (-1, -1))
self.assertEqual(func(max_val + 1), (-1, +1))
self.assertRaises(SystemError, func, None)
self.assertRaises(TypeError, func, 1.0)
def test_long_asint(self):
# Test PyLong_AsInt()
PyLong_AsInt = _testlimitedcapi.PyLong_AsInt
from _testcapi import INT_MIN, INT_MAX
self.check_long_asint(PyLong_AsInt, INT_MIN, INT_MAX)
def test_long_aslong(self):
# Test PyLong_AsLong() and PyLong_FromLong()
aslong = _testlimitedcapi.pylong_aslong
from _testcapi import LONG_MIN, LONG_MAX
self.check_long_asint(aslong, LONG_MIN, LONG_MAX)
def test_long_aslongandoverflow(self):
# Test PyLong_AsLongAndOverflow()
aslongandoverflow = _testlimitedcapi.pylong_aslongandoverflow
from _testcapi import LONG_MIN, LONG_MAX
self.check_long_asintandoverflow(aslongandoverflow, LONG_MIN, LONG_MAX)
def test_long_asunsignedlong(self):
# Test PyLong_AsUnsignedLong() and PyLong_FromUnsignedLong()
asunsignedlong = _testlimitedcapi.pylong_asunsignedlong
from _testcapi import ULONG_MAX
self.check_long_asint(asunsignedlong, 0, ULONG_MAX,
use_index=False)
def test_long_asunsignedlongmask(self):
# Test PyLong_AsUnsignedLongMask()
asunsignedlongmask = _testlimitedcapi.pylong_asunsignedlongmask
from _testcapi import ULONG_MAX
self.check_long_asint(asunsignedlongmask, 0, ULONG_MAX, mask=True)
def test_long_aslonglong(self):
# Test PyLong_AsLongLong() and PyLong_FromLongLong()
aslonglong = _testlimitedcapi.pylong_aslonglong
from _testcapi import LLONG_MIN, LLONG_MAX
self.check_long_asint(aslonglong, LLONG_MIN, LLONG_MAX)
def test_long_aslonglongandoverflow(self):
# Test PyLong_AsLongLongAndOverflow()
aslonglongandoverflow = _testlimitedcapi.pylong_aslonglongandoverflow
from _testcapi import LLONG_MIN, LLONG_MAX
self.check_long_asintandoverflow(aslonglongandoverflow, LLONG_MIN, LLONG_MAX)
def test_long_asunsignedlonglong(self):
# Test PyLong_AsUnsignedLongLong() and PyLong_FromUnsignedLongLong()
asunsignedlonglong = _testlimitedcapi.pylong_asunsignedlonglong
from _testcapi import ULLONG_MAX
self.check_long_asint(asunsignedlonglong, 0, ULLONG_MAX, use_index=False)
def test_long_asunsignedlonglongmask(self):
# Test PyLong_AsUnsignedLongLongMask()
asunsignedlonglongmask = _testlimitedcapi.pylong_asunsignedlonglongmask
from _testcapi import ULLONG_MAX
self.check_long_asint(asunsignedlonglongmask, 0, ULLONG_MAX, mask=True)
def test_long_as_ssize_t(self):
# Test PyLong_AsSsize_t() and PyLong_FromSsize_t()
as_ssize_t = _testlimitedcapi.pylong_as_ssize_t
from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX
self.check_long_asint(as_ssize_t, PY_SSIZE_T_MIN, PY_SSIZE_T_MAX,
use_index=False)
def test_long_as_size_t(self):
# Test PyLong_AsSize_t() and PyLong_FromSize_t()
as_size_t = _testlimitedcapi.pylong_as_size_t
from _testcapi import SIZE_MAX
self.check_long_asint(as_size_t, 0, SIZE_MAX, use_index=False)
def test_long_asdouble(self):
# Test PyLong_AsDouble()
asdouble = _testlimitedcapi.pylong_asdouble
MAX = int(sys.float_info.max)
for value in (-MAX, MAX, -1, 0, 1, 1234):
with self.subTest(value=value):
self.assertEqual(asdouble(value), float(value))
self.assertIsInstance(asdouble(value), float)
self.assertEqual(asdouble(IntSubclass(42)), 42.0)
self.assertRaises(TypeError, asdouble, Index(42))
self.assertRaises(TypeError, asdouble, MyIndexAndInt())
self.assertRaises(OverflowError, asdouble, 2 * MAX)
self.assertRaises(OverflowError, asdouble, -2 * MAX)
self.assertRaises(TypeError, asdouble, 1.0)
self.assertRaises(TypeError, asdouble, b'2')
self.assertRaises(TypeError, asdouble, '3')
self.assertRaises(SystemError, asdouble, NULL)
def test_long_asvoidptr(self):
# Test PyLong_AsVoidPtr()
fromvoidptr = _testlimitedcapi.pylong_fromvoidptr
asvoidptr = _testlimitedcapi.pylong_asvoidptr
obj = object()
x = fromvoidptr(obj)
y = fromvoidptr(NULL)
self.assertIs(asvoidptr(x), obj)
self.assertIs(asvoidptr(y), NULL)
self.assertIs(asvoidptr(IntSubclass(x)), obj)
# negative values
M = (1 << _testcapi.SIZEOF_VOID_P * 8)
if x >= M//2:
self.assertIs(asvoidptr(x - M), obj)
if y >= M//2:
self.assertIs(asvoidptr(y - M), NULL)
self.assertRaises(TypeError, asvoidptr, Index(x))
self.assertRaises(TypeError, asvoidptr, object())
self.assertRaises(OverflowError, asvoidptr, 2**1000)
self.assertRaises(OverflowError, asvoidptr, -2**1000)
# CRASHES asvoidptr(NULL)
def _test_long_aspid(self, aspid):
# Test PyLong_AsPid()
from _testcapi import SIZEOF_PID_T
bits = 8 * SIZEOF_PID_T
PID_T_MIN = -2**(bits-1)
PID_T_MAX = 2**(bits-1) - 1
self.check_long_asint(aspid, PID_T_MIN, PID_T_MAX)
def test_long_aspid(self):
self._test_long_aspid(_testcapi.pylong_aspid)
def test_long_aspid_limited(self):
self._test_long_aspid(_testlimitedcapi.pylong_aspid)
def test_long_asnativebytes(self):
import math
from _testcapi import (
pylong_asnativebytes as asnativebytes,
SIZE_MAX,
)
# Abbreviate sizeof(Py_ssize_t) to SZ because we use it a lot
SZ = int(math.ceil(math.log(SIZE_MAX + 1) / math.log(2)) / 8)
MAX_SSIZE = 2 ** (SZ * 8 - 1) - 1
MAX_USIZE = 2 ** (SZ * 8) - 1
if support.verbose:
print(f"SIZEOF_SIZE={SZ}\n{MAX_SSIZE=:016X}\n{MAX_USIZE=:016X}")
# These tests check that the requested buffer size is correct.
# This matches our current implementation: We only specify that the
# return value is a size *sufficient* to hold the result when queried
# using n_bytes=0. If our implementation changes, feel free to update
# the expectations here -- or loosen them to be range checks.
# (i.e. 0 *could* be stored in 1 byte and 512 in 2)
for v, expect in [
(0, SZ),
(512, SZ),
(-512, SZ),
(MAX_SSIZE, SZ),
(MAX_USIZE, SZ + 1),
(-MAX_SSIZE, SZ),
(-MAX_USIZE, SZ + 1),
(2**255-1, 32),
(-(2**255-1), 32),
(2**255, 33),
(-(2**255), 33), # if you ask, we'll say 33, but 32 would do
(2**256-1, 33),
(-(2**256-1), 33),
(2**256, 33),
(-(2**256), 33),
]:
with self.subTest(f"sizeof-{v:X}"):
buffer = bytearray(b"\x5a")
self.assertEqual(expect, asnativebytes(v, buffer, 0, -1),
"PyLong_AsNativeBytes(v, <unknown>, 0, -1)")
self.assertEqual(buffer, b"\x5a",
"buffer overwritten when it should not have been")
# Also check via the __index__ path.
# We pass Py_ASNATIVEBYTES_NATIVE_ENDIAN | ALLOW_INDEX
self.assertEqual(expect, asnativebytes(Index(v), buffer, 0, 3 | 16),
"PyLong_AsNativeBytes(Index(v), <unknown>, 0, -1)")
self.assertEqual(buffer, b"\x5a",
"buffer overwritten when it should not have been")
# Test that we populate n=2 bytes but do not overwrite more.
buffer = bytearray(b"\x99"*3)
self.assertEqual(2, asnativebytes(4, buffer, 2, 0), # BE
"PyLong_AsNativeBytes(v, <3 byte buffer>, 2, 0) // BE")
self.assertEqual(buffer, b"\x00\x04\x99")
self.assertEqual(2, asnativebytes(4, buffer, 2, 1), # LE
"PyLong_AsNativeBytes(v, <3 byte buffer>, 2, 1) // LE")
self.assertEqual(buffer, b"\x04\x00\x99")
# We request as many bytes as `expect_be` contains, and always check
# the result (both big and little endian). We check the return value
# independently, since the buffer should always be filled correctly even
# if we need more bytes
for v, expect_be, expect_n in [
(0, b'\x00', 1),
(0, b'\x00' * 2, 2),
(0, b'\x00' * 8, min(8, SZ)),
(1, b'\x01', 1),
(1, b'\x00' * 10 + b'\x01', min(11, SZ)),
(42, b'\x2a', 1),
(42, b'\x00' * 10 + b'\x2a', min(11, SZ)),
(-1, b'\xff', 1),
(-1, b'\xff' * 10, min(11, SZ)),
(-42, b'\xd6', 1),
(-42, b'\xff' * 10 + b'\xd6', min(11, SZ)),
# Extracts 255 into a single byte, but requests 2
# (this is currently a special case, and "should" request SZ)
(255, b'\xff', 2),
(255, b'\x00\xff', 2),
(256, b'\x01\x00', 2),
(0x80, b'\x00' * 7 + b'\x80', min(8, SZ)),
# Extracts successfully (unsigned), but requests 9 bytes
(2**63, b'\x80' + b'\x00' * 7, 9),
(2**63, b'\x00\x80' + b'\x00' * 7, 9),
# Extracts into 8 bytes, but if you provide 9 we'll say 9
(-2**63, b'\x80' + b'\x00' * 7, 8),
(-2**63, b'\xff\x80' + b'\x00' * 7, 9),
(2**255-1, b'\x7f' + b'\xff' * 31, 32),
(-(2**255-1), b'\x80' + b'\x00' * 30 + b'\x01', 32),
# Request extra bytes, but result says we only needed 32
(-(2**255-1), b'\xff\x80' + b'\x00' * 30 + b'\x01', 32),
(-(2**255-1), b'\xff\xff\x80' + b'\x00' * 30 + b'\x01', 32),
# Extracting 256 bits of integer will request 33 bytes, but still
# copy as many bits as possible into the buffer. So we *can* copy
# into a 32-byte buffer, though negative number may be unrecoverable
(2**256-1, b'\xff' * 32, 33),
(2**256-1, b'\x00' + b'\xff' * 32, 33),
(-(2**256-1), b'\x00' * 31 + b'\x01', 33),
(-(2**256-1), b'\xff' + b'\x00' * 31 + b'\x01', 33),
(-(2**256-1), b'\xff\xff' + b'\x00' * 31 + b'\x01', 33),
# However, -2**255 precisely will extract into 32 bytes and return
# success. For bigger buffers, it will still succeed, but will
# return 33
(-(2**255), b'\x80' + b'\x00' * 31, 32),
(-(2**255), b'\xff\x80' + b'\x00' * 31, 33),
# The classic "Windows HRESULT as negative number" case
# HRESULT hr;
# PyLong_AsNativeBytes(<-2147467259>, &hr, sizeof(HRESULT), -1)
# assert(hr == E_FAIL)
(-2147467259, b'\x80\x00\x40\x05', 4),
]:
with self.subTest(f"{v:X}-{len(expect_be)}bytes"):
n = len(expect_be)
# Fill the buffer with dummy data to ensure all bytes
# are overwritten.
buffer = bytearray(b"\xa5"*n)
expect_le = expect_be[::-1]
self.assertEqual(expect_n, asnativebytes(v, buffer, n, 0),
f"PyLong_AsNativeBytes(v, buffer, {n}, <big>)")
self.assertEqual(expect_be, buffer[:n], "<big>")
self.assertEqual(expect_n, asnativebytes(v, buffer, n, 1),
f"PyLong_AsNativeBytes(v, buffer, {n}, <little>)")
self.assertEqual(expect_le, buffer[:n], "<little>")
# Test cases that do not request size for a sign bit when we pass the
# Py_ASNATIVEBYTES_UNSIGNED_BUFFER flag
for v, expect_be, expect_n in [
(255, b'\xff', 1),
# We pass a 2 byte buffer so it just uses the whole thing
(255, b'\x00\xff', 2),
(2**63, b'\x80' + b'\x00' * 7, 8),
# We pass a 9 byte buffer so it uses the whole thing
(2**63, b'\x00\x80' + b'\x00' * 7, 9),
(2**256-1, b'\xff' * 32, 32),
# We pass a 33 byte buffer so it uses the whole thing
(2**256-1, b'\x00' + b'\xff' * 32, 33),
]:
with self.subTest(f"{v:X}-{len(expect_be)}bytes-unsigned"):
n = len(expect_be)
buffer = bytearray(b"\xa5"*n)
self.assertEqual(expect_n, asnativebytes(v, buffer, n, 4),
f"PyLong_AsNativeBytes(v, buffer, {n}, <big|unsigned>)")
self.assertEqual(expect_n, asnativebytes(v, buffer, n, 5),
f"PyLong_AsNativeBytes(v, buffer, {n}, <little|unsigned>)")
# Ensure Py_ASNATIVEBYTES_REJECT_NEGATIVE raises on negative value
with self.assertRaises(ValueError):
asnativebytes(-1, buffer, 0, 8)
# Ensure omitting Py_ASNATIVEBYTES_ALLOW_INDEX raises on __index__ value
with self.assertRaises(TypeError):
asnativebytes(Index(1), buffer, 0, -1)
with self.assertRaises(TypeError):
asnativebytes(Index(1), buffer, 0, 3)
# Check a few error conditions. These are validated in code, but are
# unspecified in docs, so if we make changes to the implementation, it's
# fine to just update these tests rather than preserve the behaviour.
with self.assertRaises(TypeError):
asnativebytes('not a number', buffer, 0, -1)
def test_long_asnativebytes_fuzz(self):
import math
from random import Random
from _testcapi import (
pylong_asnativebytes as asnativebytes,
SIZE_MAX,
)
# Abbreviate sizeof(Py_ssize_t) to SZ because we use it a lot
SZ = int(math.ceil(math.log(SIZE_MAX + 1) / math.log(2)) / 8)
rng = Random()
# Allocate bigger buffer than actual values are going to be
buffer = bytearray(260)
for _ in range(1000):
n = rng.randrange(1, 256)
bytes_be = bytes([
# Ensure the most significant byte is nonzero
rng.randrange(1, 256),
*[rng.randrange(256) for _ in range(n - 1)]
])
bytes_le = bytes_be[::-1]
v = int.from_bytes(bytes_le, 'little')
expect_1 = expect_2 = (SZ, n)
if bytes_be[0] & 0x80:
# All values are positive, so if MSB is set, expect extra bit
# when we request the size or have a large enough buffer
expect_1 = (SZ, n + 1)
# When passing Py_ASNATIVEBYTES_UNSIGNED_BUFFER, we expect the
# return to be exactly the right size.
expect_2 = (n,)
try:
actual = asnativebytes(v, buffer, 0, -1)
self.assertIn(actual, expect_1)
actual = asnativebytes(v, buffer, len(buffer), 0)
self.assertIn(actual, expect_1)
self.assertEqual(bytes_be, buffer[-n:])
actual = asnativebytes(v, buffer, len(buffer), 1)
self.assertIn(actual, expect_1)
self.assertEqual(bytes_le, buffer[:n])
actual = asnativebytes(v, buffer, n, 4)
self.assertIn(actual, expect_2, bytes_be.hex())
actual = asnativebytes(v, buffer, n, 5)
self.assertIn(actual, expect_2, bytes_be.hex())
except AssertionError as ex:
value_hex = ''.join(reversed([
f'{b:02X}{"" if i % 8 else "_"}'
for i, b in enumerate(bytes_le, start=1)
])).strip('_')
if support.verbose:
print()
print(n, 'bytes')
print('hex =', value_hex)
print('int =', v)
raise
raise AssertionError(f"Value: 0x{value_hex}") from ex
def test_long_fromnativebytes(self):
import math
from _testcapi import (
pylong_fromnativebytes as fromnativebytes,
SIZE_MAX,
)
# Abbreviate sizeof(Py_ssize_t) to SZ because we use it a lot
SZ = int(math.ceil(math.log(SIZE_MAX + 1) / math.log(2)) / 8)
MAX_SSIZE = 2 ** (SZ * 8 - 1) - 1
MAX_USIZE = 2 ** (SZ * 8) - 1
for v_be, expect_s, expect_u in [
(b'\x00', 0, 0),
(b'\x01', 1, 1),
(b'\xff', -1, 255),
(b'\x00\xff', 255, 255),
(b'\xff\xff', -1, 65535),
]:
with self.subTest(f"{expect_s}-{expect_u:X}-{len(v_be)}bytes"):
n = len(v_be)
v_le = v_be[::-1]
self.assertEqual(expect_s, fromnativebytes(v_be, n, 0, 1),
f"PyLong_FromNativeBytes(buffer, {n}, <big>)")
self.assertEqual(expect_s, fromnativebytes(v_le, n, 1, 1),
f"PyLong_FromNativeBytes(buffer, {n}, <little>)")
self.assertEqual(expect_u, fromnativebytes(v_be, n, 0, 0),
f"PyLong_FromUnsignedNativeBytes(buffer, {n}, <big>)")
self.assertEqual(expect_u, fromnativebytes(v_le, n, 1, 0),
f"PyLong_FromUnsignedNativeBytes(buffer, {n}, <little>)")
# Check native endian when the result would be the same either
# way and we can test it.
if v_be == v_le:
self.assertEqual(expect_s, fromnativebytes(v_be, n, -1, 1),
f"PyLong_FromNativeBytes(buffer, {n}, <native>)")
self.assertEqual(expect_u, fromnativebytes(v_be, n, -1, 0),
f"PyLong_FromUnsignedNativeBytes(buffer, {n}, <native>)")
# Swap the unsigned request for tests and use the
# Py_ASNATIVEBYTES_UNSIGNED_BUFFER flag instead
self.assertEqual(expect_u, fromnativebytes(v_be, n, 4, 1),
f"PyLong_FromNativeBytes(buffer, {n}, <big|unsigned>)")
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,178 @@
import re
import textwrap
import unittest
from test import support
from test.support import import_helper, requires_subprocess
from test.support.script_helper import assert_python_failure, assert_python_ok
# Skip this test if the _testcapi and _testinternalcapi extensions are not
# available.
_testcapi = import_helper.import_module('_testcapi')
_testinternalcapi = import_helper.import_module('_testinternalcapi')
@requires_subprocess()
class PyMemDebugTests(unittest.TestCase):
PYTHONMALLOC = 'debug'
# '0x04c06e0' or '04C06E0'
PTR_REGEX = r'(?:0x)?[0-9a-fA-F]+'
def check(self, code):
with support.SuppressCrashReport():
out = assert_python_failure(
'-c', code,
PYTHONMALLOC=self.PYTHONMALLOC,
# FreeBSD: instruct jemalloc to not fill freed() memory
# with junk byte 0x5a, see JEMALLOC(3)
MALLOC_CONF="junk:false",
)
stderr = out.err
return stderr.decode('ascii', 'replace')
def test_buffer_overflow(self):
out = self.check('import _testcapi; _testcapi.pymem_buffer_overflow()')
regex = (r"Debug memory block at address p={ptr}: API 'm'\n"
r" 16 bytes originally requested\n"
r" The [0-9] pad bytes at p-[0-9] are FORBIDDENBYTE, as expected.\n"
r" The [0-9] pad bytes at tail={ptr} are not all FORBIDDENBYTE \(0x[0-9a-f]{{2}}\):\n"
r" at tail\+0: 0x78 \*\*\* OUCH\n"
r" at tail\+1: 0xfd\n"
r" at tail\+2: 0xfd\n"
r" .*\n"
r"( The block was made by call #[0-9]+ to debug malloc/realloc.\n)?"
r" Data at p: cd cd cd .*\n"
r"\n"
r"Enable tracemalloc to get the memory block allocation traceback\n"
r"\n"
r"Fatal Python error: _PyMem_DebugRawFree: bad trailing pad byte")
regex = regex.format(ptr=self.PTR_REGEX)
regex = re.compile(regex, flags=re.DOTALL)
self.assertRegex(out, regex)
def test_api_misuse(self):
out = self.check('import _testcapi; _testcapi.pymem_api_misuse()')
regex = (r"Debug memory block at address p={ptr}: API 'm'\n"
r" 16 bytes originally requested\n"
r" The [0-9] pad bytes at p-[0-9] are FORBIDDENBYTE, as expected.\n"
r" The [0-9] pad bytes at tail={ptr} are FORBIDDENBYTE, as expected.\n"
r"( The block was made by call #[0-9]+ to debug malloc/realloc.\n)?"
r" Data at p: cd cd cd .*\n"
r"\n"
r"Enable tracemalloc to get the memory block allocation traceback\n"
r"\n"
r"Fatal Python error: _PyMem_DebugRawFree: bad ID: Allocated using API 'm', verified using API 'r'\n")
regex = regex.format(ptr=self.PTR_REGEX)
self.assertRegex(out, regex)
def check_malloc_without_gil(self, code):
out = self.check(code)
expected = ('Fatal Python error: _PyMem_DebugMalloc: '
'Python memory allocator called without holding the GIL')
self.assertIn(expected, out)
def test_pymem_malloc_without_gil(self):
# Debug hooks must raise an error if PyMem_Malloc() is called
# without holding the GIL
code = 'import _testcapi; _testcapi.pymem_malloc_without_gil()'
self.check_malloc_without_gil(code)
def test_pyobject_malloc_without_gil(self):
# Debug hooks must raise an error if PyObject_Malloc() is called
# without holding the GIL
code = 'import _testcapi; _testcapi.pyobject_malloc_without_gil()'
self.check_malloc_without_gil(code)
def check_pyobject_is_freed(self, func_name):
code = textwrap.dedent(f'''
import gc, os, sys, _testinternalcapi
# Disable the GC to avoid crash on GC collection
gc.disable()
_testinternalcapi.{func_name}()
# Exit immediately to avoid a crash while deallocating
# the invalid object
os._exit(0)
''')
assert_python_ok(
'-c', code,
PYTHONMALLOC=self.PYTHONMALLOC,
MALLOC_CONF="junk:false",
)
def test_pyobject_null_is_freed(self):
self.check_pyobject_is_freed('check_pyobject_null_is_freed')
def test_pyobject_uninitialized_is_freed(self):
self.check_pyobject_is_freed('check_pyobject_uninitialized_is_freed')
def test_pyobject_forbidden_bytes_is_freed(self):
self.check_pyobject_is_freed('check_pyobject_forbidden_bytes_is_freed')
def test_pyobject_freed_is_freed(self):
self.check_pyobject_is_freed('check_pyobject_freed_is_freed')
# Python built with Py_TRACE_REFS fail with a fatal error in
# _PyRefchain_Trace() on memory allocation error.
@unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build')
def test_set_nomemory(self):
code = """if 1:
import _testcapi
class C(): pass
# The first loop tests both functions and that remove_mem_hooks()
# can be called twice in a row. The second loop checks a call to
# set_nomemory() after a call to remove_mem_hooks(). The third
# loop checks the start and stop arguments of set_nomemory().
for outer_cnt in range(1, 4):
start = 10 * outer_cnt
for j in range(100):
if j == 0:
if outer_cnt != 3:
_testcapi.set_nomemory(start)
else:
_testcapi.set_nomemory(start, start + 1)
try:
C()
except MemoryError as e:
if outer_cnt != 3:
_testcapi.remove_mem_hooks()
print('MemoryError', outer_cnt, j)
_testcapi.remove_mem_hooks()
break
"""
rc, out, err = assert_python_ok('-c', code)
lines = out.splitlines()
for i, line in enumerate(lines, 1):
self.assertIn(b'MemoryError', out)
*_, count = line.split(b' ')
count = int(count)
self.assertLessEqual(count, i*10)
self.assertGreaterEqual(count, i*10-4)
# free-threading requires mimalloc (not malloc)
@support.requires_gil_enabled()
class PyMemMallocDebugTests(PyMemDebugTests):
PYTHONMALLOC = 'malloc_debug'
@unittest.skipUnless(support.with_pymalloc(), 'need pymalloc')
class PyMemPymallocDebugTests(PyMemDebugTests):
PYTHONMALLOC = 'pymalloc_debug'
@unittest.skipUnless(support.with_mimalloc(), 'need mimaloc')
class PyMemMimallocDebugTests(PyMemDebugTests):
PYTHONMALLOC = 'mimalloc_debug'
@unittest.skipUnless(support.Py_DEBUG, 'need Py_DEBUG')
class PyMemDefaultTests(PyMemDebugTests):
# test default allocator of Python compiled in debug mode
PYTHONMALLOC = ''
if __name__ == "__main__":
unittest.main()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,369 @@
import itertools
import operator
import sys
import unittest
import warnings
from test.support import cpython_only, import_helper
_testcapi = import_helper.import_module('_testcapi')
from _testcapi import PY_SSIZE_T_MAX, PY_SSIZE_T_MIN
try:
from _testbuffer import ndarray
except ImportError:
ndarray = None
NULL = None
class BadDescr:
def __get__(self, obj, objtype=None):
raise RuntimeError
class WithDunder:
def _meth(self, *args):
if self.val:
return self.val
if self.exc:
raise self.exc
@classmethod
def with_val(cls, val):
obj = super().__new__(cls)
obj.val = val
obj.exc = None
setattr(cls, cls.methname, cls._meth)
return obj
@classmethod
def with_exc(cls, exc):
obj = super().__new__(cls)
obj.val = None
obj.exc = exc
setattr(cls, cls.methname, cls._meth)
return obj
class HasBadAttr:
def __new__(cls):
obj = super().__new__(cls)
setattr(cls, cls.methname, BadDescr())
return obj
class IndexLike(WithDunder):
methname = '__index__'
class IntLike(WithDunder):
methname = '__int__'
class FloatLike(WithDunder):
methname = '__float__'
def subclassof(base):
return type(base.__name__ + 'Subclass', (base,), {})
class SomeError(Exception):
pass
class OtherError(Exception):
pass
class CAPITest(unittest.TestCase):
def test_check(self):
# Test PyNumber_Check()
check = _testcapi.number_check
self.assertTrue(check(1))
self.assertTrue(check(IndexLike.with_val(1)))
self.assertTrue(check(IntLike.with_val(99)))
self.assertTrue(check(0.5))
self.assertTrue(check(FloatLike.with_val(4.25)))
self.assertTrue(check(1+2j))
self.assertFalse(check([]))
self.assertFalse(check("abc"))
self.assertFalse(check(object()))
self.assertFalse(check(NULL))
def test_unary_ops(self):
methmap = {'__neg__': _testcapi.number_negative, # PyNumber_Negative()
'__pos__': _testcapi.number_positive, # PyNumber_Positive()
'__abs__': _testcapi.number_absolute, # PyNumber_Absolute()
'__invert__': _testcapi.number_invert} # PyNumber_Invert()
for name, func in methmap.items():
# Generic object, has no tp_as_number structure
self.assertRaises(TypeError, func, object())
# C-API function accepts NULL
self.assertRaises(SystemError, func, NULL)
# Behave as corresponding unary operation
op = getattr(operator, name)
for x in [0, 42, -1, 3.14, 1+2j]:
try:
op(x)
except TypeError:
self.assertRaises(TypeError, func, x)
else:
self.assertEqual(func(x), op(x))
def test_binary_ops(self):
methmap = {'__add__': _testcapi.number_add, # PyNumber_Add()
'__sub__': _testcapi.number_subtract, # PyNumber_Subtract()
'__mul__': _testcapi.number_multiply, # PyNumber_Multiply()
'__matmul__': _testcapi.number_matrixmultiply, # PyNumber_MatrixMultiply()
'__floordiv__': _testcapi.number_floordivide, # PyNumber_FloorDivide()
'__truediv__': _testcapi.number_truedivide, # PyNumber_TrueDivide()
'__mod__': _testcapi.number_remainder, # PyNumber_Remainder()
'__divmod__': _testcapi.number_divmod, # PyNumber_Divmod()
'__lshift__': _testcapi.number_lshift, # PyNumber_Lshift()
'__rshift__': _testcapi.number_rshift, # PyNumber_Rshift()
'__and__': _testcapi.number_and, # PyNumber_And()
'__xor__': _testcapi.number_xor, # PyNumber_Xor()
'__or__': _testcapi.number_or, # PyNumber_Or()
'__pow__': _testcapi.number_power, # PyNumber_Power()
'__iadd__': _testcapi.number_inplaceadd, # PyNumber_InPlaceAdd()
'__isub__': _testcapi.number_inplacesubtract, # PyNumber_InPlaceSubtract()
'__imul__': _testcapi.number_inplacemultiply, # PyNumber_InPlaceMultiply()
'__imatmul__': _testcapi.number_inplacematrixmultiply, # PyNumber_InPlaceMatrixMultiply()
'__ifloordiv__': _testcapi.number_inplacefloordivide, # PyNumber_InPlaceFloorDivide()
'__itruediv__': _testcapi.number_inplacetruedivide, # PyNumber_InPlaceTrueDivide()
'__imod__': _testcapi.number_inplaceremainder, # PyNumber_InPlaceRemainder()
'__ilshift__': _testcapi.number_inplacelshift, # PyNumber_InPlaceLshift()
'__irshift__': _testcapi.number_inplacershift, # PyNumber_InPlaceRshift()
'__iand__': _testcapi.number_inplaceand, # PyNumber_InPlaceAnd()
'__ixor__': _testcapi.number_inplacexor, # PyNumber_InPlaceXor()
'__ior__': _testcapi.number_inplaceor, # PyNumber_InPlaceOr()
'__ipow__': _testcapi.number_inplacepower, # PyNumber_InPlacePower()
}
for name, func in methmap.items():
cases = [0, 42, 3.14, -1, 123, 1+2j]
# Generic object, has no tp_as_number structure
for x in cases:
self.assertRaises(TypeError, func, object(), x)
self.assertRaises(TypeError, func, x, object())
# Behave as corresponding binary operation
op = getattr(operator, name, divmod)
for x, y in itertools.combinations(cases, 2):
try:
op(x, y)
except (TypeError, ValueError, ZeroDivisionError) as exc:
self.assertRaises(exc.__class__, func, x, y)
else:
self.assertEqual(func(x, y), op(x, y))
# CRASHES func(NULL, object())
# CRASHES func(object(), NULL)
@unittest.skipIf(ndarray is None, "needs _testbuffer")
def test_misc_add(self):
# PyNumber_Add(), PyNumber_InPlaceAdd()
add = _testcapi.number_add
inplaceadd = _testcapi.number_inplaceadd
# test sq_concat/sq_inplace_concat slots
a, b, r = [1, 2], [3, 4], [1, 2, 3, 4]
self.assertEqual(add(a, b), r)
self.assertEqual(a, [1, 2])
self.assertRaises(TypeError, add, ndarray([1], (1,)), 2)
a, b, r = [1, 2], [3, 4], [1, 2, 3, 4]
self.assertEqual(inplaceadd(a, b), r)
self.assertEqual(a, r)
self.assertRaises(TypeError, inplaceadd, ndarray([1], (1,)), 2)
@unittest.skipIf(ndarray is None, "needs _testbuffer")
def test_misc_multiply(self):
# PyNumber_Multiply(), PyNumber_InPlaceMultiply()
multiply = _testcapi.number_multiply
inplacemultiply = _testcapi.number_inplacemultiply
# test sq_repeat/sq_inplace_repeat slots
a, b, r = [1], 2, [1, 1]
self.assertEqual(multiply(a, b), r)
self.assertEqual((a, b), ([1], 2))
self.assertEqual(multiply(b, a), r)
self.assertEqual((a, b), ([1], 2))
self.assertEqual(multiply([1], -1), [])
self.assertRaises(TypeError, multiply, ndarray([1], (1,)), 2)
self.assertRaises(TypeError, multiply, [1], 0.5)
self.assertRaises(OverflowError, multiply, [1], PY_SSIZE_T_MAX + 1)
self.assertRaises(MemoryError, multiply, [1, 2], PY_SSIZE_T_MAX//2 + 1)
a, b, r = [1], 2, [1, 1]
self.assertEqual(inplacemultiply(a, b), r)
self.assertEqual((a, b), (r, 2))
a = [1]
self.assertEqual(inplacemultiply(b, a), r)
self.assertEqual((a, b), ([1], 2))
self.assertRaises(TypeError, inplacemultiply, ndarray([1], (1,)), 2)
self.assertRaises(OverflowError, inplacemultiply, [1], PY_SSIZE_T_MAX + 1)
self.assertRaises(MemoryError, inplacemultiply, [1, 2], PY_SSIZE_T_MAX//2 + 1)
def test_misc_power(self):
# PyNumber_Power(), PyNumber_InPlacePower()
power = _testcapi.number_power
inplacepower = _testcapi.number_inplacepower
class HasPow(WithDunder):
methname = '__pow__'
# ternary op
self.assertEqual(power(4, 11, 5), pow(4, 11, 5))
self.assertRaises(TypeError, power, 4, 11, 1.25)
self.assertRaises(TypeError, power, 4, 11, HasPow.with_val(NotImplemented))
self.assertRaises(TypeError, power, 4, 11, object())
self.assertEqual(inplacepower(4, 11, 5), pow(4, 11, 5))
self.assertRaises(TypeError, inplacepower, 4, 11, 1.25)
self.assertRaises(TypeError, inplacepower, 4, 11, object())
class X:
def __pow__(*args):
return args
x = X()
self.assertEqual(power(x, 11), (x, 11))
self.assertEqual(inplacepower(x, 11), (x, 11))
self.assertEqual(power(x, 11, 5), (x, 11, 5))
self.assertEqual(inplacepower(x, 11, 5), (x, 11, 5))
class X:
def __rpow__(*args):
return args
x = X()
self.assertEqual(power(4, x), (x, 4))
self.assertEqual(inplacepower(4, x), (x, 4))
# XXX: Three-arg power doesn't use __rpow__.
self.assertRaises(TypeError, power, 4, x, 5)
self.assertRaises(TypeError, inplacepower, 4, x, 5)
class X:
def __ipow__(*args):
return args
x = X()
self.assertEqual(inplacepower(x, 11), (x, 11))
# XXX: In-place power doesn't pass the third arg to __ipow__.
self.assertEqual(inplacepower(x, 11, 5), (x, 11))
@cpython_only
def test_rshift_print(self):
# This tests correct syntax hint for py2 redirection (>>).
rshift = _testcapi.number_rshift
with self.assertRaises(TypeError) as context:
rshift(print, 42)
self.assertIn('Did you mean "print(<message>, '
'file=<output_stream>)"?', str(context.exception))
with self.assertRaises(TypeError) as context:
rshift(max, sys.stderr)
self.assertNotIn('Did you mean ', str(context.exception))
with self.assertRaises(TypeError) as context:
rshift(1, "spam")
def test_long(self):
# Test PyNumber_Long()
long = _testcapi.number_long
self.assertEqual(long(42), 42)
self.assertEqual(long(1.25), 1)
self.assertEqual(long("42"), 42)
self.assertEqual(long(b"42"), 42)
self.assertEqual(long(bytearray(b"42")), 42)
self.assertEqual(long(memoryview(b"42")), 42)
self.assertEqual(long(IndexLike.with_val(99)), 99)
self.assertEqual(long(IntLike.with_val(99)), 99)
self.assertRaises(TypeError, long, IntLike.with_val(1.0))
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
self.assertRaises(DeprecationWarning, long, IntLike.with_val(True))
with self.assertWarns(DeprecationWarning):
self.assertEqual(long(IntLike.with_val(True)), 1)
self.assertRaises(RuntimeError, long, IntLike.with_exc(RuntimeError))
self.assertRaises(TypeError, long, 1j)
self.assertRaises(TypeError, long, object())
self.assertRaises(SystemError, long, NULL)
def test_float(self):
# Test PyNumber_Float()
float_ = _testcapi.number_float
self.assertEqual(float_(1.25), 1.25)
self.assertEqual(float_(123), 123.)
self.assertEqual(float_("1.25"), 1.25)
self.assertEqual(float_(FloatLike.with_val(4.25)), 4.25)
self.assertEqual(float_(IndexLike.with_val(99)), 99.0)
self.assertEqual(float_(IndexLike.with_val(-1)), -1.0)
self.assertRaises(TypeError, float_, FloatLike.with_val(687))
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
self.assertRaises(DeprecationWarning, float_, FloatLike.with_val(subclassof(float)(4.25)))
with self.assertWarns(DeprecationWarning):
self.assertEqual(float_(FloatLike.with_val(subclassof(float)(4.25))), 4.25)
self.assertRaises(RuntimeError, float_, FloatLike.with_exc(RuntimeError))
self.assertRaises(TypeError, float_, IndexLike.with_val(1.25))
self.assertRaises(OverflowError, float_, IndexLike.with_val(2**2000))
self.assertRaises(TypeError, float_, 1j)
self.assertRaises(TypeError, float_, object())
self.assertRaises(SystemError, float_, NULL)
def test_index(self):
# Test PyNumber_Index()
index = _testcapi.number_index
self.assertEqual(index(11), 11)
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
self.assertRaises(DeprecationWarning, index, IndexLike.with_val(True))
with self.assertWarns(DeprecationWarning):
self.assertEqual(index(IndexLike.with_val(True)), 1)
self.assertRaises(TypeError, index, IndexLike.with_val(1.0))
self.assertRaises(RuntimeError, index, IndexLike.with_exc(RuntimeError))
self.assertRaises(TypeError, index, 1.25)
self.assertRaises(TypeError, index, "42")
self.assertRaises(TypeError, index, object())
self.assertRaises(SystemError, index, NULL)
def test_tobase(self):
# Test PyNumber_ToBase()
tobase = _testcapi.number_tobase
self.assertEqual(tobase(10, 2), bin(10))
self.assertEqual(tobase(11, 8), oct(11))
self.assertEqual(tobase(16, 10), str(16))
self.assertEqual(tobase(13, 16), hex(13))
self.assertRaises(SystemError, tobase, NULL, 2)
self.assertRaises(SystemError, tobase, 2, 3)
self.assertRaises(TypeError, tobase, 1.25, 2)
self.assertRaises(TypeError, tobase, "42", 2)
def test_asssizet(self):
# Test PyNumber_AsSsize_t()
asssizet = _testcapi.number_asssizet
for n in [*range(-6, 7), PY_SSIZE_T_MIN, PY_SSIZE_T_MAX]:
self.assertEqual(asssizet(n, OverflowError), n)
self.assertEqual(asssizet(PY_SSIZE_T_MAX+10, NULL), PY_SSIZE_T_MAX)
self.assertEqual(asssizet(PY_SSIZE_T_MIN-10, NULL), PY_SSIZE_T_MIN)
self.assertRaises(OverflowError, asssizet, PY_SSIZE_T_MAX + 10, OverflowError)
self.assertRaises(RuntimeError, asssizet, PY_SSIZE_T_MAX + 10, RuntimeError)
self.assertRaises(SystemError, asssizet, NULL, TypeError)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,135 @@
import enum
import unittest
from test.support import import_helper
from test.support import os_helper
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
_testcapi = import_helper.import_module('_testcapi')
class Constant(enum.IntEnum):
Py_CONSTANT_NONE = 0
Py_CONSTANT_FALSE = 1
Py_CONSTANT_TRUE = 2
Py_CONSTANT_ELLIPSIS = 3
Py_CONSTANT_NOT_IMPLEMENTED = 4
Py_CONSTANT_ZERO = 5
Py_CONSTANT_ONE = 6
Py_CONSTANT_EMPTY_STR = 7
Py_CONSTANT_EMPTY_BYTES = 8
Py_CONSTANT_EMPTY_TUPLE = 9
INVALID_CONSTANT = Py_CONSTANT_EMPTY_TUPLE + 1
class GetConstantTest(unittest.TestCase):
def check_get_constant(self, get_constant):
self.assertIs(get_constant(Constant.Py_CONSTANT_NONE), None)
self.assertIs(get_constant(Constant.Py_CONSTANT_FALSE), False)
self.assertIs(get_constant(Constant.Py_CONSTANT_TRUE), True)
self.assertIs(get_constant(Constant.Py_CONSTANT_ELLIPSIS), Ellipsis)
self.assertIs(get_constant(Constant.Py_CONSTANT_NOT_IMPLEMENTED), NotImplemented)
for constant_id, constant_type, value in (
(Constant.Py_CONSTANT_ZERO, int, 0),
(Constant.Py_CONSTANT_ONE, int, 1),
(Constant.Py_CONSTANT_EMPTY_STR, str, ""),
(Constant.Py_CONSTANT_EMPTY_BYTES, bytes, b""),
(Constant.Py_CONSTANT_EMPTY_TUPLE, tuple, ()),
):
with self.subTest(constant_id=constant_id):
obj = get_constant(constant_id)
self.assertEqual(type(obj), constant_type, obj)
self.assertEqual(obj, value)
with self.assertRaises(SystemError):
get_constant(Constant.INVALID_CONSTANT)
def test_get_constant(self):
self.check_get_constant(_testlimitedcapi.get_constant)
def test_get_constant_borrowed(self):
self.check_get_constant(_testlimitedcapi.get_constant_borrowed)
class PrintTest(unittest.TestCase):
def testPyObjectPrintObject(self):
class PrintableObject:
def __repr__(self):
return "spam spam spam"
def __str__(self):
return "egg egg egg"
obj = PrintableObject()
output_filename = os_helper.TESTFN
self.addCleanup(os_helper.unlink, output_filename)
# Test repr printing
_testcapi.call_pyobject_print(obj, output_filename, False)
with open(output_filename, 'r') as output_file:
self.assertEqual(output_file.read(), repr(obj))
# Test str printing
_testcapi.call_pyobject_print(obj, output_filename, True)
with open(output_filename, 'r') as output_file:
self.assertEqual(output_file.read(), str(obj))
def testPyObjectPrintNULL(self):
output_filename = os_helper.TESTFN
self.addCleanup(os_helper.unlink, output_filename)
# Test repr printing
_testcapi.pyobject_print_null(output_filename)
with open(output_filename, 'r') as output_file:
self.assertEqual(output_file.read(), '<nil>')
def testPyObjectPrintNoRefObject(self):
output_filename = os_helper.TESTFN
self.addCleanup(os_helper.unlink, output_filename)
# Test repr printing
correct_output = _testcapi.pyobject_print_noref_object(output_filename)
with open(output_filename, 'r') as output_file:
self.assertEqual(output_file.read(), correct_output)
def testPyObjectPrintOSError(self):
output_filename = os_helper.TESTFN
self.addCleanup(os_helper.unlink, output_filename)
open(output_filename, "w+").close()
with self.assertRaises(OSError):
_testcapi.pyobject_print_os_error(output_filename)
class ClearWeakRefsNoCallbacksTest(unittest.TestCase):
"""Test PyUnstable_Object_ClearWeakRefsNoCallbacks"""
def test_ClearWeakRefsNoCallbacks(self):
"""Ensure PyUnstable_Object_ClearWeakRefsNoCallbacks works"""
import weakref
import gc
class C:
pass
obj = C()
messages = []
ref = weakref.ref(obj, lambda: messages.append("don't add this"))
self.assertIs(ref(), obj)
self.assertFalse(messages)
_testcapi.pyobject_clear_weakrefs_no_callbacks(obj)
self.assertIsNone(ref())
gc.collect()
self.assertFalse(messages)
def test_ClearWeakRefsNoCallbacks_no_weakref_support(self):
"""Don't fail on objects that don't support weakrefs"""
import weakref
obj = object()
with self.assertRaises(TypeError):
ref = weakref.ref(obj)
_testcapi.pyobject_clear_weakrefs_no_callbacks(obj)
if __name__ == "__main__":
unittest.main()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
import unittest
from test.support import import_helper
# Skip this test if the _testcapi module isn't available.
_testcapi = import_helper.import_module('_testcapi')
class PyAtomicTests(unittest.TestCase):
pass
for name in sorted(dir(_testcapi)):
if name.startswith('test_atomic'):
setattr(PyAtomicTests, name, getattr(_testcapi, name))
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,118 @@
import os
import unittest
from collections import UserDict
from test.support import import_helper
from test.support.os_helper import unlink, TESTFN, TESTFN_ASCII, TESTFN_UNDECODABLE
NULL = None
_testcapi = import_helper.import_module('_testcapi')
Py_single_input = _testcapi.Py_single_input
Py_file_input = _testcapi.Py_file_input
Py_eval_input = _testcapi.Py_eval_input
class DictSubclass(dict):
pass
class CAPITest(unittest.TestCase):
# TODO: Test the following functions:
#
# PyRun_SimpleStringFlags
# PyRun_AnyFileExFlags
# PyRun_SimpleFileExFlags
# PyRun_InteractiveOneFlags
# PyRun_InteractiveOneObject
# PyRun_InteractiveLoopFlags
# PyRun_String (may be a macro)
# PyRun_AnyFile (may be a macro)
# PyRun_AnyFileEx (may be a macro)
# PyRun_AnyFileFlags (may be a macro)
# PyRun_SimpleString (may be a macro)
# PyRun_SimpleFile (may be a macro)
# PyRun_SimpleFileEx (may be a macro)
# PyRun_InteractiveOne (may be a macro)
# PyRun_InteractiveLoop (may be a macro)
# PyRun_File (may be a macro)
# PyRun_FileEx (may be a macro)
# PyRun_FileFlags (may be a macro)
def test_run_stringflags(self):
# Test PyRun_StringFlags().
# XXX: fopen() uses different path encoding than Python on Windows.
def run(s, *args):
return _testcapi.run_stringflags(s, Py_file_input, *args)
source = b'a\n'
self.assertIsNone(run(b'a\n', dict(a=1)))
self.assertIsNone(run(b'a\n', dict(a=1), {}))
self.assertIsNone(run(b'a\n', {}, dict(a=1)))
self.assertIsNone(run(b'a\n', {}, UserDict(a=1)))
self.assertRaises(NameError, run, b'a\n', {})
self.assertRaises(NameError, run, b'a\n', {}, {})
self.assertRaises(TypeError, run, b'a\n', dict(a=1), [])
self.assertRaises(TypeError, run, b'a\n', dict(a=1), 1)
self.assertIsNone(run(b'a\n', DictSubclass(a=1)))
self.assertIsNone(run(b'a\n', DictSubclass(), dict(a=1)))
self.assertRaises(NameError, run, b'a\n', DictSubclass())
self.assertIsNone(run(b'\xc3\xa4\n', {'\xe4': 1}))
self.assertRaises(SyntaxError, run, b'\xe4\n', {})
self.assertRaises(SystemError, run, b'a\n', NULL)
self.assertRaises(SystemError, run, b'a\n', NULL, {})
self.assertRaises(SystemError, run, b'a\n', NULL, dict(a=1))
self.assertRaises(SystemError, run, b'a\n', UserDict())
self.assertRaises(SystemError, run, b'a\n', UserDict(), {})
self.assertRaises(SystemError, run, b'a\n', UserDict(), dict(a=1))
# CRASHES run(NULL, {})
def test_run_fileexflags(self):
# Test PyRun_FileExFlags().
filename = os.fsencode(TESTFN if os.name != 'nt' else TESTFN_ASCII)
with open(filename, 'wb') as fp:
fp.write(b'a\n')
self.addCleanup(unlink, filename)
def run(*args):
return _testcapi.run_fileexflags(filename, Py_file_input, *args)
self.assertIsNone(run(dict(a=1)))
self.assertIsNone(run(dict(a=1), {}))
self.assertIsNone(run({}, dict(a=1)))
self.assertIsNone(run({}, UserDict(a=1)))
self.assertIsNone(run(dict(a=1), {}, 1)) # closeit = True
self.assertRaises(NameError, run, {})
self.assertRaises(NameError, run, {}, {})
self.assertRaises(TypeError, run, dict(a=1), [])
self.assertRaises(TypeError, run, dict(a=1), 1)
self.assertIsNone(run(DictSubclass(a=1)))
self.assertIsNone(run(DictSubclass(), dict(a=1)))
self.assertRaises(NameError, run, DictSubclass())
self.assertRaises(SystemError, run, NULL)
self.assertRaises(SystemError, run, NULL, {})
self.assertRaises(SystemError, run, NULL, dict(a=1))
self.assertRaises(SystemError, run, UserDict())
self.assertRaises(SystemError, run, UserDict(), {})
self.assertRaises(SystemError, run, UserDict(), dict(a=1))
@unittest.skipUnless(TESTFN_UNDECODABLE, 'only works if there are undecodable paths')
@unittest.skipIf(os.name == 'nt', 'does not work on Windows')
def test_run_fileexflags_with_undecodable_filename(self):
run = _testcapi.run_fileexflags
try:
with open(TESTFN_UNDECODABLE, 'wb') as fp:
fp.write(b'a\n')
self.addCleanup(unlink, TESTFN_UNDECODABLE)
except OSError:
self.skipTest('undecodable paths are not supported')
self.assertIsNone(run(TESTFN_UNDECODABLE, Py_file_input, dict(a=1)))
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,271 @@
import unittest
from test.support import import_helper
# Skip this test if the _testcapi, _testlimitedcapi or _testinternalcapi
# modules aren't available.
_testcapi = import_helper.import_module('_testcapi')
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
_testinternalcapi = import_helper.import_module('_testinternalcapi')
class set_subclass(set):
pass
class frozenset_subclass(frozenset):
pass
class BaseSetTests:
def assertImmutable(self, action, *args):
self.assertRaises(SystemError, action, frozenset(), *args)
self.assertRaises(SystemError, action, frozenset({1}), *args)
self.assertRaises(SystemError, action, frozenset_subclass(), *args)
self.assertRaises(SystemError, action, frozenset_subclass({1}), *args)
class TestSetCAPI(BaseSetTests, unittest.TestCase):
def test_set_check(self):
check = _testlimitedcapi.set_check
self.assertTrue(check(set()))
self.assertTrue(check({1, 2}))
self.assertFalse(check(frozenset()))
self.assertTrue(check(set_subclass()))
self.assertFalse(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)
def test_set_check_exact(self):
check = _testlimitedcapi.set_checkexact
self.assertTrue(check(set()))
self.assertTrue(check({1, 2}))
self.assertFalse(check(frozenset()))
self.assertFalse(check(set_subclass()))
self.assertFalse(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)
def test_frozenset_check(self):
check = _testlimitedcapi.frozenset_check
self.assertFalse(check(set()))
self.assertTrue(check(frozenset()))
self.assertTrue(check(frozenset({1, 2})))
self.assertFalse(check(set_subclass()))
self.assertTrue(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)
def test_frozenset_check_exact(self):
check = _testlimitedcapi.frozenset_checkexact
self.assertFalse(check(set()))
self.assertTrue(check(frozenset()))
self.assertTrue(check(frozenset({1, 2})))
self.assertFalse(check(set_subclass()))
self.assertFalse(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)
def test_anyset_check(self):
check = _testlimitedcapi.anyset_check
self.assertTrue(check(set()))
self.assertTrue(check({1, 2}))
self.assertTrue(check(frozenset()))
self.assertTrue(check(frozenset({1, 2})))
self.assertTrue(check(set_subclass()))
self.assertTrue(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)
def test_anyset_check_exact(self):
check = _testlimitedcapi.anyset_checkexact
self.assertTrue(check(set()))
self.assertTrue(check({1, 2}))
self.assertTrue(check(frozenset()))
self.assertTrue(check(frozenset({1, 2})))
self.assertFalse(check(set_subclass()))
self.assertFalse(check(frozenset_subclass()))
self.assertFalse(check(object()))
# CRASHES: check(NULL)
def test_set_new(self):
set_new = _testlimitedcapi.set_new
self.assertEqual(set_new().__class__, set)
self.assertEqual(set_new(), set())
self.assertEqual(set_new((1, 1, 2)), {1, 2})
self.assertEqual(set_new([1, 1, 2]), {1, 2})
with self.assertRaisesRegex(TypeError, 'object is not iterable'):
set_new(object())
with self.assertRaisesRegex(TypeError, 'object is not iterable'):
set_new(1)
with self.assertRaisesRegex(TypeError, "unhashable type: 'dict'"):
set_new((1, {}))
def test_frozenset_new(self):
frozenset_new = _testlimitedcapi.frozenset_new
self.assertEqual(frozenset_new().__class__, frozenset)
self.assertEqual(frozenset_new(), frozenset())
self.assertEqual(frozenset_new((1, 1, 2)), frozenset({1, 2}))
self.assertEqual(frozenset_new([1, 1, 2]), frozenset({1, 2}))
with self.assertRaisesRegex(TypeError, 'object is not iterable'):
frozenset_new(object())
with self.assertRaisesRegex(TypeError, 'object is not iterable'):
frozenset_new(1)
with self.assertRaisesRegex(TypeError, "unhashable type: 'dict'"):
frozenset_new((1, {}))
def test_set_size(self):
get_size = _testlimitedcapi.set_size
self.assertEqual(get_size(set()), 0)
self.assertEqual(get_size(frozenset()), 0)
self.assertEqual(get_size({1, 1, 2}), 2)
self.assertEqual(get_size(frozenset({1, 1, 2})), 2)
self.assertEqual(get_size(set_subclass((1, 2, 3))), 3)
self.assertEqual(get_size(frozenset_subclass((1, 2, 3))), 3)
with self.assertRaises(SystemError):
get_size(object())
# CRASHES: get_size(NULL)
def test_set_get_size(self):
get_size = _testcapi.set_get_size
self.assertEqual(get_size(set()), 0)
self.assertEqual(get_size(frozenset()), 0)
self.assertEqual(get_size({1, 1, 2}), 2)
self.assertEqual(get_size(frozenset({1, 1, 2})), 2)
self.assertEqual(get_size(set_subclass((1, 2, 3))), 3)
self.assertEqual(get_size(frozenset_subclass((1, 2, 3))), 3)
# CRASHES: get_size(NULL)
# CRASHES: get_size(object())
def test_set_contains(self):
contains = _testlimitedcapi.set_contains
for cls in (set, frozenset, set_subclass, frozenset_subclass):
with self.subTest(cls=cls):
instance = cls((1, 2))
self.assertTrue(contains(instance, 1))
self.assertFalse(contains(instance, 'missing'))
with self.assertRaisesRegex(TypeError, "unhashable type: 'list'"):
contains(instance, [])
# CRASHES: contains(instance, NULL)
# CRASHES: contains(NULL, object())
# CRASHES: contains(NULL, NULL)
def test_add(self):
add = _testlimitedcapi.set_add
for cls in (set, set_subclass):
with self.subTest(cls=cls):
instance = cls((1, 2))
self.assertEqual(add(instance, 1), 0)
self.assertEqual(instance, {1, 2})
self.assertEqual(add(instance, 3), 0)
self.assertEqual(instance, {1, 2, 3})
with self.assertRaisesRegex(TypeError, "unhashable type: 'list'"):
add(instance, [])
with self.assertRaises(SystemError):
add(object(), 1)
self.assertImmutable(add, 1)
# CRASHES: add(NULL, object())
# CRASHES: add(instance, NULL)
# CRASHES: add(NULL, NULL)
def test_discard(self):
discard = _testlimitedcapi.set_discard
for cls in (set, set_subclass):
with self.subTest(cls=cls):
instance = cls((1, 2))
self.assertEqual(discard(instance, 3), 0)
self.assertEqual(instance, {1, 2})
self.assertEqual(discard(instance, 1), 1)
self.assertEqual(instance, {2})
self.assertEqual(discard(instance, 2), 1)
self.assertEqual(instance, set())
self.assertEqual(discard(instance, 2), 0)
self.assertEqual(instance, set())
with self.assertRaisesRegex(TypeError, "unhashable type: 'list'"):
discard(instance, [])
with self.assertRaises(SystemError):
discard(object(), 1)
self.assertImmutable(discard, 1)
# CRASHES: discard(NULL, object())
# CRASHES: discard(instance, NULL)
# CRASHES: discard(NULL, NULL)
def test_pop(self):
pop = _testlimitedcapi.set_pop
orig = (1, 2)
for cls in (set, set_subclass):
with self.subTest(cls=cls):
instance = cls(orig)
self.assertIn(pop(instance), orig)
self.assertEqual(len(instance), 1)
self.assertIn(pop(instance), orig)
self.assertEqual(len(instance), 0)
with self.assertRaises(KeyError):
pop(instance)
with self.assertRaises(SystemError):
pop(object())
self.assertImmutable(pop)
# CRASHES: pop(NULL)
def test_clear(self):
clear = _testlimitedcapi.set_clear
for cls in (set, set_subclass):
with self.subTest(cls=cls):
instance = cls((1, 2))
self.assertEqual(clear(instance), 0)
self.assertEqual(instance, set())
self.assertEqual(clear(instance), 0)
self.assertEqual(instance, set())
with self.assertRaises(SystemError):
clear(object())
self.assertImmutable(clear)
# CRASHES: clear(NULL)
class TestInternalCAPI(BaseSetTests, unittest.TestCase):
def test_set_update(self):
update = _testinternalcapi.set_update
for cls in (set, set_subclass):
for it in ('ab', ('a', 'b'), ['a', 'b'],
set('ab'), set_subclass('ab'),
frozenset('ab'), frozenset_subclass('ab')):
with self.subTest(cls=cls, it=it):
instance = cls()
self.assertEqual(update(instance, it), 0)
self.assertEqual(instance, {'a', 'b'})
instance = cls(it)
self.assertEqual(update(instance, it), 0)
self.assertEqual(instance, {'a', 'b'})
with self.assertRaisesRegex(TypeError, 'object is not iterable'):
update(cls(), 1)
with self.assertRaisesRegex(TypeError, "unhashable type: 'dict'"):
update(cls(), [{}])
with self.assertRaises(SystemError):
update(object(), 'ab')
self.assertImmutable(update, 'ab')
# CRASHES: update(NULL, object())
# CRASHES: update(instance, NULL)
# CRASHES: update(NULL, NULL)
def test_set_next_entry(self):
set_next = _testinternalcapi.set_next_entry
for cls in (set, set_subclass, frozenset, frozenset_subclass):
with self.subTest(cls=cls):
instance = cls('abc')
pos = 0
items = []
while True:
res = set_next(instance, pos)
if res is None:
break
rc, pos, hash_, item = res
items.append(item)
self.assertEqual(rc, 1)
self.assertIn(item, instance)
self.assertEqual(hash(item), hash_)
self.assertEqual(items, list(instance))
with self.assertRaises(SystemError):
set_next(object(), 0)
# CRASHES: set_next(NULL, 0)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,174 @@
import unittest
from test.support import import_helper
from test.support import warnings_helper
# Skip this test if the _testcapi module isn't available.
import_helper.import_module('_testcapi')
from _testcapi import (_test_structmembersType_OldAPI,
_test_structmembersType_NewAPI,
CHAR_MAX, CHAR_MIN, UCHAR_MAX,
SHRT_MAX, SHRT_MIN, USHRT_MAX,
INT_MAX, INT_MIN, UINT_MAX,
LONG_MAX, LONG_MIN, ULONG_MAX,
LLONG_MAX, LLONG_MIN, ULLONG_MAX,
PY_SSIZE_T_MAX, PY_SSIZE_T_MIN,
)
class Index:
def __init__(self, value):
self.value = value
def __index__(self):
return self.value
# There are two classes: one using <structmember.h> and another using
# `Py_`-prefixed API. They should behave the same in Python
def _make_test_object(cls):
return cls(False, # T_BOOL
1, # T_BYTE
2, # T_UBYTE
3, # T_SHORT
4, # T_USHORT
5, # T_INT
6, # T_UINT
7, # T_LONG
8, # T_ULONG
23, # T_PYSSIZET
9.99999,# T_FLOAT
10.1010101010, # T_DOUBLE
"hi", # T_STRING_INPLACE
)
class ReadWriteTests:
def setUp(self):
self.ts = _make_test_object(self.cls)
def _test_write(self, name, value, expected=None):
if expected is None:
expected = value
ts = self.ts
setattr(ts, name, value)
self.assertEqual(getattr(ts, name), expected)
def _test_warn(self, name, value, expected=None):
ts = self.ts
self.assertWarns(RuntimeWarning, setattr, ts, name, value)
if expected is not None:
self.assertEqual(getattr(ts, name), expected)
def _test_overflow(self, name, value):
ts = self.ts
self.assertRaises(OverflowError, setattr, ts, name, value)
def _test_int_range(self, name, minval, maxval, *, hardlimit=None,
indexlimit=None):
if hardlimit is None:
hardlimit = (minval, maxval)
ts = self.ts
self._test_write(name, minval)
self._test_write(name, maxval)
hardminval, hardmaxval = hardlimit
self._test_overflow(name, hardminval-1)
self._test_overflow(name, hardmaxval+1)
self._test_overflow(name, 2**1000)
self._test_overflow(name, -2**1000)
if hardminval < minval:
self._test_warn(name, hardminval)
self._test_warn(name, minval-1, maxval)
if maxval < hardmaxval:
self._test_warn(name, maxval+1, minval)
self._test_warn(name, hardmaxval)
if indexlimit is False:
self.assertRaises(TypeError, setattr, ts, name, Index(minval))
self.assertRaises(TypeError, setattr, ts, name, Index(maxval))
else:
self._test_write(name, Index(minval), minval)
self._test_write(name, Index(maxval), maxval)
self._test_overflow(name, Index(hardminval-1))
self._test_overflow(name, Index(hardmaxval+1))
self._test_overflow(name, Index(2**1000))
self._test_overflow(name, Index(-2**1000))
if hardminval < minval:
self._test_warn(name, Index(hardminval))
self._test_warn(name, Index(minval-1), maxval)
if maxval < hardmaxval:
self._test_warn(name, Index(maxval+1), minval)
self._test_warn(name, Index(hardmaxval))
def test_bool(self):
ts = self.ts
ts.T_BOOL = True
self.assertIs(ts.T_BOOL, True)
ts.T_BOOL = False
self.assertIs(ts.T_BOOL, False)
self.assertRaises(TypeError, setattr, ts, 'T_BOOL', 1)
self.assertRaises(TypeError, setattr, ts, 'T_BOOL', 0)
self.assertRaises(TypeError, setattr, ts, 'T_BOOL', None)
def test_byte(self):
self._test_int_range('T_BYTE', CHAR_MIN, CHAR_MAX,
hardlimit=(LONG_MIN, LONG_MAX))
self._test_int_range('T_UBYTE', 0, UCHAR_MAX,
hardlimit=(LONG_MIN, LONG_MAX))
def test_short(self):
self._test_int_range('T_SHORT', SHRT_MIN, SHRT_MAX,
hardlimit=(LONG_MIN, LONG_MAX))
self._test_int_range('T_USHORT', 0, USHRT_MAX,
hardlimit=(LONG_MIN, LONG_MAX))
def test_int(self):
self._test_int_range('T_INT', INT_MIN, INT_MAX,
hardlimit=(LONG_MIN, LONG_MAX))
self._test_int_range('T_UINT', 0, UINT_MAX,
hardlimit=(LONG_MIN, ULONG_MAX))
def test_long(self):
self._test_int_range('T_LONG', LONG_MIN, LONG_MAX)
self._test_int_range('T_ULONG', 0, ULONG_MAX,
hardlimit=(LONG_MIN, ULONG_MAX))
def test_py_ssize_t(self):
self._test_int_range('T_PYSSIZET', PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, indexlimit=False)
def test_longlong(self):
self._test_int_range('T_LONGLONG', LLONG_MIN, LLONG_MAX)
self._test_int_range('T_ULONGLONG', 0, ULLONG_MAX,
hardlimit=(LONG_MIN, ULLONG_MAX))
def test_bad_assignments(self):
ts = self.ts
integer_attributes = [
'T_BOOL',
'T_BYTE', 'T_UBYTE',
'T_SHORT', 'T_USHORT',
'T_INT', 'T_UINT',
'T_LONG', 'T_ULONG',
'T_LONGLONG', 'T_ULONGLONG',
'T_PYSSIZET'
]
# issue8014: this produced 'bad argument to internal function'
# internal error
for nonint in None, 3.2j, "full of eels", {}, []:
for attr in integer_attributes:
self.assertRaises(TypeError, setattr, ts, attr, nonint)
def test_inplace_string(self):
ts = self.ts
self.assertEqual(ts.T_STRING_INPLACE, "hi")
self.assertRaises(TypeError, setattr, ts, "T_STRING_INPLACE", "s")
self.assertRaises(TypeError, delattr, ts, "T_STRING_INPLACE")
class ReadWriteTests_OldAPI(ReadWriteTests, unittest.TestCase):
cls = _test_structmembersType_OldAPI
class ReadWriteTests_NewAPI(ReadWriteTests, unittest.TestCase):
cls = _test_structmembersType_NewAPI
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,154 @@
import unittest
import contextlib
import sys
from test import support
from test.support import import_helper
try:
import _testlimitedcapi
except ImportError:
_testlimitedcapi = None
NULL = None
class CAPITest(unittest.TestCase):
# TODO: Test the following functions:
#
# PySys_Audit()
# PySys_AuditTuple()
maxDiff = None
@support.cpython_only
@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
def test_sys_getobject(self):
# Test PySys_GetObject()
getobject = _testlimitedcapi.sys_getobject
self.assertIs(getobject(b'stdout'), sys.stdout)
with support.swap_attr(sys, '\U0001f40d', 42):
self.assertEqual(getobject('\U0001f40d'.encode()), 42)
self.assertIs(getobject(b'nonexisting'), AttributeError)
with support.catch_unraisable_exception() as cm:
self.assertIs(getobject(b'\xff'), AttributeError)
self.assertEqual(cm.unraisable.exc_type, UnicodeDecodeError)
self.assertRegex(str(cm.unraisable.exc_value),
"'utf-8' codec can't decode")
# CRASHES getobject(NULL)
@support.cpython_only
@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
def test_sys_setobject(self):
# Test PySys_SetObject()
setobject = _testlimitedcapi.sys_setobject
value = ['value']
value2 = ['value2']
try:
self.assertEqual(setobject(b'newattr', value), 0)
self.assertIs(sys.newattr, value)
self.assertEqual(setobject(b'newattr', value2), 0)
self.assertIs(sys.newattr, value2)
self.assertEqual(setobject(b'newattr', NULL), 0)
self.assertFalse(hasattr(sys, 'newattr'))
self.assertEqual(setobject(b'newattr', NULL), 0)
finally:
with contextlib.suppress(AttributeError):
del sys.newattr
try:
self.assertEqual(setobject('\U0001f40d'.encode(), value), 0)
self.assertIs(getattr(sys, '\U0001f40d'), value)
self.assertEqual(setobject('\U0001f40d'.encode(), NULL), 0)
self.assertFalse(hasattr(sys, '\U0001f40d'))
finally:
with contextlib.suppress(AttributeError):
delattr(sys, '\U0001f40d')
with self.assertRaises(UnicodeDecodeError):
setobject(b'\xff', value)
# CRASHES setobject(NULL, value)
@support.cpython_only
@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
def test_sys_getxoptions(self):
# Test PySys_GetXOptions()
getxoptions = _testlimitedcapi.sys_getxoptions
self.assertIs(getxoptions(), sys._xoptions)
xoptions = sys._xoptions
try:
sys._xoptions = 'non-dict'
self.assertEqual(getxoptions(), {})
self.assertIs(getxoptions(), sys._xoptions)
del sys._xoptions
self.assertEqual(getxoptions(), {})
self.assertIs(getxoptions(), sys._xoptions)
finally:
sys._xoptions = xoptions
self.assertIs(getxoptions(), sys._xoptions)
def _test_sys_formatstream(self, funname, streamname):
import_helper.import_module('ctypes')
from ctypes import pythonapi, c_char_p, py_object
func = getattr(pythonapi, funname)
func.argtypes = (c_char_p,)
# Supports plain C types.
with support.captured_output(streamname) as stream:
func(b'Hello, %s!', c_char_p(b'world'))
self.assertEqual(stream.getvalue(), 'Hello, world!')
# Supports Python objects.
with support.captured_output(streamname) as stream:
func(b'Hello, %R!', py_object('world'))
self.assertEqual(stream.getvalue(), "Hello, 'world'!")
# The total length is not limited.
with support.captured_output(streamname) as stream:
func(b'Hello, %s!', c_char_p(b'world'*200))
self.assertEqual(stream.getvalue(), 'Hello, ' + 'world'*200 + '!')
def test_sys_formatstdout(self):
# Test PySys_FormatStdout()
self._test_sys_formatstream('PySys_FormatStdout', 'stdout')
def test_sys_formatstderr(self):
# Test PySys_FormatStderr()
self._test_sys_formatstream('PySys_FormatStderr', 'stderr')
def _test_sys_writestream(self, funname, streamname):
import_helper.import_module('ctypes')
from ctypes import pythonapi, c_char_p
func = getattr(pythonapi, funname)
func.argtypes = (c_char_p,)
# Supports plain C types.
with support.captured_output(streamname) as stream:
func(b'Hello, %s!', c_char_p(b'world'))
self.assertEqual(stream.getvalue(), 'Hello, world!')
# There is a limit on the total length.
with support.captured_output(streamname) as stream:
func(b'Hello, %s!', c_char_p(b'world'*100))
self.assertEqual(stream.getvalue(), 'Hello, ' + 'world'*100 + '!')
with support.captured_output(streamname) as stream:
func(b'Hello, %s!', c_char_p(b'world'*200))
out = stream.getvalue()
self.assertEqual(out[:20], 'Hello, worldworldwor')
self.assertEqual(out[-13:], '... truncated')
self.assertGreater(len(out), 1000)
def test_sys_writestdout(self):
# Test PySys_WriteStdout()
self._test_sys_writestream('PySys_WriteStdout', 'stdout')
def test_sys_writestderr(self):
# Test PySys_WriteStderr()
self._test_sys_writestream('PySys_WriteStderr', 'stderr')
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,78 @@
import time
import unittest
from test.support import import_helper
_testcapi = import_helper.import_module('_testcapi')
PyTime_MIN = _testcapi.PyTime_MIN
PyTime_MAX = _testcapi.PyTime_MAX
SEC_TO_NS = 10 ** 9
DAY_TO_SEC = (24 * 60 * 60)
# Worst clock resolution: maximum delta between two clock reads.
CLOCK_RES = 0.050
class CAPITest(unittest.TestCase):
def test_min_max(self):
# PyTime_t is just int64_t
self.assertEqual(PyTime_MIN, -2**63)
self.assertEqual(PyTime_MAX, 2**63 - 1)
def test_assecondsdouble(self):
# Test PyTime_AsSecondsDouble()
def ns_to_sec(ns):
if abs(ns) % SEC_TO_NS == 0:
return float(ns // SEC_TO_NS)
else:
return float(ns) / SEC_TO_NS
seconds = (
0,
1,
DAY_TO_SEC,
365 * DAY_TO_SEC,
)
values = {
PyTime_MIN,
PyTime_MIN + 1,
PyTime_MAX - 1,
PyTime_MAX,
}
for second in seconds:
ns = second * SEC_TO_NS
values.add(ns)
# test nanosecond before/after to test rounding
values.add(ns - 1)
values.add(ns + 1)
for ns in list(values):
if (-ns) > PyTime_MAX:
continue
values.add(-ns)
for ns in sorted(values):
with self.subTest(ns=ns):
self.assertEqual(_testcapi.PyTime_AsSecondsDouble(ns),
ns_to_sec(ns))
def check_clock(self, c_func, py_func):
t1 = c_func()
t2 = py_func()
self.assertAlmostEqual(t1, t2, delta=CLOCK_RES)
def test_monotonic(self):
# Test PyTime_Monotonic() and PyTime_MonotonicRaw()
self.check_clock(_testcapi.PyTime_Monotonic, time.monotonic)
self.check_clock(_testcapi.PyTime_MonotonicRaw, time.monotonic)
def test_perf_counter(self):
# Test PyTime_PerfCounter() and PyTime_PerfCounterRaw()
self.check_clock(_testcapi.PyTime_PerfCounter, time.perf_counter)
self.check_clock(_testcapi.PyTime_PerfCounterRaw, time.perf_counter)
def test_time(self):
# Test PyTime_Time() and PyTime_TimeRaw()
self.check_clock(_testcapi.PyTime_Time, time.time)
self.check_clock(_testcapi.PyTime_TimeRaw, time.time)
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,261 @@
import unittest
import sys
from collections import namedtuple
from test.support import import_helper
_testcapi = import_helper.import_module('_testcapi')
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
NULL = None
PY_SSIZE_T_MIN = _testcapi.PY_SSIZE_T_MIN
PY_SSIZE_T_MAX = _testcapi.PY_SSIZE_T_MAX
class TupleSubclass(tuple):
pass
class CAPITest(unittest.TestCase):
def test_check(self):
# Test PyTuple_Check()
check = _testlimitedcapi.tuple_check
self.assertTrue(check((1, 2)))
self.assertTrue(check(()))
self.assertTrue(check(TupleSubclass((1, 2))))
self.assertFalse(check({1: 2}))
self.assertFalse(check([1, 2]))
self.assertFalse(check(42))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_tuple_checkexact(self):
# Test PyTuple_CheckExact()
check = _testlimitedcapi.tuple_checkexact
self.assertTrue(check((1, 2)))
self.assertTrue(check(()))
self.assertFalse(check(TupleSubclass((1, 2))))
self.assertFalse(check({1: 2}))
self.assertFalse(check([1, 2]))
self.assertFalse(check(42))
self.assertFalse(check(object()))
# CRASHES check(NULL)
def test_tuple_new(self):
# Test PyTuple_New()
tuple_new = _testlimitedcapi.tuple_new
size = _testlimitedcapi.tuple_size
checknull = _testcapi._check_tuple_item_is_NULL
tup1 = tuple_new(0)
self.assertEqual(tup1, ())
self.assertEqual(size(tup1), 0)
self.assertIs(type(tup1), tuple)
tup2 = tuple_new(1)
self.assertIs(type(tup2), tuple)
self.assertEqual(size(tup2), 1)
self.assertIsNot(tup2, tup1)
self.assertTrue(checknull(tup2, 0))
self.assertRaises(SystemError, tuple_new, -1)
self.assertRaises(SystemError, tuple_new, PY_SSIZE_T_MIN)
self.assertRaises(MemoryError, tuple_new, PY_SSIZE_T_MAX)
def test_tuple_pack(self):
# Test PyTuple_Pack()
pack = _testlimitedcapi.tuple_pack
self.assertEqual(pack(0), ())
self.assertEqual(pack(1, [1]), ([1],))
self.assertEqual(pack(2, [1], [2]), ([1], [2]))
self.assertRaises(SystemError, pack, PY_SSIZE_T_MIN)
self.assertRaises(SystemError, pack, -1)
self.assertRaises(MemoryError, pack, PY_SSIZE_T_MAX)
# CRASHES pack(1, NULL)
# CRASHES pack(2, [1])
def test_tuple_size(self):
# Test PyTuple_Size()
size = _testlimitedcapi.tuple_size
self.assertEqual(size(()), 0)
self.assertEqual(size((1, 2)), 2)
self.assertEqual(size(TupleSubclass((1, 2))), 2)
self.assertRaises(SystemError, size, [])
self.assertRaises(SystemError, size, 42)
self.assertRaises(SystemError, size, object())
# CRASHES size(NULL)
def test_tuple_get_size(self):
# Test PyTuple_GET_SIZE()
size = _testcapi.tuple_get_size
self.assertEqual(size(()), 0)
self.assertEqual(size((1, 2)), 2)
self.assertEqual(size(TupleSubclass((1, 2))), 2)
def test_tuple_getitem(self):
# Test PyTuple_GetItem()
getitem = _testlimitedcapi.tuple_getitem
tup = ([1], [2], [3])
self.assertEqual(getitem(tup, 0), [1])
self.assertEqual(getitem(tup, 2), [3])
tup2 = TupleSubclass(([1], [2], [3]))
self.assertEqual(getitem(tup2, 0), [1])
self.assertEqual(getitem(tup2, 2), [3])
self.assertRaises(IndexError, getitem, tup, PY_SSIZE_T_MIN)
self.assertRaises(IndexError, getitem, tup, -1)
self.assertRaises(IndexError, getitem, tup, len(tup))
self.assertRaises(IndexError, getitem, tup, PY_SSIZE_T_MAX)
self.assertRaises(SystemError, getitem, [1, 2, 3], 1)
self.assertRaises(SystemError, getitem, 42, 1)
# CRASHES getitem(NULL, 0)
def test_tuple_get_item(self):
# Test PyTuple_GET_ITEM()
get_item = _testcapi.tuple_get_item
tup = ([1], [2], [3])
self.assertEqual(get_item(tup, 0), [1])
self.assertEqual(get_item(tup, 2), [3])
tup2 = TupleSubclass(([1], [2], [3]))
self.assertEqual(get_item(tup2, 0), [1])
self.assertEqual(get_item(tup2, 2), [3])
# CRASHES get_item(NULL, 0)
def test_tuple_getslice(self):
# Test PyTuple_GetSlice()
getslice = _testlimitedcapi.tuple_getslice
# empty
tup = ([1], [2], [3])
self.assertEqual(getslice(tup, PY_SSIZE_T_MIN, 0), ())
self.assertEqual(getslice(tup, -1, 0), ())
self.assertEqual(getslice(tup, 3, PY_SSIZE_T_MAX), ())
self.assertEqual(getslice(tup, 1, 1), ())
self.assertEqual(getslice(tup, 2, 1), ())
tup = TupleSubclass(([1], [2], [3]))
self.assertEqual(getslice(tup, PY_SSIZE_T_MIN, 0), ())
self.assertEqual(getslice(tup, -1, 0), ())
self.assertEqual(getslice(tup, 3, PY_SSIZE_T_MAX), ())
self.assertEqual(getslice(tup, 1, 1), ())
self.assertEqual(getslice(tup, 2, 1), ())
# slice
tup = ([1], [2], [3], [4])
self.assertEqual(getslice(tup, 1, 3), ([2], [3]))
tup = TupleSubclass(([1], [2], [3], [4]))
self.assertEqual(getslice(tup, 1, 3), ([2], [3]))
# whole
tup = ([1], [2], [3])
self.assertEqual(getslice(tup, 0, 3), tup)
self.assertEqual(getslice(tup, 0, 100), tup)
self.assertEqual(getslice(tup, -100, 100), tup)
tup = TupleSubclass(([1], [2], [3]))
self.assertEqual(getslice(tup, 0, 3), tup)
self.assertEqual(getslice(tup, 0, 100), tup)
self.assertEqual(getslice(tup, -100, 100), tup)
self.assertRaises(SystemError, getslice, [[1], [2], [3]], 0, 0)
self.assertRaises(SystemError, getslice, 42, 0, 0)
# CRASHES getslice(NULL, 0, 0)
def test_tuple_setitem(self):
# Test PyTuple_SetItem()
setitem = _testlimitedcapi.tuple_setitem
checknull = _testcapi._check_tuple_item_is_NULL
tup = ([1], [2])
self.assertEqual(setitem(tup, 0, []), ([], [2]))
self.assertEqual(setitem(tup, 1, []), ([1], []))
tup2 = setitem(tup, 1, NULL)
self.assertTrue(checknull(tup2, 1))
tup2 = TupleSubclass(([1], [2]))
self.assertRaises(SystemError, setitem, tup2, 0, [])
self.assertRaises(IndexError, setitem, tup, PY_SSIZE_T_MIN, [])
self.assertRaises(IndexError, setitem, tup, -1, [])
self.assertRaises(IndexError, setitem, tup, len(tup), [])
self.assertRaises(IndexError, setitem, tup, PY_SSIZE_T_MAX, [])
self.assertRaises(SystemError, setitem, [1], 0, [])
self.assertRaises(SystemError, setitem, 42, 0, [])
# CRASHES setitem(NULL, 0, [])
def test_tuple_set_item(self):
# Test PyTuple_SET_ITEM()
set_item = _testcapi.tuple_set_item
checknull = _testcapi._check_tuple_item_is_NULL
tup = ([1], [2])
self.assertEqual(set_item(tup, 0, []), ([], [2]))
self.assertEqual(set_item(tup, 1, []), ([1], []))
tup2 = set_item(tup, 1, NULL)
self.assertTrue(checknull(tup2, 1))
tup2 = TupleSubclass(([1], [2]))
self.assertIs(set_item(tup2, 0, []), tup2)
self.assertEqual(tup2, ([], [2]))
# CRASHES set_item(tup, -1, [])
# CRASHES set_item(tup, len(tup), [])
# CRASHES set_item([1], 0, [])
# CRASHES set_item(NULL, 0, [])
def test__tuple_resize(self):
# Test _PyTuple_Resize()
resize = _testcapi._tuple_resize
checknull = _testcapi._check_tuple_item_is_NULL
a = ()
b = resize(a, 0, False)
self.assertEqual(len(a), 0)
self.assertEqual(len(b), 0)
b = resize(a, 2, False)
self.assertEqual(len(a), 0)
self.assertEqual(len(b), 2)
self.assertTrue(checknull(b, 0))
self.assertTrue(checknull(b, 1))
a = ([1], [2], [3])
b = resize(a, 3)
self.assertEqual(b, a)
b = resize(a, 2)
self.assertEqual(b, a[:2])
b = resize(a, 5)
self.assertEqual(len(b), 5)
self.assertEqual(b[:3], a)
self.assertTrue(checknull(b, 3))
self.assertTrue(checknull(b, 4))
a = ()
self.assertRaises(MemoryError, resize, a, PY_SSIZE_T_MAX)
self.assertRaises(SystemError, resize, a, -1)
self.assertRaises(SystemError, resize, a, PY_SSIZE_T_MIN)
# refcount > 1
a = (1, 2, 3)
self.assertRaises(SystemError, resize, a, 3, False)
self.assertRaises(SystemError, resize, a, 0, False)
# non-tuple
self.assertRaises(SystemError, resize, [1, 2, 3], 0, False)
self.assertRaises(SystemError, resize, NULL, 0, False)
if __name__ == "__main__":
unittest.main()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,590 @@
import unittest
from contextlib import contextmanager, ExitStack
from test.support import (
catch_unraisable_exception, import_helper,
gc_collect, suppress_immortalization)
# Skip this test if the _testcapi module isn't available.
_testcapi = import_helper.import_module('_testcapi')
class TestDictWatchers(unittest.TestCase):
# types of watchers testcapimodule can add:
EVENTS = 0 # appends dict events as strings to global event list
ERROR = 1 # unconditionally sets and signals a RuntimeException
SECOND = 2 # always appends "second" to global event list
def add_watcher(self, kind=EVENTS):
return _testcapi.add_dict_watcher(kind)
def clear_watcher(self, watcher_id):
_testcapi.clear_dict_watcher(watcher_id)
@contextmanager
def watcher(self, kind=EVENTS):
wid = self.add_watcher(kind)
try:
yield wid
finally:
self.clear_watcher(wid)
def assert_events(self, expected):
actual = _testcapi.get_dict_watcher_events()
self.assertEqual(actual, expected)
def watch(self, wid, d):
_testcapi.watch_dict(wid, d)
def unwatch(self, wid, d):
_testcapi.unwatch_dict(wid, d)
def test_set_new_item(self):
d = {}
with self.watcher() as wid:
self.watch(wid, d)
d["foo"] = "bar"
self.assert_events(["new:foo:bar"])
def test_set_existing_item(self):
d = {"foo": "bar"}
with self.watcher() as wid:
self.watch(wid, d)
d["foo"] = "baz"
self.assert_events(["mod:foo:baz"])
def test_clone(self):
d = {}
d2 = {"foo": "bar"}
with self.watcher() as wid:
self.watch(wid, d)
d.update(d2)
self.assert_events(["clone"])
def test_no_event_if_not_watched(self):
d = {}
with self.watcher() as wid:
d["foo"] = "bar"
self.assert_events([])
def test_del(self):
d = {"foo": "bar"}
with self.watcher() as wid:
self.watch(wid, d)
del d["foo"]
self.assert_events(["del:foo"])
def test_pop(self):
d = {"foo": "bar"}
with self.watcher() as wid:
self.watch(wid, d)
d.pop("foo")
self.assert_events(["del:foo"])
def test_clear(self):
d = {"foo": "bar"}
with self.watcher() as wid:
self.watch(wid, d)
d.clear()
self.assert_events(["clear"])
def test_dealloc(self):
d = {"foo": "bar"}
with self.watcher() as wid:
self.watch(wid, d)
del d
self.assert_events(["dealloc"])
def test_object_dict(self):
class MyObj: pass
o = MyObj()
with self.watcher() as wid:
self.watch(wid, o.__dict__)
o.foo = "bar"
o.foo = "baz"
del o.foo
self.assert_events(["new:foo:bar", "mod:foo:baz", "del:foo"])
with self.watcher() as wid:
self.watch(wid, o.__dict__)
for _ in range(100):
o.foo = "bar"
self.assert_events(["new:foo:bar"] + ["mod:foo:bar"] * 99)
def test_unwatch(self):
d = {}
with self.watcher() as wid:
self.watch(wid, d)
d["foo"] = "bar"
self.unwatch(wid, d)
d["hmm"] = "baz"
self.assert_events(["new:foo:bar"])
def test_error(self):
d = {}
with self.watcher(kind=self.ERROR) as wid:
self.watch(wid, d)
with catch_unraisable_exception() as cm:
d["foo"] = "bar"
self.assertIn(
"Exception ignored in "
"PyDict_EVENT_ADDED watcher callback for <dict at ",
cm.unraisable.err_msg
)
self.assertIsNone(cm.unraisable.object)
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
self.assert_events([])
def test_dealloc_error(self):
d = {}
with self.watcher(kind=self.ERROR) as wid:
self.watch(wid, d)
with catch_unraisable_exception() as cm:
del d
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
def test_two_watchers(self):
d1 = {}
d2 = {}
with self.watcher() as wid1:
with self.watcher(kind=self.SECOND) as wid2:
self.watch(wid1, d1)
self.watch(wid2, d2)
d1["foo"] = "bar"
d2["hmm"] = "baz"
self.assert_events(["new:foo:bar", "second"])
def test_watch_non_dict(self):
with self.watcher() as wid:
with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"):
self.watch(wid, 1)
def test_watch_out_of_range_watcher_id(self):
d = {}
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"):
self.watch(-1, d)
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"):
self.watch(8, d) # DICT_MAX_WATCHERS = 8
def test_watch_unassigned_watcher_id(self):
d = {}
with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 3"):
self.watch(3, d)
def test_unwatch_non_dict(self):
with self.watcher() as wid:
with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"):
self.unwatch(wid, 1)
def test_unwatch_out_of_range_watcher_id(self):
d = {}
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"):
self.unwatch(-1, d)
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"):
self.unwatch(8, d) # DICT_MAX_WATCHERS = 8
def test_unwatch_unassigned_watcher_id(self):
d = {}
with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 3"):
self.unwatch(3, d)
def test_clear_out_of_range_watcher_id(self):
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"):
self.clear_watcher(-1)
with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"):
self.clear_watcher(8) # DICT_MAX_WATCHERS = 8
def test_clear_unassigned_watcher_id(self):
with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 3"):
self.clear_watcher(3)
class TestTypeWatchers(unittest.TestCase):
# types of watchers testcapimodule can add:
TYPES = 0 # appends modified types to global event list
ERROR = 1 # unconditionally sets and signals a RuntimeException
WRAP = 2 # appends modified type wrapped in list to global event list
# duplicating the C constant
TYPE_MAX_WATCHERS = 8
def add_watcher(self, kind=TYPES):
return _testcapi.add_type_watcher(kind)
def clear_watcher(self, watcher_id):
_testcapi.clear_type_watcher(watcher_id)
@contextmanager
def watcher(self, kind=TYPES):
wid = self.add_watcher(kind)
try:
yield wid
finally:
self.clear_watcher(wid)
def assert_events(self, expected):
actual = _testcapi.get_type_modified_events()
self.assertEqual(actual, expected)
def watch(self, wid, t):
_testcapi.watch_type(wid, t)
def unwatch(self, wid, t):
_testcapi.unwatch_type(wid, t)
def test_watch_type(self):
class C: pass
with self.watcher() as wid:
self.watch(wid, C)
C.foo = "bar"
self.assert_events([C])
def test_event_aggregation(self):
class C: pass
with self.watcher() as wid:
self.watch(wid, C)
C.foo = "bar"
C.bar = "baz"
# only one event registered for both modifications
self.assert_events([C])
def test_lookup_resets_aggregation(self):
class C: pass
with self.watcher() as wid:
self.watch(wid, C)
C.foo = "bar"
# lookup resets type version tag
self.assertEqual(C.foo, "bar")
C.bar = "baz"
# both events registered
self.assert_events([C, C])
def test_unwatch_type(self):
class C: pass
with self.watcher() as wid:
self.watch(wid, C)
C.foo = "bar"
self.assertEqual(C.foo, "bar")
self.assert_events([C])
self.unwatch(wid, C)
C.bar = "baz"
self.assert_events([C])
def test_clear_watcher(self):
class C: pass
# outer watcher is unused, it's just to keep events list alive
with self.watcher() as _:
with self.watcher() as wid:
self.watch(wid, C)
C.foo = "bar"
self.assertEqual(C.foo, "bar")
self.assert_events([C])
C.bar = "baz"
# Watcher on C has been cleared, no new event
self.assert_events([C])
def test_watch_type_subclass(self):
class C: pass
class D(C): pass
with self.watcher() as wid:
self.watch(wid, D)
C.foo = "bar"
self.assert_events([D])
def test_error(self):
class C: pass
with self.watcher(kind=self.ERROR) as wid:
self.watch(wid, C)
with catch_unraisable_exception() as cm:
C.foo = "bar"
self.assertEqual(cm.unraisable.err_msg,
f"Exception ignored in type watcher callback #0 for {C!r}")
self.assertIs(cm.unraisable.object, None)
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
self.assert_events([])
def test_two_watchers(self):
class C1: pass
class C2: pass
with self.watcher() as wid1:
with self.watcher(kind=self.WRAP) as wid2:
self.assertNotEqual(wid1, wid2)
self.watch(wid1, C1)
self.watch(wid2, C2)
C1.foo = "bar"
C2.hmm = "baz"
self.assert_events([C1, [C2]])
def test_all_watchers(self):
class C: pass
with ExitStack() as stack:
last_wid = -1
# don't make assumptions about how many watchers are already
# registered, just go until we reach the max ID
while last_wid < self.TYPE_MAX_WATCHERS - 1:
last_wid = stack.enter_context(self.watcher())
self.watch(last_wid, C)
C.foo = "bar"
self.assert_events([C])
def test_watch_non_type(self):
with self.watcher() as wid:
with self.assertRaisesRegex(ValueError, r"Cannot watch non-type"):
self.watch(wid, 1)
def test_watch_out_of_range_watcher_id(self):
class C: pass
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"):
self.watch(-1, C)
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"):
self.watch(self.TYPE_MAX_WATCHERS, C)
def test_watch_unassigned_watcher_id(self):
class C: pass
with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"):
self.watch(1, C)
def test_unwatch_non_type(self):
with self.watcher() as wid:
with self.assertRaisesRegex(ValueError, r"Cannot watch non-type"):
self.unwatch(wid, 1)
def test_unwatch_out_of_range_watcher_id(self):
class C: pass
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"):
self.unwatch(-1, C)
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"):
self.unwatch(self.TYPE_MAX_WATCHERS, C)
def test_unwatch_unassigned_watcher_id(self):
class C: pass
with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"):
self.unwatch(1, C)
def test_clear_out_of_range_watcher_id(self):
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"):
self.clear_watcher(-1)
with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"):
self.clear_watcher(self.TYPE_MAX_WATCHERS)
def test_clear_unassigned_watcher_id(self):
with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"):
self.clear_watcher(1)
def test_no_more_ids_available(self):
with self.assertRaisesRegex(RuntimeError, r"no more type watcher IDs"):
with ExitStack() as stack:
for _ in range(self.TYPE_MAX_WATCHERS + 1):
stack.enter_context(self.watcher())
class TestCodeObjectWatchers(unittest.TestCase):
@contextmanager
def code_watcher(self, which_watcher):
wid = _testcapi.add_code_watcher(which_watcher)
try:
yield wid
finally:
_testcapi.clear_code_watcher(wid)
def assert_event_counts(self, exp_created_0, exp_destroyed_0,
exp_created_1, exp_destroyed_1):
gc_collect() # code objects are collected by GC in free-threaded build
self.assertEqual(
exp_created_0, _testcapi.get_code_watcher_num_created_events(0))
self.assertEqual(
exp_destroyed_0, _testcapi.get_code_watcher_num_destroyed_events(0))
self.assertEqual(
exp_created_1, _testcapi.get_code_watcher_num_created_events(1))
self.assertEqual(
exp_destroyed_1, _testcapi.get_code_watcher_num_destroyed_events(1))
@suppress_immortalization()
def test_code_object_events_dispatched(self):
# verify that all counts are zero before any watchers are registered
self.assert_event_counts(0, 0, 0, 0)
# verify that all counts remain zero when a code object is
# created and destroyed with no watchers registered
co1 = _testcapi.code_newempty("test_watchers", "dummy1", 0)
self.assert_event_counts(0, 0, 0, 0)
del co1
self.assert_event_counts(0, 0, 0, 0)
# verify counts are as expected when first watcher is registered
with self.code_watcher(0):
self.assert_event_counts(0, 0, 0, 0)
co2 = _testcapi.code_newempty("test_watchers", "dummy2", 0)
self.assert_event_counts(1, 0, 0, 0)
del co2
self.assert_event_counts(1, 1, 0, 0)
# again with second watcher registered
with self.code_watcher(1):
self.assert_event_counts(1, 1, 0, 0)
co3 = _testcapi.code_newempty("test_watchers", "dummy3", 0)
self.assert_event_counts(2, 1, 1, 0)
del co3
self.assert_event_counts(2, 2, 1, 1)
# verify counts are reset and don't change after both watchers are cleared
co4 = _testcapi.code_newempty("test_watchers", "dummy4", 0)
self.assert_event_counts(0, 0, 0, 0)
del co4
self.assert_event_counts(0, 0, 0, 0)
def test_error(self):
with self.code_watcher(2):
with catch_unraisable_exception() as cm:
co = _testcapi.code_newempty("test_watchers", "dummy0", 0)
self.assertEqual(
cm.unraisable.err_msg,
f"Exception ignored in "
f"PY_CODE_EVENT_CREATE watcher callback for {co!r}"
)
self.assertIsNone(cm.unraisable.object)
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
@suppress_immortalization()
def test_dealloc_error(self):
co = _testcapi.code_newempty("test_watchers", "dummy0", 0)
with self.code_watcher(2):
with catch_unraisable_exception() as cm:
del co
gc_collect()
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
def test_clear_out_of_range_watcher_id(self):
with self.assertRaisesRegex(ValueError, r"Invalid code watcher ID -1"):
_testcapi.clear_code_watcher(-1)
with self.assertRaisesRegex(ValueError, r"Invalid code watcher ID 8"):
_testcapi.clear_code_watcher(8) # CODE_MAX_WATCHERS = 8
def test_clear_unassigned_watcher_id(self):
with self.assertRaisesRegex(ValueError, r"No code watcher set for ID 1"):
_testcapi.clear_code_watcher(1)
def test_allocate_too_many_watchers(self):
with self.assertRaisesRegex(RuntimeError, r"no more code watcher IDs available"):
_testcapi.allocate_too_many_code_watchers()
class TestFuncWatchers(unittest.TestCase):
@contextmanager
def add_watcher(self, func):
wid = _testcapi.add_func_watcher(func)
try:
yield
finally:
_testcapi.clear_func_watcher(wid)
def test_func_events_dispatched(self):
events = []
def watcher(*args):
events.append(args)
with self.add_watcher(watcher):
def myfunc():
pass
self.assertIn((_testcapi.PYFUNC_EVENT_CREATE, myfunc, None), events)
myfunc_id = id(myfunc)
new_code = self.test_func_events_dispatched.__code__
myfunc.__code__ = new_code
self.assertIn((_testcapi.PYFUNC_EVENT_MODIFY_CODE, myfunc, new_code), events)
new_defaults = (123,)
myfunc.__defaults__ = new_defaults
self.assertIn((_testcapi.PYFUNC_EVENT_MODIFY_DEFAULTS, myfunc, new_defaults), events)
new_defaults = (456,)
_testcapi.set_func_defaults_via_capi(myfunc, new_defaults)
self.assertIn((_testcapi.PYFUNC_EVENT_MODIFY_DEFAULTS, myfunc, new_defaults), events)
new_kwdefaults = {"self": 123}
myfunc.__kwdefaults__ = new_kwdefaults
self.assertIn((_testcapi.PYFUNC_EVENT_MODIFY_KWDEFAULTS, myfunc, new_kwdefaults), events)
new_kwdefaults = {"self": 456}
_testcapi.set_func_kwdefaults_via_capi(myfunc, new_kwdefaults)
self.assertIn((_testcapi.PYFUNC_EVENT_MODIFY_KWDEFAULTS, myfunc, new_kwdefaults), events)
# Clear events reference to func
events = []
del myfunc
self.assertIn((_testcapi.PYFUNC_EVENT_DESTROY, myfunc_id, None), events)
def test_multiple_watchers(self):
events0 = []
def first_watcher(*args):
events0.append(args)
events1 = []
def second_watcher(*args):
events1.append(args)
with self.add_watcher(first_watcher):
with self.add_watcher(second_watcher):
def myfunc():
pass
event = (_testcapi.PYFUNC_EVENT_CREATE, myfunc, None)
self.assertIn(event, events0)
self.assertIn(event, events1)
def test_watcher_raises_error(self):
class MyError(Exception):
pass
def watcher(*args):
raise MyError("testing 123")
with self.add_watcher(watcher):
with catch_unraisable_exception() as cm:
def myfunc():
pass
self.assertEqual(
cm.unraisable.err_msg,
f"Exception ignored in "
f"PyFunction_EVENT_CREATE watcher callback for {repr(myfunc)[1:-1]}"
)
self.assertIsNone(cm.unraisable.object)
def test_dealloc_watcher_raises_error(self):
class MyError(Exception):
pass
def watcher(*args):
raise MyError("testing 123")
def myfunc():
pass
with self.add_watcher(watcher):
with catch_unraisable_exception() as cm:
del myfunc
self.assertIsInstance(cm.unraisable.exc_value, MyError)
def test_clear_out_of_range_watcher_id(self):
with self.assertRaisesRegex(ValueError, r"invalid func watcher ID -1"):
_testcapi.clear_func_watcher(-1)
with self.assertRaisesRegex(ValueError, r"invalid func watcher ID 8"):
_testcapi.clear_func_watcher(8) # FUNC_MAX_WATCHERS = 8
def test_clear_unassigned_watcher_id(self):
with self.assertRaisesRegex(ValueError, r"no func watcher set for ID 1"):
_testcapi.clear_func_watcher(1)
def test_allocate_too_many_watchers(self):
with self.assertRaisesRegex(RuntimeError, r"no more func watcher IDs"):
_testcapi.allocate_too_many_func_watchers()
if __name__ == "__main__":
unittest.main()