[Python-checkins] cpython (merge 3.4 -> default): 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/9291b28157e1
changeset: 95391:9291b28157e1
parent: 95388:a064abfd436c
parent: 95390:a6f4d8fa7ab8
user: Serhiy Storchaka <storchaka at gmail.com>
date: Thu Apr 02 18:49:14 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 | 44 +++++-
 Misc/NEWS | 3 +
 Modules/_tkinter.c | 209 ++++++++++++++++++++++++++++--
 3 files changed, 233 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,20 @@
 tcl = self.interp
 self.assertRaises(TclError,tcl.unsetvar,'a')
 
+ def get_integers(self):
+ integers = (0, 1, -1, 2**31-1, -2**31, 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 +281,7 @@
 check('"a\xbd\u20ac"', 'a\xbd\u20ac')
 check(r'"a\xbd\u20ac"', 'a\xbd\u20ac')
 check(r'"a0円b"', '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 +313,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 +345,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 +386,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 +408,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 +440,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):
@@ -475,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
@@ -16,6 +16,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
@@ -56,6 +56,11 @@
 #error "Tk older than 8.4 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
@@ -234,6 +239,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;
@@ -591,6 +598,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");
@@ -883,12 +892,49 @@
 #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)) {
 if (PyBytes_GET_SIZE(value) >= INT_MAX) {
@@ -898,18 +944,45 @@
 return Tcl_NewByteArrayObj((unsigned char *)PyBytes_AS_STRING(value),
 (int)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) || PyList_Check(value)) {
+
+ if (PyTuple_Check(value) || PyList_Check(value)) {
 Tcl_Obj **argv;
 Py_ssize_t size, i;
 
@@ -933,7 +1006,8 @@
 PyMem_Free(argv);
 return result;
 }
- else if (PyUnicode_Check(value)) {
+
+ if (PyUnicode_Check(value)) {
 void *inbuf;
 Py_ssize_t size;
 int kind;
@@ -983,12 +1057,14 @@
 PyMem_Free(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;
@@ -1008,6 +1084,60 @@
 }
 
 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;
+}
+
+#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)
 {
 PyObject *result = NULL;
@@ -1034,9 +1164,31 @@
 }
 
 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. */
 }
 
+ 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. */
+ }
+
+#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;
@@ -1084,6 +1236,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);
 }
 
@@ -1718,7 +1879,8 @@
 Tkapp_GetInt(PyObject *self, PyObject *args)
 {
 char *s;
- int v;
+ Tcl_Obj *value;
+ PyObject *result;
 
 if (PyTuple_Size(args) == 1) {
 PyObject* o = PyTuple_GetItem(args, 0);
@@ -1730,9 +1892,24 @@
 if (!PyArg_ParseTuple(args, "s:getint", &s))
 return NULL;
 CHECK_STRING_LENGTH(s);
- if (Tcl_GetInt(Tkapp_Interp(self), s, &v) == TCL_ERROR)
+ 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;
+ return Tkinter_Error(self);
 }
 
 static PyObject *
-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /