Published on

25 Feb 2008

SystemError when using PIL

When I moved the code powering this site from my 64-bit laptop to my 32-bit server, it failed with the message SystemError: new style getargs format but argument is not a tuple. The problem was with my use of the Python Imaging Library (PIL). This article describes the cause and a workaround.

Consider the following simple script , which attempts to produce an image consisting of one black pixel (all examples are valid for OpenBSD 4.3-beta, Python 2.5, and PIL (“py-Imaging”) 1.1.5):

#!/usr/local/bin/python2.5
import Image

im = Image.new('RGBA', (1, 1))
im.putdata([0xFFFFFFFF])

When using py-Imaging-1.1.5p1 on amd64, the script quietly runs to completion. However, running the same script with py-Imaging-1.1.5p2-python2.5 on i386 results in

Traceback (most recent call last):
  File "/home/joachim/pil.py", line 4, in <module>
    im.putdata([0xFFFFFFFF])
  File "/usr/local/lib/python2.5/site-packages/PIL/Image.py", line 1120, in putdata
    self.im.putdata(data, scale, offset)
SystemError: new style getargs format but argument is not a tuple

The following script does work, printing (255, 255, 255, 255):

#!/usr/local/bin/python2.5
import Image
import sys

def uint(i):
    i = int(i)
    if i > sys.maxint and i <= 2 * sys.maxint + 1:
        return int((i & sys.maxint) - sys.maxint - 1)
    else:
        return i

im = Image.new('RGBA', (1, 1))

im.putdata([uint(0xFFFFFFFF)])
print im.getdata()[0]

If you are in a hurry, try calling the uint() function defined above on color values before passing them into PIL. Works for me on both i386 and amd64, PIL version 1.1.5.

The rest of this article is an explanation of why this function is required and why it works. Credit is due to Bernhard Herzog for this post.

Python has an int type – essentially C’s (signed) long – and a type for arbitrary-length integers, which is called long because bignum would be unnecessarily clear. Any number that can not be represented by an int is automatically handled as a long. This makes sense, and works as documented.

PIL expects a Python int (i.e. a long), and handles this as a 32-byte unsigned integer (i.e. uint32, which is equivalent to unsigned long on i386). However, a 32-bit signed integer (int32, long, Python’s int) can represent -0x80000000 to 0x7FFFFFFF, while a 32-bit unsigned integer (uint32/unsigned long) can represent 0 to 0xFFFFFFFF.

When working on images with an alpha (transparency) channel (RGBA or LA mode), 0xFFFFFFFF is a valid color (opaque black; in LA mode the two middle bytes are ignored). However, if you attempt to directly specify a value above 0x7FFFFFFF, Python automatically converts such values to it’s own long (which, again, should be called bignum) – and PIL does not handle long variables, leading to the error.

The uint() function works around this by creating a signed long that, when interpreted as an unsigned long, has the correct value. Of course, one could say it’s incorrect to pass negative values into PIL – but this has the advantage over the “correct” method of actually working. (One could also point out that this only works on two’s complement machines, but everything that runs Python is.)