2013-08-19 18:27:50 +00:00
|
|
|
from __future__ import unicode_literals
|
2012-10-18 01:00:32 +00:00
|
|
|
import logging
|
2013-12-14 16:56:47 +00:00
|
|
|
import re
|
2013-01-24 03:34:29 +00:00
|
|
|
from tempfile import NamedTemporaryFile
|
2013-12-14 16:56:47 +00:00
|
|
|
from hashlib import md5
|
2009-01-08 21:11:15 +00:00
|
|
|
|
2013-04-30 13:09:08 +00:00
|
|
|
from django.conf import settings
|
2012-09-06 01:50:11 +00:00
|
|
|
from django.core.exceptions import ImproperlyConfigured
|
2013-01-24 03:34:29 +00:00
|
|
|
from django.core.files import File
|
2015-02-25 16:51:28 +00:00
|
|
|
try:
|
|
|
|
|
from importlib import import_module
|
|
|
|
|
except ImportError:
|
|
|
|
|
from django.utils.importlib import import_module
|
2013-02-08 04:10:05 +00:00
|
|
|
from pilkit.utils import *
|
2013-12-14 18:02:21 +00:00
|
|
|
from .lib import NullHandler, force_bytes
|
2013-04-30 13:09:08 +00:00
|
|
|
|
|
|
|
|
|
2013-12-14 17:54:26 +00:00
|
|
|
bad_memcached_key_chars = re.compile('[\u0000-\u001f\\s]+')
|
2011-09-22 00:12:49 +00:00
|
|
|
|
2013-08-15 02:07:46 +00:00
|
|
|
_autodiscovered = False
|
2011-09-22 00:12:49 +00:00
|
|
|
|
2013-02-09 05:55:49 +00:00
|
|
|
def get_nonabstract_descendants(model):
|
|
|
|
|
""" Returns all non-abstract descendants of the model. """
|
2013-02-10 20:55:32 +00:00
|
|
|
if not model._meta.abstract:
|
|
|
|
|
yield model
|
|
|
|
|
for s in model.__subclasses__():
|
|
|
|
|
for m in get_nonabstract_descendants(s):
|
|
|
|
|
yield m
|
2013-02-09 05:55:49 +00:00
|
|
|
|
|
|
|
|
|
2013-02-01 03:01:01 +00:00
|
|
|
def get_by_qname(path, desc):
|
2012-09-06 01:50:11 +00:00
|
|
|
try:
|
|
|
|
|
dot = path.rindex('.')
|
|
|
|
|
except ValueError:
|
|
|
|
|
raise ImproperlyConfigured("%s isn't a %s module." % (path, desc))
|
2013-02-01 03:01:01 +00:00
|
|
|
module, objname = path[:dot], path[dot + 1:]
|
2012-09-06 01:50:11 +00:00
|
|
|
try:
|
|
|
|
|
mod = import_module(module)
|
2013-08-19 18:27:50 +00:00
|
|
|
except ImportError as e:
|
2012-09-06 01:50:11 +00:00
|
|
|
raise ImproperlyConfigured('Error importing %s module %s: "%s"' %
|
|
|
|
|
(desc, module, e))
|
|
|
|
|
try:
|
2013-02-01 03:01:01 +00:00
|
|
|
obj = getattr(mod, objname)
|
|
|
|
|
return obj
|
2012-09-06 01:50:11 +00:00
|
|
|
except AttributeError:
|
2013-02-01 03:01:01 +00:00
|
|
|
raise ImproperlyConfigured('%s module "%s" does not define "%s"'
|
|
|
|
|
% (desc[0].upper() + desc[1:], module, objname))
|
2012-09-06 01:50:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
_singletons = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_singleton(class_path, desc):
|
|
|
|
|
global _singletons
|
2013-02-01 03:01:01 +00:00
|
|
|
cls = get_by_qname(class_path, desc)
|
2012-09-06 01:50:11 +00:00
|
|
|
instance = _singletons.get(cls)
|
|
|
|
|
if not instance:
|
|
|
|
|
instance = _singletons[cls] = cls()
|
|
|
|
|
return instance
|
2012-10-04 19:46:50 +00:00
|
|
|
|
|
|
|
|
|
2012-02-14 03:05:33 +00:00
|
|
|
def autodiscover():
|
|
|
|
|
"""
|
2013-02-01 05:30:15 +00:00
|
|
|
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.
|
2012-02-14 03:05:33 +00:00
|
|
|
|
|
|
|
|
Copied from django.contrib.admin
|
|
|
|
|
"""
|
2013-08-15 02:07:46 +00:00
|
|
|
global _autodiscovered
|
|
|
|
|
|
|
|
|
|
if _autodiscovered:
|
|
|
|
|
return
|
2012-02-14 03:05:33 +00:00
|
|
|
|
|
|
|
|
from django.conf import settings
|
2015-02-25 16:51:28 +00:00
|
|
|
try:
|
|
|
|
|
from importlib import import_module
|
|
|
|
|
except ImportError:
|
|
|
|
|
from django.utils.importlib import import_module
|
2012-02-14 03:05:33 +00:00
|
|
|
from django.utils.module_loading import module_has_submodule
|
|
|
|
|
|
2013-08-15 02:07:46 +00:00
|
|
|
_autodiscovered = True
|
|
|
|
|
|
2012-02-14 03:05:33 +00:00
|
|
|
for app in settings.INSTALLED_APPS:
|
2014-09-08 22:28:49 +00:00
|
|
|
# As of Django 1.7, settings.INSTALLED_APPS may contain classes instead of modules, hence the try/except
|
|
|
|
|
# See here: https://docs.djangoproject.com/en/dev/releases/1.7/#introspecting-applications
|
2012-02-14 03:05:33 +00:00
|
|
|
try:
|
2014-09-08 22:28:49 +00:00
|
|
|
mod = import_module(app)
|
|
|
|
|
# Attempt to import the app's admin module.
|
|
|
|
|
try:
|
|
|
|
|
import_module('%s.imagegenerators' % app)
|
|
|
|
|
except:
|
|
|
|
|
# Decide whether to bubble up this error. If the app just
|
|
|
|
|
# doesn't have an imagegenerators module, we can ignore the error
|
|
|
|
|
# attempting to import it, otherwise we want it to bubble up.
|
|
|
|
|
if module_has_submodule(mod, 'imagegenerators'):
|
|
|
|
|
raise
|
|
|
|
|
except ImportError:
|
|
|
|
|
pass
|
2012-10-18 01:00:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_logger(logger_name='imagekit', add_null_handler=True):
|
|
|
|
|
logger = logging.getLogger(logger_name)
|
|
|
|
|
if add_null_handler:
|
2013-05-20 23:18:35 +00:00
|
|
|
logger.addHandler(NullHandler())
|
2012-10-18 01:00:32 +00:00
|
|
|
return logger
|
2013-01-24 02:47:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_field_info(field_file):
|
|
|
|
|
"""
|
|
|
|
|
A utility for easily extracting information about the host model from a
|
|
|
|
|
Django FileField (or subclass). This is especially useful for when you want
|
|
|
|
|
to alter processors based on a property of the source model. For example::
|
|
|
|
|
|
|
|
|
|
class MySpec(ImageSpec):
|
|
|
|
|
def __init__(self, source):
|
|
|
|
|
instance, attname = get_field_info(source)
|
|
|
|
|
self.processors = [SmartResize(instance.thumbnail_width,
|
|
|
|
|
instance.thumbnail_height)]
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
return (
|
|
|
|
|
getattr(field_file, 'instance', None),
|
|
|
|
|
getattr(getattr(field_file, 'field', None), 'attname', None),
|
|
|
|
|
)
|
2013-01-24 03:34:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate(generator):
|
|
|
|
|
"""
|
|
|
|
|
Calls the ``generate()`` method of a generator instance, and then wraps the
|
|
|
|
|
result in a Django File object so Django knows how to save it.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
content = generator.generate()
|
2016-07-10 22:34:50 +00:00
|
|
|
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
|
2013-01-29 06:40:00 +00:00
|
|
|
|
|
|
|
|
|
2013-05-10 04:51:47 +00:00
|
|
|
def call_strategy_method(file, method_name):
|
|
|
|
|
strategy = getattr(file, 'cachefile_strategy', None)
|
2013-01-29 06:40:00 +00:00
|
|
|
fn = getattr(strategy, method_name, None)
|
|
|
|
|
if fn is not None:
|
2013-05-10 04:51:47 +00:00
|
|
|
fn(file)
|
2013-04-30 13:09:08 +00:00
|
|
|
|
|
|
|
|
|
2015-10-29 22:03:48 +00:00
|
|
|
def get_cache(backend, **kwargs):
|
|
|
|
|
try:
|
|
|
|
|
from django.core.cache import caches
|
|
|
|
|
except ImportError:
|
|
|
|
|
from django.core.cache import get_cache
|
|
|
|
|
return get_cache(backend, **kwargs)
|
|
|
|
|
|
|
|
|
|
return caches[backend]
|
|
|
|
|
|
|
|
|
|
|
2013-04-30 13:09:08 +00:00
|
|
|
def sanitize_cache_key(key):
|
|
|
|
|
if settings.IMAGEKIT_USE_MEMCACHED_SAFE_CACHE_KEY:
|
|
|
|
|
# Memcached keys can't contain whitespace or control characters.
|
|
|
|
|
new_key = bad_memcached_key_chars.sub('', key)
|
|
|
|
|
|
|
|
|
|
# The also can't be > 250 chars long. Since we don't know what the
|
|
|
|
|
# user's cache ``KEY_FUNCTION`` setting is like, we'll limit it to 200.
|
|
|
|
|
if len(new_key) >= 200:
|
2013-12-14 16:56:47 +00:00
|
|
|
new_key = '%s:%s' % (new_key[:200-33], md5(force_bytes(key)).hexdigest())
|
2013-04-30 13:09:08 +00:00
|
|
|
|
|
|
|
|
key = new_key
|
|
|
|
|
return key
|