]> granicus.if.org Git - python/commitdiff
Issue #16840: Tkinter now supports 64-bit integers added in Tcl 8.4 and
authorSerhiy Storchaka <storchaka@gmail.com>
Thu, 2 Apr 2015 15:49:14 +0000 (18:49 +0300)
committerSerhiy Storchaka <storchaka@gmail.com>
Thu, 2 Apr 2015 15:49:14 +0000 (18:49 +0300)
arbitrary precision integers added in Tcl 8.5.

1  2 
Lib/test/test_tcl.py
Misc/NEWS
Modules/_tkinter.c

index 1e6ac663f99a6d02e239e06bfb483378b757bd6e,9b1a8d7701d6836da8ad57a0746a2f25608352e1..03f28d4d3e911f4c354c87f87382556b078ec6d6
@@@ -133,9 -133,22 +133,20 @@@ class TclTest(unittest.TestCase)
          tcl = self.interp
          self.assertRaises(TclError,tcl.unsetvar,'a')
  
 -        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)
+     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')
diff --cc Misc/NEWS
Simple merge
index 403cd71ba629369f0432f2e21b423f7b6353473e,6599564b383f6f24eb0068df59792c85248e9724..29cf23315f642996c7f173742dad2eac7a8f0b88
@@@ -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 <tclTomMath.h>
+ #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;
  
          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;
  #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)
  {
      }
  
      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);
      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 *