From de991d404869fbf81a5ce5a2935c903b762b301f Mon Sep 17 00:00:00 2001 From: Roman Gorbil Date: Fri, 20 Oct 2017 18:26:37 +0700 Subject: [PATCH] Fix pickle serialization for ImageCacheFile When Celery CachedFileBackend used with filesystem storage (django.core.files.storage.FileSystemStorage), everything works fine. But there are issues with storages.backends.s3boto3.S3Boto3Storage (and it's fix from #391), as well as with django_s3_storage.storage.S3Storage. Exception was: ``` Traceback (most recent call last): ... File "/src/django-imagekit/imagekit/cachefiles/__init__.py", line 131, in __bool__ existence_required.send(sender=self, file=self) ... File "/libs/utils.py", line 380, in on_existence_required file.generate() File "/src/django-imagekit/imagekit/cachefiles/__init__.py", line 94, in generate self.cachefile_backend.generate(self, force) File "/src/django-imagekit/imagekit/cachefiles/backends.py", line 136, in generate self.schedule_generation(file, force=force) File "/src/django-imagekit/imagekit/cachefiles/backends.py", line 165, in schedule_generation _celery_task.delay(self, file.generator, force=force) ... File "/lib/python3.6/site-packages/kombu/serialization.py", line 221, in dumps payload = encoder(data) File "/lib/python3.6/site-packages/kombu/serialization.py", line 350, in pickle_dumps return dumper(obj, protocol=pickle_protocol) kombu.exceptions.EncodeError: can't pickle _thread._local objects ``` --- docs/caching.rst | 5 +++++ imagekit/cachefiles/__init__.py | 16 ++++++++++++++++ tests/test_serialization.py | 5 ++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/caching.rst b/docs/caching.rst index 4e2b8a9..8264089 100644 --- a/docs/caching.rst +++ b/docs/caching.rst @@ -185,6 +185,11 @@ Or, in Python: def on_source_saved(self, file): file.generate() +.. note:: + + If you use custom storage backend for some specs, + (storage passed to the field different than configured one) + it's required the storage to be pickleable __ https://pypi.python.org/pypi/django-celery diff --git a/imagekit/cachefiles/__init__.py b/imagekit/cachefiles/__init__.py index 594df57..08ea04b 100644 --- a/imagekit/cachefiles/__init__.py +++ b/imagekit/cachefiles/__init__.py @@ -144,8 +144,24 @@ class ImageCacheFile(BaseIKFile, ImageFile): # file is hidden link to "file" attribute state.pop('_file', None) + # remove storage from state as some non-FileSystemStorage can't be + # pickled + settings_storage = get_singleton( + settings.IMAGEKIT_DEFAULT_FILE_STORAGE, + 'file storage backend' + ) + if state['storage'] == settings_storage: + state.pop('storage') return state + def __setstate__(self, state): + if 'storage' not in state: + state['storage'] = get_singleton( + settings.IMAGEKIT_DEFAULT_FILE_STORAGE, + 'file storage backend' + ) + self.__dict__.update(state) + def __nonzero__(self): # Python 2 compatibility return self.__bool__() diff --git a/tests/test_serialization.py b/tests/test_serialization.py index b995658..10dd550 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -37,4 +37,7 @@ def test_cachefiles(): # remove link to file from spec source generator # test __getstate__ of ImageCacheFile file.generator.source = None - pickleback(file) + restored_file = pickleback(file) + assert file is not restored_file + # Assertion for #437 and #451 + assert file.storage is restored_file.storage