[Python-checkins] cpython (3.4): Issue #16840: Tkinter now supports 64-bit integers added in Tcl 8.4 and

serhiy.storchaka python-checkins at python.org
Thu Apr 2 17:49:41 CEST 2015


https://hg.python.org/cpython/rev/a6f4d8fa7ab8
changeset:   95390:a6f4d8fa7ab8
branch:      3.4
parent:      95387:436bb7ad6349
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Thu Apr 02 18:46:50 2015 +0300
summary:
  Issue #16840: Tkinter now supports 64-bit integers added in Tcl 8.4 and
arbitrary precision integers added in Tcl 8.5.

files:
  Lib/test/test_tcl.py |   46 +++++-
  Misc/NEWS            |    3 +
  Modules/_tkinter.c   |  222 ++++++++++++++++++++++++++++--
  3 files changed, 248 insertions(+), 23 deletions(-)


diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py
--- a/Lib/test/test_tcl.py
+++ b/Lib/test/test_tcl.py
@@ -133,9 +133,22 @@
         tcl = self.interp
         self.assertRaises(TclError,tcl.unsetvar,'a')
 
+    def get_integers(self):
+        integers = (0, 1, -1, 2**31-1, -2**31)
+        if tcl_version >= (8, 4):  # wideInt was added in Tcl 8.4
+            integers += (2**31, -2**31-1, 2**63-1, -2**63)
+        if tcl_version >= (8, 5):  # bignum was added in Tcl 8.5
+            integers += (2**63, -2**63-1, 2**1000, -2**1000)
+        return integers
+
     def test_getint(self):
         tcl = self.interp.tk
-        self.assertEqual(tcl.getint(' 42 '), 42)
+        for i in self.get_integers():
+            self.assertEqual(tcl.getint(' %d ' % i), i)
+            self.assertEqual(tcl.getint(' %#o ' % i), i)
+            self.assertEqual(tcl.getint(' %#x ' % i), i)
+        if tcl_version < (8, 5):  # bignum was added in Tcl 8.5
+            self.assertRaises(TclError, tcl.getint, str(2**1000))
         self.assertEqual(tcl.getint(42), 42)
         self.assertRaises(TypeError, tcl.getint)
         self.assertRaises(TypeError, tcl.getint, '42', '10')
@@ -270,7 +283,7 @@
         check('"a\xbd\u20ac"', 'a\xbd\u20ac')
         check(r'"a\xbd\u20ac"', 'a\xbd\u20ac')
         check(r'"a\0b"', 'a\x00b')
-        if tcl_version >= (8, 5):
+        if tcl_version >= (8, 5):  # bignum was added in Tcl 8.5
             check('2**64', str(2**64))
 
     def test_exprdouble(self):
@@ -302,7 +315,7 @@
         check('[string length "a\xbd\u20ac"]', 3.0)
         check(r'[string length "a\xbd\u20ac"]', 3.0)
         self.assertRaises(TclError, tcl.exprdouble, '"abc"')
-        if tcl_version >= (8, 5):
+        if tcl_version >= (8, 5):  # bignum was added in Tcl 8.5
             check('2**64', float(2**64))
 
     def test_exprlong(self):
@@ -334,7 +347,7 @@
         check('[string length "a\xbd\u20ac"]', 3)
         check(r'[string length "a\xbd\u20ac"]', 3)
         self.assertRaises(TclError, tcl.exprlong, '"abc"')
-        if tcl_version >= (8, 5):
+        if tcl_version >= (8, 5):  # bignum was added in Tcl 8.5
             self.assertRaises(TclError, tcl.exprlong, '2**64')
 
     def test_exprboolean(self):
@@ -375,7 +388,7 @@
         check('[string length "a\xbd\u20ac"]', True)
         check(r'[string length "a\xbd\u20ac"]', True)
         self.assertRaises(TclError, tcl.exprboolean, '"abc"')
-        if tcl_version >= (8, 5):
+        if tcl_version >= (8, 5):  # bignum was added in Tcl 8.5
             check('2**64', True)
 
     def test_booleans(self):
@@ -397,6 +410,21 @@
         check('1 < 2', True)
         check('1 > 2', False)
 
+    def test_expr_bignum(self):
+        tcl = self.interp
+        for i in self.get_integers():
+            result = tcl.call('expr', str(i))
+            if self.wantobjects:
+                self.assertEqual(result, i)
+                self.assertIsInstance(result, int)
+            else:
+                self.assertEqual(result, str(i))
+                self.assertIsInstance(result, str)
+        if tcl_version < (8, 5):  # bignum was added in Tcl 8.5
+            result = tcl.call('expr', str(2**1000))
+            self.assertEqual(result, str(2**1000))
+            self.assertIsInstance(result, str)
+
     def test_passing_values(self):
         def passValue(value):
             return self.interp.call('set', '_', value)
