Przeglądaj źródła

Removed PIL compatability layer per deprecation timeline.

refs #19934.
Tim Graham 11 lat temu
rodzic
commit
4965a77407

+ 6 - 6
django/core/files/images.py

@@ -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)

+ 2 - 3
django/db/models/fields/files.py

@@ -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.',

+ 2 - 2
django/forms/fields.py

@@ -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',

+ 0 - 157
django/utils/image.py

@@ -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()

+ 1 - 1
docs/faq/contributing.txt

@@ -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?
 

+ 6 - 8
docs/ref/forms/fields.txt

@@ -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``
 ~~~~~~~~~~~~~~~~

+ 2 - 2
docs/ref/models/fields.txt

@@ -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

+ 2 - 2
docs/topics/security.txt

@@ -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.
 

+ 8 - 8
tests/files/tests.py

@@ -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

+ 2 - 3
tests/invalid_models_tests/test_ordinary_fields.py

@@ -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

+ 4 - 6
tests/model_fields/models.py

@@ -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):
         """

+ 8 - 8
tests/model_fields/test_imagefield.py

@@ -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.

+ 3 - 3
tests/model_forms/models.py

@@ -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
 
 

+ 1 - 1
tests/model_forms/tests.py

@@ -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

+ 1 - 1
tests/serializers_regress/models.py

@@ -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