mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-03-31 20:10:23 +00:00
Merge branch 'release/2.0.2'
* release/2.0.2: (25 commits) Changelog for 2.0.2. Bumping the version number. Change assertRaises for Python 2.6 compatibility Test correct versions of Django Fix docs typo; closes #147 fix API documentation Change how signals are used Add __init__ to Reflection processor; closes #141 Add Google Group to README Use django-appconf Allow callables for AdminThumbnail image_field arg Whoops! Messed that up. Code blocks. Fix pickling of ImageSpecFieldFile Create failing test to illustrate #97 Remove unused stuff Derp, forgot to change the tox command. add irc channel to docs and README Adding a travis-ci configuration file. Adding basepython to the tox directives. ... Conflicts: docs/changelog.rst docs/conf.py imagekit/__init__.py
This commit is contained in:
commit
996c75f7d5
24 changed files with 230 additions and 110 deletions
7
.travis.yml
Normal file
7
.travis.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
language: python
|
||||
python:
|
||||
- 2.7
|
||||
install: pip install tox --use-mirrors
|
||||
script: tox -e py27-django13,py27-django12,py26-django13,py27-django12
|
||||
notifications:
|
||||
irc: "irc.freenode.org#imagekit"
|
||||
48
README.rst
48
README.rst
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
ImageKit is a Django app that helps you to add variations of uploaded images
|
||||
to your models. These variations are called "specs" and can include things
|
||||
like different sizes (e.g. thumbnails) and black and white versions.
|
||||
|
|
@ -32,7 +31,9 @@ Adding Specs to a Model
|
|||
-----------------------
|
||||
|
||||
Much like ``django.db.models.ImageField``, Specs are defined as properties
|
||||
of a model class::
|
||||
of a model class:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.db import models
|
||||
from imagekit.models import ImageSpecField
|
||||
|
|
@ -44,7 +45,9 @@ of a model class::
|
|||
|
||||
Accessing the spec through a model instance will create the image and return
|
||||
an ImageFile-like object (just like with a normal
|
||||
``django.db.models.ImageField``)::
|
||||
``django.db.models.ImageField``):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
photo = Photo.objects.all()[0]
|
||||
photo.original_image.url # > '/media/photos/birthday.tiff'
|
||||
|
|
@ -53,13 +56,15 @@ an ImageFile-like object (just like with a normal
|
|||
Check out ``imagekit.models.ImageSpecField`` for more information.
|
||||
|
||||
If you only want to save the processed image (without maintaining the original),
|
||||
you can use a ``ProcessedImageField``::
|
||||
you can use a ``ProcessedImageField``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.db import models
|
||||
from imagekit.models.fields import ProcessedImageField
|
||||
|
||||
class Photo(models.Model):
|
||||
processed_image = ImageSpecField(format='JPEG', options={'quality': 90})
|
||||
processed_image = ProcessedImageField(format='JPEG', options={'quality': 90})
|
||||
|
||||
See the class documentation for details.
|
||||
|
||||
|
|
@ -69,7 +74,9 @@ Processors
|
|||
|
||||
The real power of ImageKit comes from processors. Processors take an image, do
|
||||
something to it, and return the result. By providing a list of processors to
|
||||
your spec, you can expose different versions of the original image::
|
||||
your spec, you can expose different versions of the original image:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.db import models
|
||||
from imagekit.models import ImageSpecField
|
||||
|
|
@ -81,7 +88,9 @@ your spec, you can expose different versions of the original image::
|
|||
ResizeToFill(50, 50)], image_field='original_image',
|
||||
format='JPEG', options={'quality': 90})
|
||||
|
||||
The ``thumbnail`` property will now return a cropped image::
|
||||
The ``thumbnail`` property will now return a cropped image:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
photo = Photo.objects.all()[0]
|
||||
photo.thumbnail.url # > '/media/cache/photos/birthday_thumbnail.jpeg'
|
||||
|
|
@ -96,7 +105,9 @@ pass processors to a ``ProcessedImageField`` instead of an ``ImageSpecField``.)
|
|||
The ``imagekit.processors`` module contains processors for many common
|
||||
image manipulations, like resizing, rotating, and color adjustments. However,
|
||||
if they aren't up to the task, you can create your own. All you have to do is
|
||||
implement a ``process()`` method::
|
||||
implement a ``process()`` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Watermark(object):
|
||||
def process(self, image):
|
||||
|
|
@ -115,7 +126,9 @@ Admin
|
|||
ImageKit also contains a class named ``imagekit.admin.AdminThumbnail``
|
||||
for displaying specs (or even regular ImageFields) in the
|
||||
`Django admin change list`_. AdminThumbnail is used as a property on
|
||||
Django admin classes::
|
||||
Django admin classes:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.contrib import admin
|
||||
from imagekit.admin import AdminThumbnail
|
||||
|
|
@ -154,17 +167,30 @@ no longer needed in any form, i.e. the model is deleted). Each of these methods
|
|||
must accept a file object, but the internals are up to you. For example, you
|
||||
could store the state (valid, invalid) of the cache in a database to avoid
|
||||
filesystem access. You can then specify your image cache backend on a per-field
|
||||
basis::
|
||||
basis:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Photo(models.Model):
|
||||
...
|
||||
thumbnail = ImageSpecField(..., image_cache_backend=MyImageCacheBackend())
|
||||
|
||||
Or in your ``settings.py`` file if you want to use it as the default::
|
||||
Or in your ``settings.py`` file if you want to use it as the default:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
IMAGEKIT_DEFAULT_IMAGE_CACHE_BACKEND = 'path.to.MyImageCacheBackend'
|
||||
|
||||
|
||||
Community
|
||||
---------
|
||||
|
||||
Please use `the GitHub issue tracker <https://github.com/jdriscoll/django-imagekit/issues>`_
|
||||
to report bugs with django-imagekit. `A mailing list <https://groups.google.com/forum/#!forum/django-imagekit>`_
|
||||
also exists to discuss the project and ask questions, as well as the official
|
||||
`#imagekit <irc://irc.freenode.net/imagekit>`_ channel on Freenode.
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ API Reference
|
|||
:mod:`models` Module
|
||||
--------------------
|
||||
|
||||
.. automodule:: imagekit.models
|
||||
.. automodule:: imagekit.models.fields
|
||||
:members:
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
v2.0.2
|
||||
------
|
||||
|
||||
- Fixed the pickling of ImageSpecFieldFile.
|
||||
- Signals are now connected without specifying the class and non-IK models
|
||||
are filitered out in the receivers. This is necessary beacuse of a bug
|
||||
with how Django handles abstract models.
|
||||
- Fixed a `ZeroDivisionError` in the Reflection processor.
|
||||
- `cStringIO` is now used if it's available.
|
||||
- Reflections on images now use RGBA instead of RGB.
|
||||
|
||||
v2.0.1
|
||||
------
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import sys, os
|
|||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings'
|
||||
|
||||
|
|
@ -50,9 +50,9 @@ copyright = u'2011, Justin Driscoll, Bryan Veloso, Greg Newman, Chris Drackett &
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '2.0.1'
|
||||
version = '2.0.2'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2.0.1'
|
||||
release = '2.0.2'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,14 @@ Authors
|
|||
.. include:: ../AUTHORS
|
||||
|
||||
|
||||
Community
|
||||
---------
|
||||
|
||||
The official Freenode channel for ImageKit is `#imagekit <irc://irc.freenode.net/imagekit>`_.
|
||||
You should always find some fine people to answer your questions
|
||||
about ImageKit there.
|
||||
|
||||
|
||||
Digging Deeper
|
||||
--------------
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
__title__ = 'django-imagekit'
|
||||
__author__ = 'Justin Driscoll, Bryan Veloso, Greg Newman, Chris Drackett, Matthew Tretter, Eric Eldredge'
|
||||
__version__ = (2, 0, 1, 'final', 0)
|
||||
__version__ = (2, 0, 2, 'final', 0)
|
||||
__license__ = 'BSD'
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,11 +21,14 @@ class AdminThumbnail(object):
|
|||
self.template = template
|
||||
|
||||
def __call__(self, obj):
|
||||
try:
|
||||
thumbnail = getattr(obj, self.image_field)
|
||||
except AttributeError:
|
||||
raise Exception('The property %s is not defined on %s.' % \
|
||||
(self.image_field, obj.__class__.__name__))
|
||||
if callable(self.image_field):
|
||||
thumbnail = self.image_field(obj)
|
||||
else:
|
||||
try:
|
||||
thumbnail = getattr(obj, self.image_field)
|
||||
except AttributeError:
|
||||
raise Exception('The property %s is not defined on %s.' % \
|
||||
(self.image_field, obj.__class__.__name__))
|
||||
|
||||
original_image = getattr(thumbnail, 'source_file', None) or thumbnail
|
||||
template = self.template or 'imagekit/admin/thumbnail.html'
|
||||
|
|
|
|||
5
imagekit/conf.py
Normal file
5
imagekit/conf.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from appconf import AppConf
|
||||
|
||||
|
||||
class ImageKitConf(AppConf):
|
||||
DEFAULT_IMAGE_CACHE_BACKEND = 'imagekit.imagecache.PessimisticImageCacheBackend'
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import os
|
||||
from StringIO import StringIO
|
||||
|
||||
from .lib import StringIO
|
||||
from .processors import ProcessorPipeline
|
||||
from .utils import (img_to_fobj, open_image, IKContentFile, extension_to_format,
|
||||
UnknownExtensionError)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ def get_default_image_cache_backend():
|
|||
"""
|
||||
global _default_image_cache_backend
|
||||
if not _default_image_cache_backend:
|
||||
from ..settings import DEFAULT_IMAGE_CACHE_BACKEND as import_path
|
||||
from django.conf import settings
|
||||
import_path = settings.IMAGEKIT_DEFAULT_IMAGE_CACHE_BACKEND
|
||||
try:
|
||||
dot = import_path.rindex('.')
|
||||
except ValueError:
|
||||
|
|
|
|||
|
|
@ -15,3 +15,8 @@ except ImportError:
|
|||
import ImageStat
|
||||
except ImportError:
|
||||
raise ImportError('ImageKit was unable to import the Python Imaging Library. Please confirm it`s installed and available on your current Python path.')
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from .. import conf
|
||||
from .fields import ImageSpecField, ProcessedImageField
|
||||
import warnings
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
import os
|
||||
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_init, post_save, post_delete
|
||||
|
||||
from ...imagecache import get_default_image_cache_backend
|
||||
from ...generators import SpecFileGenerator
|
||||
from .files import ImageSpecFieldFile, ProcessedImageFieldFile
|
||||
from ..receivers import configure_receivers
|
||||
from .utils import ImageSpecFileDescriptor, ImageKitMeta, BoundImageKitMeta
|
||||
from ...utils import suggest_extension
|
||||
|
||||
|
||||
configure_receivers()
|
||||
|
||||
|
||||
class ImageSpecField(object):
|
||||
"""
|
||||
The heart and soul of the ImageKit library, ImageSpecField allows you to add
|
||||
|
|
@ -91,51 +94,12 @@ class ImageSpecField(object):
|
|||
setattr(cls, '_ik', ik)
|
||||
ik.spec_fields.append(name)
|
||||
|
||||
# Connect to the signals only once for this class.
|
||||
uid = '%s.%s' % (cls.__module__, cls.__name__)
|
||||
post_init.connect(ImageSpecField._post_init_receiver, sender=cls,
|
||||
dispatch_uid=uid)
|
||||
post_save.connect(ImageSpecField._post_save_receiver, sender=cls,
|
||||
dispatch_uid=uid)
|
||||
post_delete.connect(ImageSpecField._post_delete_receiver, sender=cls,
|
||||
dispatch_uid=uid)
|
||||
|
||||
# Register the field with the image_cache_backend
|
||||
try:
|
||||
self.image_cache_backend.register_field(cls, self, name)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _post_save_receiver(sender, instance=None, created=False, raw=False, **kwargs):
|
||||
if not raw:
|
||||
old_hashes = instance._ik._source_hashes.copy()
|
||||
new_hashes = ImageSpecField._update_source_hashes(instance)
|
||||
for attname in instance._ik.spec_fields:
|
||||
if old_hashes[attname] != new_hashes[attname]:
|
||||
getattr(instance, attname).invalidate()
|
||||
|
||||
@staticmethod
|
||||
def _update_source_hashes(instance):
|
||||
"""
|
||||
Stores hashes of the source image files so that they can be compared
|
||||
later to see whether the source image has changed (and therefore whether
|
||||
the spec file needs to be regenerated).
|
||||
|
||||
"""
|
||||
instance._ik._source_hashes = dict((f.attname, hash(f.source_file)) \
|
||||
for f in instance._ik.spec_files)
|
||||
return instance._ik._source_hashes
|
||||
|
||||
@staticmethod
|
||||
def _post_delete_receiver(sender, instance=None, **kwargs):
|
||||
for spec_file in instance._ik.spec_files:
|
||||
spec_file.clear()
|
||||
|
||||
@staticmethod
|
||||
def _post_init_receiver(sender, instance, **kwargs):
|
||||
ImageSpecField._update_source_hashes(instance)
|
||||
|
||||
|
||||
class ProcessedImageField(models.ImageField):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ class ImageSpecFieldFile(ImageFieldFile):
|
|||
def __init__(self, instance, field, attname):
|
||||
super(ImageSpecFieldFile, self).__init__(instance, field, None)
|
||||
self.attname = attname
|
||||
self.storage = self.field.storage or self.source_file.storage
|
||||
|
||||
@property
|
||||
def source_file(self):
|
||||
|
|
@ -100,7 +99,7 @@ class ImageSpecFieldFile(ImageFieldFile):
|
|||
filepath, basename = os.path.split(path)
|
||||
filename = os.path.splitext(basename)[0]
|
||||
new_name = '%s_%s%s' % (filename, specname, extension)
|
||||
return os.path.join(os.path.join('cache', filepath), new_name)
|
||||
return os.path.join('cache', filepath, new_name)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
|
@ -145,6 +144,25 @@ class ImageSpecFieldFile(ImageFieldFile):
|
|||
# it at least that one time.
|
||||
pass
|
||||
|
||||
@property
|
||||
def storage(self):
|
||||
return getattr(self, '_storage', None) or self.field.storage or self.source_file.storage
|
||||
|
||||
@storage.setter
|
||||
def storage(self, storage):
|
||||
self._storage = storage
|
||||
|
||||
def __getstate__(self):
|
||||
return dict(
|
||||
attname=self.attname,
|
||||
instance=self.instance,
|
||||
)
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.attname = state['attname']
|
||||
self.instance = state['instance']
|
||||
self.field = getattr(self.instance.__class__, self.attname)
|
||||
|
||||
|
||||
class ProcessedImageFieldFile(ImageFieldFile):
|
||||
def save(self, name, content, save=True):
|
||||
|
|
|
|||
48
imagekit/models/receivers.py
Normal file
48
imagekit/models/receivers.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
from django.db.models.signals import post_init, post_save, post_delete
|
||||
from ..utils import ik_model_receiver
|
||||
|
||||
|
||||
def update_source_hashes(instance):
|
||||
"""
|
||||
Stores hashes of the source image files so that they can be compared
|
||||
later to see whether the source image has changed (and therefore whether
|
||||
the spec file needs to be regenerated).
|
||||
|
||||
"""
|
||||
instance._ik._source_hashes = dict((f.attname, hash(f.source_file)) \
|
||||
for f in instance._ik.spec_files)
|
||||
return instance._ik._source_hashes
|
||||
|
||||
|
||||
@ik_model_receiver
|
||||
def post_save_receiver(sender, instance=None, created=False, raw=False, **kwargs):
|
||||
if not raw:
|
||||
old_hashes = instance._ik._source_hashes.copy()
|
||||
new_hashes = update_source_hashes(instance)
|
||||
for attname in instance._ik.spec_fields:
|
||||
if old_hashes[attname] != new_hashes[attname]:
|
||||
getattr(instance, attname).invalidate()
|
||||
|
||||
|
||||
@ik_model_receiver
|
||||
def post_delete_receiver(sender, instance=None, **kwargs):
|
||||
for spec_file in instance._ik.spec_files:
|
||||
spec_file.clear()
|
||||
|
||||
|
||||
@ik_model_receiver
|
||||
def post_init_receiver(sender, instance, **kwargs):
|
||||
update_source_hashes(instance)
|
||||
|
||||
|
||||
def configure_receivers():
|
||||
# Connect the signals. We have to listen to every model (not just those
|
||||
# with IK fields) and filter in our receivers because of a Django issue with
|
||||
# abstract base models.
|
||||
# Related:
|
||||
# https://github.com/jdriscoll/django-imagekit/issues/126
|
||||
# https://code.djangoproject.com/ticket/9318
|
||||
uid = 'ik_spec_field_receivers'
|
||||
post_init.connect(post_init_receiver, dispatch_uid=uid)
|
||||
post_save.connect(post_save_receiver, dispatch_uid=uid)
|
||||
post_delete.connect(post_delete_receiver, dispatch_uid=uid)
|
||||
|
|
@ -69,19 +69,20 @@ class Reflection(object):
|
|||
Creates an image with a reflection.
|
||||
|
||||
"""
|
||||
background_color = '#FFFFFF'
|
||||
size = 0.0
|
||||
opacity = 0.6
|
||||
def __init__(self, background_color='#FFFFFF', size=0.0, opacity=0.6):
|
||||
self.background_color = background_color
|
||||
self.size = size
|
||||
self.opacity = opacity
|
||||
|
||||
def process(self, img):
|
||||
# Convert bgcolor string to RGB value.
|
||||
background_color = ImageColor.getrgb(self.background_color)
|
||||
# Handle palleted images.
|
||||
img = img.convert('RGB')
|
||||
img = img.convert('RGBA')
|
||||
# Copy orignial image and flip the orientation.
|
||||
reflection = img.copy().transpose(Image.FLIP_TOP_BOTTOM)
|
||||
# Create a new image filled with the bgcolor the same size.
|
||||
background = Image.new("RGB", img.size, background_color)
|
||||
background = Image.new("RGBA", img.size, background_color)
|
||||
# Calculate our alpha mask.
|
||||
start = int(255 - (255 * self.opacity)) # The start of our gradient.
|
||||
steps = int(255 * self.size) # The number of intermedite values.
|
||||
|
|
@ -101,7 +102,7 @@ class Reflection(object):
|
|||
reflection = reflection.crop((0, 0, img.size[0], reflection_height))
|
||||
# Create new image sized to hold both the original image and
|
||||
# the reflection.
|
||||
composite = Image.new("RGB", (img.size[0], img.size[1] + reflection_height), background_color)
|
||||
composite = Image.new("RGBA", (img.size[0], img.size[1] + reflection_height), background_color)
|
||||
# Paste the orignal image and the reflection into the composite image.
|
||||
composite.paste(img, (0, 0))
|
||||
composite.paste(reflection, (0, img.size[1]))
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
from django.conf import settings
|
||||
|
||||
DEFAULT_IMAGE_CACHE_BACKEND = getattr(settings,
|
||||
'IMAGEKIT_DEFAULT_IMAGE_CACHE_BACKEND',
|
||||
'imagekit.imagecache.PessimisticImageCacheBackend')
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import os
|
||||
import mimetypes
|
||||
from StringIO import StringIO
|
||||
import sys
|
||||
import types
|
||||
|
||||
|
|
@ -9,7 +8,7 @@ from django.db.models.loading import cache
|
|||
from django.utils.functional import wraps
|
||||
from django.utils.encoding import smart_str, smart_unicode
|
||||
|
||||
from .lib import Image, ImageFile
|
||||
from .lib import Image, ImageFile, StringIO
|
||||
|
||||
|
||||
RGBA_TRANSPARENCY_FORMATS = ['PNG']
|
||||
|
|
@ -372,3 +371,11 @@ def prepare_image(img, format):
|
|||
save_kwargs['optimize'] = True
|
||||
|
||||
return img, save_kwargs
|
||||
|
||||
|
||||
def ik_model_receiver(fn):
|
||||
@wraps(fn)
|
||||
def receiver(sender, **kwargs):
|
||||
if getattr(sender, '_ik', None):
|
||||
fn(sender, **kwargs)
|
||||
return receiver
|
||||
|
|
|
|||
|
|
@ -1 +1,3 @@
|
|||
Django >= 1.3.1
|
||||
Django>=1.3.1
|
||||
django-appconf>=0.5
|
||||
PIL>=1.1.7
|
||||
|
|
|
|||
3
setup.py
3
setup.py
|
|
@ -28,6 +28,9 @@ setup(
|
|||
packages=find_packages(),
|
||||
zip_safe=False,
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'django-appconf>=0.5',
|
||||
],
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Web Environment',
|
||||
|
|
|
|||
|
|
@ -1,23 +1,16 @@
|
|||
from __future__ import with_statement
|
||||
|
||||
import os
|
||||
import pickle
|
||||
from StringIO import StringIO
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from imagekit import utils
|
||||
from .models import (Photo, AbstractImageModel, ConcreteImageModel1,
|
||||
ConcreteImageModel2)
|
||||
from .testutils import generate_lenna, create_photo
|
||||
from .testutils import create_photo, pickleback
|
||||
|
||||
|
||||
class IKTest(TestCase):
|
||||
def generate_image(self):
|
||||
tmp = tempfile.TemporaryFile()
|
||||
Image.new('RGB', (800, 600)).save(tmp, 'JPEG')
|
||||
tmp.seek(0)
|
||||
return tmp
|
||||
|
||||
def setUp(self):
|
||||
self.photo = create_photo('test.jpg')
|
||||
|
|
@ -27,7 +20,6 @@ class IKTest(TestCase):
|
|||
|
||||
"""
|
||||
filename = self.photo.thumbnail.file.name
|
||||
thumbnail_timestamp = os.path.getmtime(filename)
|
||||
self.photo.save()
|
||||
self.assertTrue(self.photo.thumbnail.storage.exists(filename))
|
||||
|
||||
|
|
@ -60,27 +52,29 @@ class IKUtilsTest(TestCase):
|
|||
self.assertEqual(utils.extension_to_format('.jpeg'), 'JPEG')
|
||||
self.assertEqual(utils.extension_to_format('.rgba'), 'SGI')
|
||||
|
||||
with self.assertRaises(utils.UnknownExtensionError):
|
||||
utils.extension_to_format('.txt')
|
||||
self.assertRaises(utils.UnknownExtensionError,
|
||||
lambda: utils.extension_to_format('.txt'))
|
||||
|
||||
def test_format_to_extension_no_init(self):
|
||||
self.assertEqual(utils.format_to_extension('PNG'), '.png')
|
||||
self.assertEqual(utils.format_to_extension('ICO'), '.ico')
|
||||
|
||||
with self.assertRaises(utils.UnknownFormatError):
|
||||
utils.format_to_extension('TXT')
|
||||
self.assertRaises(utils.UnknownFormatError,
|
||||
lambda: utils.format_to_extension('TXT'))
|
||||
|
||||
|
||||
class PickleTest(TestCase):
|
||||
def test_source_file(self):
|
||||
ph = create_photo('pickletest.jpg')
|
||||
pickled_model = StringIO()
|
||||
pickle.dump(ph, pickled_model)
|
||||
pickled_model.seek(0)
|
||||
unpickled_model = pickle.load(pickled_model)
|
||||
def test_model(self):
|
||||
ph = pickleback(create_photo('pickletest.jpg'))
|
||||
|
||||
# This isn't supposed to error.
|
||||
unpickled_model.thumbnail.source_file
|
||||
ph.thumbnail.source_file
|
||||
|
||||
def test_field(self):
|
||||
thumbnail = pickleback(create_photo('pickletest2.jpg').thumbnail)
|
||||
|
||||
# This isn't supposed to error.
|
||||
thumbnail.source_file
|
||||
|
||||
|
||||
class InheritanceTest(TestCase):
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ import tempfile
|
|||
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from imagekit.lib import Image
|
||||
from imagekit.lib import Image, StringIO
|
||||
from .models import Photo
|
||||
import pickle
|
||||
|
||||
|
||||
def generate_lenna():
|
||||
|
|
@ -36,3 +37,10 @@ def create_instance(model_class, image_name):
|
|||
|
||||
def create_photo(name):
|
||||
return create_instance(Photo, name)
|
||||
|
||||
|
||||
def pickleback(obj):
|
||||
pickled = StringIO()
|
||||
pickle.dump(obj, pickled)
|
||||
pickled.seek(0)
|
||||
return pickle.load(pickled)
|
||||
|
|
|
|||
32
tox.ini
32
tox.ini
|
|
@ -1,31 +1,45 @@
|
|||
[tox]
|
||||
envlist =
|
||||
py27-django13,
|
||||
py27-django12,
|
||||
py26-django13,
|
||||
py26-django12
|
||||
py27-django14, py27-django13, py27-django12,
|
||||
py26-django14, py26-django13, py26-django12
|
||||
|
||||
[testenv]
|
||||
changedir = tests
|
||||
setenv = PYTHONPATH = {toxinidir}/tests
|
||||
commands = django-admin.py test core --settings=settings
|
||||
|
||||
[testenv:py27-django13]
|
||||
[testenv:py27-django14]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
Django>=1.3,<=1.4
|
||||
Django>=1.4,<1.5
|
||||
Pillow
|
||||
|
||||
[testenv:py27-django13]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
Django>=1.3,<1.4
|
||||
Pillow
|
||||
|
||||
[testenv:py27-django12]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
Django>=1.2,<=1.3
|
||||
Django>=1.2,<1.3
|
||||
Pillow
|
||||
|
||||
[testenv:py26-django14]
|
||||
basepython = python2.6
|
||||
deps =
|
||||
Django>=1.4,<1.5
|
||||
Pillow
|
||||
|
||||
[testenv:py26-django13]
|
||||
basepython = python2.6
|
||||
deps =
|
||||
Django>=1.3,<=1.4
|
||||
Django>=1.3,<1.4
|
||||
Pillow
|
||||
|
||||
[testenv:py26-django12]
|
||||
basepython = python2.6
|
||||
deps =
|
||||
Django>=1.2,<=1.3
|
||||
Django>=1.2,<1.3
|
||||
Pillow
|
||||
|
|
|
|||
Loading…
Reference in a new issue