Numerous articles have been written about why you want to install Pillow instead of PIL to get the Python Imaging tools. Like Problems with PIL? Use Pillow instead!
(Find more by searching for “IOError: decoder zip not available”.)

This note concerns something more insidious: a seemingly broken Pillow installation after the removal of PIL.

~$ mkvirtualenv piltest
(piltest)~$ pip install PIL
(piltest)~$ pip freeze | grep -i pil
PIL==1.1.7

Now this should work:

(piltest)site-packages$ python -c "import Image; im = Image.open('/lib/plymouth/ubuntu_logo.png'); im.load()"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File ".../site-packages/PIL/ImageFile.py", line 189, in load
    d = Image._getdecoder(self.mode, d, a, self.decoderconfig)
  File ".../site-packages/PIL/Image.py", line 385, in _getdecoder
    raise IOError("decoder %s not available" % decoder_name)
IOError: decoder zip not available

Ok. This still doesn't work. This is why the “Problems with PIL? Use Pillow instead!“ articles exist. So. Time to switch to Pillow.

Let's examine what happens when someone comes along and removes the PIL dir by hand. Perhaps because his pip is so old that it doesn't have an uninstall option.

(piltest)~$ cdsitepackages
(piltest)site-packages$ rm -rf PIL
(piltest)site-packages$ pip freeze | grep -i pil

Excellent. No PIL installed. Let's resume installing Pillow.

(piltest)site-packages$ pip install Pillow
(piltest)site-packages$ pip freeze | grep -i pil
Pillow==2.1.0

And yet, stuff is broken.

This works (like documented):

(piltest)site-packages$ python -c "from PIL import Image; im = Image.open('/lib/plymouth/ubuntu_logo.png'); im.load()"

But this — observe the lack of from PIL in the import statement — seems to work, but breaks.

There is no nice ImportError, but rather a runtime error after loading a file. (Which of course happens right in the middle of a presentation.)

(piltest)site-packages$ python -c "import Image; im = Image.open('/lib/plymouth/ubuntu_logo.png'); im.load()"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File ".../site-packages/PIL/Image.py", line 2008, in open
    raise IOError("cannot identify image file")
IOError: cannot identify image file

That means that a construct like this would fail:

try:
    import Image
except ImportError:
    from PIL import Image

How did that happen?

Well, the devil is in the details. The rogue removal of the PIL directory bypassed the removal of PIL.pth. That pth file is parsed by the virtualenv custom site.py~/.virtualenvs/piltest/lib/python2.7/site.py. That, in turn, causes the addition of the PIL path in sys.path which breaks proper Pillow functioning.

A path configuration file is a file whose name has the form <package>.pth; its contents are additional directories (one per line) to be added to sys.path. Non-existing directories (or non-directories) are never added to sys.path; no directory is added to sys.path more than once. Blank lines and lines beginning with '#' are skipped. Lines starting with 'import' are executed.

The fix is to clear out PIL.pth or remove it altogether.

(piltest)site-packages$ cat PIL.pth 
PIL
(piltest)site-packages$ rm PIL.pth

python virtualenv imaging