Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImageTk int overflow error #1902

Closed
vlachoudis opened this issue May 10, 2016 · 4 comments
Closed

ImageTk int overflow error #1902

vlachoudis opened this issue May 10, 2016 · 4 comments
Assignees
Labels
Bug Any unexpected behavior, until confirmed feature.

Comments

@vlachoudis
Copy link

I am running on a 32bit Fedora-23 virtual machine via virtualbox, version 3.0.0-4
When I try to convert an image to tkimage I get an overflow error
since the tk.interpaddr() returns a 64bit address which cannot be converted to int.

The same code in 64bit Fedora-23 save version works ok.

File "/usr/local/flair/lib/ZoomImage.py", line 162, in zoomImage
    self.tkimage = ImageTk.PhotoImage(self.photo)
  File "/usr/lib/python2.7/site-packages/PIL/ImageTk.py", line 115, in __init__
    self.paste(image)
  File "/usr/lib/python2.7/site-packages/PIL/ImageTk.py", line 182, in paste
    _imagingtk.tkinit(tk.interpaddr(), 1)
OverflowError: Python int too large to convert to C long
>>> from Tkinter import *
>>> tk=Tk()
>>> tk.interpaddr()
2161299816L
@wiredfool
Copy link
Member

wiredfool commented May 10, 2016

It's not actually a 64 bit address, but it is in the upper 2gb of a 32 bit address. Which is too big to fit into a Py_ssize_t on 32 bit, which is limited to 2^31.

The value is actually a PyInt_FromLong, from _tkinter.c:

static PyObject *
Tkapp_InterpAddr(PyObject *self, PyObject *args)
{

    if (!PyArg_ParseTuple(args, ":interpaddr"))
        return NULL;

    return PyInt_FromLong((long)Tkapp_Interp(self));
}

Which is interpreted as a an int->Py_ssize_t in _imagingtk.c

static PyObject*
_tkinit(PyObject* self, PyObject* args)
{
    Tcl_Interp* interp;

    Py_ssize_t arg;
    int is_interp;
    if (!PyArg_ParseTuple(args, "ni", &arg, &is_interp))
        return NULL;

    if (is_interp)
        interp = (Tcl_Interp*) arg;
    else {
        TkappObject* app;
    /* Do it the hard way.  This will break if the TkappObject
       layout changes */
        app = (TkappObject*) arg;
        interp = app->interp;
    }

Given the evidence here, I don't think that it's actually possible to reliably (in all cases, with all corners of 32/64 signed/unsigned silliness) pass a pointer through as an interpreted int. It can be passed as a PyInt, and then it needs to be interpreted as something like:

    void *arg;
    PyObject *int_obj;
    if (!PyArg_ParseTuple(args, "Oi", &int_obj, &is_interp)){
        return NULL;
    }

    if (PyInt_Check(int_obj)) {
        if (sizeof(arg) == 4) {
            arg =  PyInt_AsUnsignedLongMask(int_obj); 
        } elseif (sizeof(arg) == 8) {
            arg =  PyInt_AsUnsignedLongLongMask(int_obj);
        } else {
            // barf loudly?
            return NULL;
        }
     } else {
         // set error here
        return NULL;
     }


@wiredfool
Copy link
Member

@wiredfool wiredfool self-assigned this Jan 9, 2017
@vlachoudis
Copy link
Author

I had a similar issue in one program of mine, and I solve it by changing the ParseTuple as

	unsigned long interpaddr;
...
	if (!PyArg_ParseTuple(args, "Osk", &geometry, &winname, &interpaddr)) return -1;
        Tcl_Interp *interp = (Tcl_Interp*)interpaddr;

and it works nicely in both 32bit and 64bit environments.

@wiredfool
Copy link
Member

I don't think that is valid in a 64 bit environment because a pointer doesn't fit into an unsigned long.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Any unexpected behavior, until confirmed feature.
Projects
None yet
Development

No branches or pull requests

2 participants