[Python-checkins] r76963 - in python/trunk: Doc/c-api/long.rst Include/longobject.h Misc/NEWS Modules/_testcapimodule.c Objects/longobject.c

mark.dickinson python-checkins at python.org
Mon Dec 21 12:21:26 CET 2009


Author: mark.dickinson
Date: Mon Dec 21 12:21:25 2009
New Revision: 76963

Log:
Issue #7528:  Backport PyLong_AsLongAndOverflow from py3k to trunk.
Thanks Case Van Horsen for the patch.


Modified:
   python/trunk/Doc/c-api/long.rst
   python/trunk/Include/longobject.h
   python/trunk/Misc/NEWS
   python/trunk/Modules/_testcapimodule.c
   python/trunk/Objects/longobject.c

Modified: python/trunk/Doc/c-api/long.rst
==============================================================================
--- python/trunk/Doc/c-api/long.rst	(original)
+++ python/trunk/Doc/c-api/long.rst	Mon Dec 21 12:21:25 2009
@@ -133,6 +133,19 @@
    and ``-1`` will be returned.
 
 
+.. cfunction:: long PyLong_AsLongAndOverflow(PyObject *pylong, int* overflow)
+
+   Return a C :ctype:`long` representation of the contents of
+   *pylong*.  If *pylong* is greater than :const:`LONG_MAX` or less
+   than :const:`LONG_MIN`, set `*overflow` to ``1`` or ``-1``,
+   respectively, and return ``-1``; otherwise, set `*overflow` to
+   ``0``.  If any other exception occurs (for example a TypeError or
+   MemoryError), then ``-1`` will be returned and ``*overflow`` will
+   be ``0``.
+
+   .. versionadded:: 2.7
+
+
 .. cfunction:: Py_ssize_t PyLong_AsSsize_t(PyObject *pylong)
 
    .. index::

Modified: python/trunk/Include/longobject.h
==============================================================================
--- python/trunk/Include/longobject.h	(original)
+++ python/trunk/Include/longobject.h	Mon Dec 21 12:21:25 2009
@@ -21,6 +21,7 @@
 PyAPI_FUNC(PyObject *) PyLong_FromSize_t(size_t);
 PyAPI_FUNC(PyObject *) PyLong_FromSsize_t(Py_ssize_t);
 PyAPI_FUNC(long) PyLong_AsLong(PyObject *);
+PyAPI_FUNC(long) PyLong_AsLongAndOverflow(PyObject *, int *);
 PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *);
 PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *);
 PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *);

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Mon Dec 21 12:21:25 2009
@@ -1648,6 +1648,8 @@
 C-API
 -----
 
+- Issue #7528: Add PyLong_AsLongAndOverflow (backported from py3k).
+
 - Issue #7228: Add '%lld' and '%llu' support to PyString_FromFormat(V)
   and PyErr_Format, on machines with HAVE_LONG_LONG defined.
 

Modified: python/trunk/Modules/_testcapimodule.c
==============================================================================
--- python/trunk/Modules/_testcapimodule.c	(original)
+++ python/trunk/Modules/_testcapimodule.c	Mon Dec 21 12:21:25 2009
@@ -358,6 +358,75 @@
 #undef F_U_TO_PY
 #undef F_PY_TO_U
 
