mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	The implementation of `PyLong_FromLong()`, `PyLong_FromLongLong()` and `PyLong_FromSsize_t()` are now handled by a common macro `PYLONG_FROM_INT` which contains fast paths depending on the size of the integer to convert. Consequently, `PyLong_FromSsize_t()` for medium-sized integers is faster by roughly 25%. --------- Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
		
			
				
	
	
		
			808 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			808 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
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, 512, 1234, max_val)
 | 
						|
        if min_val < 0:
 | 
						|
            values += (-1, -512, -1234, 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)
 | 
						|
 | 
						|
    @support.bigmemtest(2**32, memuse=0.35)
 | 
						|
    def test_long_asnativebytes_huge(self, size):
 | 
						|
        asnativebytes = _testcapi.pylong_asnativebytes
 | 
						|
        v = 1 << size
 | 
						|
        buffer = bytearray(size * 2 // 15 + 10)
 | 
						|
        r = asnativebytes(v, buffer, 0, -1)
 | 
						|
        self.assertEqual(r, size // 8 + 1)
 | 
						|
        self.assertEqual(buffer.count(0), len(buffer))
 | 
						|
        r = asnativebytes(v, buffer, len(buffer), -1)
 | 
						|
        self.assertEqual(r, size // 8 + 1)
 | 
						|
        self.assertEqual(buffer.count(0), len(buffer) - 1)
 | 
						|
 | 
						|
    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>)")
 | 
						|
 | 
						|
    def test_long_getsign(self):
 | 
						|
        # Test PyLong_GetSign()
 | 
						|
        getsign = _testcapi.pylong_getsign
 | 
						|
        self.assertEqual(getsign(1), 1)
 | 
						|
        self.assertEqual(getsign(123456), 1)
 | 
						|
        self.assertEqual(getsign(-2), -1)
 | 
						|
        self.assertEqual(getsign(0), 0)
 | 
						|
        self.assertEqual(getsign(True), 1)
 | 
						|
        self.assertEqual(getsign(IntSubclass(-11)), -1)
 | 
						|
        self.assertEqual(getsign(False), 0)
 | 
						|
 | 
						|
        self.assertRaises(TypeError, getsign, 1.0)
 | 
						|
        self.assertRaises(TypeError, getsign, Index(123))
 | 
						|
 | 
						|
        # CRASHES getsign(NULL)
 | 
						|
 | 
						|
    def test_long_ispositive(self):
 | 
						|
        # Test PyLong_IsPositive()
 | 
						|
        ispositive = _testcapi.pylong_ispositive
 | 
						|
        self.assertEqual(ispositive(1), 1)
 | 
						|
        self.assertEqual(ispositive(123), 1)
 | 
						|
        self.assertEqual(ispositive(-1), 0)
 | 
						|
        self.assertEqual(ispositive(0), 0)
 | 
						|
        self.assertEqual(ispositive(True), 1)
 | 
						|
        self.assertEqual(ispositive(False), 0)
 | 
						|
        self.assertEqual(ispositive(IntSubclass(-1)), 0)
 | 
						|
        self.assertRaises(TypeError, ispositive, 1.0)
 | 
						|
        self.assertRaises(TypeError, ispositive, Index(123))
 | 
						|
 | 
						|
        # CRASHES ispositive(NULL)
 | 
						|
 | 
						|
    def test_long_isnegative(self):
 | 
						|
        # Test PyLong_IsNegative()
 | 
						|
        isnegative = _testcapi.pylong_isnegative
 | 
						|
        self.assertEqual(isnegative(1), 0)
 | 
						|
        self.assertEqual(isnegative(123), 0)
 | 
						|
        self.assertEqual(isnegative(-1), 1)
 | 
						|
        self.assertEqual(isnegative(0), 0)
 | 
						|
        self.assertEqual(isnegative(True), 0)
 | 
						|
        self.assertEqual(isnegative(False), 0)
 | 
						|
        self.assertEqual(isnegative(IntSubclass(-1)), 1)
 | 
						|
        self.assertRaises(TypeError, isnegative, 1.0)
 | 
						|
        self.assertRaises(TypeError, isnegative, Index(123))
 | 
						|
 | 
						|
        # CRASHES isnegative(NULL)
 | 
						|
 | 
						|
    def test_long_iszero(self):
 | 
						|
        # Test PyLong_IsZero()
 | 
						|
        iszero = _testcapi.pylong_iszero
 | 
						|
        self.assertEqual(iszero(1), 0)
 | 
						|
        self.assertEqual(iszero(-1), 0)
 | 
						|
        self.assertEqual(iszero(0), 1)
 | 
						|
        self.assertEqual(iszero(True), 0)
 | 
						|
        self.assertEqual(iszero(False), 1)
 | 
						|
        self.assertEqual(iszero(IntSubclass(-1)), 0)
 | 
						|
        self.assertEqual(iszero(IntSubclass(0)), 1)
 | 
						|
        self.assertRaises(TypeError, iszero, 1.0)
 | 
						|
        self.assertRaises(TypeError, iszero, Index(123))
 | 
						|
 | 
						|
        # CRASHES iszero(NULL)
 | 
						|
 | 
						|
    def test_long_asint32(self):
 | 
						|
        # Test PyLong_AsInt32() and PyLong_FromInt32()
 | 
						|
        to_int32 = _testlimitedcapi.pylong_asint32
 | 
						|
        from _testcapi import INT32_MIN, INT32_MAX
 | 
						|
        self.check_long_asint(to_int32, INT32_MIN, INT32_MAX)
 | 
						|
 | 
						|
    def test_long_asint64(self):
 | 
						|
        # Test PyLong_AsInt64() and PyLong_FromInt64()
 | 
						|
        as_int64 = _testlimitedcapi.pylong_asint64
 | 
						|
        from _testcapi import INT64_MIN, INT64_MAX
 | 
						|
        self.check_long_asint(as_int64, INT64_MIN, INT64_MAX)
 | 
						|
 | 
						|
    def test_long_asuint32(self):
 | 
						|
        # Test PyLong_AsUInt32() and PyLong_FromUInt32()
 | 
						|
        as_uint32 = _testlimitedcapi.pylong_asuint32
 | 
						|
        from _testcapi import UINT32_MAX
 | 
						|
        self.check_long_asint(as_uint32, 0, UINT32_MAX,
 | 
						|
                              negative_value_error=ValueError)
 | 
						|
 | 
						|
    def test_long_asuint64(self):
 | 
						|
        # Test PyLong_AsUInt64() and PyLong_FromUInt64()
 | 
						|
        as_uint64 = _testlimitedcapi.pylong_asuint64
 | 
						|
        from _testcapi import UINT64_MAX
 | 
						|
        self.check_long_asint(as_uint64, 0, UINT64_MAX,
 | 
						|
                              negative_value_error=ValueError)
 | 
						|
 | 
						|
    def test_long_layout(self):
 | 
						|
        # Test PyLong_GetNativeLayout()
 | 
						|
        int_info = sys.int_info
 | 
						|
        layout = _testcapi.get_pylong_layout()
 | 
						|
        expected = {
 | 
						|
            'bits_per_digit': int_info.bits_per_digit,
 | 
						|
            'digit_size': int_info.sizeof_digit,
 | 
						|
            'digits_order': -1,
 | 
						|
            'digit_endianness': -1 if sys.byteorder == 'little' else 1,
 | 
						|
        }
 | 
						|
        self.assertEqual(layout, expected)
 | 
						|
 | 
						|
    def test_long_export(self):
 | 
						|
        # Test PyLong_Export()
 | 
						|
        layout = _testcapi.get_pylong_layout()
 | 
						|
        base = 2 ** layout['bits_per_digit']
 | 
						|
 | 
						|
        pylong_export = _testcapi.pylong_export
 | 
						|
 | 
						|
        # value fits into int64_t
 | 
						|
        self.assertEqual(pylong_export(0), 0)
 | 
						|
        self.assertEqual(pylong_export(123), 123)
 | 
						|
        self.assertEqual(pylong_export(-123), -123)
 | 
						|
        self.assertEqual(pylong_export(IntSubclass(123)), 123)
 | 
						|
 | 
						|
        # use an array, doesn't fit into int64_t
 | 
						|
        self.assertEqual(pylong_export(base**10 * 2 + 1),
 | 
						|
                         (0, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]))
 | 
						|
        self.assertEqual(pylong_export(-(base**10 * 2 + 1)),
 | 
						|
                         (1, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]))
 | 
						|
        self.assertEqual(pylong_export(IntSubclass(base**10 * 2 + 1)),
 | 
						|
                         (0, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]))
 | 
						|
 | 
						|
        self.assertRaises(TypeError, pylong_export, 1.0)
 | 
						|
        self.assertRaises(TypeError, pylong_export, 0+1j)
 | 
						|
        self.assertRaises(TypeError, pylong_export, "abc")
 | 
						|
 | 
						|
    def test_longwriter_create(self):
 | 
						|
        # Test PyLongWriter_Create()
 | 
						|
        layout = _testcapi.get_pylong_layout()
 | 
						|
        base = 2 ** layout['bits_per_digit']
 | 
						|
 | 
						|
        pylongwriter_create = _testcapi.pylongwriter_create
 | 
						|
        self.assertRaises(ValueError, pylongwriter_create, 0, [])
 | 
						|
        self.assertRaises(ValueError, pylongwriter_create, -123, [])
 | 
						|
        self.assertEqual(pylongwriter_create(0, [0]), 0)
 | 
						|
        self.assertEqual(pylongwriter_create(0, [123]), 123)
 | 
						|
        self.assertEqual(pylongwriter_create(1, [123]), -123)
 | 
						|
        self.assertEqual(pylongwriter_create(1, [1, 2]),
 | 
						|
                         -(base * 2 + 1))
 | 
						|
        self.assertEqual(pylongwriter_create(0, [1, 2, 3]),
 | 
						|
                         base**2 * 3 + base * 2 + 1)
 | 
						|
        max_digit = base - 1
 | 
						|
        self.assertEqual(pylongwriter_create(0, [max_digit, max_digit, max_digit]),
 | 
						|
                         base**2 * max_digit + base * max_digit + max_digit)
 | 
						|
 | 
						|
        # normalize
 | 
						|
        self.assertEqual(pylongwriter_create(0, [123, 0, 0]), 123)
 | 
						|
 | 
						|
        # test singletons + normalize
 | 
						|
        for num in (-2, 0, 1, 5, 42, 100):
 | 
						|
            self.assertIs(pylongwriter_create(bool(num < 0), [abs(num), 0]),
 | 
						|
                          num)
 | 
						|
 | 
						|
        def to_digits(num):
 | 
						|
            digits = []
 | 
						|
            while True:
 | 
						|
                num, digit = divmod(num, base)
 | 
						|
                digits.append(digit)
 | 
						|
                if not num:
 | 
						|
                    break
 | 
						|
            return digits
 | 
						|
 | 
						|
        # round trip: Python int -> export -> Python int
 | 
						|
        pylong_export = _testcapi.pylong_export
 | 
						|
        numbers = [*range(0, 10), 12345, 0xdeadbeef, 2**100, 2**100-1]
 | 
						|
        numbers.extend(-num for num in list(numbers))
 | 
						|
        for num in numbers:
 | 
						|
            with self.subTest(num=num):
 | 
						|
                data = pylong_export(num)
 | 
						|
                if isinstance(data, tuple):
 | 
						|
                    negative, digits = data
 | 
						|
                else:
 | 
						|
                    value = data
 | 
						|
                    negative = int(value < 0)
 | 
						|
                    digits = to_digits(abs(value))
 | 
						|
                self.assertEqual(pylongwriter_create(negative, digits), num,
 | 
						|
                                 (negative, digits))
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    unittest.main()
 |