From: Serhiy Storchaka Date: Thu, 2 Apr 2015 15:49:14 +0000 (+0300) Subject: Issue #16840: Tkinter now supports 64-bit integers added in Tcl 8.4 and X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4c7dc48ea579164e98c9a399711b4bb1c67d70eb;p=python Issue #16840: Tkinter now supports 64-bit integers added in Tcl 8.4 and arbitrary precision integers added in Tcl 8.5. --- 4c7dc48ea579164e98c9a399711b4bb1c67d70eb diff --cc Lib/test/test_tcl.py index 1e6ac663f9,9b1a8d7701..03f28d4d3e --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@@ -133,9 -133,22 +133,20 @@@ class TclTest(unittest.TestCase) 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) ++ 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') diff --cc Modules/_tkinter.c index 403cd71ba6,6599564b38..29cf23315f --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@@ -52,10 -58,22 +52,15 @@@ Copyright (C) 1994 Steen Lumholt #include "tkinter.h" -/* For Tcl 8.2 and 8.3, CONST* is not defined (except on Cygwin). */ -#ifndef CONST84_RETURN -#define CONST84_RETURN -#undef CONST -#define CONST -#endif - -#if TK_VERSION_HEX < 0x08030102 -#error "Tk older than 8.3.1 not supported" +#if TK_VERSION_HEX < 0x08040002 +#error "Tk older than 8.4 not supported" #endif + #if TK_VERSION_HEX >= 0x08050000 + #define HAVE_LIBTOMMAMTH + #include + #endif + #if !(defined(MS_WINDOWS) || defined(__CYGWIN__)) #define HAVE_CREATEFILEHANDLER #endif @@@ -887,29 -927,49 +935,54 @@@ static Tcl_Obj AsObj(PyObject *value) { Tcl_Obj *result; - long longVal; - int overflow; - if (PyBytes_Check(value)) + if (PyBytes_Check(value)) { + if (PyBytes_GET_SIZE(value) >= INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "bytes object is too long"); + return NULL; + } return Tcl_NewByteArrayObj((unsigned char *)PyBytes_AS_STRING(value), - PyBytes_GET_SIZE(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)) { ++ if (PyTuple_Check(value) || PyList_Check(value)) { Tcl_Obj **argv; Py_ssize_t size, i; @@@ -917,23 -977,20 +990,24 @@@ if (size == 0) return Tcl_NewListObj(0, NULL); if (!CHECK_SIZE(size, sizeof(Tcl_Obj *))) { - PyErr_SetString(PyExc_OverflowError, "tuple is too long"); + PyErr_SetString(PyExc_OverflowError, + PyTuple_Check(value) ? "tuple is too long" : + "list is too long"); return NULL; } - argv = (Tcl_Obj **) attemptckalloc(((size_t)size) * sizeof(Tcl_Obj *)); - if(!argv) - return 0; + argv = (Tcl_Obj **) PyMem_Malloc(((size_t)size) * sizeof(Tcl_Obj *)); + if (!argv) { + PyErr_NoMemory(); + return NULL; + } for (i = 0; i < size; i++) - argv[i] = AsObj(PyTuple_GetItem(value,i)); - result = Tcl_NewListObj(PyTuple_Size(value), argv); - ckfree(FREECAST argv); + argv[i] = AsObj(PySequence_Fast_GET_ITEM(value,i)); + result = Tcl_NewListObj((int)size, argv); + PyMem_Free(argv); return result; } - else if (PyUnicode_Check(value)) { + + if (PyUnicode_Check(value)) { void *inbuf; Py_ssize_t size; int kind; @@@ -979,11 -1036,12 +1053,12 @@@ #endif outbuf[i] = ch; } - result = Tcl_NewUnicodeObj(outbuf, size); - ckfree(FREECAST outbuf); + result = Tcl_NewUnicodeObj(outbuf, (int)size); + 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; @@@ -1007,6 -1066,62 +1083,60 @@@ fromBoolean(PyObject* tkapp, Tcl_Obj *v 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) { @@@ -1034,8 -1149,32 +1164,30 @@@ } 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; @@@ -1718,7 -1865,12 +1879,8 @@@ static PyObject 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); @@@ -1730,9 -1882,29 +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) -#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 *