From fe2fb844af76f4e3c986d04aa4ac34f7404df910 Mon Sep 17 00:00:00 2001 From: Eric Eldredge Date: Wed, 21 Sep 2011 10:37:50 -0400 Subject: [PATCH] Changed the way post_save and post_delete signals are being handled. One handler is created per model instead of per bound image spec. This cuts down on the number of handlers created, and also offloads the policing of the handlers in memory to the signal framework. Since they are no longer being created per spec, the handlers can be weakly referenced. --- imagekit/specs.py | 57 ++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/imagekit/specs.py b/imagekit/specs.py index 8477650..271132d 100644 --- a/imagekit/specs.py +++ b/imagekit/specs.py @@ -64,15 +64,13 @@ class ImageSpec(object): def contribute_to_class(self, cls, name): setattr(cls, name, _ImageSpecDescriptor(self, name)) - # Connect to the signals only once for this class's attribute. - uid = '%s.%s_%s' % (cls.__module__, cls.__name__, name) - post_save.connect(_create_post_save_handler(name), + # Connect to the signals only once for this class. + uid = '%s.%s' % (cls.__module__, cls.__name__) + post_save.connect(_post_save_handler, sender=cls, - weak=False, dispatch_uid='%s_save' % uid) - post_delete.connect(_create_post_delete_handler(name, uid), + post_delete.connect(_post_delete_handler, sender=cls, - weak=False, dispatch_uid='%s.delete' % uid) @@ -240,21 +238,21 @@ class _ImageSpecDescriptor(object): return BoundImageSpec(instance, self._spec, self._property_name) -def _create_post_save_handler(accessor_name): - def handler(sender, instance=None, created=False, raw=False, **kwargs): - if raw: - return - accessor = getattr(instance, accessor_name) - spec = accessor.spec - imgfield = spec._get_imgfield(instance) +def _post_save_handler(sender, instance=None, created=False, raw=False, **kwargs): + if raw: + return + bound_specs = _get_bound_specs(instance) + for bound_spec in bound_specs: + name = bound_spec.property_name + imgfield = bound_spec._get_imgfield(instance) newfile = imgfield.storage.open(str(imgfield)) img = Image.open(newfile) - img, format = spec.process(img, instance) + img, format = bound_spec.process(img, instance) if format != 'JPEG': imgfile = img_to_fobj(img, format) else: imgfile = img_to_fobj(img, format, - quality=int(spec.quality), + quality=int(bound_spec.quality), optimize=True) content = ContentFile(imgfile.read()) newfile.close() @@ -262,17 +260,24 @@ def _create_post_save_handler(accessor_name): imgfield.storage.delete(name) imgfield.storage.save(name, content) if not created: - accessor._delete() - accessor._create() - return handler + bound_spec._delete() + bound_spec._create() -def _create_post_delete_handler(accessor_name, uid): - def handler(sender, instance=None, **kwargs): - assert instance._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (instance._meta.object_name, instance._meta.pk.attname) - accessor = getattr(instance, accessor_name) - accessor._delete() - post_save.disconnect(dispatch_uid='%s_save' % uid) - post_delete.disconnect(dispatch_uid='%s.delete' % uid) +def _post_delete_handler(sender, instance=None, **kwargs): + assert instance._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (instance._meta.object_name, instance._meta.pk.attname) + bound_specs = _get_bound_specs(instance) + for bound_spec in bound_specs: + bound_spec._delete() - return handler + +def _get_bound_specs(instance): + bound_specs = [] + for key in dir(instance): + try: + value = getattr(instance, key) + except AttributeError: + continue + if isinstance(value, BoundImageSpec): + bound_specs.append(value) + return bound_specs