@@ -414,8 +442,10 @@
                          b'str\xc0\x80ing' if self.wantobjects else 'str\xc0\x80ing')
         self.assertEqual(passValue(b'str\xbding'),
                          b'str\xbding' if self.wantobjects else 'str\xbding')
-        for i in (0, 1, -1, 2**31-1, -2**31):
+        for i in self.get_integers():
             self.assertEqual(passValue(i), i if self.wantobjects else str(i))
+        if tcl_version < (8, 5):  # bignum was added in Tcl 8.5
+            self.assertEqual(passValue(2**1000), str(2**1000))
         for f in (0.0, 1.0, -1.0, 1/3,
                   sys.float_info.min, sys.float_info.max,
                   -sys.float_info.min, -sys.float_info.max):
@@ -473,8 +503,10 @@
         check(b'str\x00ing', 'str\x00ing')
         check(b'str\xc0\x80ing', 'str\xc0\x80ing')
         check(b'str\xc0\x80ing\xe2\x82\xac', 'str\xc0\x80ing\xe2\x82\xac')
-        for i in (0, 1, -1, 2**31-1, -2**31):
+        for i in self.get_integers():
             check(i, str(i))
+        if tcl_version < (8, 5):  # bignum was added in Tcl 8.5
+            check(2**1000, str(2**1000))
         for f in (0.0, 1.0, -1.0):
             check(f, repr(f))
         for f in (1/3.0, sys.float_info.min, sys.float_info.max,
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -24,6 +24,9 @@
 Library
 -------
 
+- Issue #16840: Tkinter now supports 64-bit integers added in Tcl 8.4 and
+  arbitrary precision integers added in Tcl 8.5.
+
 - Issue #23834: Fix socket.sendto(), use the C Py_ssize_t type to store the
   result of sendto() instead of the C int type.
 
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -69,6 +69,11 @@
 #error "Tk older than 8.3.1 not supported"
 #endif
 
+#if TK_VERSION_HEX >= 0x08050000
+#define HAVE_LIBTOMMAMTH
+#include <tclTomMath.h>
+#endif
+
 #if !(defined(MS_WINDOWS) || defined(__CYGWIN__))
 #define HAVE_CREATEFILEHANDLER
 #endif
@@ -247,6 +252,8 @@
     const Tcl_ObjType *ByteArrayType;
     const Tcl_ObjType *DoubleType;
     const Tcl_ObjType *IntType;
+    const Tcl_ObjType *WideIntType;
+    const Tcl_ObjType *BignumType;
     const Tcl_ObjType *ListType;
     const Tcl_ObjType *ProcBodyType;
     const Tcl_ObjType *StringType;
@@ -583,6 +590,8 @@
     v->ByteArrayType = Tcl_GetObjType("bytearray");
     v->DoubleType = Tcl_GetObjType("double");
     v->IntType = Tcl_GetObjType("int");
+    v->WideIntType = Tcl_GetObjType("wideInt");
+    v->BignumType = Tcl_GetObjType("bignum");
     v->ListType = Tcl_GetObjType("list");
     v->ProcBodyType = Tcl_GetObjType("procbody");
     v->StringType = Tcl_GetObjType("string");
@@ -875,28 +884,92 @@
 #define CHECK_STRING_LENGTH(s)
 #endif
 
+#ifdef HAVE_LIBTOMMAMTH
+static Tcl_Obj*
+asBignumObj(PyObject *value)
+{
+    Tcl_Obj *result;
+    int neg;
+    PyObject *hexstr;
+    char *hexchars;
+    mp_int bigValue;
+
+    neg = Py_SIZE(value) < 0;
+    hexstr = _PyLong_Format(value, 16);
+    if (hexstr == NULL)
+        return NULL;
+    hexchars = PyUnicode_AsUTF8(hexstr);
+    if (hexchars == NULL) {
+        Py_DECREF(hexstr);
+        return NULL;
+    }
+    hexchars += neg + 2; /* skip sign and "0x" */
+    mp_init(&bigValue);
+    if (mp_read_radix(&bigValue, hexchars, 16) != MP_OKAY) {
+        mp_clear(&bigValue);
+        Py_DECREF(hexstr);
+        PyErr_NoMemory();
+        return NULL;
+    }
+    Py_DECREF(hexstr);
+    bigValue.sign = neg ? MP_NEG : MP_ZPOS;
+    result = Tcl_NewBignumObj(&bigValue);
+    mp_clear(&bigValue);
+    if (result == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+    return result;
+}
+#endif
+
 static Tcl_Obj*
 AsObj(PyObject *value)
 {
     Tcl_Obj *result;
-    long longVal;
-    int overflow;
 
     if (PyBytes_Check(value))
         return Tcl_NewByteArrayObj((unsigned char *)PyBytes_AS_STRING(value),
                                    PyBytes_GET_SIZE(value));
-    else if (PyBool_Check(value))
+
+    if (PyBool_Check(value))
         return Tcl_NewBooleanObj(PyObject_IsTrue(value));
-    else if (PyLong_CheckExact(value) &&
-             ((longVal = PyLong_AsLongAndOverflow(value, &overflow)),
-              !overflow)) {
+
+    if (PyLong_CheckExact(value)) {
+        int overflow;
+        long longValue;
+#ifdef TCL_WIDE_INT_TYPE
+        Tcl_WideInt wideValue;
+#endif
+        longValue = PyLong_AsLongAndOverflow(value, &overflow);
+        if (!overflow) {
+            return Tcl_NewLongObj(longValue);
+        }
         /* If there is an overflow in the long conversion,
+           fall through to wideInt handling. */
+#ifdef TCL_WIDE_INT_TYPE
+        if (_PyLong_AsByteArray((PyLongObject *)value,
+                                (unsigned char *)(void *)&wideValue,
+                                sizeof(wideValue),
+                                PY_LITTLE_ENDIAN,
+                                /* signed */ 1) == 0) {
+            return Tcl_NewWideIntObj(wideValue);
+        }
+        PyErr_Clear();
+#endif
+        /* If there is an overflow in the wideInt conversion,
+           fall through to bignum handling. */
+#ifdef HAVE_LIBTOMMAMTH
+        return asBignumObj(value);
+#endif
+        /* If there is no wideInt or bignum support,
            fall through to default object handling. */
-        return Tcl_NewLongObj(longVal);
     }
-    else if (PyFloat_Check(value))
+
+    if (PyFloat_Check(value))
         return Tcl_NewDoubleObj(PyFloat_AS_DOUBLE(value));
-    else if (PyTuple_Check(value)) {
+
+    if (PyTuple_Check(value)) {
         Tcl_Obj **argv;
         Py_ssize_t size, i;
 
@@ -916,7 +989,8 @@
         ckfree(FREECAST argv);
         return result;
     }
-    else if (PyUnicode_Check(value)) {
+
+    if (PyUnicode_Check(value)) {
         void *inbuf;
         Py_ssize_t size;
         int kind;
@@ -966,12 +1040,14 @@
         ckfree(FREECAST outbuf);
         return result;
     }
-    else if(PyTclObject_Check(value)) {
+
+    if (PyTclObject_Check(value)) {
         Tcl_Obj *v = ((PyTclObject*)value)->value;
         Tcl_IncrRefCount(v);
         return v;
     }
-    else {
+
+    {
         PyObject *v = PyObject_Str(value);
         if (!v)
             return 0;
@@ -990,6 +1066,62 @@
     return PyBool_FromLong(boolValue);
 }
 
+#ifdef TCL_WIDE_INT_TYPE
+static PyObject*
+fromWideIntObj(PyObject* tkapp, Tcl_Obj *value)
+{
+        Tcl_WideInt wideValue;
+        if (Tcl_GetWideIntFromObj(Tkapp_Interp(tkapp), value, &wideValue) == TCL_OK) {
+#ifdef HAVE_LONG_LONG
+            if (sizeof(wideValue) <= SIZEOF_LONG_LONG)
+                return PyLong_FromLongLong(wideValue);
+#endif
+            return _PyLong_FromByteArray((unsigned char *)(void *)&wideValue,
+                                         sizeof(wideValue),
+                                         PY_LITTLE_ENDIAN,
+                                         /* signed */ 1);
+        }
+        return NULL;
+}
+#endif
+
+#ifdef HAVE_LIBTOMMAMTH
+static PyObject*
+fromBignumObj(PyObject* tkapp, Tcl_Obj *value)
+{
+    mp_int bigValue;
+    unsigned long numBytes;
+    unsigned char *bytes;
+    PyObject *res;
+
+    if (Tcl_GetBignumFromObj(Tkapp_Interp(tkapp), value, &bigValue) != TCL_OK)
+        return Tkinter_Error(tkapp);
+    numBytes = mp_unsigned_bin_size(&bigValue);
+    bytes = PyMem_Malloc(numBytes);
+    if (bytes == NULL) {
+        mp_clear(&bigValue);
+        return PyErr_NoMemory();
+    }
+    if (mp_to_unsigned_bin_n(&bigValue, bytes,
+                                &numBytes) != MP_OKAY) {
+        mp_clear(&bigValue);
+        PyMem_Free(bytes);
+        return PyErr_NoMemory();
+    }
+    res = _PyLong_FromByteArray(bytes, numBytes,
+                                /* big-endian */ 0,
+                                /* unsigned */ 0);
+    PyMem_Free(bytes);
+    if (res != NULL && bigValue.sign == MP_NEG) {
+        PyObject *res2 = PyNumber_Negative(res);
+        Py_DECREF(res);
+        res = res2;
+    }
+    mp_clear(&bigValue);
+    return res;
+}
+#endif
+
 static PyObject*
 FromObj(PyObject* tkapp, Tcl_Obj *value)
 {
@@ -1017,9 +1149,33 @@
     }
 
     if (value->typePtr == app->IntType) {
-        return PyLong_FromLong(value->internalRep.longValue);
+        long longValue;
+        if (Tcl_GetLongFromObj(interp, value, &longValue) == TCL_OK)
+            return PyLong_FromLong(longValue);
+        /* If there is an error in the long conversion,
+           fall through to wideInt handling. */
     }
 
+#ifdef TCL_WIDE_INT_TYPE
+    if (value->typePtr == app->IntType ||
+        value->typePtr == app->WideIntType) {
+        result = fromWideIntObj(tkapp, value);
+        if (result != NULL || PyErr_Occurred())
+            return result;
+        Tcl_ResetResult(interp);
+        /* If there is an error in the wideInt conversion,
+           fall through to bignum handling. */
+    }
+#endif
+
+#ifdef HAVE_LIBTOMMAMTH
+    if (value->typePtr == app->IntType ||
+        value->typePtr == app->WideIntType ||
+        value->typePtr == app->BignumType) {
+        return fromBignumObj(tkapp, value);
+    }
+#endif
+
     if (value->typePtr == app->ListType) {
         int size;
         int i, status;
@@ -1067,6 +1223,15 @@
     }
 #endif
 
+#ifdef HAVE_LIBTOMMAMTH
+    if (app->BignumType == NULL &&
+        strcmp(value->typePtr->name, "bignum") == 0) {
+        /* bignum type is not registered in Tcl */
+        app->BignumType = value->typePtr;
+        return fromBignumObj(tkapp, value);
+    }
+#endif
+
     return newPyTclObject(value);
 }
 
@@ -1700,7 +1865,12 @@
 Tkapp_GetInt(PyObject *self, PyObject *args)
 {
     char *s;
-    int v;
+#if defined(TCL_WIDE_INT_TYPE) || defined(HAVE_LIBTOMMAMTH)
+    Tcl_Obj *value;
+    PyObject *result;
+#else
+    int intValue;
+#endif
 
     if (PyTuple_Size(args) == 1) {
         PyObject* o = PyTuple_GetItem(args, 0);
@@ -1712,9 +1882,29 @@
     if (!PyArg_ParseTuple(args, "s:getint", &s))
         return NULL;
     CHECK_STRING_LENGTH(s);
-    if (Tcl_GetInt(Tkapp_Interp(self), s, &v) == TCL_ERROR)
+#if defined(TCL_WIDE_INT_TYPE) || defined(HAVE_LIBTOMMAMTH)
+    value = Tcl_NewStringObj(s, -1);
+    if (value == NULL)
         return Tkinter_Error(self);
-    return Py_BuildValue("i", v);
+    /* Don't use Tcl_GetInt() because it returns ambiguous result for value
+       in ranges -2**32..-2**31-1 and 2**31..2**32-1 (on 32-bit platform).
+
+       Prefer bignum because Tcl_GetWideIntFromObj returns ambiguous result for
+       value in ranges -2**64..-2**63-1 and 2**63..2**64-1 (on 32-bit platform).
+     */
+#ifdef HAVE_LIBTOMMAMTH
+    result = fromBignumObj(self, value);
+#else
+    result = fromWideIntObj(self, value);
+#endif
+    Tcl_DecrRefCount(value);
+    if (result != NULL || PyErr_Occurred())
+        return result;
+#else
+    if (Tcl_GetInt(Tkapp_Interp(self), s, &intValue) == TCL_OK)
+        return PyLong_FromLong(intValue);
+#endif
+    return Tkinter_Error(self);
 }
 
 static PyObject *

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list