parent
6d1ae5e27c
commit
4965a77407
|
@ -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)
|
||||
|
|
|
@ -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.',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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()
|
|
@ -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?
|
||||
|
||||
|
|
|
@ -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``
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue