Code

Opened 6 years ago

Closed 5 years ago

#8187 closed (fixed)

Import PIL consistently and with exception handling, list as optional dependency in INSTALL

Reported by: jfkw Owned by: nobody
Component: Uncategorized Version: master
Severity: Keywords: PIL imaging easy_install
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

Some projects import PIL, some Imaging intending to reach the same code depending on how PIL was installed. Django appears to use the second form in at least one spot.

Some imports handle ImportError exceptions, some don't.

PIL is not mentioned in the INSTALL document.

$ ack -B 4 -A 4 'PIL|Imaging'
django/forms/fields.py
472-
473-    def clean(self, data, initial=None):
474-        """
475-        Checks that the file-upload field data contains a valid image (GIF, JPG,
476:        PNG, possibly others -- whatever the Python Imaging Library supports).
477-        """
478-        f = super(ImageField, self).clean(data, initial)
479-        if f is None:
480-            return None
481-        elif not data and initial:
482-            return initial
483:        from PIL import Image
484-
485:        # We need to get a file object for PIL. We might have a path or we might
486-        # have to read the data into memory.
487-        if hasattr(data, 'temporary_file_path'):
488-            file = data.temporary_file_path()
489-        else:
--
507-            #  but it must be called immediately after the constructor
508-            trial_image = Image.open(file)
509-            trial_image.verify()
510-        except ImportError: 
511:            # Under PyPy, it is possible to import PIL. However, the underlying
512-            # _imaging C module isn't available, so an ImportError will be 
513-            # raised. Catch and re-raise. 
514-            raise
515:        except Exception: # Python Imaging Library doesn't recognize it as an image
516-            raise ValidationError(self.error_messages['invalid_image'])
517-        if hasattr(f, 'seek') and callable(f.seek):
518-            f.seek(0)
519-        return f

django/core/files/images.py
1-"""
2-Utility functions for handling images.
3-
4:Requires PIL, as you might imagine.
5-"""
6-
7-from django.core.files import File
8-
--
25-        return self._dimensions_cache
26-
27-def get_image_dimensions(file_or_path):
28-    """Returns the (width, height) of an image, given an open file or a path."""
29:    from PIL import ImageFile as PILImageFile
30:    p = PILImageFile.Parser()
31-    if hasattr(file_or_path, 'read'):
32-        file = file_or_path
33-    else:
34-        file = open(file_or_path, 'rb')

django/core/validators.py
167-
168-def isValidImage(field_data, all_data):
169-    """
170-    Checks that the file-upload field data contains a valid image (GIF, JPG,
171:    PNG, possibly others -- whatever the Python Imaging Library supports).
172-    """
173:    from PIL import Image
174-    from cStringIO import StringIO
175-    try:
176-        content = field_data.read()
177-    except TypeError:
--
184-        # verify() is the only method that can spot a corrupt PNG,
185-        #  but it must be called immediately after the constructor
186-        trial_image = Image.open(StringIO(content))
187-        trial_image.verify()
188:    except Exception: # Python Imaging Library doesn't recognize it as an image
189-        raise ValidationError, _("Upload a valid image. The file you uploaded was either not an image or a corrupted image.")
190-
191-def isValidImageURL(field_data, all_data):
192-    uc = URLMimeTypeCheck(('image/jpeg', 'image/gif', 'image/png'))

django/core/management/validation.py
47-            if isinstance(f, models.FileField) and not f.upload_to:
48-                e.add(opts, '"%s": FileFields require an "upload_to" attribute.' % f.name)
49-            if isinstance(f, models.ImageField):
50-                try:
51:                    from PIL import Image
52-                except ImportError:
53:                    e.add(opts, '"%s": To use ImageFields, you need to install the Python Imaging Library. Get it at http://www.pythonware.com/products/pil/ .' % f.name)
54-            if f.choices:
55-                if isinstance(f.choices, basestring) or not is_iterable(f.choices):
56-                    e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name)
57-                else:

tests/regressiontests/file_storage/models.py
6-
7-temp_storage = FileSystemStorage(tempfile.gettempdir())
8-
9-# Test for correct behavior of width_field/height_field.
10:# Of course, we can't run this without PIL.
11-
12-try:
13-    # Checking for the existence of Image is enough for CPython, but
14-    # for PyPy, you need to check for the underlying modules
15-    import Image, _imaging
16-except ImportError:
17-    Image = None
18-
19:# If we have PIL, do these tests
20-if Image:
21-    class Person(models.Model):
22-        name = models.CharField(max_length=50)
23-        mugshot = models.ImageField(storage=temp_storage, upload_to='tests', 

tests/regressiontests/serializers_regress/models.py
1-"""
2-A test spanning all the capabilities of all the serializers.
3-
4-This class sets up a model for each model field type
5:(except for image types, because of the PIL dependency).
6-"""
7-
8-from django.db import models
9-from django.contrib.contenttypes import generic

tests/modeltests/model_forms/models.py
70-
71-class ImageFile(models.Model):
72-    description = models.CharField(max_length=20)
73-    try:
74:        # If PIL is available, try testing PIL.
75-        # Checking for the existence of Image is enough for CPython, but
76-        # for PyPy, you need to check for the underlying modules
77:        # If PIL is not available, this test is equivalent to TextFile above.
78-        import Image, _imaging
79-        image = models.ImageField(storage=temp_storage, upload_to='tests')
80-    except ImportError:
81-        image = models.FileField(storage=temp_storage, upload_to='tests')

Attachments (1)

patch-8185-vs-r8255.diff (1.2 KB) - added by bastih 6 years ago.
Replacing Image imports with from PIL import Image

Download all attachments as: .zip

Change History (4)

Changed 6 years ago by bastih

Replacing Image imports with from PIL import Image

comment:1 Changed 6 years ago by jacob

  • Resolution set to fixed
  • Status changed from new to closed

(In [8257]) Fixed #8187: made PIL imports consistant. Thanks, bastih.

comment:2 Changed 5 years ago by edrex

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution fixed deleted
  • Status changed from closed to reopened

Imports fail if PIL is installed via an egg, since in this case there is no PIL module, only Image, ImageFile, etc.

   virtualenv --no-site-packages env
   source env/bin/activate
   easy_install --find-links http://www.pythonware.com/products/pil/ Imaging

See how django-photologue does the imports: http://code.google.com/p/django-photologue/source/browse/trunk/photologue/models.py#20

so should be

try:
    import Image
except ImportError:
    try:
        from PIL import Image
    except ImportError:
        raise ImportError('...')

comment:3 Changed 5 years ago by edrex

  • Resolution set to fixed
  • Status changed from reopened to closed

I realized that the way of installing PIL listed above is invalid, should always be in a PIL module. Sorry for the noise.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.