Removed PIL compatability layer per deprecation timeline.

refs #19934.
This commit is contained in:
Tim Graham 2014-03-21 10:54:53 -04:00
parent 6d1ae5e27c
commit 4965a77407
15 changed files with 48 additions and 211 deletions

View File

@ -1,7 +1,7 @@
"""
Utility functions for handling images.
Requires Pillow (or PIL), as you might imagine.
Requires Pillow as you might imagine.
"""
import zlib
@ -35,9 +35,9 @@ def get_image_dimensions(file_or_path, close=False):
'close' to True to close the file at the end if it is initially in an open
state.
"""
from django.utils.image import ImageFile as PILImageFile
from PIL import ImageFile as PillowImageFile
p = PILImageFile.Parser()
p = PillowImageFile.Parser()
if hasattr(file_or_path, 'read'):
file = file_or_path
file_pos = file.tell()
@ -46,9 +46,9 @@ def get_image_dimensions(file_or_path, close=False):
file = open(file_or_path, 'rb')
close = True
try:
# Most of the time PIL only needs a small chunk to parse the image and
# get the dimensions, but with some TIFF files PIL needs to parse the
# whole file.
# Most of the time Pillow only needs a small chunk to parse the image
# and get the dimensions, but with some TIFF files Pillow needs to
# parse the whole file.
chunk_size = 1024
while 1:
data = file.read(chunk_size)

View File

@ -4,7 +4,6 @@ import os
from django import forms
from django.db.models.fields import Field
from django.core import checks
from django.core.exceptions import ImproperlyConfigured
from django.core.files.base import File
from django.core.files.storage import default_storage
from django.core.files.images import ImageFile
@ -386,8 +385,8 @@ class ImageField(FileField):
def _check_image_library_installed(self):
try:
from django.utils.image import Image # NOQA
except ImproperlyConfigured:
from PIL import Image # NOQA
except ImportError:
return [
checks.Error(
'Cannot use ImageField because Pillow is not installed.',

View File

@ -641,7 +641,7 @@ class ImageField(FileField):
if f is None:
return None
from django.utils.image import Image
from PIL import Image
# We need to get a file object for Pillow. We might have a path or we might
# have to read the data into memory.
@ -659,7 +659,7 @@ class ImageField(FileField):
# verify() must be called immediately after the constructor.
Image.open(file).verify()
except Exception:
# Pillow (or PIL) doesn't recognize it as an image.
# Pillow doesn't recognize it as an image.
six.reraise(ValidationError, ValidationError(
self.error_messages['invalid_image'],
code='invalid_image',

View File

@ -1,157 +0,0 @@
# -*- coding: utf-8 -*-
"""
To provide a shim layer over Pillow/PIL situation until the PIL support is
removed. See #19934.
Combinations To Account For
===========================
* Pillow:
* never has ``_imaging`` under any Python
* has the ``Image.alpha_composite``, which may aid in detection
* PIL
* CPython 2.x may have _imaging (& work)
* CPython 2.x may *NOT* have _imaging (broken & needs a error message)
* CPython 3.x doesn't work
* PyPy will *NOT* have _imaging (but works?)
* On some platforms (Homebrew and RHEL6 reported) _imaging isn't available,
the needed import is from PIL import _imaging (refs #21355)
Restated, that looks like:
* If we're on Python 2.x, it could be either Pillow or PIL:
* If ``import _imaging`` results in ``ImportError``, either they have a
working Pillow installation or a broken PIL installation, so we need to
detect further:
* To detect, we first ``import Image``.
* If ``Image`` has a ``alpha_composite`` attribute present, only Pillow
has this, so we assume it's working.
* If ``Image`` DOES NOT have a ``alpha_composite``attribute, it must be
PIL & is a broken (likely C compiler-less) install, which we need to
warn the user about.
* If ``import _imaging`` works, it must be PIL & is a working install.
* Python 3.x
* If ``import Image`` works, it must be Pillow, since PIL isn't Python 3.x
compatible.
* PyPy
* If ``import _imaging`` results in ``ImportError``, it could be either
Pillow or PIL, both of which work without it on PyPy, so we're fine.
Approach
========
* Attempt to import ``Image``
* ``ImportError`` - nothing is installed, toss an exception
* Either Pillow or the PIL is installed, so continue detecting
* Attempt to ``hasattr(Image, 'alpha_composite')``
* If it works, it's Pillow & working
* If it fails, we've got a PIL install, continue detecting
* The only option here is that we're on Python 2.x or PyPy, of which
we only care about if we're on CPython.
* If we're on CPython, attempt to ``from PIL import _imaging`` and
``import _imaging``
* ``ImportError`` - Bad install, toss an exception
"""
from __future__ import unicode_literals
import warnings
from django.core.exceptions import ImproperlyConfigured
from django.utils.deprecation import RemovedInDjango18Warning
from django.utils.translation import ugettext_lazy as _
Image = None
_imaging = None
ImageFile = None
def _detect_image_library():
global Image
global _imaging
global ImageFile
# Skip re-attempting to import if we've already run detection.
if Image is not None:
return Image, _imaging, ImageFile
# Assume it's not there.
PIL_imaging = False
try:
# Try from the Pillow (or one variant of PIL) install location first.
from PIL import Image as PILImage
except ImportError as err:
try:
# If that failed, try the alternate import syntax for PIL.
import Image as PILImage
except ImportError as err:
# Neither worked, so it's likely not installed.
raise ImproperlyConfigured(
_("Neither Pillow nor PIL could be imported: %s") % err
)
# ``Image.alpha_composite`` was added to Pillow in SHA: e414c6 & is not
# available in any version of the PIL.
if hasattr(PILImage, 'alpha_composite'):
PIL_imaging = False
else:
# We're dealing with the PIL. Determine if we're on CPython & if
# ``_imaging`` is available.
import platform
# This is the Alex Approved™ way.
# See http://mail.python.org/pipermail//pypy-dev/2011-November/008739.html
if platform.python_implementation().lower() == 'cpython':
# We're on CPython (likely 2.x). Since a C compiler is needed to
# produce a fully-working PIL & will create a ``_imaging`` module,
# we'll attempt to import it to verify their kit works.
try:
from PIL import _imaging as PIL_imaging
except ImportError:
try:
import _imaging as PIL_imaging
except ImportError as err:
raise ImproperlyConfigured(
_("The '_imaging' module for the PIL could not be "
"imported: %s") % err
)
# Try to import ImageFile as well.
try:
from PIL import ImageFile as PILImageFile
except ImportError:
# This import cannot fail unless Pillow/PIL install is completely
# broken (e.g. missing Python modules).
import ImageFile as PILImageFile
# Finally, warn about deprecation...
if PIL_imaging is not False:
warnings.warn(
"Support for the PIL will be removed in Django 1.8. Please " +
"uninstall it & install Pillow instead.",
RemovedInDjango18Warning
)
return PILImage, PIL_imaging, PILImageFile
Image, _imaging, ImageFile = _detect_image_library()

View File

@ -27,7 +27,7 @@ to make it dead easy, even for someone who may not be intimately familiar with
that area of the code, to understand the problem and verify the fix:
* Are there clear instructions on how to reproduce the bug? If this
touches a dependency (such as Pillow/PIL), a contrib module, or a specific
touches a dependency (such as Pillow), a contrib module, or a specific
database, are those instructions clear enough even for someone not
familiar with it?

View File

@ -619,22 +619,20 @@ For each field, we describe the default widget used if you don't specify
* Normalizes to: An ``UploadedFile`` object that wraps the file content
and file name into a single object.
* Validates that file data has been bound to the form, and that the
file is of an image format understood by Pillow/PIL.
file is of an image format understood by Pillow.
* Error message keys: ``required``, ``invalid``, ``missing``, ``empty``,
``invalid_image``
Using an ``ImageField`` requires that either `Pillow`_ (recommended) or the
`Python Imaging Library`_ (PIL) are installed and supports the image
formats you use. If you encounter a ``corrupt image`` error when you
upload an image, it usually means either Pillow or PIL
doesn't understand its format. To fix this, install the appropriate
library and reinstall Pillow or PIL.
Using an ``ImageField`` requires that `Pillow`_ is installed with support
for the image formats you use. If you encounter a ``corrupt image`` error
when you upload an image, it usually means that Pillow doesn't understand
its format. To fix this, install the appropriate library and reinstall
Pillow.
When you use an ``ImageField`` on a form, you must also remember to
:ref:`bind the file data to the form <binding-uploaded-files>`.
.. _Pillow: http://python-imaging.github.io/Pillow/
.. _Python Imaging Library: http://www.pythonware.com/products/pil/
``IntegerField``
~~~~~~~~~~~~~~~~

View File

@ -841,9 +841,9 @@ optional arguments:
Name of a model field which will be auto-populated with the width of the
image each time the model instance is saved.
Requires the `Python Imaging Library`_.
Requires the `Pillow`_ library.
.. _Python Imaging Library: http://www.pythonware.com/products/pil/
.. _Pillow: http://python-imaging.github.io/Pillow/
By default, :class:`ImageField` instances are created as ``varchar(100)``
columns in your database. As with other fields, you can change the maximum

View File

@ -226,8 +226,8 @@ User-uploaded content
served in ways that do not follow security best practices. Specifically, an
HTML file can be uploaded as an image if that file contains a valid PNG
header followed by malicious HTML. This file will pass verification of the
libraries that Django uses for :class:`~django.db.models.ImageField` image
processing (PIL or Pillow). When this file is subsequently displayed to a
library that Django uses for :class:`~django.db.models.ImageField` image
processing (Pillow). When this file is subsequently displayed to a
user, it may be displayed as HTML depending on the type and configuration of
your web server.

View File

@ -8,7 +8,6 @@ import tempfile
import unittest
import zlib
from django.core.exceptions import ImproperlyConfigured
from django.core.files import File
from django.core.files.move import file_move_safe
from django.core.files.base import ContentFile
@ -18,10 +17,11 @@ from django.utils._os import upath
from django.utils import six
try:
from django.utils.image import Image
from django.core.files import images
except ImproperlyConfigured:
from PIL import Image
except ImportError:
Image = None
else:
from django.core.files import images
class FileTests(unittest.TestCase):
@ -112,7 +112,7 @@ class DimensionClosingBug(unittest.TestCase):
"""
Test that get_image_dimensions() properly closes files (#8817)
"""
@unittest.skipUnless(Image, "Pillow/PIL not installed")
@unittest.skipUnless(Image, "Pillow not installed")
def test_not_closing_of_files(self):
"""
Open files passed into get_image_dimensions() should stay opened.
@ -123,7 +123,7 @@ class DimensionClosingBug(unittest.TestCase):
finally:
self.assertTrue(not empty_io.closed)
@unittest.skipUnless(Image, "Pillow/PIL not installed")
@unittest.skipUnless(Image, "Pillow not installed")
def test_closing_of_filenames(self):
"""
get_image_dimensions() called with a filename should closed the file.
@ -163,7 +163,7 @@ class InconsistentGetImageDimensionsBug(unittest.TestCase):
Test that get_image_dimensions() works properly after various calls
using a file handler (#11158)
"""
@unittest.skipUnless(Image, "Pillow/PIL not installed")
@unittest.skipUnless(Image, "Pillow not installed")
def test_multiple_calls(self):
"""
Multiple calls of get_image_dimensions() should return the same size.
@ -177,7 +177,7 @@ class InconsistentGetImageDimensionsBug(unittest.TestCase):
self.assertEqual(image_pil.size, size_1)
self.assertEqual(size_1, size_2)
@unittest.skipUnless(Image, "Pillow/PIL not installed")
@unittest.skipUnless(Image, "Pillow not installed")
def test_bug_19457(self):
"""
Regression test for #19457

View File

@ -4,7 +4,6 @@ from __future__ import unicode_literals
import unittest
from django.core.checks import Error
from django.core.exceptions import ImproperlyConfigured
from django.db import connection, models
from .base import IsolatedModelsTestCase
@ -379,8 +378,8 @@ class ImageFieldTests(IsolatedModelsTestCase):
def test_pillow_installed(self):
try:
import django.utils.image # NOQA
except ImproperlyConfigured:
from PIL import Image # NOQA
except ImportError:
pillow_installed = False
else:
pillow_installed = True

View File

@ -2,11 +2,9 @@ import os
import tempfile
import warnings
from django.core.exceptions import ImproperlyConfigured
try:
from django.utils.image import Image
except ImproperlyConfigured:
from PIL import Image
except ImportError:
Image = None
from django.core.files.storage import FileSystemStorage
@ -114,7 +112,7 @@ class VerboseNameField(models.Model):
field9 = models.FileField("verbose field9", upload_to="unused")
field10 = models.FilePathField("verbose field10")
field11 = models.FloatField("verbose field11")
# Don't want to depend on Pillow/PIL in this test
# Don't want to depend on Pillow in this test
#field_image = models.ImageField("verbose field")
field12 = models.IntegerField("verbose field12")
with warnings.catch_warnings(record=True) as w:
@ -151,7 +149,7 @@ class Document(models.Model):
###############################################################################
# ImageField
# If Pillow/PIL available, do these tests.
# If Pillow available, do these tests.
if Image:
class TestImageFieldFile(ImageFieldFile):
"""

View File

@ -20,7 +20,7 @@ if Image:
PersonDimensionsFirst, PersonTwoImages, TestImageFieldFile)
from .models import temp_storage_dir
else:
# Pillow/PIL not available, create dummy classes (tests will be skipped anyway)
# Pillow not available, create dummy classes (tests will be skipped anyway)
class Person():
pass
PersonWithHeight = PersonWithHeightAndWidth = PersonDimensionsFirst = Person
@ -93,7 +93,7 @@ class ImageFieldTestMixin(object):
self.assertEqual(getattr(instance, height_field_name), height)
@skipIf(Image is None, "Pillow/PIL is required to test ImageField")
@skipIf(Image is None, "Pillow is required to test ImageField")
class ImageFieldTests(ImageFieldTestMixin, TestCase):
"""
Tests for ImageField that don't need to be run with each of the
@ -180,7 +180,7 @@ class ImageFieldTests(ImageFieldTestMixin, TestCase):
self.assertEqual(p.mugshot, loaded_p.mugshot)
@skipIf(Image is None, "Pillow/PIL is required to test ImageField")
@skipIf(Image is None, "Pillow is required to test ImageField")
class ImageFieldTwoDimensionsTests(ImageFieldTestMixin, TestCase):
"""
Tests behavior of an ImageField and its dimensions fields.
@ -294,7 +294,7 @@ class ImageFieldTwoDimensionsTests(ImageFieldTestMixin, TestCase):
self.assertEqual(p.mugshot.was_opened, True)
@skipIf(Image is None, "Pillow/PIL is required to test ImageField")
@skipIf(Image is None, "Pillow is required to test ImageField")
class ImageFieldNoDimensionsTests(ImageFieldTwoDimensionsTests):
"""
Tests behavior of an ImageField with no dimension fields.
@ -303,7 +303,7 @@ class ImageFieldNoDimensionsTests(ImageFieldTwoDimensionsTests):
PersonModel = Person
@skipIf(Image is None, "Pillow/PIL is required to test ImageField")
@skipIf(Image is None, "Pillow is required to test ImageField")
class ImageFieldOneDimensionTests(ImageFieldTwoDimensionsTests):
"""
Tests behavior of an ImageField with one dimensions field.
@ -312,7 +312,7 @@ class ImageFieldOneDimensionTests(ImageFieldTwoDimensionsTests):
PersonModel = PersonWithHeight
@skipIf(Image is None, "Pillow/PIL is required to test ImageField")
@skipIf(Image is None, "Pillow is required to test ImageField")
class ImageFieldDimensionsFirstTests(ImageFieldTwoDimensionsTests):
"""
Tests behavior of an ImageField where the dimensions fields are
@ -322,7 +322,7 @@ class ImageFieldDimensionsFirstTests(ImageFieldTwoDimensionsTests):
PersonModel = PersonDimensionsFirst
@skipIf(Image is None, "Pillow/PIL is required to test ImageField")
@skipIf(Image is None, "Pillow is required to test ImageField")
class ImageFieldUsingFileTests(ImageFieldTwoDimensionsTests):
"""
Tests behavior of an ImageField when assigning it a File instance
@ -333,7 +333,7 @@ class ImageFieldUsingFileTests(ImageFieldTwoDimensionsTests):
File = File
@skipIf(Image is None, "Pillow/PIL is required to test ImageField")
@skipIf(Image is None, "Pillow is required to test ImageField")
class TwoImageFieldTests(ImageFieldTestMixin, TestCase):
"""
Tests a model with two ImageFields.

View File

@ -13,7 +13,7 @@ import os
import tempfile
from django.core import validators
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.exceptions import ValidationError
from django.core.files.storage import FileSystemStorage
from django.db import models
from django.utils import six
@ -154,7 +154,7 @@ class FilePathModel(models.Model):
try:
from django.utils.image import Image # NOQA: detect if Pillow is installed
from PIL import Image # NOQA: detect if Pillow is installed
test_images = True
@ -193,7 +193,7 @@ try:
def __str__(self):
return self.description
except ImproperlyConfigured:
except ImportError:
test_images = False

View File

@ -1852,7 +1852,7 @@ class FileAndImageFieldTests(TestCase):
names.sort()
self.assertEqual(names, ['---------', '__init__.py', 'models.py', 'tests.py'])
@skipUnless(test_images, "Pillow/PIL not installed")
@skipUnless(test_images, "Pillow not installed")
def test_image_field(self):
# ImageField and FileField are nearly identical, but they differ slighty when
# it comes to validation. This specifically tests that #6302 is fixed for

View File

@ -2,7 +2,7 @@
A test spanning all the capabilities of all the serializers.
This class sets up a model for each model field type
(except for image types, because of the Pillow/PIL dependency).
(except for image types, because of the Pillow dependency).
"""
import warnings