mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-03-16 21:30:23 +00:00
Merge branch 'release/4.0'
* release/4.0: Bump version to 4.0 Universal wheels! Replaces #301 autodiscover works with AppConfig Ignore autogenerated CTags file Do not try south modelinspector when not needed Make it possible to configure IMAGEKIT_CACHE_TIMEOUT Test against Django 1.11 Close the file only if it has been opened locally Cleanup caching configuration updated readme.rst with a svg badge honor post_save's update_fields and only fire the source_saved signal when needed Ignore VSCode workspace config files Fixed #350: Error when trying to access width/height after url Fixes #382: Tests no longer leave junk Fixes #379 Support for Django 1.10 Ignore .idea from git Include the test suite in the sourcetarball but do not install it. Make travis happy Drop support for older Django and Python versions Replace Lenna image in tests with a truly free alternative. Move compat module outside of templatetags package Fix test requirements for django 1.9 and Python3.5
This commit is contained in:
commit
f724cb0118
31 changed files with 312 additions and 184 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -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
|
||||
|
|
|
|||
90
.travis.yml
90
.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
|
||||
|
||||
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
|
||||
- pip install tox
|
||||
|
||||
script:
|
||||
- tox -e $TOX_ENV
|
||||
- tox
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
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
|
||||
|
||||
|
||||
notifications:
|
||||
irc: "irc.freenode.org#imagekit"
|
||||
|
|
|
|||
18
MANIFEST.in
18
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:'``
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from ..utils import get_singleton, get_cache, sanitize_cache_key
|
|||
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 @@ class CachedFileBackend(object):
|
|||
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__)
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
from django.core.cache import DEFAULT_CACHE_ALIAS
|
||||
return DEFAULT_CACHE_ALIAS
|
||||
|
||||
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 value not in settings.CACHES:
|
||||
raise ImproperlyConfigured("{0} is not present in settings.CACHES".format(value))
|
||||
|
||||
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)
|
||||
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):
|
||||
|
|
|
|||
|
|
@ -56,7 +56,18 @@ class BaseIKFile(File):
|
|||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ class ProcessedImageField(ImageField, SpecHost):
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 @@ class ProcessedImageField(models.ImageField, SpecHostField):
|
|||
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$'])
|
||||
|
|
|
|||
|
|
@ -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__']
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ class ImageSpec(BaseImageSpec):
|
|||
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 @@ class ImageSpec(BaseImageSpec):
|
|||
|
||||
# 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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -87,12 +87,15 @@ class ModelSignalRouter(object):
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from django import template
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
2
setup.cfg
Normal file
2
setup.cfg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[bdist_wheel]
|
||||
universal = 1
|
||||
19
setup.py
19
setup.py
|
|
@ -13,7 +13,7 @@ except ImportError:
|
|||
|
||||
|
||||
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 @@ setup(
|
|||
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 @@ setup(
|
|||
'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'
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 463 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 198 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 206 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 463 KiB |
BIN
tests/media/reference.png
Normal file
BIN
tests/media/reference.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
|
|
@ -47,6 +47,23 @@ NOSE_ARGS = [
|
|||
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',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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(), '')
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ deserialized. This is important when using IK with Celery.
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
121
tox.ini
121
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
|
||||
[testenv:py27-django111]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
Django>=1.7,<1.8
|
||||
django-nose==1.4
|
||||
Django>=1.11a1,<1.12
|
||||
django-nose==1.4.4
|
||||
|
||||
[testenv:py32-django16]
|
||||
basepython = python3.2
|
||||
[testenv:py27-django110]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
Django>=1.6,<1.7
|
||||
|
||||
[testenv:py32-django15]
|
||||
basepython = python3.2
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in a new issue