From 9d40ebf1902812fad6aa85ede7b6f1fdff3c1291 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 14 Oct 2023 08:53:57 +0300 Subject: [PATCH] gh-110628: Add tests for PyLong C API (GH-110629) --- Lib/test/test_capi/test_long.py | 393 ++++++++++++++++++++++++++++++-- Modules/_testcapi/long.c | 222 ++++++++++++++++++ Modules/_testcapimodule.c | 2 + 3 files changed, 600 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_capi/test_long.py b/Lib/test/test_capi/test_long.py index 101fe1f0de77f1..8e3ef25d1ff86f 100644 --- a/Lib/test/test_capi/test_long.py +++ b/Lib/test/test_capi/test_long.py @@ -6,6 +6,25 @@ # Skip this test if the _testcapi module isn't available. _testcapi = import_helper.import_module('_testcapi') +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): @@ -34,35 +53,375 @@ def test_compact_known(self): self.assertEqual(_testcapi.call_long_compact_api(sys.maxsize), (False, -1)) + def test_long_check(self): + # Test PyLong_Check() + check = _testcapi.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 = _testcapi.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 = _testcapi.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 = _testcapi.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 = _testcapi.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 test_long_asint(self): + # Test PyLong_AsInt() PyLong_AsInt = _testcapi.PyLong_AsInt - INT_MIN = _testcapi.INT_MIN - INT_MAX = _testcapi.INT_MAX + from _testcapi import INT_MIN, INT_MAX # round trip (object -> int -> object) for value in (INT_MIN, INT_MAX, -1, 0, 1, 123): with self.subTest(value=value): self.assertEqual(PyLong_AsInt(value), value) - - # use __index__(), not __int__() - class MyIndex: - def __index__(self): - return 10 - def __int__(self): - return 22 - self.assertEqual(PyLong_AsInt(MyIndex()), 10) + self.assertEqual(PyLong_AsInt(IntSubclass(42)), 42) + self.assertEqual(PyLong_AsInt(Index(42)), 42) + self.assertEqual(PyLong_AsInt(MyIndexAndInt()), 10) # bound checking - with self.assertRaises(OverflowError): - PyLong_AsInt(INT_MIN - 1) - with self.assertRaises(OverflowError): - PyLong_AsInt(INT_MAX + 1) + self.assertRaises(OverflowError, PyLong_AsInt, INT_MIN - 1) + self.assertRaises(OverflowError, PyLong_AsInt, INT_MAX + 1) # invalid type - for value in (1.0, b'2', '3'): + self.assertRaises(TypeError, PyLong_AsInt, 1.0) + self.assertRaises(TypeError, PyLong_AsInt, b'2') + self.assertRaises(TypeError, PyLong_AsInt, '3') + self.assertRaises(SystemError, PyLong_AsInt, NULL) + + def test_long_aslong(self): + # Test PyLong_AsLong() and PyLong_FromLong() + aslong = _testcapi.pylong_aslong + from _testcapi import LONG_MIN, LONG_MAX + # round trip (object -> long -> object) + for value in (LONG_MIN, LONG_MAX, -1, 0, 1, 1234): + with self.subTest(value=value): + self.assertEqual(aslong(value), value) + + self.assertEqual(aslong(IntSubclass(42)), 42) + self.assertEqual(aslong(Index(42)), 42) + self.assertEqual(aslong(MyIndexAndInt()), 10) + + self.assertRaises(OverflowError, aslong, LONG_MIN - 1) + self.assertRaises(OverflowError, aslong, LONG_MAX + 1) + self.assertRaises(TypeError, aslong, 1.0) + self.assertRaises(TypeError, aslong, b'2') + self.assertRaises(TypeError, aslong, '3') + self.assertRaises(SystemError, aslong, NULL) + + def test_long_aslongandoverflow(self): + # Test PyLong_AsLongAndOverflow() + aslongandoverflow = _testcapi.pylong_aslongandoverflow + from _testcapi import LONG_MIN, LONG_MAX + # round trip (object -> long -> object) + for value in (LONG_MIN, LONG_MAX, -1, 0, 1, 1234): + with self.subTest(value=value): + self.assertEqual(aslongandoverflow(value), (value, 0)) + + self.assertEqual(aslongandoverflow(IntSubclass(42)), (42, 0)) + self.assertEqual(aslongandoverflow(Index(42)), (42, 0)) + self.assertEqual(aslongandoverflow(MyIndexAndInt()), (10, 0)) + + self.assertEqual(aslongandoverflow(LONG_MIN - 1), (-1, -1)) + self.assertEqual(aslongandoverflow(LONG_MAX + 1), (-1, 1)) + # CRASHES aslongandoverflow(1.0) + # CRASHES aslongandoverflow(NULL) + + def test_long_asunsignedlong(self): + # Test PyLong_AsUnsignedLong() and PyLong_FromUnsignedLong() + asunsignedlong = _testcapi.pylong_asunsignedlong + from _testcapi import ULONG_MAX + # round trip (object -> unsigned long -> object) + for value in (ULONG_MAX, 0, 1, 1234): + with self.subTest(value=value): + self.assertEqual(asunsignedlong(value), value) + + self.assertEqual(asunsignedlong(IntSubclass(42)), 42) + self.assertRaises(TypeError, asunsignedlong, Index(42)) + self.assertRaises(TypeError, asunsignedlong, MyIndexAndInt()) + + self.assertRaises(OverflowError, asunsignedlong, -1) + self.assertRaises(OverflowError, asunsignedlong, ULONG_MAX + 1) + self.assertRaises(TypeError, asunsignedlong, 1.0) + self.assertRaises(TypeError, asunsignedlong, b'2') + self.assertRaises(TypeError, asunsignedlong, '3') + self.assertRaises(SystemError, asunsignedlong, NULL) + + def test_long_asunsignedlongmask(self): + # Test PyLong_AsUnsignedLongMask() + asunsignedlongmask = _testcapi.pylong_asunsignedlongmask + from _testcapi import ULONG_MAX + # round trip (object -> unsigned long -> object) + for value in (ULONG_MAX, 0, 1, 1234): with self.subTest(value=value): - with self.assertRaises(TypeError): - PyLong_AsInt(value) + self.assertEqual(asunsignedlongmask(value), value) + + self.assertEqual(asunsignedlongmask(IntSubclass(42)), 42) + self.assertEqual(asunsignedlongmask(Index(42)), 42) + self.assertEqual(asunsignedlongmask(MyIndexAndInt()), 10) + + self.assertEqual(asunsignedlongmask(-1), ULONG_MAX) + self.assertEqual(asunsignedlongmask(ULONG_MAX + 1), 0) + self.assertRaises(TypeError, asunsignedlongmask, 1.0) + self.assertRaises(TypeError, asunsignedlongmask, b'2') + self.assertRaises(TypeError, asunsignedlongmask, '3') + self.assertRaises(SystemError, asunsignedlongmask, NULL) + + def test_long_aslonglong(self): + # Test PyLong_AsLongLong() and PyLong_FromLongLong() + aslonglong = _testcapi.pylong_aslonglong + from _testcapi import LLONG_MIN, LLONG_MAX + # round trip (object -> long long -> object) + for value in (LLONG_MIN, LLONG_MAX, -1, 0, 1, 1234): + with self.subTest(value=value): + self.assertEqual(aslonglong(value), value) + + self.assertEqual(aslonglong(IntSubclass(42)), 42) + self.assertEqual(aslonglong(Index(42)), 42) + self.assertEqual(aslonglong(MyIndexAndInt()), 10) + + self.assertRaises(OverflowError, aslonglong, LLONG_MIN - 1) + self.assertRaises(OverflowError, aslonglong, LLONG_MAX + 1) + self.assertRaises(TypeError, aslonglong, 1.0) + self.assertRaises(TypeError, aslonglong, b'2') + self.assertRaises(TypeError, aslonglong, '3') + self.assertRaises(SystemError, aslonglong, NULL) + + def test_long_aslonglongandoverflow(self): + # Test PyLong_AsLongLongAndOverflow() + aslonglongandoverflow = _testcapi.pylong_aslonglongandoverflow + from _testcapi import LLONG_MIN, LLONG_MAX + # round trip (object -> long long -> object) + for value in (LLONG_MIN, LLONG_MAX, -1, 0, 1, 1234): + with self.subTest(value=value): + self.assertEqual(aslonglongandoverflow(value), (value, 0)) + + self.assertEqual(aslonglongandoverflow(IntSubclass(42)), (42, 0)) + self.assertEqual(aslonglongandoverflow(Index(42)), (42, 0)) + self.assertEqual(aslonglongandoverflow(MyIndexAndInt()), (10, 0)) + + self.assertEqual(aslonglongandoverflow(LLONG_MIN - 1), (-1, -1)) + self.assertEqual(aslonglongandoverflow(LLONG_MAX + 1), (-1, 1)) + # CRASHES aslonglongandoverflow(1.0) + # CRASHES aslonglongandoverflow(NULL) + + def test_long_asunsignedlonglong(self): + # Test PyLong_AsUnsignedLongLong() and PyLong_FromUnsignedLongLong() + asunsignedlonglong = _testcapi.pylong_asunsignedlonglong + from _testcapi import ULLONG_MAX + # round trip (object -> unsigned long long -> object) + for value in (ULLONG_MAX, 0, 1, 1234): + with self.subTest(value=value): + self.assertEqual(asunsignedlonglong(value), value) + + self.assertEqual(asunsignedlonglong(IntSubclass(42)), 42) + self.assertRaises(TypeError, asunsignedlonglong, Index(42)) + self.assertRaises(TypeError, asunsignedlonglong, MyIndexAndInt()) + + self.assertRaises(OverflowError, asunsignedlonglong, -1) + self.assertRaises(OverflowError, asunsignedlonglong, ULLONG_MAX + 1) + self.assertRaises(TypeError, asunsignedlonglong, 1.0) + self.assertRaises(TypeError, asunsignedlonglong, b'2') + self.assertRaises(TypeError, asunsignedlonglong, '3') + self.assertRaises(SystemError, asunsignedlonglong, NULL) + + def test_long_asunsignedlonglongmask(self): + # Test PyLong_AsUnsignedLongLongMask() + asunsignedlonglongmask = _testcapi.pylong_asunsignedlonglongmask + from _testcapi import ULLONG_MAX + # round trip (object -> unsigned long long -> object) + for value in (ULLONG_MAX, 0, 1, 1234): + with self.subTest(value=value): + self.assertEqual(asunsignedlonglongmask(value), value) + + self.assertEqual(asunsignedlonglongmask(IntSubclass(42)), 42) + self.assertEqual(asunsignedlonglongmask(Index(42)), 42) + self.assertEqual(asunsignedlonglongmask(MyIndexAndInt()), 10) + + self.assertEqual(asunsignedlonglongmask(-1), ULLONG_MAX) + self.assertEqual(asunsignedlonglongmask(ULLONG_MAX + 1), 0) + self.assertRaises(TypeError, asunsignedlonglongmask, 1.0) + self.assertRaises(TypeError, asunsignedlonglongmask, b'2') + self.assertRaises(TypeError, asunsignedlonglongmask, '3') + self.assertRaises(SystemError, asunsignedlonglongmask, NULL) + + def test_long_as_ssize_t(self): + # Test PyLong_AsSsize_t() and PyLong_FromSsize_t() + as_ssize_t = _testcapi.pylong_as_ssize_t + from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX + # round trip (object -> Py_ssize_t -> object) + for value in (PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, -1, 0, 1, 1234): + with self.subTest(value=value): + self.assertEqual(as_ssize_t(value), value) + + self.assertEqual(as_ssize_t(IntSubclass(42)), 42) + self.assertRaises(TypeError, as_ssize_t, Index(42)) + self.assertRaises(TypeError, as_ssize_t, MyIndexAndInt()) + + self.assertRaises(OverflowError, as_ssize_t, PY_SSIZE_T_MIN - 1) + self.assertRaises(OverflowError, as_ssize_t, PY_SSIZE_T_MAX + 1) + self.assertRaises(TypeError, as_ssize_t, 1.0) + self.assertRaises(TypeError, as_ssize_t, b'2') + self.assertRaises(TypeError, as_ssize_t, '3') + self.assertRaises(SystemError, as_ssize_t, NULL) + + def test_long_as_size_t(self): + # Test PyLong_AsSize_t() and PyLong_FromSize_t() + as_size_t = _testcapi.pylong_as_size_t + from _testcapi import SIZE_MAX + # round trip (object -> size_t -> object) + for value in (SIZE_MAX, 0, 1, 1234): + with self.subTest(value=value): + self.assertEqual(as_size_t(value), value) + + self.assertEqual(as_size_t(IntSubclass(42)), 42) + self.assertRaises(TypeError, as_size_t, Index(42)) + self.assertRaises(TypeError, as_size_t, MyIndexAndInt()) + + self.assertRaises(OverflowError, as_size_t, -1) + self.assertRaises(OverflowError, as_size_t, SIZE_MAX + 1) + self.assertRaises(TypeError, as_size_t, 1.0) + self.assertRaises(TypeError, as_size_t, b'2') + self.assertRaises(TypeError, as_size_t, '3') + self.assertRaises(SystemError, as_size_t, NULL) + + def test_long_asdouble(self): + # Test PyLong_AsDouble() + asdouble = _testcapi.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 = _testcapi.pylong_fromvoidptr + asvoidptr = _testcapi.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) if __name__ == "__main__": diff --git a/Modules/_testcapi/long.c b/Modules/_testcapi/long.c index 4362f431fc3f4d..32ad8d32ab8523 100644 --- a/Modules/_testcapi/long.c +++ b/Modules/_testcapi/long.c @@ -3,6 +3,7 @@ #endif #include "parts.h" +#include "util.h" #include "clinic/long.c.h" /*[clinic input] @@ -554,6 +555,69 @@ _testcapi_call_long_compact_api(PyObject *module, PyObject *arg) return Py_BuildValue("in", is_compact, value); } +static PyObject * +pylong_check(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyLong_Check(obj)); +} + +static PyObject * +pylong_checkexact(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyLong_CheckExact(obj)); +} + +static PyObject * +pylong_fromdouble(PyObject *module, PyObject *arg) +{ + double value; + if (!PyArg_Parse(arg, "d", &value)) { + return NULL; + } + return PyLong_FromDouble(value); +} + +static PyObject * +pylong_fromstring(PyObject *module, PyObject *args) +{ + const char *str; + Py_ssize_t len; + int base; + char *end = UNINITIALIZED_PTR; + if (!PyArg_ParseTuple(args, "z#i", &str, &len, &base)) { + return NULL; + } + + PyObject *result = PyLong_FromString(str, &end, base); + if (result == NULL) { + // XXX 'end' is not always set. + return NULL; + } + return Py_BuildValue("Nn", result, (Py_ssize_t)(end - str)); +} + +static PyObject * +pylong_fromunicodeobject(PyObject *module, PyObject *args) +{ + PyObject *unicode; + int base; + if (!PyArg_ParseTuple(args, "Oi", &unicode, &base)) { + return NULL; + } + + NULLABLE(unicode); + return PyLong_FromUnicodeObject(unicode, base); +} + +static PyObject * +pylong_fromvoidptr(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + return PyLong_FromVoidPtr((void *)arg); +} + /*[clinic input] _testcapi.PyLong_AsInt arg: object @@ -564,6 +628,7 @@ static PyObject * _testcapi_PyLong_AsInt(PyObject *module, PyObject *arg) /*[clinic end generated code: output=0df9f19de5fa575b input=9561b97105493a67]*/ { + NULLABLE(arg); assert(!PyErr_Occurred()); int value = PyLong_AsInt(arg); if (value == -1 && PyErr_Occurred()) { @@ -572,6 +637,145 @@ _testcapi_PyLong_AsInt(PyObject *module, PyObject *arg) return PyLong_FromLong(value); } +static PyObject * +pylong_aslong(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + long value = PyLong_AsLong(arg); + if (value == -1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromLong(value); +} + +static PyObject * +pylong_aslongandoverflow(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + int overflow = UNINITIALIZED_INT; + long value = PyLong_AsLongAndOverflow(arg, &overflow); + if (value == -1 && PyErr_Occurred()) { + assert(overflow == -1); + return NULL; + } + return Py_BuildValue("li", value, overflow); +} + +static PyObject * +pylong_asunsignedlong(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + unsigned long value = PyLong_AsUnsignedLong(arg); + if (value == (unsigned long)-1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromUnsignedLong(value); +} + +static PyObject * +pylong_asunsignedlongmask(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + unsigned long value = PyLong_AsUnsignedLongMask(arg); + if (value == (unsigned long)-1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromUnsignedLong(value); +} + +static PyObject * +pylong_aslonglong(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + long long value = PyLong_AsLongLong(arg); + if (value == -1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromLongLong(value); +} + +static PyObject * +pylong_aslonglongandoverflow(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + int overflow = UNINITIALIZED_INT; + long long value = PyLong_AsLongLongAndOverflow(arg, &overflow); + if (value == -1 && PyErr_Occurred()) { + assert(overflow == -1); + return NULL; + } + return Py_BuildValue("Li", value, overflow); +} + +static PyObject * +pylong_asunsignedlonglong(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + unsigned long long value = PyLong_AsUnsignedLongLong(arg); + if (value == (unsigned long long)-1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromUnsignedLongLong(value); +} + +static PyObject * +pylong_asunsignedlonglongmask(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + unsigned long long value = PyLong_AsUnsignedLongLongMask(arg); + if (value == (unsigned long long)-1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromUnsignedLongLong(value); +} + +static PyObject * +pylong_as_ssize_t(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + Py_ssize_t value = PyLong_AsSsize_t(arg); + if (value == -1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromSsize_t(value); +} + +static PyObject * +pylong_as_size_t(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + size_t value = PyLong_AsSize_t(arg); + if (value == (size_t)-1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromSize_t(value); +} + +static PyObject * +pylong_asdouble(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + double value = PyLong_AsDouble(arg); + if (value == -1.0 && PyErr_Occurred()) { + return NULL; + } + return PyFloat_FromDouble(value); +} + +static PyObject * +pylong_asvoidptr(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + void *value = PyLong_AsVoidPtr(arg); + if (value == NULL) { + if (PyErr_Occurred()) { + return NULL; + } + Py_RETURN_NONE; + } + return Py_NewRef((PyObject *)value); +} + static PyMethodDef test_methods[] = { _TESTCAPI_TEST_LONG_AND_OVERFLOW_METHODDEF _TESTCAPI_TEST_LONG_API_METHODDEF @@ -581,7 +785,25 @@ static PyMethodDef test_methods[] = { _TESTCAPI_TEST_LONG_LONG_AND_OVERFLOW_METHODDEF _TESTCAPI_TEST_LONGLONG_API_METHODDEF _TESTCAPI_CALL_LONG_COMPACT_API_METHODDEF + {"pylong_check", pylong_check, METH_O}, + {"pylong_checkexact", pylong_checkexact, METH_O}, + {"pylong_fromdouble", pylong_fromdouble, METH_O}, + {"pylong_fromstring", pylong_fromstring, METH_VARARGS}, + {"pylong_fromunicodeobject", pylong_fromunicodeobject, METH_VARARGS}, + {"pylong_fromvoidptr", pylong_fromvoidptr, METH_O}, _TESTCAPI_PYLONG_ASINT_METHODDEF + {"pylong_aslong", pylong_aslong, METH_O}, + {"pylong_aslongandoverflow", pylong_aslongandoverflow, METH_O}, + {"pylong_asunsignedlong", pylong_asunsignedlong, METH_O}, + {"pylong_asunsignedlongmask", pylong_asunsignedlongmask, METH_O}, + {"pylong_aslonglong", pylong_aslonglong, METH_O}, + {"pylong_aslonglongandoverflow", pylong_aslonglongandoverflow, METH_O}, + {"pylong_asunsignedlonglong", pylong_asunsignedlonglong, METH_O}, + {"pylong_asunsignedlonglongmask", pylong_asunsignedlonglongmask, METH_O}, + {"pylong_as_ssize_t", pylong_as_ssize_t, METH_O}, + {"pylong_as_size_t", pylong_as_size_t, METH_O}, + {"pylong_asdouble", pylong_asdouble, METH_O}, + {"pylong_asvoidptr", pylong_asvoidptr, METH_O}, {NULL}, }; diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 2b9b2321c81e7b..c6a8dda58f6c22 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3960,7 +3960,9 @@ PyInit__testcapi(void) PyModule_AddObject(m, "ULLONG_MAX", PyLong_FromUnsignedLongLong(ULLONG_MAX)); PyModule_AddObject(m, "PY_SSIZE_T_MAX", PyLong_FromSsize_t(PY_SSIZE_T_MAX)); PyModule_AddObject(m, "PY_SSIZE_T_MIN", PyLong_FromSsize_t(PY_SSIZE_T_MIN)); + PyModule_AddObject(m, "SIZE_MAX", PyLong_FromSize_t(SIZE_MAX)); PyModule_AddObject(m, "SIZEOF_WCHAR_T", PyLong_FromSsize_t(sizeof(wchar_t))); + PyModule_AddObject(m, "SIZEOF_VOID_P", PyLong_FromSsize_t(sizeof(void*))); PyModule_AddObject(m, "SIZEOF_TIME_T", PyLong_FromSsize_t(sizeof(time_t))); PyModule_AddObject(m, "Py_Version", PyLong_FromUnsignedLong(Py_Version)); Py_INCREF(&PyInstanceMethod_Type);