diff --git a/.gitignore b/.gitignore index 575a8003..7a5aeaa0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,11 +4,14 @@ *.pyc .DS_Store .tox +.idea +.vscode MANIFEST build dist /tests/media/* -!/tests/media/lenna.png +!/tests/media/reference.png /venv /venv3 /.env +/tags diff --git a/.travis.yml b/.travis.yml index 563884a3..b8528b43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,46 +1,62 @@ + language: python -python: "2.7" + sudo: false -env: - - TOX_ENV=py26-django12 - - TOX_ENV=py26-django13 - - TOX_ENV=py26-django14 - - TOX_ENV=py26-django15 - - TOX_ENV=py26-django16 - - TOX_ENV=py27-django12 - - TOX_ENV=py27-django13 - - TOX_ENV=py27-django14 - - TOX_ENV=py27-django15 - - TOX_ENV=py27-django16 - - TOX_ENV=py27-django17 - - TOX_ENV=py27-django18 - - TOX_ENV=py27-django19 - - TOX_ENV=py32-django15 - - TOX_ENV=py32-django16 - - TOX_ENV=py32-django17 - - TOX_ENV=py32-django18 - - TOX_ENV=py33-django15 - - TOX_ENV=py33-django16 - - TOX_ENV=py33-django17 - - TOX_ENV=py33-django18 - - TOX_ENV=py34-django16 - - TOX_ENV=py34-django17 - - TOX_ENV=py34-django18 - - TOX_ENV=py34-django19 - - TOX_ENV=py35-django19 +install: + - pip install tox + +script: + - tox matrix: - # Python 3.5 not yet available on travis, watch this to see when it is. fast_finish: true - allow_failures: - - env: TOX_ENV=py35-django19 - -install: - - pip install tox --use-mirrors + include: + - env: TOXENV=py27-django14 + python: 2.7 + - env: TOXENV=py27-django15 + python: 2.7 + - env: TOXENV=py27-django16 + python: 2.7 + - env: TOXENV=py27-django17 + python: 2.7 + - env: TOXENV=py27-django18 + python: 2.7 + - env: TOXENV=py27-django19 + python: 2.7 + - env: TOXENV=py27-django110 + python: 2.7 + - env: TOXENV=py27-django111 + python: 2.7 + - env: TOXENV=py33-django15 + python: 3.3 + - env: TOXENV=py33-django16 + python: 3.3 + - env: TOXENV=py33-django17 + python: 3.3 + - env: TOXENV=py33-django18 + python: 3.3 + - env: TOXENV=py34-django16 + python: 3.4 + - env: TOXENV=py34-django17 + python: 3.4 + - env: TOXENV=py34-django18 + python: 3.4 + - env: TOXENV=py34-django19 + python: 3.4 + - env: TOXENV=py34-django110 + python: 3.4 + - env: TOXENV=py34-django111 + python: 3.4 + - env: TOXENV=py35-django18 + python: 3.5 + - env: TOXENV=py35-django19 + python: 3.5 + - env: TOXENV=py35-django110 + python: 3.5 + - env: TOXENV=py35-django111 + python: 3.5 -script: - - tox -e $TOX_ENV notifications: irc: "irc.freenode.org#imagekit" diff --git a/MANIFEST.in b/MANIFEST.in index 94be0c57..b125b907 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,18 @@ include AUTHORS include LICENSE include README.rst -recursive-include docs * -recursive-include imagekit/templates * -prune tests +include testrunner.py +include setup.cfg +include tests/*.py +include tests/assets/Lenna.png +include tests/assets/lenna-*.jpg +include tests/media/lenna.png +prune tests/media/CACHE +prune tests/media/b +prune tests/media/photos +include docs/Makefile +include docs/conf.py +include docs/make.bat +include docs/*.rst +recursive-include docs/_themes LICENSE README.rst flask_theme_support.py theme.conf *.css_t *.css *.html +recursive-include imagekit/templates *.html diff --git a/README.rst b/README.rst index bb3bb002..84ec2944 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ |Build Status|_ -.. |Build Status| image:: https://travis-ci.org/matthewwithanm/django-imagekit.png?branch=develop +.. |Build Status| image:: https://travis-ci.org/matthewwithanm/django-imagekit.svg?branch=develop .. _Build Status: https://travis-ci.org/matthewwithanm/django-imagekit ImageKit is a Django app for processing images. Need a thumbnail? A diff --git a/docs/caching.rst b/docs/caching.rst index 6aa7a1e1..4e2b8a91 100644 --- a/docs/caching.rst +++ b/docs/caching.rst @@ -100,13 +100,21 @@ ImageKit. Each has its own pros and cons. Caching Data About Generated Files ---------------------------------- -The easiest, and most significant improvement you can make to improve the -performance of your site is to have ImageKit cache the state of your generated -files. The default cache file backend will already do this (if ``DEBUG`` is -``False``), using your default Django cache backend, but you can make it way -better by setting ``IMAGEKIT_CACHE_BACKEND``. Generally, once a file is -generated, you will never be removing it; therefore, if you can, you should set -``IMAGEKIT_CACHE_BACKEND`` to a cache backend that will cache forever. +Generally, once a file is generated, you will never be removing it, so by +default ImageKit will use default cache to cache the state of generated +files "forever" (or only 5 minutes when ``DEBUG = True``). + +The time for which ImageKit will cache state is configured with +``IMAGEKIT_CACHE_TIMEOUT``. If set to ``None`` this means "never expire" +(default when ``DEBUG = False``). You can reduce this timeout if you want +or set it to some numeric value in seconds if your cache backend behaves +differently and for example do not cache values if timeout is ``None``. + +If you clear your cache durring deployment or some other reason probably +you do not want to lose the cache for generated images especcialy if you +are using some slow remote storage (like Amazon S3). Then you can configure +seprate cache (for example redis) in your ``CACHES`` config and tell ImageKit +to use it instead of the default cache by setting ``IMAGEKIT_CACHE_BACKEND``. Pre-Generating Images diff --git a/docs/configuration.rst b/docs/configuration.rst index f65ed526..236191e3 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -55,6 +55,15 @@ Settings .. _`Django cache section`: https://docs.djangoproject.com/en/1.8/topics/cache/#accessing-the-cache +.. attribute:: IMAGEKIT_CACHE_TIMEOUT + + :default: ``None`` + + Use when you need to override the timeout used to cache file state. + By default it is "cache forever". + It's highly recommended that you use a very high timeout. + + .. attribute:: IMAGEKIT_CACHE_PREFIX :default: ``'imagekit:'`` diff --git a/imagekit/cachefiles/backends.py b/imagekit/cachefiles/backends.py index e615f2ea..e006dc80 100644 --- a/imagekit/cachefiles/backends.py +++ b/imagekit/cachefiles/backends.py @@ -2,6 +2,7 @@ import warnings from copy import copy from django.core.exceptions import ImproperlyConfigured +from django.conf import settings class CacheFileState(object): @@ -52,8 +53,7 @@ class CachedFileBackend(object): @property def cache(self): if not getattr(self, '_cache', None): - from django.conf import settings - self._cache = get_cache(settings.IMAGEKIT_CACHE_BACKEND) + self._cache = get_cache() return self._cache def get_key(self, file): @@ -75,7 +75,7 @@ def set_state(self, file, state): if state == CacheFileState.DOES_NOT_EXIST: self.cache.set(key, state, self.existence_check_timeout) else: - self.cache.set(key, state) + self.cache.set(key, state, settings.IMAGEKIT_CACHE_TIMEOUT) def __getstate__(self): state = copy(self.__dict__) diff --git a/imagekit/templatetags/compat.py b/imagekit/compat.py similarity index 100% rename from imagekit/templatetags/compat.py rename to imagekit/compat.py diff --git a/imagekit/conf.py b/imagekit/conf.py index 84dffb6b..9d2ca1f3 100644 --- a/imagekit/conf.py +++ b/imagekit/conf.py @@ -1,5 +1,6 @@ from appconf import AppConf from django.conf import settings +from django.core.exceptions import ImproperlyConfigured class ImageKitConf(AppConf): @@ -13,32 +14,24 @@ class ImageKitConf(AppConf): CACHE_BACKEND = None CACHE_PREFIX = 'imagekit:' + CACHE_TIMEOUT = None USE_MEMCACHED_SAFE_CACHE_KEY = True def configure_cache_backend(self, value): if value is None: - # DEFAULT_CACHE_ALIAS doesn't exist in Django<=1.2 - try: - from django.core.cache import DEFAULT_CACHE_ALIAS as default_cache_alias - except ImportError: - default_cache_alias = 'default' - - caches = getattr(settings, 'CACHES', None) - if caches is None: - # Support Django<=1.2 there is no default `CACHES` setting - try: - from django.core.cache.backends.dummy import DummyCache - except ImportError: - dummy_cache = 'dummy://' - else: - dummy_cache = 'django.core.cache.backends.dummy.DummyCache' - return dummy_cache - - if default_cache_alias in caches: - value = default_cache_alias - else: - raise ValueError("The default cache alias '%s' is not available in CACHES" % default_cache_alias) + from django.core.cache import DEFAULT_CACHE_ALIAS + return DEFAULT_CACHE_ALIAS + if value not in settings.CACHES: + raise ImproperlyConfigured("{0} is not present in settings.CACHES".format(value)) + + return value + + def configure_cache_timeout(self, value): + if value is None and settings.DEBUG: + # If value is not configured and is DEBUG set it to 5 minutes + return 300 + # Otherwise leave it as is. If it is None then valies will never expire return value def configure_default_file_storage(self, value): diff --git a/imagekit/files.py b/imagekit/files.py index e7178880..84fa544f 100644 --- a/imagekit/files.py +++ b/imagekit/files.py @@ -56,7 +56,18 @@ def _get_size(self): def open(self, mode='rb'): self._require_file() - self.file.open(mode) + try: + self.file.open(mode) + except ValueError: + # if the underlaying file can't be reopened + # then we will use the storage to try to open it again + if self.file.closed: + # clear cached file instance + del self.file + # Because file is a property we can acces it after + # we deleted it + return self.file.open(mode) + raise def _get_closed(self): file = getattr(self, '_file', None) diff --git a/imagekit/forms/fields.py b/imagekit/forms/fields.py index d032ff85..cf3ccba6 100644 --- a/imagekit/forms/fields.py +++ b/imagekit/forms/fields.py @@ -24,6 +24,10 @@ def clean(self, data, initial=None): if data and data != initial: spec = self.get_spec(source=data) - data = generate(spec) + f = generate(spec) + # Name is required in Django 1.4. When we drop support for it + # then we can dirrectly return the result from `generate(spec)` + f.name = data.name + return f return data diff --git a/imagekit/models/fields/__init__.py b/imagekit/models/fields/__init__.py index 2543f905..04a65460 100644 --- a/imagekit/models/fields/__init__.py +++ b/imagekit/models/fields/__init__.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +from django.conf import settings from django.db import models from django.db.models.signals import class_prepared from .files import ProcessedImageFieldFile @@ -111,9 +112,11 @@ def contribute_to_class(self, cls, name): return super(ProcessedImageField, self).contribute_to_class(cls, name) -try: - from south.modelsinspector import add_introspection_rules -except ImportError: - pass -else: - add_introspection_rules([], [r'^imagekit\.models\.fields\.ProcessedImageField$']) +# If the project does not use south, then we will not try to add introspection +if 'south' in settings.INSTALLED_APPS: + try: + from south.modelsinspector import add_introspection_rules + except ImportError: + pass + else: + add_introspection_rules([], [r'^imagekit\.models\.fields\.ProcessedImageField$']) diff --git a/imagekit/pkgmeta.py b/imagekit/pkgmeta.py index 5872ac01..9a389ab8 100644 --- a/imagekit/pkgmeta.py +++ b/imagekit/pkgmeta.py @@ -1,5 +1,5 @@ __title__ = 'django-imagekit' __author__ = 'Matthew Tretter, Venelin Stoykov, Eric Eldredge, Bryan Veloso, Greg Newman, Chris Drackett, Justin Driscoll' -__version__ = '3.3' +__version__ = '4.0' __license__ = 'BSD' __all__ = ['__title__', '__author__', '__version__', '__license__'] diff --git a/imagekit/specs/__init__.py b/imagekit/specs/__init__.py index 829dce1f..1ef298e1 100644 --- a/imagekit/specs/__init__.py +++ b/imagekit/specs/__init__.py @@ -143,6 +143,7 @@ def generate(self): raise MissingSource("The spec '%s' has no source file associated" " with it." % self) + file_opened_locally = False # TODO: Move into a generator base class # TODO: Factor out a generate_image function so you can create a generator and only override the PIL.Image creating part. (The tricky part is how to deal with original_format since generator base class won't have one.) try: @@ -151,12 +152,14 @@ def generate(self): # Re-open the file -- https://code.djangoproject.com/ticket/13750 self.source.open() + file_opened_locally = True img = open_image(self.source) new_image = process_image(img, processors=self.processors, format=self.format, autoconvert=self.autoconvert, options=self.options) - self.source.close() + if file_opened_locally: + self.source.close() return new_image diff --git a/imagekit/specs/sourcegroups.py b/imagekit/specs/sourcegroups.py index d8ea3a7f..03a33224 100644 --- a/imagekit/specs/sourcegroups.py +++ b/imagekit/specs/sourcegroups.py @@ -87,12 +87,15 @@ def get_source_fields(self, instance): if isinstance(instance, src.model_class)) @ik_model_receiver - def post_save_receiver(self, sender, instance=None, created=False, raw=False, **kwargs): + def post_save_receiver(self, sender, instance=None, created=False, update_fields=None, raw=False, **kwargs): if not raw: self.init_instance(instance) old_hashes = instance._ik.get('source_hashes', {}).copy() new_hashes = self.update_source_hashes(instance) for attname in self.get_source_fields(instance): + if update_fields and attname not in update_fields: + continue + file = getattr(instance, attname) if file and old_hashes.get(attname) != new_hashes[attname]: self.dispatch_signal(source_saved, file, sender, instance, diff --git a/imagekit/templatetags/imagekit.py b/imagekit/templatetags/imagekit.py index 25c2cca9..5a69c915 100644 --- a/imagekit/templatetags/imagekit.py +++ b/imagekit/templatetags/imagekit.py @@ -4,7 +4,7 @@ from django.utils.html import escape from django.utils.safestring import mark_safe -from .compat import parse_bits +from ..compat import parse_bits from ..cachefiles import ImageCacheFile from ..registry import generator_registry from ..lib import force_text diff --git a/imagekit/utils.py b/imagekit/utils.py index d768f570..27902c9a 100644 --- a/imagekit/utils.py +++ b/imagekit/utils.py @@ -69,6 +69,30 @@ def autodiscover(): """ global _autodiscovered + if _autodiscovered: + return + + try: + from django.utils.module_loading import autodiscover_modules + except ImportError: + # Django<1.7 + _autodiscover_modules_fallback() + else: + autodiscover_modules('imagegenerators') + + +def _autodiscover_modules_fallback(): + """ + Auto-discover INSTALLED_APPS imagegenerators.py modules and fail silently + when not present. This forces an import on them to register any admin bits + they may want. + + Copied from django.contrib.admin + + Used for Django versions < 1.7 + """ + global _autodiscovered + if _autodiscovered: return @@ -132,16 +156,13 @@ def generate(generator): """ content = generator.generate() - - # If the file doesn't have a name, Django will raise an Exception while - # trying to save it, so we create a named temporary file. - if not getattr(content, 'name', None): - f = NamedTemporaryFile() - f.write(content.read()) - f.seek(0) - content = f - - return File(content) + f = File(content) + # The size of the File must be known or Django will try to open a file + # without a name and raise an Exception. + f.size = len(content.read()) + # After getting the size reset the file pointer for future reads. + content.seek(0) + return f def call_strategy_method(file, method_name): @@ -151,14 +172,15 @@ def call_strategy_method(file, method_name): fn(file) -def get_cache(backend, **kwargs): +def get_cache(): try: from django.core.cache import caches except ImportError: + # Django < 1.7 from django.core.cache import get_cache - return get_cache(backend, **kwargs) + return get_cache(settings.IMAGEKIT_CACHE_BACKEND) - return caches[backend] + return caches[settings.IMAGEKIT_CACHE_BACKEND] def sanitize_cache_key(key): diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..7c2b2874 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 \ No newline at end of file diff --git a/setup.py b/setup.py index f26c81ab..8244049a 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ if 'publish' in sys.argv: - os.system('python setup.py sdist upload') + os.system('python setup.py sdist bdist_wheel upload') sys.exit() @@ -39,16 +39,16 @@ def exec_file(filepath, globalz=None, localz=None): maintainer_email='bryan@revyver.com', license='BSD', url='http://github.com/matthewwithanm/django-imagekit/', - packages=find_packages(), + packages=find_packages(exclude=['*.tests', '*.tests.*', 'tests.*', 'tests']), zip_safe=False, include_package_data=True, tests_require=[ - 'beautifulsoup4==4.1.3', - 'nose>=1.3.6,<1.4', - 'nose-progressive==1.5.1', - 'django-nose>=1.2,<1.5', - 'Pillow<3.0', - 'mock==1.0.1', + 'beautifulsoup4>=4.4.0', + 'nose>=1.3.6', + 'nose-progressive>=1.5.1', + 'django-nose>=1.4', + 'Pillow', + 'mock>=1.0.1', ], test_suite='testrunner.run_tests', install_requires=[ @@ -68,12 +68,11 @@ def exec_file(filepath, globalz=None, localz=None): 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Topic :: Utilities' ], ) diff --git a/testrunner.py b/testrunner.py index e4d27c77..6b41b0fb 100644 --- a/testrunner.py +++ b/testrunner.py @@ -16,4 +16,7 @@ def run_tests(): cls = get_runner(settings) runner = cls() failures = runner.run_tests(['tests']) + # Clean autogenerated junk before exit + from tests.utils import clear_imagekit_test_files + clear_imagekit_test_files() sys.exit(failures) diff --git a/tests/assets/Lenna.png b/tests/assets/Lenna.png deleted file mode 100644 index 59ef68aa..00000000 Binary files a/tests/assets/Lenna.png and /dev/null differ diff --git a/tests/assets/lenna-800x600-white-border.jpg b/tests/assets/lenna-800x600-white-border.jpg deleted file mode 100644 index d0b1183b..00000000 Binary files a/tests/assets/lenna-800x600-white-border.jpg and /dev/null differ diff --git a/tests/assets/lenna-800x600.jpg b/tests/assets/lenna-800x600.jpg deleted file mode 100644 index 7c2ccd83..00000000 Binary files a/tests/assets/lenna-800x600.jpg and /dev/null differ diff --git a/tests/media/lenna.png b/tests/media/lenna.png deleted file mode 100644 index 59ef68aa..00000000 Binary files a/tests/media/lenna.png and /dev/null differ diff --git a/tests/media/reference.png b/tests/media/reference.png new file mode 100644 index 00000000..385716ca Binary files /dev/null and b/tests/media/reference.png differ diff --git a/tests/settings.py b/tests/settings.py index 0c519896..dc3bb3c7 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -47,6 +47,23 @@ if os.getenv('TERM'): NOSE_ARGS.append('--with-progressive') -DEBUG = True -TEMPLATE_DEBUG = DEBUG CACHE_BACKEND = 'locmem://' + +# Django >= 1.8 +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.contrib.auth.context_processors.auth', + 'django.template.context_processors.debug', + 'django.template.context_processors.i18n', + 'django.template.context_processors.media', + 'django.template.context_processors.static', + 'django.template.context_processors.tz', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] diff --git a/tests/test_generateimage_tag.py b/tests/test_generateimage_tag.py index c39b7944..db3f5772 100644 --- a/tests/test_generateimage_tag.py +++ b/tests/test_generateimage_tag.py @@ -1,11 +1,12 @@ from django.template import TemplateSyntaxError from nose.tools import eq_, assert_false, raises, assert_not_equal from . import imagegenerators # noqa -from .utils import render_tag, get_html_attrs +from .utils import render_tag, get_html_attrs, clear_imagekit_cache def test_img_tag(): ttag = r"""{% generateimage 'testspec' source=img %}""" + clear_imagekit_cache() attrs = get_html_attrs(ttag) expected_attrs = set(['src', 'width', 'height']) eq_(set(attrs.keys()), expected_attrs) @@ -15,6 +16,7 @@ def test_img_tag(): def test_img_tag_attrs(): ttag = r"""{% generateimage 'testspec' source=img -- alt="Hello" %}""" + clear_imagekit_cache() attrs = get_html_attrs(ttag) eq_(attrs.get('alt'), 'Hello') @@ -42,11 +44,13 @@ def test_single_dimension_attr(): """ ttag = r"""{% generateimage 'testspec' source=img -- width="50" %}""" + clear_imagekit_cache() attrs = get_html_attrs(ttag) assert_false('height' in attrs) def test_assignment_tag(): - ttag = r"""{% generateimage 'testspec' source=img as th %}{{ th.url }}""" + ttag = r"""{% generateimage 'testspec' source=img as th %}{{ th.url }}{{ th.height }}{{ th.width }}""" + clear_imagekit_cache() html = render_tag(ttag) assert_not_equal(html.strip(), '') diff --git a/tests/test_serialization.py b/tests/test_serialization.py index 9b68af93..b995658d 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -6,10 +6,11 @@ from imagekit.cachefiles import ImageCacheFile from .imagegenerators import TestSpec -from .utils import create_photo, pickleback, get_unique_image_file +from .utils import create_photo, pickleback, get_unique_image_file, clear_imagekit_cache def test_imagespecfield(): + clear_imagekit_cache() instance = create_photo('pickletest2.jpg') thumbnail = pickleback(instance.thumbnail) thumbnail.generate() @@ -22,12 +23,14 @@ def test_circular_ref(): This corresponds to #234 """ + clear_imagekit_cache() instance = create_photo('pickletest3.jpg') instance.thumbnail # Cause thumbnail to be added to instance's __dict__ pickleback(instance) - + def test_cachefiles(): + clear_imagekit_cache() spec = TestSpec(source=get_unique_image_file()) file = ImageCacheFile(spec) file.url diff --git a/tests/test_thumbnail_tag.py b/tests/test_thumbnail_tag.py index e31304af..c9382241 100644 --- a/tests/test_thumbnail_tag.py +++ b/tests/test_thumbnail_tag.py @@ -1,11 +1,12 @@ from django.template import TemplateSyntaxError from nose.tools import eq_, raises, assert_not_equal from . import imagegenerators # noqa -from .utils import render_tag, get_html_attrs +from .utils import render_tag, get_html_attrs, clear_imagekit_cache def test_img_tag(): ttag = r"""{% thumbnail '100x100' img %}""" + clear_imagekit_cache() attrs = get_html_attrs(ttag) expected_attrs = set(['src', 'width', 'height']) eq_(set(attrs.keys()), expected_attrs) @@ -15,6 +16,7 @@ def test_img_tag(): def test_img_tag_attrs(): ttag = r"""{% thumbnail '100x100' img -- alt="Hello" %}""" + clear_imagekit_cache() attrs = get_html_attrs(ttag) eq_(attrs.get('alt'), 'Hello') @@ -50,17 +52,20 @@ def test_html_attrs_assignment(): def test_assignment_tag(): ttag = r"""{% thumbnail '100x100' img as th %}{{ th.url }}""" + clear_imagekit_cache() html = render_tag(ttag) assert_not_equal(html, '') def test_single_dimension(): ttag = r"""{% thumbnail '100x' img as th %}{{ th.width }}""" + clear_imagekit_cache() html = render_tag(ttag) eq_(html, '100') def test_alternate_generator(): ttag = r"""{% thumbnail '1pxsq' '100x' img as th %}{{ th.width }}""" + clear_imagekit_cache() html = render_tag(ttag) eq_(html, '1') diff --git a/tests/utils.py b/tests/utils.py index b6c49520..8b16a09a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,10 +1,12 @@ from bs4 import BeautifulSoup import os -from django.conf import settings +import shutil from django.core.files import File from django.template import Context, Template from imagekit.cachefiles.backends import Simple, CacheFileState +from imagekit.conf import settings from imagekit.lib import Image, StringIO +from imagekit.utils import get_cache from nose.tools import assert_true, assert_false import pickle from tempfile import NamedTemporaryFile @@ -17,9 +19,10 @@ def get_image_file(): http://en.wikipedia.org/wiki/Lenna http://sipi.usc.edu/database/database.php?volume=misc&image=12 - + https://lintian.debian.org/tags/license-problem-non-free-img-lenna.html + https://github.com/libav/libav/commit/8895bf7b78650c0c21c88cec0484e138ec511a4b """ - path = os.path.join(settings.MEDIA_ROOT, 'lenna.png') + path = os.path.join(settings.MEDIA_ROOT, 'reference.png') return open(path, 'r+b') @@ -81,3 +84,23 @@ class DummyAsyncCacheFileBackend(Simple): def generate(self, file, force=False): pass + + +def clear_imagekit_cache(): + cache = get_cache() + cache.clear() + # Clear IMAGEKIT_CACHEFILE_DIR + cache_dir = os.path.join(settings.MEDIA_ROOT, settings.IMAGEKIT_CACHEFILE_DIR) + if os.path.exists(cache_dir): + shutil.rmtree(cache_dir) + + +def clear_imagekit_test_files(): + clear_imagekit_cache() + for fname in os.listdir(settings.MEDIA_ROOT): + if fname != 'reference.png': + path = os.path.join(settings.MEDIA_ROOT, fname) + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.remove(path) diff --git a/tox.ini b/tox.ini index 2bc14a5e..9b127952 100644 --- a/tox.ini +++ b/tox.ini @@ -1,32 +1,60 @@ [tox] envlist = - py35-django19, - py34-django19, py34-django18, py34-django17, py34-django16, + py35-django111, py35-django110, py35-django19, py35-django18, + py34-django111, py34-django110, py34-django19, py34-django18, py34-django17, py34-django16, py33-django18, py33-django17, py33-django16, py33-django15, - py32-django18, py32-django17, py32-django16, py32-django15, - py27-django19, py27-django18, py27-django17, py27-django16, py27-django15, py27-django14, py27-django13, py27-django12, - py26-django16, py26-django15, py26-django14, py26-django13, py26-django12 + py27-django111, py27-django110, py27-django19, py27-django18, py27-django17, py27-django16, py27-django15, py27-django14, [testenv] commands = python setup.py test +[testenv:py35-django111] +basepython = python3.5 +deps = + Django>=1.11a1,<1.12 + django-nose==1.4.4 + +[testenv:py35-django110] +basepython = python3.5 +deps = + Django>=1.10,<1.11 + django-nose==1.4.4 + [testenv:py35-django19] basepython = python3.5 deps = - git+https://github.com/django/django.git@stable/1.9.x#egg=Django + Django>=1.9,<1.10 django-nose==1.4.2 +[testenv:py35-django18] +basepython = python3.5 +deps = + Django>=1.8,<1.9 + django-nose==1.4.2 + +[testenv:py34-django111] +basepython = python3.4 +deps = + Django>=1.11a1,<1.12 + django-nose==1.4.4 + +[testenv:py34-django110] +basepython = python3.4 +deps = + Django>=1.10,<1.11 + django-nose==1.4.4 + [testenv:py34-django19] basepython = python3.4 deps = - git+https://github.com/django/django.git@stable/1.9.x#egg=Django + Django>=1.9,<1.10 django-nose==1.4.2 [testenv:py34-django18] basepython = python3.4 deps = Django>=1.8,<1.9 - django-nose==1.4 + django-nose==1.4.2 [testenv:py34-django17] basepython = python3.4 @@ -38,12 +66,13 @@ deps = basepython = python3.4 deps = Django>=1.6,<1.7 + django-nose<=1.4.2 [testenv:py33-django18] basepython = python3.3 deps = Django>=1.8,<1.9 - django-nose==1.4 + django-nose==1.4.2 [testenv:py33-django17] basepython = python3.3 @@ -55,45 +84,37 @@ deps = basepython = python3.3 deps = Django>=1.6,<1.7 + django-nose<=1.4.2 [testenv:py33-django15] basepython = python3.3 deps = Django>=1.5,<1.6 - -[testenv:py32-django18] -basepython = python3.4 -deps = - Django>=1.8,<1.9 - django-nose==1.4 - -[testenv:py32-django17] -basepython = python3.4 -deps = - Django>=1.7,<1.8 django-nose==1.4 -[testenv:py32-django16] -basepython = python3.2 +[testenv:py27-django111] +basepython = python2.7 deps = - Django>=1.6,<1.7 + Django>=1.11a1,<1.12 + django-nose==1.4.4 -[testenv:py32-django15] -basepython = python3.2 +[testenv:py27-django110] +basepython = python2.7 deps = - Django>=1.5,<1.6 + Django>=1.10,<1.11 + django-nose==1.4.4 [testenv:py27-django19] basepython = python2.7 deps = - git+https://github.com/django/django.git@stable/1.9.x#egg=Django - git+https://github.com/django-nose/django-nose@master#egg=django-nose + Django>=1.9,<1.10 + django-nose==1.4.2 [testenv:py27-django18] basepython = python2.7 deps = Django>=1.8,<1.9 - django-nose==1.4 + django-nose==1.4.2 [testenv:py27-django17] basepython = python2.7 @@ -105,52 +126,16 @@ deps = basepython = python2.7 deps = Django>=1.6,<1.7 + django-nose<=1.4.2 [testenv:py27-django15] basepython = python2.7 deps = Django>=1.5,<1.6 + django-nose==1.4 [testenv:py27-django14] basepython = python2.7 deps = Django>=1.4,<1.5 - -[testenv:py27-django13] -basepython = python2.7 -deps = - Django>=1.3,<1.4 - django-nose==1.2 - -[testenv:py27-django12] -basepython = python2.7 -deps = - Django>=1.2,<1.3 - django-nose==1.2 - -[testenv:py26-django16] -basepython = python2.6 -deps = - Django>=1.6,<1.7 - -[testenv:py26-django15] -basepython = python2.6 -deps = - Django>=1.5,<1.6 - -[testenv:py26-django14] -basepython = python2.6 -deps = - Django>=1.4,<1.5 - -[testenv:py26-django13] -basepython = python2.6 -deps = - Django>=1.3,<1.4 - django-nose==1.2 - -[testenv:py26-django12] -basepython = python2.6 -deps = - Django>=1.2,<1.3 - django-nose==1.2 + django-nose==1.4