diff -r 17d64eb0636a Doc/c-api/long.rst --- a/Doc/c-api/long.rst Mon Apr 14 11:48:29 2014 -0400 +++ b/Doc/c-api/long.rst Mon Apr 14 16:44:52 2014 -0400 @@ -201,6 +201,21 @@ :c:type:`unsigned long`. +.. c:function:: unsigned long PyLong_AsUnsignedLongAndOverflow(PyObject *obj, int *overflow) + + Return a C :c:type:`unsigned long` representation of *pylong*. *pylong* + must be an instance of :c:type:`PyLongObject`. + + If *pylong* is out of range, *\*overflow* is set as follows: if the value of + *pylong* is negative, set *\*overflow* to ``-1`` and return ``(unsigned + long)-1``. If the value of *pylong* exceeds :const:`ULONG_MAX`, set + *\*overflow* to ``1`` and return ``(unsigned long)-1``. Otherwise, set + *\*overflow* to ``0``. If any other exception occurs set *\*overflow to + ``0`` and return ``-1`` as usual. + + .. versionadded:: 3.5 + + .. c:function:: size_t PyLong_AsSize_t(PyObject *pylong) Return a C :c:type:`size_t` representation of *pylong*. *pylong* must be diff -r 17d64eb0636a Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst Mon Apr 14 11:48:29 2014 -0400 +++ b/Doc/whatsnew/3.5.rst Mon Apr 14 16:44:52 2014 -0400 @@ -168,7 +168,7 @@ Changes to Python's build process and to the C API include: -* None yet. +* New function PyLong_AsUnsignedLongAndOverflow (issue #21111). Deprecated diff -r 17d64eb0636a Include/longobject.h --- a/Include/longobject.h Mon Apr 14 11:48:29 2014 -0400 +++ b/Include/longobject.h Mon Apr 14 16:44:52 2014 -0400 @@ -25,6 +25,7 @@ PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *); PyAPI_FUNC(size_t) PyLong_AsSize_t(PyObject *); PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *); +PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongAndOverflow(PyObject *, int *); PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *); #ifndef Py_LIMITED_API PyAPI_FUNC(int) _PyLong_AsInt(PyObject *); diff -r 17d64eb0636a Misc/NEWS --- a/Misc/NEWS Mon Apr 14 11:48:29 2014 -0400 +++ b/Misc/NEWS Mon Apr 14 16:44:52 2014 -0400 @@ -197,6 +197,10 @@ C API ----- + +- Issue #21111: New function PyLong_AsUnsignedLongAndOverflow (which is to + PyLong_AsUnsignedLong as PyLong_AsLongAndOverflow is to PyLong_AsLong). + - Issue #20942: PyImport_ImportFrozenModuleObject() no longer sets __file__ to match what importlib does; this affects _frozen_importlib as well as any module loaded using imp.init_frozen(). diff -r 17d64eb0636a Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Mon Apr 14 11:48:29 2014 -0400 +++ b/Modules/_testcapimodule.c Mon Apr 14 16:44:52 2014 -0400 @@ -749,6 +749,106 @@ return Py_None; } +/* Test the PyLong_AsUnsignedLongAndOverflow API. General conversion to + unsigned long is tested by test_long_api_inner. This test will + concentrate on proper handling of overflow. +*/ + +static PyObject * +test_unsigned_long_and_overflow(PyObject *self) +{ + PyObject *num, *one, *temp; + unsigned long value; + int overflow; + + /* Test that overflow is set properly for a negative value. */ + num = PyLong_FromString("-1", NULL, 10); + if (num == NULL) + return NULL; + + overflow = 1234; + value = PyLong_AsUnsignedLongAndOverflow(num, &overflow); + Py_DECREF(num); + if (value == -1 && PyErr_Occurred()) + return NULL; + + if (value != -1) + return raiseTestError("test_unsigned_long_and_overflow", + "return value was not set to -1"); + if (overflow != -1) + return raiseTestError("test_unsigned_long_and_overflow", + "overflow was not set to -1"); + + /* And with a value of ULONG_MAX + 1. */ + num = PyLong_FromUnsignedLong(ULONG_MAX); + if (num == NULL) + return NULL; + one = PyLong_FromLong(1); + if (one == NULL) { + Py_DECREF(num); + return NULL; + } + temp = PyNumber_Add(num, one); + Py_DECREF(one); + Py_DECREF(num); + num = temp; + if (num == NULL) + return NULL; + + overflow = 0; + value = PyLong_AsUnsignedLongAndOverflow(num, &overflow); + Py_DECREF(num); + if (value == -1 && PyErr_Occurred()) + return NULL; + + if (value != -1) + return raiseTestError("test_unsigned_long_and_overflow", + "return value was not set to -1"); + if (overflow != 1) + return raiseTestError("test_unsigned_long_and_overflow", + "overflow was not set to 1"); + + /* Test that overflow is cleared properly for in-range inputs. */ + /* Testing input of ULONG_MAX */ + num = PyLong_FromUnsignedLong(ULONG_MAX); + if (num == NULL) + return NULL; + + overflow = 1234; + value = PyLong_AsUnsignedLongAndOverflow(num, &overflow); + Py_DECREF(num); + if (value == -1 && PyErr_Occurred()) + return NULL; + + if (value != ULONG_MAX) + return raiseTestError("test_unsigned_long_and_overflow", + "return value incorrect"); + if (overflow) + return raiseTestError("test_unsigned_long_and_overflow", + "overflow was incorrectly set; should be zero"); + + /* Testing input of 0. */ + num = PyLong_FromUnsignedLong(0UL); + if (num == NULL) + return NULL; + + overflow = 1234; + value = PyLong_AsUnsignedLongAndOverflow(num, &overflow); + Py_DECREF(num); + if (value == -1 && PyErr_Occurred()) + return NULL; + + if (value != 0UL) + return raiseTestError("test_unsigned_long_and_overflow", + "return value incorrect"); + if (overflow) + return raiseTestError("test_unsigned_long_and_overflow", + "overflow was incorrectly set; should be zero"); + + Py_INCREF(Py_None); + return Py_None; +} + /* Test the PyLong_As{Size,Ssize}_t API. At present this just tests that non-integer arguments are handled correctly. It should be extended to test overflow handling. @@ -3018,6 +3118,8 @@ {"test_incref_decref_API", (PyCFunction)test_incref_decref_API, METH_NOARGS}, {"test_long_and_overflow", (PyCFunction)test_long_and_overflow, METH_NOARGS}, + {"test_unsigned_long_and_overflow", (PyCFunction)test_unsigned_long_and_overflow, + METH_NOARGS}, {"test_long_as_double", (PyCFunction)test_long_as_double,METH_NOARGS}, {"test_long_as_size_t", (PyCFunction)test_long_as_size_t,METH_NOARGS}, {"test_long_numbits", (PyCFunction)test_long_numbits, METH_NOARGS}, diff -r 17d64eb0636a Objects/longobject.c --- a/Objects/longobject.c Mon Apr 14 11:48:29 2014 -0400 +++ b/Objects/longobject.c Mon Apr 14 16:44:52 2014 -0400 @@ -555,15 +555,22 @@ } /* Get a C unsigned long int from an int object. - Returns -1 and sets an error condition if overflow occurs. */ + + On overflow, return -1 and set *overflow to 1 or -1 depending + on the sign of the result. Otherwise *overflow is 0. + + For other errors (e.g., TypeError), return -1 and set and error + condition. In this case *overflow will be 0. +*/ unsigned long -PyLong_AsUnsignedLong(PyObject *vv) +PyLong_AsUnsignedLongAndOverflow(PyObject *vv, int *overflow) { PyLongObject *v; unsigned long x, prev; Py_ssize_t i; + *overflow = 0; if (vv == NULL) { PyErr_BadInternalCall(); return (unsigned long)-1; @@ -577,9 +584,8 @@ i = Py_SIZE(v); x = 0; if (i < 0) { - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to unsigned int"); - return (unsigned long) -1; + *overflow = -1; + return (unsigned long)-1; } switch (i) { case 0: return 0; @@ -589,15 +595,33 @@ prev = x; x = (x << PyLong_SHIFT) | v->ob_digit[i]; if ((x >> PyLong_SHIFT) != prev) { - PyErr_SetString(PyExc_OverflowError, - "Python int too large to convert " - "to C unsigned long"); - return (unsigned long) -1; + *overflow = 1; + return (unsigned long)-1; } } return x; } +/* Get a C unsigned long int from an int object. + Returns -1 and sets an error condition if overflow occurs. */ + +unsigned long +PyLong_AsUnsignedLong(PyObject *obj) +{ + int overflow; + unsigned long result = PyLong_AsUnsignedLongAndOverflow(obj, &overflow); + if (overflow == -1) { + PyErr_SetString(PyExc_OverflowError, + "can't convert negative value to unsigned int"); + } + else if (overflow == 1) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert " + "to C unsigned long"); + } + return result; +} + /* Get a C size_t from an int object. Returns (size_t)-1 and sets an error condition if overflow occurs. */