[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