[Python-modules-commits] [sorl-thumbnail] 01/03: Import sorl-thumbnail_12.3+git20170708.orig.tar.gz
Wolfgang Borgert
debacle at moszumanska.debian.org
Sat Aug 5 00:13:21 UTC 2017
This is an automated email from the git hooks/post-receive script.
debacle pushed a commit to branch master
in repository sorl-thumbnail.
commit b09e34efdd882186bb18c56256a8a9791b82374d
Author: W. Martin Borgert <debacle at debian.org>
Date: Sat Aug 5 01:05:04 2017 +0200
Import sorl-thumbnail_12.3+git20170708.orig.tar.gz
---
.editorconfig | 25 +++++++++
.gitignore | 12 ++--
.travis.yml | 84 ++++++++++++++--------------
CONTRIBUTING.md | 5 ++
README.rst | 26 ++++++---
docs/contributing.rst | 4 +-
docs/news.rst | 17 ++++--
setup.cfg | 81 +++++++++++++++++++++++++++
setup.py | 6 +-
sorl/__init__.py | 1 +
sorl/thumbnail/admin/current.py | 2 +-
sorl/thumbnail/base.py | 8 ++-
sorl/thumbnail/engines/base.py | 8 +++
sorl/thumbnail/engines/convert_engine.py | 28 +++++++++-
sorl/thumbnail/engines/pgmagick_engine.py | 8 +++
sorl/thumbnail/engines/pil_engine.py | 16 +++++-
sorl/thumbnail/engines/wand_engine.py | 3 +
sorl/thumbnail/images.py | 18 +++++-
sorl/thumbnail/kvstores/cached_db_kvstore.py | 5 +-
tests/.coveragerc | 16 ------
tests/thumbnail_tests/test_engines.py | 5 +-
tests/thumbnail_tests/views.py | 5 +-
tox.ini | 42 --------------
23 files changed, 284 insertions(+), 141 deletions(-)
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..985b176
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,25 @@
+; This file is for unifying the coding style for different editors and IDEs.
+; More information at http://EditorConfig.org
+
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+
+[*.py]
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = space
+indent_size = 4
+
+[*.rst]
+trim_trailing_whitespace = false
+
+[Makefile]
+indent_style = tab
+
+[.travis.yml]
+indent_style = space
+indent_size = 2
diff --git a/.gitignore b/.gitignore
index 4df9375..d0954e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,11 +15,8 @@ parts/
sdist/
var/
*.egg-info/
-.installed.cfg
*.egg
-.coverage
-tests/htmlcov
-.tox
+.installed.cfg
# Installer logs
pip-log.txt
@@ -52,10 +49,15 @@ pip-delete-this-directory.txt
docs/_build/
# Virtualenv
-
+.python-version
.env/
# dbm stuff
tests/thumbnail_kvstore
tests/thumbnail_kvstore.db
tests/thumbnail_kvstore.lock
+
+# test related
+.coverage
+.tox
+htmlcov/
diff --git a/.travis.yml b/.travis.yml
index a7caf81..48a80c3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
language: python
python:
- - "3.4"
+ - "3.6"
cache:
pip:
@@ -10,31 +10,32 @@ cache:
- .tox
env:
- - 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
- - TOX_ENV=py27-django18-redis
- - 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-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
+ - TOXENV=py27-django18-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
+ - TOXENV=py27-django18-imagemagick APT=imagemagick
+ - TOXENV=py27-django18-graphicsmagick APT=graphicsmagick
+ - TOXENV=py27-django18-redis
+ - TOXENV=py27-django18-wand
+ - TOXENV=py27-django18-pgmagick APT='libgraphicsmagick++1-dev libboost-python-dev'
+ - TOXENV=py27-django18-dbm
+ - TOXENV=py34-django18-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
+ - TOXENV=py34-django18-imagemagick APT=imagemagick
+ - TOXENV=py34-django18-graphicsmagick APT=graphicsmagick
+ - TOXENV=py34-django18-redis
+ - TOXENV=py34-django18-wand
+ - TOXENV=py34-django18-dbm
+ - TOXENV=py35-django110-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
+ - TOXENV=py35-django110-imagemagick APT=imagemagick
+ - TOXENV=py35-django110-graphicsmagick APT=graphicsmagick
+ - TOXENV=py35-django110-redis
+ - TOXENV=py35-django110-wand
+ - TOXENV=py35-django110-dbm
+ - TOXENV=py36-django111-pil APT='libjpeg62 libjpeg62-dev zlib1g-dev'
+ - TOXENV=py36-django111-imagemagick APT=imagemagick
+ - TOXENV=py36-django111-graphicsmagick APT=graphicsmagick
+ - TOXENV=py36-django111-redis
+ - TOXENV=py36-django111-wand
+ - TOXENV=py36-django111-dbm
+
before_install:
- sudo apt-get update -qq
@@ -43,31 +44,28 @@ 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 flake8 tox
+ - pip install -q flake8 tox coveralls
script:
- env | sort
- - tox -e $TOX_ENV
+ - tox
- flake8 --show-source sorl/
services:
- redis-server
notifications:
- irc:
- channels:
- - "irc.freenode.org#sorl-thumbnail"
- on_success: change
- on_failure: change
- webhooks:
- urls:
- - https://webhooks.gitter.im/e/b368c47d69637f9a01a0
- on_success: change
- on_failure: change
- on_start: false # default: false
+ email: false
+
+deploy:
+ provider: pypi
+ user: jazzband
+ password:
+ secure:
+ on:
+ tags: true
+ distributions: sdist bdist_wheel
+ repo: jazzband/sorl-thumbnail
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..83d9096
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,5 @@
+[![Jazzband](https://jazzband.co/static/img/jazzband.svg)](https://jazzband.co/)
+
+This is a [Jazzband](https://jazzband.co/) project. By contributing you agree
+to abide by the [Contributor Code of Conduct](https://jazzband.co/about/conduct)
+and follow the [guidelines](https://jazzband.co/about/guidelines).
diff --git a/README.rst b/README.rst
index b0d3eac..aa505f8 100644
--- a/README.rst
+++ b/README.rst
@@ -5,7 +5,7 @@ Thumbnails for Django.
Features at a glance
====================
-- Support for Django 1.8, 1.9, 1.10, following the `Django supported versions policy`_
+- Support for Django 1.8, 1.10, 1.11, following the `Django supported versions policy`_
- Python 3 support
- Storage support
- Pluggable Engine support for `Pillow`_, `ImageMagick`_, `PIL`_, `Wand`_, `pgmagick`_, and `vipsthumbnail`_
@@ -25,6 +25,12 @@ Read more in `the documentation (latest version) <http://sorl-thumbnail.rtfd.org
Developers
==========
+|jazzband|
+
+This is a `Jazzband <https://jazzband.co>`_ project. By contributing you agree to
+abide by the `Contributor Code of Conduct <https://jazzband.co/about/conduct>`_
+and follow the `guidelines <https://jazzband.co/about/guidelines>`_.
+
Feel free to create a new Pull request if you want to propose a new feature.
If you need development support or want to discuss with other developers
join us in the channel #sorl-thumnbnail at freenode.net or Gitter.
@@ -34,7 +40,7 @@ in Google Groups.
- IRC Channel: irc://irc.freenode.net/#sorl-thumbnail
-- Gitter: https://gitter.im/mariocesar/sorl-thumbnail
+- Gitter: https://gitter.im/jazzband/sorl-thumbnail
- Mailing List: sorl-thumbnail at googlegroups.com https://groups.google.com/d/forum/sorl-thumbnail
@@ -133,7 +139,7 @@ Frequently asked questions
Is so slow in Amazon S3 !
-------------------------
-Posible related to the implementation of your Amazon S3 Backend, see the `issue #351`_
+Possible 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
@@ -144,13 +150,15 @@ A fast workaround if you are not willing to tweak your storage backend is to set
So it will avoid to overly query the S3 API.
-
-.. |travis| image:: https://secure.travis-ci.org/mariocesar/sorl-thumbnail.png?branch=master
- :target: https://travis-ci.org/mariocesar/sorl-thumbnail
+.. |travis| image:: https://travis-ci.org/jazzband/sorl-thumbnail.svg?branch=master
+ :target: https://travis-ci.org/jazzband/sorl-thumbnail
.. |pypi| image:: https://badge.fury.io/py/sorl-thumbnail.png
:target: http://badge.fury.io/py/sorl-thumbnail
-.. |coveralls| image:: https://coveralls.io/repos/mariocesar/sorl-thumbnail/badge.png?branch=master
- :target: https://coveralls.io/r/mariocesar/sorl-thumbnail?branch=master
+.. |coveralls| image:: https://coveralls.io/repos/jazzband//sorl-thumbnail/badge.png?branch=master
+ :target: https://coveralls.io/r/jazzband//sorl-thumbnail?branch=master
+.. |jazzband| image:: https://jazzband.co/static/img/jazzband.svg
+ :target: https://jazzband.co/
+ :alt: Jazzband
.. _`Pillow`: http://pillow.readthedocs.org/en/latest/
.. _`ImageMagick`: http://www.imagemagick.org/script/index.php
@@ -162,5 +170,5 @@ So it will avoid to overly query the S3 API.
.. _`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
-.. _`issue #351`: https://github.com/mariocesar/sorl-thumbnail/issues/351
+.. _`issue #351`: https://github.com/jazzband/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 3301b09..04a1861 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -39,7 +39,7 @@ For full list of tox environments, see ``tox.ini``
You can get away without using Vagrant if you install all packages locally yourself,
however, this is not recommended.
-.. _Travis CI: https://travis-ci.org/mariocesar/sorl-thumbnail
+.. _Travis CI: https://travis-ci.org/jazzband/sorl-thumbnail
.. _Vagrant: http://www.vagrantup.com/
.. _tox: https://testrun.org/tox/latest/
.. _Install Vagrant: http://docs.vagrantup.com/v2/installation/index.html
@@ -51,7 +51,7 @@ Sending pull requests
1. Fork the repo::
- git at github.com:mariocesar/sorl-thumbnail.git
+ git at github.com:jazzband/sorl-thumbnail.git
2. Create a branch for your specific changes::
diff --git a/docs/news.rst b/docs/news.rst
index e0ffd1b..356ca5b 100644
--- a/docs/news.rst
+++ b/docs/news.rst
@@ -2,13 +2,20 @@
News
====
-Next Release
-============
+Announcements
+=============
-A final 12.0 version is planned for end of 2013.
+2017-07-07
+----------
+
+ at Flimm: sorl-thumbnail was welcomed into the `Jazzband organization and project
+<https://jazzband.co/>`__. Jazzband is open to all, and any member of Jazzband
+can contribute directly to sorl-thumbnail's GitHub repo. We hope this will
+encourage more open source programmers to contribute. Thank you @mariocesar for
+taking this step and for the years of effort in this project.
-Announces
-=========
+ at Flimm: As part of joining Jazzband, the GitHub repo has been moved to
+https://github.com/jazzband/sorl-thumbnail
2013-11-12
----------
diff --git a/setup.cfg b/setup.cfg
index 2a9acf1..48bded5 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,2 +1,83 @@
+[wheel]
+universal = 1
+
[bdist_wheel]
universal = 1
+
+;; flake8 analyzes and detect varios errors in the source code.
+
+[flake8]
+# D105 - Missing docstring in magic method `__func__`
+ignore = D105
+max-line-length = 100
+exclude = .git,
+ .tox,
+ docs/,
+ */migrations/*,
+ tests/settings/
+ tests/thumbnail_tests/compat.py,
+ sorl/thumbnail/__init__.py,
+ sorl/thumbnail/admin/__init__.py,
+ sorl/thumbnail/compat.py
+
+max_complexity = 15
+
+;; Coverage.py, Code coverage testing for Python.
+
+[cov:run]
+source = sorl
+omit = */sorl-thumbnail/sorl/__init__.py
+ */sorl/thumbnail/__init__.py
+ */sorl/thumbnail/conf/__init__.py
+ */sorl/thumbnail/admin/compat.py
+ */sorl/thumbnail/admin/__init__.py
+ */sorl/thumbnail/compat.py
+
+[cov:report]
+exclude_lines = pragma: no cover
+ if __name__ == .__main__.:
+
+;; The pytest framework
+
+[tool:pytest]
+python_files = test_*.py *tests.py
+norecursedirs = .* tmp* __pycache__
+testpaths = tests
+django_find_project = false
+
+;; Tox is a generic virtualenv management to run tests in different configurations.
+
+[tox]
+skipsdist = True
+envlist = {py27,py33,py34,py35}-django18-{pil,imagemagick,graphicsmagick,redis,dynamodb,wand,pgmagick,dbm,vipsthumbnail},
+ {py27,py34,py35}-django110-{pil,imagemagick,graphicsmagick,redis,dynamodb,wand,pgmagick,dbm,vipsthumbnail},
+ {py27,py34,py35,py36}-django111-{pil,imagemagick,graphicsmagick,redis,dynamodb,wand,pgmagick,dbm,vipsthumbnail}
+
+[testenv]
+changedir = {toxinidir}/tests
+deps = pytest
+ pytest-cov
+ coverage <= 3.7.1
+ pytest-django
+ Pillow
+ redis: redis
+ dynamodb: boto
+ pgmagick: pgmagick
+ wand: wand
+ django18: Django>=1.8,<1.9
+ django19: Django>=1.9,<1.10
+ django110: Django>=1.10a1,<1.11
+ django111: Django>=1.11a,<1.12
+
+setenv = PYTHONPATH = {toxinidir}:{toxinidir}
+ pil: DJANGO_SETTINGS_MODULE=tests.settings.pil
+ imagemagick: DJANGO_SETTINGS_MODULE=tests.settings.imagemagick
+ graphicsmagick: DJANGO_SETTINGS_MODULE=tests.settings.graphicsmagick
+ vipsthumbnail: DJANGO_SETTINGS_MODULE=tests.settings.vipsthumbnail
+ redis: DJANGO_SETTINGS_MODULE=tests.settings.redis
+ dynamodb: DJANGO_SETTINGS_MODULE=tests.settings.dynamodb
+ wand: DJANGO_SETTINGS_MODULE=tests.settings.wand
+ pgmagick: DJANGO_SETTINGS_MODULE=tests.settings.pgmagick
+ dbm: DJANGO_SETTINGS_MODULE=tests.settings.dbm
+
+commands = py.test -rw --cov-config setup.cfg --cov sorl
diff --git a/setup.py b/setup.py
index b17c9c4..84bbf31 100644
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@ setup(
maintainer=__maintainer__,
maintainer_email=__email__,
license=__license__,
- url='https://github.com/mariocesar/sorl-thumbnail',
+ url='https://github.com/jazzband/sorl-thumbnail',
packages=find_packages(exclude=['tests', 'tests.*']),
platforms='any',
zip_safe=False,
@@ -36,12 +36,14 @@ setup(
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Multimedia :: Graphics',
'Framework :: Django',
'Framework :: Django :: 1.8',
- 'Framework :: Django :: 1.9',
'Framework :: Django :: 1.10',
+ 'Framework :: Django :: 1.11',
],
cmdclass={"test": TestCommand},
)
diff --git a/sorl/__init__.py b/sorl/__init__.py
index 19fb7cf..9f005e5 100644
--- a/sorl/__init__.py
+++ b/sorl/__init__.py
@@ -14,6 +14,7 @@ class NullHandler(logging.Handler):
def emit(self, record):
pass
+
# Add a logging handler that does nothing to silence messages with no logger
# configured
logging.getLogger('sorl').addHandler(NullHandler())
diff --git a/sorl/thumbnail/admin/current.py b/sorl/thumbnail/admin/current.py
index 175de85..b96ac4f 100644
--- a/sorl/thumbnail/admin/current.py
+++ b/sorl/thumbnail/admin/current.py
@@ -46,7 +46,7 @@ class AdminImageWidget(forms.ClearableFileInput):
'target="_blank" href="%s">'
'<img src="%s"></a>%s</div>'
) % (mini.width, value.url, mini.url, output)
- except AttributeError:
+ except (AttributeError, TypeError):
pass
return mark_safe(output)
diff --git a/sorl/thumbnail/base.py b/sorl/thumbnail/base.py
index c9ed177..9ae6f8a 100644
--- a/sorl/thumbnail/base.py
+++ b/sorl/thumbnail/base.py
@@ -76,10 +76,12 @@ class ThumbnailBackend(object):
if file_:
source = ImageFile(file_)
- elif settings.THUMBNAIL_DUMMY:
- return DummyImageFile(geometry_string)
else:
- return None
+ if settings.THUMBNAIL_DUMMY:
+ return DummyImageFile(geometry_string)
+ else:
+ logger.error('missing file_ argument in get_thumbnail()')
+ return
# preserve image filetype
if settings.THUMBNAIL_PRESERVE_FORMAT:
diff --git a/sorl/thumbnail/engines/base.py b/sorl/thumbnail/engines/base.py
index b8e2a9f..a3698bb 100644
--- a/sorl/thumbnail/engines/base.py
+++ b/sorl/thumbnail/engines/base.py
@@ -43,8 +43,16 @@ class EngineBase(object):
"""
if options.get('orientation', settings.THUMBNAIL_ORIENTATION):
return self._orientation(image)
+ self.reoriented = True
return image
+ def flip_dimensions(self, image, geometry=None, options=None):
+ options = options or {}
+ reoriented = hasattr(self, 'reoriented')
+ if options.get('orientation', settings.THUMBNAIL_ORIENTATION) and not reoriented:
+ return self._flip_dimensions(image)
+ return False
+
def colorspace(self, image, geometry, options):
"""
Wrapper for ``_colorspace``
diff --git a/sorl/thumbnail/engines/convert_engine.py b/sorl/thumbnail/engines/convert_engine.py
index 18664fc..5f3806c 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
+import logging
from collections import OrderedDict
from django.utils.encoding import smart_str
@@ -12,6 +13,7 @@ from sorl.thumbnail.compat import b
from sorl.thumbnail.conf import settings
from sorl.thumbnail.engines.base import EngineBase
+logger = logging.getLogger(__name__)
size_re = re.compile(r'^(?:.+) (?:[A-Z]+) (?P<x>\d+)x(?P<y>\d+)')
@@ -53,11 +55,16 @@ class Engine(EngineBase):
args.append(fp.name)
args = map(smart_str, args)
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- p.wait()
+ returncode = p.wait()
out, err = p.communicate()
- if err:
- raise Exception(err)
+ if returncode:
+ raise EngineError(
+ "The command %r exited with a non-zero exit code and printed this to stderr: %s"
+ % (args, err)
+ )
+ elif err:
+ logger.error("Captured stderr: %s", err)
thumbnail.write(fp.read())
@@ -134,6 +141,17 @@ class Engine(EngineBase):
image['options']['auto-orient'] = None
return image
+ def _flip_dimensions(self, image):
+ if settings.THUMBNAIL_CONVERT.endswith('gm convert'):
+ args = settings.THUMBNAIL_IDENTIFY.split()
+ args.extend(['-format', '%[exif:orientation]', image['source']])
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ p.wait()
+ result = p.stdout.read().strip()
+ return result and result != 'unknown' and int(result) in [5, 6, 7, 8]
+ else:
+ return False
+
def _colorspace(self, image, colorspace):
"""
`Valid colorspaces
@@ -170,3 +188,7 @@ class Engine(EngineBase):
image['options']['gravity'] = 'center'
image['options']['extent'] = '%sx%s' % (geometry[0], geometry[1])
return image
+
+
+class EngineError(Exception):
+ pass
diff --git a/sorl/thumbnail/engines/pgmagick_engine.py b/sorl/thumbnail/engines/pgmagick_engine.py
index 62340e0..443dfaf 100644
--- a/sorl/thumbnail/engines/pgmagick_engine.py
+++ b/sorl/thumbnail/engines/pgmagick_engine.py
@@ -56,6 +56,14 @@ class Engine(EngineBase):
return image
+ def flip_dimensions(self, image):
+ return image.orientation() in [
+ OrientationType.LeftTopOrientation,
+ OrientationType.RightTopOrientation,
+ OrientationType.RightBottomOrientation,
+ OrientationType.LeftBottomOrientation,
+ ]
+
def _colorspace(self, image, colorspace):
if colorspace == 'RGB':
image.type(ImageType.TrueColorMatteType)
diff --git a/sorl/thumbnail/engines/pil_engine.py b/sorl/thumbnail/engines/pil_engine.py
index fef582a..295d1ca 100644
--- a/sorl/thumbnail/engines/pil_engine.py
+++ b/sorl/thumbnail/engines/pil_engine.py
@@ -11,6 +11,8 @@ except ImportError:
import ImageFile
import ImageDraw
+EXIF_ORIENTATION = 0x0112
+
def round_corner(radius, fill):
"""Draw a round corner"""
@@ -73,7 +75,7 @@ class Engine(EngineBase):
exif = None
if exif:
- orientation = exif.get(0x0112)
+ orientation = exif.get(EXIF_ORIENTATION)
if orientation == 2:
image = image.transpose(Image.FLIP_LEFT_RIGHT)
@@ -92,6 +94,18 @@ class Engine(EngineBase):
return image
+ def _flip_dimensions(self, image):
+ try:
+ exif = image._getexif()
+ except (AttributeError, IOError, KeyError, IndexError):
+ exif = None
+
+ if exif:
+ orientation = exif.get(0x0112)
+ return orientation in [5, 6, 7, 8]
+
+ return False
+
def _colorspace(self, image, colorspace):
if colorspace == 'RGB':
if image.mode == 'RGBA':
diff --git a/sorl/thumbnail/engines/wand_engine.py b/sorl/thumbnail/engines/wand_engine.py
index 189e2ef..3ac2141 100644
--- a/sorl/thumbnail/engines/wand_engine.py
+++ b/sorl/thumbnail/engines/wand_engine.py
@@ -48,6 +48,9 @@ class Engine(EngineBase):
image.orientation = 'top_left'
return image
+ def _flip_dimensions(self, image):
+ return image.orientation in ['left_top', 'right_top', 'right_bottom', 'left_bottom']
+
def _colorspace(self, image, colorspace):
if colorspace == 'RGB':
if image.alpha_channel:
diff --git a/sorl/thumbnail/images.py b/sorl/thumbnail/images.py
index afc7d5b..7a4d1ca 100644
--- a/sorl/thumbnail/images.py
+++ b/sorl/thumbnail/images.py
@@ -138,8 +138,18 @@ class ImageFile(BaseImageFile):
# This is the worst case scenario
image = default.engine.get_image(self)
size = default.engine.get_image_size(image)
+ if self.flip_dimensions(image):
+ size = list(size)
+ size.reverse()
self._size = list(size)
+ def flip_dimensions(self, image):
+ """
+ Do not manipulate image, but ask engine whether we'd be doing a 90deg
+ rotation at some point.
+ """
+ return default.engine.flip_dimensions(image)
+
@property
def size(self):
return self._size
@@ -149,7 +159,11 @@ class ImageFile(BaseImageFile):
return self.storage.url(self.name)
def read(self):
- return self.storage.open(self.name).read()
+ f = self.storage.open(self.name)
+ try:
+ return f.read()
+ finally:
+ f.close()
def write(self, content):
if not isinstance(content, File):
@@ -228,7 +242,7 @@ class UrlStorage(Storage):
def delete_all_thumbnails():
storage = default.storage
- path = os.path.join(storage.location, settings.THUMBNAIL_PREFIX)
+ path = settings.THUMBNAIL_PREFIX
def walk(path):
dirs, files = storage.listdir(path)
diff --git a/sorl/thumbnail/kvstores/cached_db_kvstore.py b/sorl/thumbnail/kvstores/cached_db_kvstore.py
index c969ab8..5b595e2 100644
--- a/sorl/thumbnail/kvstores/cached_db_kvstore.py
+++ b/sorl/thumbnail/kvstores/cached_db_kvstore.py
@@ -46,8 +46,11 @@ class KVStore(KVStoreBase):
return value
def _set_raw(self, key, value):
- KVStoreModel.objects.get_or_create(
+ kvstore_value, created = KVStoreModel.objects.get_or_create(
key=key, defaults={'value': value})
+ if not created:
+ kvstore_value.value = value
+ kvstore_value.save()
self.cache.set(key, value, settings.THUMBNAIL_CACHE_TIMEOUT)
def _delete_raw(self, *keys):
diff --git a/tests/.coveragerc b/tests/.coveragerc
deleted file mode 100644
index d5f54c9..0000000
--- a/tests/.coveragerc
+++ /dev/null
@@ -1,16 +0,0 @@
-[run]
-source = sorl
-
-omit =
- */sorl-thumbnail/sorl/__init__.py
- */sorl/thumbnail/__init__.py
- */sorl/thumbnail/conf/__init__.py
- */sorl/thumbnail/admin/compat.py
- */sorl/thumbnail/admin/__init__.py
- */sorl/thumbnail/compat.py
-
-[report]
-exclude_lines =
- pragma: no cover
- if __name__ == .__main__.:
-
diff --git a/tests/thumbnail_tests/test_engines.py b/tests/thumbnail_tests/test_engines.py
index 4ef4617..74899aa 100644
--- a/tests/thumbnail_tests/test_engines.py
+++ b/tests/thumbnail_tests/test_engines.py
@@ -392,7 +392,7 @@ class ImageValidationTestCase(unittest.TestCase):
def setUp(self):
self.BACKEND = get_module_class(settings.THUMBNAIL_BACKEND)()
- @unittest.expectedFailure # See issue #427
+ @unittest.skip("See issue #427")
def test_truncated_validation(self):
"""
Test that is_valid_image returns false for a truncated image.
@@ -405,8 +405,7 @@ class ImageValidationTestCase(unittest.TestCase):
self.assertFalse(engine.is_valid_image(data))
- @unittest.expectedFailure
- # See issue #427. This seems to not-fail with wand.
+ @unittest.skip("See issue #427. This seems to not-fail with wand")
def test_truncated_generation_failure(self):
"""
Confirm that generating a thumbnail for our "broken" image fails.
diff --git a/tests/thumbnail_tests/views.py b/tests/thumbnail_tests/views.py
index d3e07e1..eb9fe10 100644
--- a/tests/thumbnail_tests/views.py
+++ b/tests/thumbnail_tests/views.py
@@ -1,8 +1,7 @@
-from django.template import loader, RequestContext
+from django.template import loader
from django.http import HttpResponse
def direct_to_template(request, template, mimetype=None, **kwargs):
- c = RequestContext(request, {})
t = loader.get_template(template)
- return HttpResponse(t.render(c), content_type=mimetype)
+ return HttpResponse(t.render({'request': request}), content_type=mimetype)
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index d237c35..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,42 +0,0 @@
-[flake8]
-max-line-length=100
-exclude=.tox,docs,tests/settings,sorl/thumbnail/__init__.py,sorl/thumbnail/admin/__init__.py,sorl/thumbnail/compat.py,tests/thumbnail_tests/compat.py,sorl/thumbnail/migrations
-
-[pytest]
-python_files = test_*.py
-django_find_project = false
-
-[tox]
-skipsdist = True
-envlist =
- {py27,py34}-django{18,19,110}-{pil,imagemagick,graphicsmagick,redis,dynamodb,wand,pgmagick,dbm,vipsthumbnail}
-
-[testenv]
-changedir = {toxinidir}/tests
-deps =
- pytest
- pytest-cov
- coverage <= 3.7.1
- pytest-django
- Pillow
- redis: redis
- dynamodb: boto
- pgmagick: pgmagick
- wand: wand
- django18: Django>=1.8,<1.9
- django19: Django>=1.9,<1.10
- django110: Django>=1.10a1,<1.11
-
-setenv =
- PYTHONPATH = {toxinidir}:{toxinidir}
- pil: DJANGO_SETTINGS_MODULE=tests.settings.pil
- imagemagick: DJANGO_SETTINGS_MODULE=tests.settings.imagemagick
- graphicsmagick: DJANGO_SETTINGS_MODULE=tests.settings.graphicsmagick
- vipsthumbnail: DJANGO_SETTINGS_MODULE=tests.settings.vipsthumbnail
- redis: DJANGO_SETTINGS_MODULE=tests.settings.redis
- dynamodb: DJANGO_SETTINGS_MODULE=tests.settings.dynamodb
- wand: DJANGO_SETTINGS_MODULE=tests.settings.wand
- pgmagick: DJANGO_SETTINGS_MODULE=tests.settings.pgmagick
- dbm: DJANGO_SETTINGS_MODULE=tests.settings.dbm
-
-commands = py.test -rw --cov-config .coveragerc --cov sorl
--
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