[Python-modules-commits] [sorl-thumbnail] 01/15: Import sorl-thumbnail_12.3+git20160928.orig.tar.gz
Wolfgang Borgert
debacle at moszumanska.debian.org
Wed Oct 5 01:39:41 UTC 2016
This is an automated email from the git hooks/post-receive script.
debacle pushed a commit to branch master
in repository sorl-thumbnail.
commit 3ffdcfe77474be5c4dadbc0c1a48660f0b6de824
Author: W. Martin Borgert <debacle at debian.org>
Date: Tue Oct 4 01:55:58 2016 +0200
Import sorl-thumbnail_12.3+git20160928.orig.tar.gz
---
.travis.yml | 65 +++---------
AUTHORS | 3 +-
README.rst | 25 +++--
docs/contributing.rst | 10 +-
docs/installation.rst | 2 +-
docs/reference/settings.rst | 2 +-
docs/requirements.rst | 7 +-
docs/template.rst | 6 +-
setup.py | 8 +-
sorl/__init__.py | 3 +-
sorl/thumbnail/admin/__init__.py | 8 +-
sorl/thumbnail/admin/compat.py | 91 -----------------
sorl/thumbnail/admin/current.py | 12 ++-
sorl/thumbnail/base.py | 24 +++--
sorl/thumbnail/compat.py | 46 ---------
sorl/thumbnail/conf/defaults.py | 10 +-
sorl/thumbnail/engines/convert_engine.py | 2 +-
sorl/thumbnail/engines/pil_engine.py | 10 +-
sorl/thumbnail/engines/vipsthumbnail_engine.py | 122 +++++++++++++++++++++++
sorl/thumbnail/fields.py | 7 --
sorl/thumbnail/helpers.py | 7 +-
sorl/thumbnail/images.py | 29 ++++--
sorl/thumbnail/kvstores/cached_db_kvstore.py | 5 +-
sorl/thumbnail/kvstores/dynamodb_kvstore.py | 38 +++++++
sorl/thumbnail/migrations/0001_initial.py | 5 +-
sorl/thumbnail/templatetags/thumbnail.py | 19 +++-
tests/.coveragerc | 11 +-
tests/data/aspect_test.jpg | Bin 0 -> 5723 bytes
tests/data/broken.jpeg | Bin 0 -> 480 bytes
tests/settings/default.py | 9 +-
tests/settings/dynamodb.py | 8 ++
tests/settings/vipsthumbnail.py | 4 +
tests/thumbnail_tests/compat.py | 11 +-
tests/thumbnail_tests/templates/thumbnaild4.html | 5 +
tests/thumbnail_tests/test_backends.py | 1 +
tests/thumbnail_tests/test_engines.py | 60 +++++++++++
tests/thumbnail_tests/urls.py | 16 +--
tox.ini | 13 +--
vagrant.sh | 8 +-
39 files changed, 417 insertions(+), 295 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 1597464..a7caf81 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,34 +10,6 @@ cache:
- .tox
env:
- - TOX_ENV=py27-django14-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
- - TOX_ENV=py27-django14-imagemagick APT=imagemagick
- - TOX_ENV=py27-django14-graphicsmagick APT=graphicsmagick
- - TOX_ENV=py27-django14-redis
- - TOX_ENV=py27-django14-wand
- - TOX_ENV=py27-django14-pgmagick APT='libgraphicsmagick++1-dev libboost-python-dev'
- - TOX_ENV=py27-django14-dbm
- - TOX_ENV=py27-django15-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
- - TOX_ENV=py27-django15-imagemagick APT=imagemagick
- - TOX_ENV=py27-django15-graphicsmagick APT=graphicsmagick
- - TOX_ENV=py27-django15-redis
- - TOX_ENV=py27-django15-wand
- - TOX_ENV=py27-django15-pgmagick APT='libgraphicsmagick++1-dev libboost-python-dev'
- - TOX_ENV=py27-django15-dbm
- - TOX_ENV=py27-django16-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
- - TOX_ENV=py27-django16-imagemagick APT=imagemagick
- - TOX_ENV=py27-django16-graphicsmagick APT=graphicsmagick
- - TOX_ENV=py27-django16-redis
- - TOX_ENV=py27-django16-wand
- - TOX_ENV=py27-django16-pgmagick APT='libgraphicsmagick++1-dev libboost-python-dev'
- - TOX_ENV=py27-django16-dbm
- - TOX_ENV=py27-django17-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
- - TOX_ENV=py27-django17-imagemagick APT=imagemagick
- - TOX_ENV=py27-django17-graphicsmagick APT=graphicsmagick
- - TOX_ENV=py27-django17-redis
- - TOX_ENV=py27-django17-wand
- - TOX_ENV=py27-django17-pgmagick APT='libgraphicsmagick++1-dev libboost-python-dev'
- - TOX_ENV=py27-django17-dbm
- TOX_ENV=py27-django18-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
- TOX_ENV=py27-django18-imagemagick APT=imagemagick
- TOX_ENV=py27-django18-graphicsmagick APT=graphicsmagick
@@ -45,30 +17,24 @@ env:
- TOX_ENV=py27-django18-wand
- TOX_ENV=py27-django18-pgmagick APT='libgraphicsmagick++1-dev libboost-python-dev'
- TOX_ENV=py27-django18-dbm
- - TOX_ENV=py34-django15-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
- - TOX_ENV=py34-django15-imagemagick APT=imagemagick
- - TOX_ENV=py34-django15-graphicsmagick APT=graphicsmagick
- - TOX_ENV=py34-django15-redis
- - TOX_ENV=py34-django15-wand
- - TOX_ENV=py34-django15-dbm
- - TOX_ENV=py34-django16-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
- - TOX_ENV=py34-django16-imagemagick APT=imagemagick
- - TOX_ENV=py34-django16-graphicsmagick APT=graphicsmagick
- - TOX_ENV=py34-django16-redis
- - TOX_ENV=py34-django16-wand
- - TOX_ENV=py34-django16-dbm
- - TOX_ENV=py34-django17-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
- - TOX_ENV=py34-django17-imagemagick APT=imagemagick
- - TOX_ENV=py34-django17-graphicsmagick APT=graphicsmagick
- - TOX_ENV=py34-django17-redis
- - TOX_ENV=py34-django17-wand
- - TOX_ENV=py34-django17-dbm
- TOX_ENV=py34-django18-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
- TOX_ENV=py34-django18-imagemagick APT=imagemagick
- TOX_ENV=py34-django18-graphicsmagick APT=graphicsmagick
- TOX_ENV=py34-django18-redis
- TOX_ENV=py34-django18-wand
- TOX_ENV=py34-django18-dbm
+ - TOX_ENV=py34-django19-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
+ - TOX_ENV=py34-django19-imagemagick APT=imagemagick
+ - TOX_ENV=py34-django19-graphicsmagick APT=graphicsmagick
+ - TOX_ENV=py34-django19-redis
+ - TOX_ENV=py34-django19-wand
+ - TOX_ENV=py34-django19-dbm
+ - TOX_ENV=py34-django110-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
+ - TOX_ENV=py34-django110-imagemagick APT=imagemagick
+ - TOX_ENV=py34-django110-graphicsmagick APT=graphicsmagick
+ - TOX_ENV=py34-django110-redis
+ - TOX_ENV=py34-django110-wand
+ - TOX_ENV=py34-django110-dbm
before_install:
- sudo apt-get update -qq
@@ -77,12 +43,13 @@ before_install:
after_failure:
- cat /home/travis/.pip/pip.log
-after_success:
- - coveralls
+# after_success:
+# - coveralls
install:
- pip install pip wheel
- - pip install -q coveralls flake8 tox
+# - pip install -q coveralls flake8 tox
+ - pip install -q flake8 tox
script:
- env | sort
diff --git a/AUTHORS b/AUTHORS
index adbc7e8..81da9c0 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -5,4 +5,5 @@ The PRIMARY AUTHORS are (and/or have been):
* Mikko Hellsing
* M. César Señoranis
* Rolf Erik Lekang
- * Frankie Robertson
\ No newline at end of file
+ * Frankie Robertson
+ * Valentin v. Seggern (Support for AWS Dynamo DB)
diff --git a/README.rst b/README.rst
index 37d5525..b0d3eac 100644
--- a/README.rst
+++ b/README.rst
@@ -5,11 +5,11 @@ Thumbnails for Django.
Features at a glance
====================
-- Support for Django 1.4, 1.5, 1.6, 1.7 and 1.8
-- Python 3 support (for Django 1.5, 1.6, 1.7, 1.8)
+- Support for Django 1.8, 1.9, 1.10, following the `Django supported versions policy`_
+- Python 3 support
- Storage support
-- Pluggable Engine support for `Pillow`_, `ImageMagick`_, `PIL`_, `Wand`_ and `pgmagick`_
-- Pluggable Key Value Store support (cached db, redis)
+- Pluggable Engine support for `Pillow`_, `ImageMagick`_, `PIL`_, `Wand`_, `pgmagick`_, and `vipsthumbnail`_
+- Pluggable Key Value Store support (cached db, redis, and dynamodb by AWS)
- Pluggable Backend support
- Admin integration with possibility to delete
- Dummy generation (placeholders)
@@ -126,21 +126,22 @@ You can use the 'get_thumbnail'::
See more examples in the section `Low level API examples`_ in the Documentation
---------------------------
+
Frequently asked questions
---------------------------
+==========================
Is so slow in Amazon S3 !
-------------------------
-Posible related to the implementation of your Amazon S3 Backend, see the issue `#351`_
+Posible related to the implementation of your Amazon S3 Backend, see the `issue #351`_
due the storage backend reviews if there is an existing thumbnail when tries to
generate the thumbnail that makes an extensive use of the S3 API
-A fast workaround if you are not willing to tweak your storage backend is to set
-the `THUMBNAIL_FORCE_OVERWRITE` setting to `True` by default is `False`, so it will
-avoid to overly query the S3 API
+A fast workaround if you are not willing to tweak your storage backend is to set::
+
+ THUMBNAIL_FORCE_OVERWRITE = True
+So it will avoid to overly query the S3 API.
@@ -156,8 +157,10 @@ avoid to overly query the S3 API
.. _`PIL`: http://www.pythonware.com/products/pil/
.. _`Wand`: http://docs.wand-py.org/
.. _`pgmagick`: http://pgmagick.readthedocs.org/en/latest/
+.. _`vipsthumbnail`: http://www.vips.ecs.soton.ac.uk/index.php?title=VIPS
.. _`Template examples`: http://sorl-thumbnail.readthedocs.org/en/latest/examples.html#template-examples
.. _`Model examples`: http://sorl-thumbnail.readthedocs.org/en/latest/examples.html#model-examples
.. _`Low level API examples`: http://sorl-thumbnail.readthedocs.org/en/latest/examples.html#low-level-api-examples
-.. _ `#351`: https://github.com/mariocesar/sorl-thumbnail/issues/351
\ No newline at end of file
+.. _`issue #351`: https://github.com/mariocesar/sorl-thumbnail/issues/351
+.. _`Django supported versions policy`: https://www.djangoproject.com/download/#supported-versions
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 26e4329..3301b09 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -19,6 +19,10 @@ Django versions, we provide an easy way to test locally across all of them.
We use `Vagrant`_ for simple interaction with virtual machines and
`tox`_ for managing python virtual environments.
+Some dependencies like pgmagick takes a lot of time to compiling. To speed up your
+vagrant box you can edit `Vagrant file`_ with mem and cpu or simply install `vagrant-faster`_.
+The resulting .tox folder containing all virtualenvs requires ~
+
* `Install Vagrant`_
* ``cd`` in your source directory
* Run ``vagrant up`` to prepare VM. It will download Ubuntu image and install all necessary dependencies.
@@ -27,9 +31,9 @@ We use `Vagrant`_ for simple interaction with virtual machines and
To run only tests against only one configuration use ``-e`` option::
- tox -e py33-1.6-pil
+ tox -e py34-django16-pil
-Py33 stands for python version, 1.6 is Django version and the latter is image library.
+Py34 stands for python version, 1.6 is Django version and the latter is image library.
For full list of tox environments, see ``tox.ini``
You can get away without using Vagrant if you install all packages locally yourself,
@@ -39,6 +43,8 @@ however, this is not recommended.
.. _Vagrant: http://www.vagrantup.com/
.. _tox: https://testrun.org/tox/latest/
.. _Install Vagrant: http://docs.vagrantup.com/v2/installation/index.html
+.. _Vagrant file: https://docs.vagrantup.com/v2/virtualbox/configuration.html
+.. _vagrant-faster: https://github.com/rdsubhas/vagrant-faster
Sending pull requests
=====================
diff --git a/docs/installation.rst b/docs/installation.rst
index 79974e0..9402faa 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -21,5 +21,5 @@ Setup
3. If you are using the cached database key value store you need to sync the
database::
- python manage.py syncdb
+ python manage.py migrate
diff --git a/docs/reference/settings.rst b/docs/reference/settings.rst
index e109766..f02f214 100644
--- a/docs/reference/settings.rst
+++ b/docs/reference/settings.rst
@@ -198,7 +198,7 @@ The port for Redis server. Only applicable for the Redis Key Value Store
Filename of the DBM database. Depending on the DBM engine selected by your
Python installation, this will be used as a prefix because multiple files may be
-created.
+created. This can be an absolute path.
``THUMBNAIL_DBM_MODE``
diff --git a/docs/requirements.rst b/docs/requirements.rst
index daff3a9..dcfbba5 100644
--- a/docs/requirements.rst
+++ b/docs/requirements.rst
@@ -4,11 +4,11 @@ Requirements
Base requirements
=================
-- `Python`_ 2.5+
+- `Python`_ 2.7+
- `Django`_
- :ref:`kvstore-requirements`
- :ref:`image-library`
-
+
.. _kvstore-requirements:
Key Value Store
@@ -42,7 +42,7 @@ Image Library
You need to have an image library installed. sorl-thumbnail ships with support
for `Python Imaging Library`_, `pgmagick`_, `ImageMagick`_ (or `GraphicsMagick`)
command line tools. `pgmagick`_ are python bindings for `GraphicsMagick`_
-(Magick++)`,
+(Magick++)`,
The `ImageMagick`_ based engine ``sorl.thumbnail.engines.convert_engine.Engine``
by default calls ``convert`` and ``identify`` shell commands. You can change the
@@ -120,4 +120,3 @@ Ubuntu installation::
.. _Python: http://www.python.org/
.. _pgmagick: http://bitbucket.org/hhatto/pgmagick/src
.. _wand: http://wand-py.org
-
diff --git a/docs/template.rst b/docs/template.rst
index aa4846b..bfdc020 100644
--- a/docs/template.rst
+++ b/docs/template.rst
@@ -42,6 +42,8 @@ Source can be an ImageField, FileField, a file name (assuming default_storage),
a url. What we need to know is name and storage, see how ImageFile figures
these things out::
+ from django.utils.encoding import force_text
+
class ImageFile(BaseImageFile):
_size = None
@@ -52,7 +54,7 @@ these things out::
if hasattr(file_, 'name'):
self.name = file_.name
else:
- self.name = force_unicode(file_)
+ self.name = force_text(file_)
# figure out storage
if storage is not None:
self.storage = storage
@@ -110,7 +112,7 @@ above text. After it is rescaled it will apply the cropping options. There are
some differences to the `css background-position`_:
- Only % and px are valid lengths (units)
-- ``noop`` (No Operation) is a valid option which means there is no
+- ``noop`` (No Operation) is a valid option which means there is no
cropping after the initial rescaling to minimum of width and height.
There are many overlapping options here for example ``center`` is equivalent to
diff --git a/setup.py b/setup.py
index 6cd7202..b17c9c4 100644
--- a/setup.py
+++ b/setup.py
@@ -27,7 +27,7 @@ setup(
platforms='any',
zip_safe=False,
classifiers=[
- 'Development Status :: 5 - Production/Stable',
+ 'Development Status :: 2 - Pre-Alpha',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
@@ -39,11 +39,9 @@ setup(
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Multimedia :: Graphics',
'Framework :: Django',
- 'Framework :: Django :: 1.4',
- 'Framework :: Django :: 1.5',
- 'Framework :: Django :: 1.6',
- 'Framework :: Django :: 1.7',
'Framework :: Django :: 1.8',
+ 'Framework :: Django :: 1.9',
+ 'Framework :: Django :: 1.10',
],
cmdclass={"test": TestCommand},
)
diff --git a/sorl/__init__.py b/sorl/__init__.py
index 5af7ec6..19fb7cf 100644
--- a/sorl/__init__.py
+++ b/sorl/__init__.py
@@ -5,10 +5,9 @@ import logging
__author__ = "Mikko Hellsing"
__license__ = "BSD"
-__version__ = '12.3'
+__version__ = '12.4a1'
__maintainer__ = "Mario César Señoranis Ayala"
__email__ = "mariocesar at humanzilla.com"
-__status__ = "Production/Stable"
class NullHandler(logging.Handler):
diff --git a/sorl/thumbnail/admin/__init__.py b/sorl/thumbnail/admin/__init__.py
index c4c7cd8..cd51e2b 100644
--- a/sorl/thumbnail/admin/__init__.py
+++ b/sorl/thumbnail/admin/__init__.py
@@ -1,8 +1,4 @@
-try:
- from django.forms import ClearableFileInput
-except ImportError:
- from .compat import AdminImageMixin
-else:
- from .current import AdminImageMixin
+from django.forms import ClearableFileInput
+from .current import AdminImageMixin
AdminInlineImageMixin = AdminImageMixin # backwards compatibility
diff --git a/sorl/thumbnail/admin/compat.py b/sorl/thumbnail/admin/compat.py
deleted file mode 100644
index ac1cf31..0000000
--- a/sorl/thumbnail/admin/compat.py
+++ /dev/null
@@ -1,91 +0,0 @@
-from __future__ import unicode_literals
-from django import forms
-from django.utils.safestring import mark_safe
-from django.utils.translation import ugettext_lazy as _
-from sorl.thumbnail.fields import ImageField, ImageFormField
-from sorl.thumbnail.shortcuts import get_thumbnail
-
-
-class ClearableImageFormField(forms.MultiValueField):
- def __init__(self, max_length=None, **kwargs):
- fields = (
- ImageFormField(max_length=max_length, **kwargs),
- forms.BooleanField()
- )
- super(ClearableImageFormField, self).__init__(fields, **kwargs)
-
- def compress(self, data_list):
- if data_list:
- if not data_list[0] and data_list[1]:
- return False
- return data_list[0]
-
-
-class AdminImageWidget(forms.FileInput):
- """
- An ImageField Widget for django.contrib.admin that shows a thumbnailed
- image as well as a link to the current one if it hase one.
- """
-
- def render(self, name, value, attrs=None):
- output = super(AdminImageWidget, self).render(name, value, attrs)
- if value and hasattr(value, 'url'):
- ext = 'JPG'
- try:
- aux_ext = str(value).split('.')
- if aux_ext[len(aux_ext)-1].lower() == 'png':
- ext = 'PNG'
- except:
- pass
- try:
- mini = get_thumbnail(value, 'x80', upscale=False, format=ext)
- except Exception:
- pass
- else:
- output = (
- '<div style="float:left">'
- '<a style="width:%spx;display:block;margin:0 0 10px" class="thumbnail"'
- 'target="_blank" href="%s">'
- '<img src="%s"></a>%s</div>'
- ) % (mini.width, value.url, mini.url, output)
- return mark_safe(output)
-
-
-class AdminClearWidget(forms.CheckboxInput):
- def render(self, name, value, attrs=None):
- output = super(AdminClearWidget, self).render(name, value, attrs)
- output = (
- '<div style="clear:both;padding-top:5px">'
- '<label for="id_%s">%s:</label>%s'
- '</div>'
- ) % (name, _('Clear image'), output)
- return mark_safe(output)
-
-
-class AdminClearableImageWidget(forms.MultiWidget):
- def __init__(self, attrs=None):
- widgets = (AdminImageWidget(attrs=attrs), AdminClearWidget())
- super(AdminClearableImageWidget, self).__init__(widgets, attrs)
-
- def decompress(self, value):
- if value:
- return (value, False)
- return (None, None)
-
-
-class AdminImageMixin(object):
- """
- This is a mix-in for ModelAdmin subclasses to make ``ImageField`` show nicer
- form class and widget
- """
-
- def formfield_for_dbfield(self, db_field, **kwargs):
- if isinstance(db_field, ImageField):
- if not db_field.blank:
- return db_field.formfield(widget=AdminImageWidget)
- return db_field.formfield(
- form_class=ClearableImageFormField,
- widget=AdminClearableImageWidget,
- )
- sup = super(AdminImageMixin, self)
- return sup.formfield_for_dbfield(db_field, **kwargs)
diff --git a/sorl/thumbnail/admin/current.py b/sorl/thumbnail/admin/current.py
index 9b77988..175de85 100644
--- a/sorl/thumbnail/admin/current.py
+++ b/sorl/thumbnail/admin/current.py
@@ -16,9 +16,11 @@ class AdminImageWidget(forms.ClearableFileInput):
image as well as a link to the current one if it hase one.
"""
- template_with_initial = '%(clear_template)s<br />%(input_text)s: %(input)s'
- template_with_clear = ('%(clear)s <label style="width:auto" for="%(clear_checkbox_id)s">'
- '%(clear_checkbox_label)s</label>')
+ template_with_initial = (
+ '%(clear_template)s <br>'
+ '<label>%(input_text)s: %(input)s</label>'
+ )
+ template_with_clear = '<label>%(clear_checkbox_label)s: %(clear)s</label>'
def render(self, name, value, attrs=None):
output = super(AdminImageWidget, self).render(name, value, attrs)
@@ -28,12 +30,14 @@ class AdminImageWidget(forms.ClearableFileInput):
aux_ext = str(value).split('.')
if aux_ext[len(aux_ext) - 1].lower() == 'png':
ext = 'PNG'
+ elif aux_ext[len(aux_ext) - 1].lower() == 'gif':
+ ext = 'GIF'
except:
pass
try:
mini = get_thumbnail(value, 'x80', upscale=False, format=ext)
except Exception as e:
- logger.warn("Unable to get the thumbnail", exc_info=e)
+ logger.warning("Unable to get the thumbnail", exc_info=e)
else:
try:
output = (
diff --git a/sorl/thumbnail/base.py b/sorl/thumbnail/base.py
index b826fed..c9ed177 100644
--- a/sorl/thumbnail/base.py
+++ b/sorl/thumbnail/base.py
@@ -4,7 +4,8 @@ import logging
import os
import re
-from sorl.thumbnail.compat import string_type, text_type
+from django.utils.six import string_types
+
from sorl.thumbnail.conf import settings, defaults as default_settings
from sorl.thumbnail.helpers import tokey, serialize
from sorl.thumbnail.images import ImageFile, DummyImageFile
@@ -17,6 +18,8 @@ logger = logging.getLogger(__name__)
EXTENSIONS = {
'JPEG': 'jpg',
'PNG': 'png',
+ 'GIF': 'gif',
+ 'WEBP': 'webp',
}
@@ -54,6 +57,10 @@ class ThumbnailBackend(object):
return 'JPEG'
elif file_extension == '.png':
return 'PNG'
+ elif file_extension == '.gif':
+ return 'GIF'
+ elif file_extension == '.webp':
+ return 'WEBP'
else:
from django.conf import settings
@@ -65,7 +72,7 @@ class ThumbnailBackend(object):
options given. First it will try to get it from the key value store,
secondly it will create it.
"""
- logger.debug(text_type('Getting thumbnail for file [%s] at [%s]'), file_, geometry_string)
+ logger.debug('Getting thumbnail for file [%s] at [%s]', file_, geometry_string)
if file_:
source = ImageFile(file_)
@@ -98,7 +105,7 @@ class ThumbnailBackend(object):
# We have to check exists() because the Storage backend does not
# overwrite in some implementations.
- if not thumbnail.exists():
+ if settings.THUMBNAIL_FORCE_OVERWRITE or not thumbnail.exists():
try:
source_image = default.engine.get_image(source)
except IOError as e:
@@ -109,9 +116,10 @@ class ThumbnailBackend(object):
# if S3Storage says file doesn't exist remotely, don't try to
# create it and exit early.
# Will return working empty image type; 404'd image
- logger.warn(text_type('Remote file [%s] at [%s] does not exist'),
- file_, geometry_string)
-
+ logger.warning(
+ 'Remote file [%s] at [%s] does not exist',
+ file_, geometry_string,
+ )
return thumbnail
# We might as well set the size since we have the image in memory
@@ -150,7 +158,7 @@ class ThumbnailBackend(object):
"""
Creates the thumbnail by using default.engine
"""
- logger.debug(text_type('Creating thumbnail file [%s] at [%s] with [%s]'),
+ logger.debug('Creating thumbnail file [%s] at [%s] with [%s]',
thumbnail.name, geometry_string, options)
ratio = default.engine.get_image_ratio(source_image, options)
geometry = parse_geometry(geometry_string, ratio)
@@ -173,7 +181,7 @@ class ThumbnailBackend(object):
for resolution in settings.THUMBNAIL_ALTERNATIVE_RESOLUTIONS:
resolution_geometry = (int(geometry[0] * resolution), int(geometry[1] * resolution))
resolution_options = options.copy()
- if 'crop' in options and isinstance(options['crop'], string_type):
+ if 'crop' in options and isinstance(options['crop'], string_types):
crop = options['crop'].split(" ")
for i in range(len(crop)):
s = re.match("(\d+)px", crop[i])
diff --git a/sorl/thumbnail/compat.py b/sorl/thumbnail/compat.py
index 30e5c63..644e283 100644
--- a/sorl/thumbnail/compat.py
+++ b/sorl/thumbnail/compat.py
@@ -2,18 +2,13 @@ from __future__ import unicode_literals
import sys
-import django
-
__all__ = [
- 'json',
'BufferIO',
'urlopen',
'urlparse',
'quote',
'quote_plus',
'URLError',
- 'get_cache',
- 'force_unicode', 'text_type'
]
PythonVersion = sys.version_info[0]
@@ -21,42 +16,6 @@ PythonVersion = sys.version_info[0]
PY2 = PythonVersion == 2
PY3 = PythonVersion == 3
-# -- import_module
-from importlib import import_module
-
-# -- Related to django 1.5 incompatibility
-
-if django.VERSION < (1, 5):
- from django.utils import simplejson as json
- from django.utils.encoding import force_unicode
-else:
- import json
- from django.utils.encoding import force_text as force_unicode
-
-# -- Cache
-
-if django.VERSION >= (1, 7):
- from django.core.cache import caches
-
- get_cache = lambda cache_name: caches[cache_name]
-else:
- from django.core.cache import get_cache
-
-# -- Text
-
-try:
- from django.utils.encoding import smart_text
-except ImportError:
- from django.utils.encoding import smart_unicode as smart_text
-
-# -- Ordered Dict
-
-try:
- from collections import OrderedDict
-except ImportError:
- from django.utils.datastructures import SortedDict as OrderedDict
-
-
# -- Python 2 and 3
if PY3:
@@ -69,9 +28,6 @@ if PY3:
from io import BytesIO as BufferIO
- text_type = str
- string_type = str
-
def b(s):
return s.encode("latin-1")
@@ -95,8 +51,6 @@ elif PY2:
from cStringIO import StringIO as BufferIO
- text_type = unicode
- string_type = basestring
urlsplit = urlparse.urlsplit
diff --git a/sorl/thumbnail/conf/defaults.py b/sorl/thumbnail/conf/defaults.py
index 83637e9..4b833d3 100644
--- a/sorl/thumbnail/conf/defaults.py
+++ b/sorl/thumbnail/conf/defaults.py
@@ -27,6 +27,10 @@ THUMBNAIL_ENGINE = 'sorl.thumbnail.engines.pil_engine.Engine'
THUMBNAIL_CONVERT = 'convert'
THUMBNAIL_IDENTIFY = 'identify'
+# Path to ``vipsthumbnail`` and ``vipsheader``
+THUMBNAIL_VIPSTHUMBNAIL = 'vipsthumbnail'
+THUMBNAIL_VIPSHEADER = 'vipsheader'
+
# Storage for the generated thumbnails
THUMBNAIL_STORAGE = settings.DEFAULT_FILE_STORAGE
@@ -54,7 +58,7 @@ THUMBNAIL_KEY_PREFIX = 'sorl-thumbnail'
# Thumbnail filename prefix
THUMBNAIL_PREFIX = 'cache/'
-# Image format, common formats are: JPEG, PNG
+# Image format, common formats are: JPEG, PNG, GIF
# Make sure the backend can handle the format you specify
THUMBNAIL_FORMAT = 'JPEG'
@@ -113,3 +117,7 @@ THUMBNAIL_FILTER_WIDTH = 500
# Should we flatten images by default (fixes a lot of transparency issues with
# imagemagick)
THUMBNAIL_FLATTEN = False
+
+# Whenever we will check an existing thumbnail exists and avoid to overwrite or not.
+# Set this to true if you have an slow .exists() implementation on your storage backend of choice.
+THUMBNAIL_FORCE_OVERWRITE = False
diff --git a/sorl/thumbnail/engines/convert_engine.py b/sorl/thumbnail/engines/convert_engine.py
index 6926aba..18664fc 100644
--- a/sorl/thumbnail/engines/convert_engine.py
+++ b/sorl/thumbnail/engines/convert_engine.py
@@ -2,6 +2,7 @@ from __future__ import unicode_literals, with_statement
import re
import os
import subprocess
+from collections import OrderedDict
from django.utils.encoding import smart_str
from django.core.files.temp import NamedTemporaryFile
@@ -10,7 +11,6 @@ from sorl.thumbnail.base import EXTENSIONS
from sorl.thumbnail.compat import b
from sorl.thumbnail.conf import settings
from sorl.thumbnail.engines.base import EngineBase
-from sorl.thumbnail.compat import OrderedDict
size_re = re.compile(r'^(?:.+) (?:[A-Z]+) (?P<x>\d+)x(?P<y>\d+)')
diff --git a/sorl/thumbnail/engines/pil_engine.py b/sorl/thumbnail/engines/pil_engine.py
index db77187..fef582a 100644
--- a/sorl/thumbnail/engines/pil_engine.py
+++ b/sorl/thumbnail/engines/pil_engine.py
@@ -82,13 +82,13 @@ class Engine(EngineBase):
elif orientation == 4:
image = image.transpose(Image.FLIP_TOP_BOTTOM)
elif orientation == 5:
- image = image.rotate(-90).transpose(Image.FLIP_LEFT_RIGHT)
+ image = image.rotate(-90, expand=1).transpose(Image.FLIP_LEFT_RIGHT)
elif orientation == 6:
- image = image.rotate(-90)
+ image = image.rotate(-90, expand=1)
elif orientation == 7:
- image = image.rotate(90).transpose(Image.FLIP_LEFT_RIGHT)
+ image = image.rotate(90, expand=1).transpose(Image.FLIP_LEFT_RIGHT)
elif orientation == 8:
- image = image.rotate(90)
+ image = image.rotate(90, expand=1)
return image
@@ -100,7 +100,7 @@ class Engine(EngineBase):
newimage = image.convert('RGBA')
transparency = image.info.get('transparency')
if transparency is not None:
- mask = Image.new('L', image.size, color=transparency)
+ mask = image.convert('RGBA').split()[-1]
newimage.putalpha(mask)
return newimage
return image.convert('RGB')
diff --git a/sorl/thumbnail/engines/vipsthumbnail_engine.py b/sorl/thumbnail/engines/vipsthumbnail_engine.py
new file mode 100644
index 0000000..9ad8ccd
--- /dev/null
+++ b/sorl/thumbnail/engines/vipsthumbnail_engine.py
@@ -0,0 +1,122 @@
+from __future__ import unicode_literals, with_statement
+import re
+import os
+import subprocess
+from collections import OrderedDict
+
+from django.utils.encoding import smart_str
+from django.core.files.temp import NamedTemporaryFile
+
+from sorl.thumbnail.base import EXTENSIONS
+from sorl.thumbnail.conf import settings
+from sorl.thumbnail.engines.base import EngineBase
+
+
+size_re = re.compile(r'^(?:.+) (?P<x>\d+)x(?P<y>\d+)')
+
+
+class Engine(EngineBase):
+ """
+ Image object is a dict with source path, options and size
+ """
+
+ def write(self, image, options, thumbnail):
+ """
+ Writes the thumbnail image
+ """
+
+ args = settings.THUMBNAIL_VIPSTHUMBNAIL.split(' ')
+ args.append(image['source'])
+
+ for k in image['options']:
+ v = image['options'][k]
+ args.append('--%s' % k)
+ if v is not None:
+ args.append('%s' % v)
+
+ suffix = '.%s' % EXTENSIONS[options['format']]
+
+ write_options = []
+ if options['format'] == 'JPEG' and options.get(
+ 'progressive', settings.THUMBNAIL_PROGRESSIVE):
+ write_options.append("interlace")
+
+ if options['quality']:
+ if options['format'] == 'JPEG':
+ write_options.append("Q=%d" % options['quality'])
+
+ with NamedTemporaryFile(suffix=suffix, mode='rb') as fp:
+ # older vipsthumbails used -o, this was renamed to -f in 8.0, use
+ # -o here for commpatibility
+ args.append("-o")
+ args.append(fp.name + "[%s]" % ",".join(write_options))
+
+ args = map(smart_str, args)
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ p.wait()
+ out, err = p.communicate()
+
+ if err:
+ raise Exception(err)
+
+ thumbnail.write(fp.read())
+
+ def cleanup(self, image):
+ os.remove(image['source']) # we should not need this now
+
+ def get_image(self, source):
+ """
+ Returns the backend image objects from a ImageFile instance
+ """
+ with NamedTemporaryFile(mode='wb', delete=False) as fp:
+ fp.write(source.read())
+ return {'source': fp.name, 'options': OrderedDict(), 'size': None}
+
+ def get_image_size(self, image):
+ """
+ Returns the image width and height as a tuple
+ """
+ if image['size'] is None:
+ args = settings.THUMBNAIL_VIPSHEADER.split(' ')
+ args.append(image['source'])
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ p.wait()
+ m = size_re.match(str(p.stdout.read()))
+ image['size'] = int(m.group('x')), int(m.group('y'))
+ return image['size']
+
+ def is_valid_image(self, raw_data):
+ """
+ vipsheader will try a lot of formats, including all those supported by
+ imagemagick if compiled with magick support, this can take a while
+ """
+ with NamedTemporaryFile(mode='wb') as fp:
+ fp.write(raw_data)
+ fp.flush()
+ args = settings.THUMBNAIL_VIPSHEADER.split(' ')
+ args.append(fp.name)
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ retcode = p.wait()
+ return retcode == 0
+
+ def _orientation(self, image):
+ # vipsthumbnail also corrects the orientation exif data for
+ # destination
+ image['options']['rotate'] = None
+
+ return image
+
+ def _colorspace(self, image, colorspace):
+ """
+ vipsthumbnail does not support greyscaling of images, but pretend it
+ does
+ """
+ return image
+
+ def _scale(self, image, width, height):
+ """
+ Does the resizing of the image
+ """
+ image['options']['size'] = '%sx%s' % (width, height)
+ image['size'] = (width, height) # update image size
+ return image
diff --git a/sorl/thumbnail/fields.py b/sorl/thumbnail/fields.py
index af4cd0c..8c39452 100644
--- a/sorl/thumbnail/fields.py
+++ b/sorl/thumbnail/fields.py
@@ -39,13 +39,6 @@ class ImageField(models.ImageField):
if data is not None:
setattr(instance, self.name, data or '')
- def south_field_triple(self):
- from south.modelsinspector import introspector
-
- cls_name = '%s.%s' % (self.__class__.__module__, self.__class__.__name__)
- args, kwargs = introspector(self)
- return (cls_name, args, kwargs)
-
class ImageFormField(forms.FileField):
default_error_messages = {
diff --git a/sorl/thumbnail/helpers.py b/sorl/thumbnail/helpers.py
index 800e4e2..1fd13f0 100644
--- a/sorl/thumbnail/helpers.py
+++ b/sorl/thumbnail/helpers.py
@@ -1,10 +1,13 @@
from __future__ import unicode_literals
import hashlib
+import json
import math
+from importlib import import_module
from django.core.exceptions import ImproperlyConfigured
-from sorl.thumbnail.compat import encode, json, smart_text, import_module
+from django.utils.encoding import force_text
+from sorl.thumbnail.compat import encode
class ThumbnailError(Exception):
@@ -40,7 +43,7 @@ def tokey(*args):
"""
Computes a unique key from arguments given.
"""
- salt = '||'.join([smart_text(arg) for arg in args])
+ salt = '||'.join([force_text(arg) for arg in args])
hash_ = hashlib.md5(encode(salt))
return hash_.hexdigest()
diff --git a/sorl/thumbnail/images.py b/sorl/thumbnail/images.py
index 22fc887..afc7d5b 100644
--- a/sorl/thumbnail/images.py
+++ b/sorl/thumbnail/images.py
@@ -1,17 +1,19 @@
# encoding=utf-8
from __future__ import unicode_literals, division
+import json
import os
import re
from django.core.files.base import File, ContentFile
-from django.core.files.storage import Storage, default_storage
+from django.core.files.storage import Storage # , default_storage
+from django.utils.encoding import force_text
from django.utils.functional import LazyObject, empty
from sorl.thumbnail import default
from sorl.thumbnail.conf import settings
-from sorl.thumbnail.compat import (json, urlopen, urlparse, urlsplit,
- quote, quote_plus,
- URLError, force_unicode, encode)
+from sorl.thumbnail.compat import (urlopen, urlparse, urlsplit,
+ quote, quote_plus, URLError, encode)
+from sorl.thumbnail.default import storage as default_storage
from sorl.thumbnail.helpers import ThumbnailError, tokey, get_module_class, deserialize
from sorl.thumbnail.parsers import parse_geometry
@@ -85,7 +87,17 @@ class ImageFile(BaseImageFile):
if hasattr(file_, 'name'):
self.name = file_.name
else:
- self.name = force_unicode(file_)
+ self.name = force_text(file_)
+
+ # TODO: Add a customizable naming method as a signal
+
+ # Remove query args from names. Fixes cache and signature arguments
+ # from third party services, like Amazon S3 and signature args.
+ self.name = self.name.split('?')[0]
+
... 468 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/sorl-thumbnail.git
More information about the Python-modules-commits
mailing list