Skip to content

Commit

Permalink
Merge branch 'release/4.0'
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
vstoykov committed Feb 22, 2017
2 parents bc93ec2 + 46d2a9e commit f724cb0
Show file tree
Hide file tree
Showing 31 changed files with 313 additions and 185 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
88 changes: 52 additions & 36 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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"
18 changes: 15 additions & 3 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
@@ -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
Expand Down
22 changes: 15 additions & 7 deletions docs/caching.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:'``
Expand Down
6 changes: 3 additions & 3 deletions imagekit/cachefiles/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import warnings
from copy import copy
from django.core.exceptions import ImproperlyConfigured
from django.conf import settings


class CacheFileState(object):
Expand Down Expand Up @@ -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):
Expand All @@ -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__)
Expand Down
File renamed without changes.
35 changes: 14 additions & 21 deletions imagekit/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from appconf import AppConf
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured


class ImageKitConf(AppConf):
Expand All @@ -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):
Expand Down
13 changes: 12 additions & 1 deletion imagekit/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 5 additions & 1 deletion imagekit/forms/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 9 additions & 6 deletions imagekit/models/fields/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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$'])
2 changes: 1 addition & 1 deletion imagekit/pkgmeta.py
Original file line number Diff line number Diff line change
@@ -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__']
5 changes: 4 additions & 1 deletion imagekit/specs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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


Expand Down
5 changes: 4 additions & 1 deletion imagekit/specs/sourcegroups.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion imagekit/templatetags/imagekit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit f724cb0

Please sign in to comment.