25 Feb 2008
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()
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 (
long – and a type for arbitrary-length integers, which is called
bignum would be unnecessarily clear.
Any number that can not be represented by an
int is automatically handled as a
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 (
int) can represent
0x7FFFFFFF, while a 32-bit unsigned integer (
unsigned long) can represent
When working on images with an alpha (transparency) channel (
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.
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.)