def __init__(self, name, file=None, storage=None, thumbnail_options=None, *args, **kwargs): fake_field = FakeField(storage=storage) super().__init__(FakeInstance(), fake_field, name, *args, **kwargs) del self.field if file: self.file = file if thumbnail_options is None: thumbnail_options = ThumbnailOptions() elif not isinstance(thumbnail_options, ThumbnailOptions): thumbnail_options = ThumbnailOptions(thumbnail_options) self.thumbnail_options = thumbnail_options
def generate_source_image(source_file, processor_options, generators=None, fail_silently=True): """ Processes a source ``File`` through a series of source generators, stopping once a generator returns an image. The return value is this image instance or ``None`` if no generators return an image. If the source file cannot be opened, it will be set to ``None`` and still passed to the generators. """ processor_options = ThumbnailOptions(processor_options) # Keep record of whether the source file was originally closed. Not all # file-like objects provide this attribute, so just fall back to False. was_closed = getattr(source_file, 'closed', False) if generators is None: generators = [ utils.dynamic_import(name) for name in settings.THUMBNAIL_SOURCE_GENERATORS ] exceptions = [] try: for generator in generators: source = source_file # First try to open the file. try: source.open() except Exception: # If that failed, maybe the file-like object doesn't support # reopening so just try seeking back to the start of the file. try: source.seek(0) except Exception: source = None try: image = generator(source, **processor_options) except Exception as e: if not fail_silently: if len(generators) == 1: raise exceptions.append(e) image = None if image: return image finally: # Attempt to close the file if it was closed originally (but fail # silently). if was_closed: try: source_file.close() except Exception: pass if exceptions and not fail_silently: raise NoSourceGenerator(*exceptions)
def get_options(self, thumbnail_options, **kwargs): """ Get the thumbnail options that includes the default options for this thumbnailer (and the project-wide default options). """ if isinstance(thumbnail_options, ThumbnailOptions): return thumbnail_options args = [] if thumbnail_options is not None: args.append(thumbnail_options) opts = ThumbnailOptions(*args, **kwargs) if 'quality' not in thumbnail_options: opts['quality'] = self.thumbnail_quality return opts
def process_image(source, processor_options, processors=None): """ Process a source PIL image through a series of image processors, returning the (potentially) altered image. """ processor_options = ThumbnailOptions(processor_options) if processors is None: processors = [ utils.dynamic_import(name) for name in settings.THUMBNAIL_PROCESSORS] image = source for processor in processors: image = processor(image, **processor_options) return image
def test_thumbnail_missed_signal(self): def signal_handler(sender, **kwargs): sender.missed_signal = kwargs.get('options') signals.thumbnail_missed.connect(signal_handler) try: # Standard generation doesn't trigger signal. self.thumbnailer.get_thumbnail({'size': (100, 100)}) self.assertFalse(hasattr(self.thumbnailer, 'missed_signal')) # Retrieval doesn't trigger signal. self.thumbnailer.get_thumbnail({'size': (100, 100)}, generate=False) self.assertFalse(hasattr(self.thumbnailer, 'missed_signal')) # A thumbnail miss does trigger it. options = {'size': (10, 20)} thumb = self.thumbnailer.get_thumbnail(options, generate=False) self.assertEqual(thumb, None) self.assertEqual(self.thumbnailer.missed_signal, ThumbnailOptions(options)) finally: signals.thumbnail_created.disconnect(signal_handler)
def test_thumbnailfile_options(self): opts = {'size': (50, 50), 'crop': True, 'upscale': True} thumb = self.thumbnailer.get_thumbnail(opts) self.assertEqual(thumb.thumbnail_options, ThumbnailOptions(opts))