+/* Test the PyLong_AsLongAndOverflow API. General conversion to PY_LONG
+   is tested by test_long_api_inner. This test will concentrate on proper
+   handling of overflow.
+*/
+
+static PyObject *
+test_long_and_overflow(PyObject *self)
+{
+	PyObject *num;
+	long value;
+	int overflow;
+
+	/* a number larger than LONG_MAX even on 64-bit platforms */
+	num = PyLong_FromString("FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16);
+	if (num == NULL)
+		return NULL;
+
+	/* Test that overflow is set properly for a large value. */
+	overflow = 1234;
+	value = PyLong_AsLongAndOverflow(num, &overflow);
+	if (overflow != 1)
+		return raiseTestError("test_long_and_overflow",
+			"overflow was not set to 1");
+
+	overflow = 0;
+	value = PyLong_AsLongAndOverflow(num, &overflow);
+	if (overflow != 1)
+		return raiseTestError("test_long_and_overflow",
+			"overflow was not set to 0");
+
+	/* a number smaller than LONG_MIN even on 64-bit platforms */
+	num = PyLong_FromString("-FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16);
+	if (num == NULL)
+		return NULL;
+
+	/* Test that overflow is set properly for a large negative value. */
+	overflow = 1234;
+	value = PyLong_AsLongAndOverflow(num, &overflow);
+	if (overflow != -1)
+		return raiseTestError("test_long_and_overflow",
+			"overflow was not set to -1");
+
+	overflow = 0;
+	value = PyLong_AsLongAndOverflow(num, &overflow);
+	if (overflow != -1)
+		return raiseTestError("test_long_and_overflow",
+			"overflow was not set to 0");
+
+	num = PyLong_FromString("FF", NULL, 16);
+	if (num == NULL)
+		return NULL;
+
+	/* Test that overflow is cleared properly for a small value. */
+	overflow = 1234;
+	value = PyLong_AsLongAndOverflow(num, &overflow);
+	if (overflow != 0)
+		return raiseTestError("test_long_and_overflow",
+			"overflow was not cleared");
+
+	overflow = 0;
+	value = PyLong_AsLongAndOverflow(num, &overflow);
+	if (overflow != 0)
+		return raiseTestError("test_long_and_overflow",
+			"overflow was set incorrectly");
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
 /* Test the L code for PyArg_ParseTuple.  This should deliver a PY_LONG_LONG
    for both long and int arguments.  The test may leak a little memory if
    it fails.
@@ -1041,6 +1110,7 @@
 	{"test_dict_iteration",	(PyCFunction)test_dict_iteration,METH_NOARGS},
 	{"test_lazy_hash_inheritance",	(PyCFunction)test_lazy_hash_inheritance,METH_NOARGS},
 	{"test_long_api",	(PyCFunction)test_long_api,	 METH_NOARGS},
+	{"test_long_and_overflow",	(PyCFunction)test_long_and_overflow,	METH_NOARGS},
 	{"test_long_numbits",	(PyCFunction)test_long_numbits,	 METH_NOARGS},
 	{"test_k_code",		(PyCFunction)test_k_code,	 METH_NOARGS},
 	{"test_empty_argparse", (PyCFunction)test_empty_argparse,METH_NOARGS},

Modified: python/trunk/Objects/longobject.c
==============================================================================
--- python/trunk/Objects/longobject.c	(original)
+++ python/trunk/Objects/longobject.c	Mon Dec 21 12:21:25 2009
@@ -223,53 +223,123 @@
 #define PY_ABS_LONG_MIN		(0-(unsigned long)LONG_MIN)
 #define PY_ABS_SSIZE_T_MIN	(0-(size_t)PY_SSIZE_T_MIN)
 
-/* Get a C long int from a long int object.
-   Returns -1 and sets an error condition if overflow occurs. */
+/* Get a C long int from a Python long or Python int object.
+   On overflow, returns -1 and sets *overflow to 1 or -1 depending
+   on the sign of the result.  Otherwise *overflow is 0.
+
+   For other errors (e.g., type error), returns -1 and sets an error
+   condition.
+*/
 
 long
-PyLong_AsLong(PyObject *vv)
+PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
 {
 	/* This version by Tim Peters */
 	register PyLongObject *v;
 	unsigned long x, prev;
+	long res;
 	Py_ssize_t i;
 	int sign;
+	int do_decref = 0; /* if nb_int was called */
 
-	if (vv == NULL || !PyLong_Check(vv)) {
-		if (vv != NULL && PyInt_Check(vv))
-			return PyInt_AsLong(vv);
+	*overflow = 0;
+	if (vv == NULL) {
 		PyErr_BadInternalCall();
 		return -1;
 	}
-	v = (PyLongObject *)vv;
-	i = v->ob_size;
-	sign = 1;
-	x = 0;
-	if (i < 0) {
-		sign = -1;
-		i = -(i);
-	}
-	while (--i >= 0) {
-		prev = x;
-		x = (x << PyLong_SHIFT) | v->ob_digit[i];
-		if ((x >> PyLong_SHIFT) != prev)
-			goto overflow;
+
+	if(PyInt_Check(vv))
+		return PyInt_AsLong(vv);
+
+	if (!PyLong_Check(vv)) {
+		PyNumberMethods *nb;
+		nb = vv->ob_type->tp_as_number;
+		if (nb == NULL || nb->nb_int == NULL) {
+			PyErr_SetString(PyExc_TypeError,
+					"an integer is required");
+			return -1;
+		}
+		vv = (*nb->nb_int) (vv);
+		if (vv == NULL)
+			return -1;
+		do_decref = 1;
+		if(PyInt_Check(vv)) {
+			res = PyInt_AsLong(vv);
+			goto exit;
+		}
+		if (!PyLong_Check(vv)) {
+			Py_DECREF(vv);
+			PyErr_SetString(PyExc_TypeError,
+					"nb_int should return int object");
+			return -1;
+		}
 	}
-	/* Haven't lost any bits, but casting to long requires extra care
-	 * (see comment above).
-         */
-	if (x <= (unsigned long)LONG_MAX) {
-		return (long)x * sign;
+
+	res = -1;
+	v = (PyLongObject *)vv;
+	i = Py_SIZE(v);
+
+	switch (i) {
+	case -1:
+		res = -(sdigit)v->ob_digit[0];
+		break;
+	case 0:
+		res = 0;
+		break;
+	case 1:
+		res = v->ob_digit[0];
+		break;
+	default:
+		sign = 1;
+		x = 0;
+		if (i < 0) {
+			sign = -1;
+			i = -(i);
+		}
+		while (--i >= 0) {
+			prev = x;
+			x = (x << PyLong_SHIFT) + v->ob_digit[i];
+			if ((x >> PyLong_SHIFT) != prev) {
+				*overflow = sign;
+				goto exit;
+			}
+		}
+		/* Haven't lost any bits, but casting to long requires extra
+		 * care (see comment above).
+		 */
+		if (x <= (unsigned long)LONG_MAX) {
+			res = (long)x * sign;
+		}
+		else if (sign < 0 && x == PY_ABS_LONG_MIN) {
+			res = LONG_MIN;
+		}
+		else {
+			*overflow = sign;
+			/* res is already set to -1 */
+		}
 	}
-	else if (sign < 0 && x == PY_ABS_LONG_MIN) {
-		return LONG_MIN;
+ exit:
+	if (do_decref) {
+		Py_DECREF(vv);
 	}
-	/* else overflow */
+	return res;
+}
 
- overflow:
-	PyErr_SetString(PyExc_OverflowError,
-			"long int too large to convert to int");
-	return -1;
+/* Get a C long int from a long int object.
+   Returns -1 and sets an error condition if overflow occurs. */
+
+long
+PyLong_AsLong(PyObject *obj)
+{
+	int overflow;
+	long result = PyLong_AsLongAndOverflow(obj, &overflow);
+	if (overflow) {
+		/* XXX: could be cute and give a different
+		   message for overflow == -1 */
+		PyErr_SetString(PyExc_OverflowError,
+				"Python int too large to convert to C long");
+	}
+	return result;
 }
 
 /* Get a Py_ssize_t from a long int object.


More information about the Python-checkins mailing list