diff --git a/CHANGES.txt b/CHANGES.txt index fb2349f..5870761 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,7 @@ 0.2.0 rc 2 ------------------ * fixed demo.py to pass in the right optimize params. it was not inline with the test +* flake8 0.2.0 rc 1 diff --git a/demo.py b/demo.py index 8438587..19d06dd 100644 --- a/demo.py +++ b/demo.py @@ -6,47 +6,47 @@ import ConfigParser Config = ConfigParser.ConfigParser() Config.read('aws.cfg') -AWS_KEY_PUBLIC = Config.get('aws','AWS_KEY_PUBLIC') -AWS_KEY_SECRET = Config.get('aws','AWS_KEY_SECRET') -AWS_BUCKET_PUBLIC = Config.get('aws','AWS_BUCKET_PUBLIC') -AWS_BUCKET_SECRET = Config.get('aws','AWS_BUCKET_SECRET') -AWS_BUCKET_ALT = Config.get('aws','AWS_BUCKET_ALT') +AWS_KEY_PUBLIC = Config.get('aws', 'AWS_KEY_PUBLIC') +AWS_KEY_SECRET = Config.get('aws', 'AWS_KEY_SECRET') +AWS_BUCKET_PUBLIC = Config.get('aws', 'AWS_BUCKET_PUBLIC') +AWS_BUCKET_SECRET = Config.get('aws', 'AWS_BUCKET_SECRET') +AWS_BUCKET_ALT = Config.get('aws', 'AWS_BUCKET_ALT') -s3Config= imagehelper.s3.S3Config( +s3Config = imagehelper.s3.S3Config( key_public = AWS_KEY_PUBLIC, key_private = AWS_KEY_SECRET, bucket_public_name = AWS_BUCKET_PUBLIC, bucket_archive_name = AWS_BUCKET_SECRET, - bucket_public_headers = { 'x-amz-acl' : 'public-read' }, - bucket_archive_headers = { }, + bucket_public_headers = {'x-amz-acl': 'public-read'}, + bucket_archive_headers = {}, archive_original = True ) -resizesSchema= { +resizesSchema = { 'thumb1': { 'width': 120, 'height': 120, 'save_quality': 50, 'suffix': 't1', - 'format':'JPEG', + 'format': 'JPEG', 'constraint-method': 'fit-within', 's3_bucket_public': AWS_BUCKET_ALT, 'filename_template': '%(guid)s.%(format)s', - 's3_headers': { 'x-amz-acl' : 'public-read' } + 's3_headers': {'x-amz-acl': 'public-read'} }, 't2': { 'width': 120, 'height': 120, 'save_quality': 50, 'suffix': 't2', - 'format':'PDF', + 'format': 'PDF', 'constraint-method': 'fit-within:ensure-width', }, 'thumb3': { 'width': 120, 'height': 120, - 'format':'GIF', + 'format': 'GIF', 'constraint-method': 'fit-within:ensure-height', }, 't4': { @@ -55,19 +55,19 @@ 'save_optimize': True, 'filename_template': '%(guid)s---%(suffix)s.%(format)s', 'suffix': 't4', - 'format':'PNG', + 'format': 'PNG', 'constraint-method': 'fit-within:crop-to', }, } -selected_resizes = ['thumb1','t2','thumb3','t4'] +selected_resizes = ['thumb1', 't2', 'thumb3', 't4'] -resizesSchema_alt= { +resizesSchema_alt = { 'og:image': { 'width': 3200, 'height': 3200, 'save_quality': 50, 'suffix': 'og', - 'format':'JPEG', + 'format': 'JPEG', 'constraint-method': 'smallest:ensure-minimum', 'filename_template': '%(guid)s-og.%(format)s', }, @@ -76,7 +76,7 @@ 'height': 200, 'save_quality': 50, 'suffix': 'og', - 'format':'JPEG', + 'format': 'JPEG', 'constraint-method': 'smallest:ensure-minimum', 'filename_template': '%(guid)s-og.%(format)s', }, @@ -85,39 +85,44 @@ 'height': 200, 'save_quality': 50, 'suffix': 'og', - 'format':'JPEG', + 'format': 'JPEG', 'constraint-method': 'smallest:ensure-minimum', 'filename_template': '%(guid)s-og.%(format)s', }, } -class CustomS3Logger( imagehelper.s3.S3Logger ): - def log_upload( self, bucket_name=None, key=None , file_size=None , file_md5=None ): +class CustomS3Logger(imagehelper.s3.S3Logger): + + def log_upload(self, bucket_name=None, key=None, file_size=None, file_md5=None): print "CustomS3Logger.log_upload" - print "\t %s , %s , %s , %s" % ( bucket_name , key , file_size , file_md5 ) - def log_delete( self, bucket_name=None, key=None ): + print "\t %s, %s, %s, %s" % (bucket_name, key, file_size, file_md5) + + def log_delete(self, bucket_name=None, key=None): print "CustomS3Logger.log_delete" - print "\t %s , %s" % ( bucket_name , key ) + print "\t %s, %s" % (bucket_name, key) s3Logger = CustomS3Logger() -resizerConfig = imagehelper.resizer.ResizerConfig(\ - resizesSchema=resizesSchema, - selected_resizes=selected_resizes, +resizerConfig = imagehelper.resizer.ResizerConfig( + resizesSchema=resizesSchema, + selected_resizes=selected_resizes, optimize_original=True, optimize_resized=True, ) + # create a factory -resizerFactory = imagehelper.resizer.ResizerFactory( resizerConfig=resizerConfig ) +resizerFactory = imagehelper.resizer.ResizerFactory(resizerConfig=resizerConfig) _img = None + + def get_imagefile(): global _img - if _img is None: - img = open('tests/henry.jpg','r') + if _img is None: + img = open('tests/henry.jpg', 'r') img.seek(0) data = img.read() img.close() @@ -127,110 +132,105 @@ def get_imagefile(): _img.seek(0) return _img - -guid= '123' + +guid = '123' def demo_direct(): "demo calling direct methods" - resizer = resizerFactory.resizer() # try to register the image - resizer.register_image_file( imagefile=get_imagefile() ) - + resizer.register_image_file(imagefile=get_imagefile()) + try: # resize the image # this should fail, because we don't want to risk changing the image before registering - results = resizer.resize( imagefile=get_imagefile() ) - except imagehelper.errors.ImageError_DuplicateAction : + results = resizer.resize(imagefile=get_imagefile()) + except imagehelper.errors.ImageError_DuplicateAction: # expected! pass - - resizer = resizerFactory.resizer( imagefile=get_imagefile() ) + resizer = resizerFactory.resizer(imagefile=get_imagefile()) resizedImages = resizer.resize() - + print resizedImages - + def demo_factory(): "demo calling factory methods" - + # resize ! - resizer = resizerFactory.resizer( imagefile=get_imagefile() ) + resizer = resizerFactory.resizer(imagefile=get_imagefile()) resizedImages = resizer.resize() - + if not os.path.exists('tests/output'): os.makedirs('tests/output') for k in resizedImages.resized.keys(): - open( 'tests/output/%s.%s' % (k,resizesSchema[k]['format']) , "w").write( resizedImages.resized[k].file.getvalue() ) - + open('tests/output/%s.%s' % (k, resizesSchema[k]['format']), "w").write(resizedImages.resized[k].file.getvalue()) + resizedImages.original.optimize() - open( 'tests/output/original.png', "w").write( resizedImages.original.file.getvalue() ) + open('tests/output/original.png', "w").write(resizedImages.original.file.getvalue()) - def demo_s3(): "demo s3 uploading" - resizer = resizerFactory.resizer( imagefile=get_imagefile() ) + resizer = resizerFactory.resizer(imagefile=get_imagefile()) resizedImages = resizer.resize() # upload the resized items - uploader = imagehelper.s3.S3Manager( s3Config=s3Config , resizerConfig=resizerConfig , s3Logger=s3Logger ) - uploaded = uploader.s3_save( resizedImages , guid ) + uploader = imagehelper.s3.S3Manager(s3Config=s3Config, resizerConfig=resizerConfig, s3Logger=s3Logger) + uploaded = uploader.s3_save(resizedImages, guid) print "uploaded! %s" % uploaded - - deleted = uploader.s3_delete( uploaded ) + + deleted = uploader.s3_delete(uploaded) print "deleted! %s" % deleted - + def demo_s3_alt(): "demo s3 uploading" - resizer = resizerFactory.resizer( imagefile=get_imagefile() ) + resizer = resizerFactory.resizer(imagefile=get_imagefile()) resizedImages = resizer.resize() # upload the resized items - uploader = imagehelper.s3.S3Manager( s3Config=s3Config , resizerConfig=resizerConfig , s3Logger=s3Logger ) - uploaded_original = uploader.s3_save( resizedImages , guid , selected_resizes=[] , archive_original=True ) + uploader = imagehelper.s3.S3Manager(s3Config=s3Config, resizerConfig=resizerConfig, s3Logger=s3Logger) + uploaded_original = uploader.s3_save(resizedImages, guid, selected_resizes=[], archive_original=True) print "uploaded_original! %s" % uploaded_original - uploaded_resizes = uploader.s3_save( resizedImages , guid , archive_original=False ) + uploaded_resizes = uploader.s3_save(resizedImages, guid, archive_original=False) print "uploaded_resizes! %s" % uploaded_resizes - - uploaded_all = dict( uploaded_original.items() + uploaded_resizes.items() ) - - deleted = uploader.s3_delete( uploaded_all ) + + uploaded_all = dict(uploaded_original.items() + uploaded_resizes.items()) + + deleted = uploader.s3_delete(uploaded_all) print "deleted! %s" % deleted - - + + def demo_alt_resizing(): - resizerConfig = imagehelper.resizer.ResizerConfig(\ + resizerConfig = imagehelper.resizer.ResizerConfig( resizesSchema=resizesSchema_alt, optimize_original=True, optimize_resized=True, - ) + ) # build a factory & resize - resizerFactory= imagehelper.resizer.ResizerFactory( resizerConfig=resizerConfig ) - resizedImages = resizerFactory.resize( imagefile=get_imagefile() ) - - - + resizerFactory = imagehelper.resizer.ResizerFactory(resizerConfig=resizerConfig) + resizedImages = resizerFactory.resize(imagefile=get_imagefile()) + def demo_md5(): "demo file md5" - + # resize ! - resizer = resizerFactory.resizer( imagefile=get_imagefile() ) + resizer = resizerFactory.resizer(imagefile=get_imagefile()) resizedImages = resizer.resize() - + print resizedImages - for k in resizedImages.resized.keys() : + for k in resizedImages.resized.keys(): print resizedImages.resized[k].file_md5 print resizedImages.original.file_md5 @@ -239,16 +239,14 @@ def demo_md5(): def demo_serialze(): "demo file serialize" - + # resize ! - resizer = resizerFactory.resizer( imagefile=get_imagefile() ) - + resizer = resizerFactory.resizer(imagefile=get_imagefile()) + file_md5 = resizer.get_original().file_md5 file_b64 = resizer.get_original().file_b64 - resizer = resizerFactory.resizer( file_b64 = file_b64 ) - - + resizer = resizerFactory.resizer(file_b64 = file_b64) if True: @@ -262,4 +260,3 @@ def demo_serialze(): demo_s3_alt() demo_md5() demo_serialze() - diff --git a/imagehelper/errors.py b/imagehelper/errors.py index f4a2563..0cd76d7 100644 --- a/imagehelper/errors.py +++ b/imagehelper/errors.py @@ -6,23 +6,30 @@ class ImageError(Exception): """Base class for Exceptions""" pass + class ImageError_ArgsError(ImageError): pass + class ImageError_ConfigError(ImageError): pass + class ImageError_DuplicateAction(ImageError): pass + class ImageError_MissingFile(ImageError): pass + class ImageError_Parsing(ImageError): pass + class ImageError_ResizeError(ImageError): pass + class ImageError_S3Upload(ImageError): pass diff --git a/imagehelper/image_wrapper.py b/imagehelper/image_wrapper.py index 1317654..65fcaf7 100644 --- a/imagehelper/image_wrapper.py +++ b/imagehelper/image_wrapper.py @@ -21,7 +21,7 @@ import types import envoy -##from subprocess import call +# from subprocess import call from . import errors @@ -30,24 +30,23 @@ USE_THUMBNAIL = False -_valid_types = [ cgi.FieldStorage, types.FileType, StringIO.StringIO, tempfile.SpooledTemporaryFile ] -_valid_types_nameless = [ StringIO.StringIO, tempfile.SpooledTemporaryFile ] -if cStringIO is not None : - _valid_types.extend( (cStringIO.InputType, cStringIO.OutputType,) ) - _valid_types_nameless.extend( (cStringIO.InputType, cStringIO.OutputType,) ) - -_valid_types = tuple( _valid_types ) -_valid_types_nameless = tuple( _valid_types_nameless ) +_valid_types = [cgi.FieldStorage, types.FileType, StringIO.StringIO, tempfile.SpooledTemporaryFile] +_valid_types_nameless = [StringIO.StringIO, tempfile.SpooledTemporaryFile] +if cStringIO is not None: + _valid_types.extend((cStringIO.InputType, cStringIO.OutputType, )) + _valid_types_nameless.extend((cStringIO.InputType, cStringIO.OutputType, )) +_valid_types = tuple(_valid_types) +_valid_types_nameless = tuple(_valid_types_nameless) class BasicImage(object): """A generic wrapper for Images - - `file` - a filelike object - ie, cStringIO - + + `file` + a filelike object + ie, cStringIO + `format` `name` @@ -55,24 +54,25 @@ class BasicImage(object): `width` `height` resized file attributes - + `file_size` - property to calculate the file's size + property to calculate the file's size `file_md5` property to calculate the file's md5 - + """ - def __init__( self , - fileObject , - name=None , - format=None , - mode=None , - width=None, - height=None , - ): + def __init__( + self, + fileObject, + name=None, + format=None, + mode=None, + width=None, + height=None, + ): """args - `resized_file` + `resized_file` * required `format` `name` @@ -80,10 +80,10 @@ def __init__( self , `width` `height` default = None - + """ self.file = fileObject - self.file.seek(0) # be kind, rewind + self.file.seek(0) # be kind, rewind self.name = name self.format = format self.mode = mode @@ -94,66 +94,72 @@ def __init__( self , @property def file_size(self): """property; calculate the file's size in bytes""" - return utils.file_size( self.file ) + return utils.file_size(self.file) @property def file_md5(self): """property; calculate the file's md5""" - return utils.file_md5( self.file ) - + return utils.file_md5(self.file) + @property def file_b64(self): """property; base64 encode the file""" - return utils.file_b64( self.file ) + return utils.file_b64(self.file) @property def format_standardized(self): """proxied format; standardized version""" - return utils.PIL_type_to_standardized( self.format ) + return utils.PIL_type_to_standardized(self.format) @property def file_extension(self): """proxied format; PIL version""" - return utils.PIL_type_to_extension( self.format ) - + return utils.PIL_type_to_extension(self.format) - def optimize( self, ): + def optimize(self, ): """this does some heavy lifting - + unix/mac only feature; sorry. - + this function creates an infile and outfile via NamedTemporaryFile it then pipes the file through lossless compression options - + this will replace the self.file object """ - if self.format_standardized not in ( 'jpg', 'png', 'gif' ): + if self.format_standardized not in ('jpg', 'png', 'gif'): return - + FilelikePreference = None - if isinstance( self.file, cStringIO.OutputType ): + if isinstance(self.file, cStringIO.OutputType): FilelikePreference = cStringIO.StringIO else: FilelikePreference = tempfile.SpooledTemporaryFile - - ## we need to write the image onto the disk with an infile and outfile - ## this does suck. + + # we need to write the image onto the disk with an infile and outfile + # this does suck. self.file.seek(0) fileInput = tempfile.NamedTemporaryFile() - fileInput.write( self.file.getvalue() ) + if hasattr(self.file, 'getvalue'): + fileInput.write(self.file.getvalue()) + elif hasattr(self.file, 'read'): + fileInput.write(self.file.read()) + else: + raise ValueError("not sure what to do") fileInput.seek(0) fileOutput = tempfile.NamedTemporaryFile() - if self.format_standardized == 'jpg' : - envoy.run("""jpegtran -copy none -optimize -perfect -outfile %s %s""" % ( fileOutput.name, fileInput.name ) ) - elif self.format_standardized == 'gif' : - envoy.run("""gifsicle -O2 %s --output %s""" % ( fileInput.name, fileOutput.name ) ) - elif self.format_standardized == 'png' : - envoy.run("""pngcrush -rem alla -reduce -brute -q %s %s""" % ( fileInput.name, fileOutput.name ) ) + if self.format_standardized == 'jpg': + envoy.run("""jpegtran -copy none -optimize -outfile %s %s""" % (fileOutput.name, fileInput.name)) + envoy.run("""jpegoptim --strip-all -q %s""" % (fileOutput.name, )) + elif self.format_standardized == 'gif': + envoy.run("""gifsicle -O2 %s --output %s""" % (fileInput.name, fileOutput.name)) + elif self.format_standardized == 'png': + # envoy.run("""pngcrush -rem alla -reduce -brute -q %s %s""" % (fileInput.name, fileOutput.name)) + envoy.run("""pngcrush -rem alla -reduce -q %s %s""" % (fileInput.name, fileOutput.name)) fileOutput.seek(0) newFile = FilelikePreference() - newFile.write(fileOutput.read()) + newFile.write(fileOutput.read()) newFile.seek(0) self.file = newFile self.is_optimized = True @@ -162,25 +168,23 @@ def optimize( self, ): class ResizedImage(BasicImage): """A class for a ResizedImage Result. """ - - def __repr__(self): - return "" % ( id(self) , self.__dict__ ) + def __repr__(self): + return "" % (id(self), self.__dict__) class FakedOriginal(BasicImage): """sometimes we need to fake an original file""" - format = None - mode = None - width = None - height = None - file_size = None - file_md5 = None - - def __init__( self, original_filename ): + format = None + mode = None + width = None + height = None + file_size = None + file_md5 = None + + def __init__(self, original_filename): file_ext = original_filename.split('.')[-1].lower() - self.format = utils.standardized_to_PIL_type( file_ext ) - + self.format = utils.standardized_to_PIL_type(file_ext) class ImageWrapper(object): @@ -188,47 +192,45 @@ class ImageWrapper(object): basicImage = None pilObject = None - - def get_original( self ): + def get_original(self): return self.basicImage - - def __init__(self , imagefile=None , imagefile_name=None, FilelikePreference=None, ): + def __init__(self, imagefile=None, imagefile_name=None, FilelikePreference=None, ): """registers and validates the image file note that we do copy the image file - + args: - + `imagefile` cgi.FieldStorage types.FileType - StringIO.StringIO , cStringIO.InputType, cStringIO.OutputType - tempfile.TemporaryFile , tempfile.SpooledTemporaryFile + StringIO.StringIO, cStringIO.InputType, cStringIO.OutputType + tempfile.TemporaryFile, tempfile.SpooledTemporaryFile `imagefile_name` only used for informational purposes - + `FilelikePreference` preference class for filelike objects cStringIo StringIO tempfile.SpooledTemporaryFile - + """ if imagefile is None: - raise errors.ImageError_MissingFile( utils.ImageErrorCodes.MISSING_FILE ) - - if not isinstance( imagefile, _valid_types ): - raise errors.ImageError_Parsing( utils.ImageErrorCodes.UNSUPPORTED_IMAGE_CLASS ) + raise errors.ImageError_MissingFile(utils.ImageErrorCodes.MISSING_FILE) + + if not isinstance(imagefile, _valid_types): + raise errors.ImageError_Parsing(utils.ImageErrorCodes.UNSUPPORTED_IMAGE_CLASS) try: # try to cache this all file_data = None file_name = None - if isinstance( imagefile, cgi.FieldStorage ): - if not hasattr( imagefile, 'filename' ): - raise errors.ImageError_Parsing( utils.ImageErrorCodes.MISSING_FILENAME_METHOD ) + if isinstance(imagefile, cgi.FieldStorage): + if not hasattr(imagefile, 'filename'): + raise errors.ImageError_Parsing(utils.ImageErrorCodes.MISSING_FILENAME_METHOD) imagefile.file.seek(0) file_data = imagefile.file.read() file_name = imagefile.file.name @@ -237,7 +239,7 @@ def __init__(self , imagefile=None , imagefile_name=None, FilelikePreference=Non # but someone else might care imagefile.file.seek(0) - elif isinstance( imagefile, _valid_types_nameless ): + elif isinstance(imagefile, _valid_types_nameless): imagefile.seek(0) file_data = imagefile.read() file_name = imagefile_name or '' @@ -245,9 +247,9 @@ def __init__(self , imagefile=None , imagefile_name=None, FilelikePreference=Non # be kind, rewind; the input obj we no longer care about # but someone else might care imagefile.seek(0) - - elif isinstance( imagefile, types.FileType ): - ## catch this last + + elif isinstance(imagefile, types.FileType): + # catch this last imagefile.seek(0) file_data = imagefile.read() file_name = imagefile.name @@ -260,10 +262,9 @@ def __init__(self , imagefile=None , imagefile_name=None, FilelikePreference=Non else: # just be safe with an extra else - raise ValueError( "where do i go? " ) - raise errors.ImageError_Parsing( utils.ImageErrorCodes.UNSUPPORTED_IMAGE_CLASS ) - - + raise ValueError("where do i go? ") + raise errors.ImageError_Parsing(utils.ImageErrorCodes.UNSUPPORTED_IMAGE_CLASS) + if FilelikePreference is None: if cStringIO is not None: FilelikePreference = cStringIO.StringIO @@ -275,87 +276,84 @@ def __init__(self , imagefile=None , imagefile_name=None, FilelikePreference=Non fh_imageData = FilelikePreference() fh_imageData.write(file_data) fh_imageData.seek(0) - fh_name = imagefile_name or file_name + fh_name = imagefile_name or file_name # make the new wrapped obj and then... # safety first! just ensure this loads. pilObject = Image.open(fh_imageData) pilObject.load() if not pilObject: - raise errors.ImageError_Parsing( utils.ImageErrorCodes.INVALID_REBUILD ) + raise errors.ImageError_Parsing(utils.ImageErrorCodes.INVALID_REBUILD) self.pilObject = pilObject - ## finally, stash our data - wrappedImage = BasicImage( fh_imageData , - name = fh_name , - format = self.pilObject.format , - mode = self.pilObject.mode , - width = self.pilObject.size[0] , - height = self.pilObject.size[1] , + # finally, stash our data + wrappedImage = BasicImage( + fh_imageData, + name = fh_name, + format = self.pilObject.format, + mode = self.pilObject.mode, + width = self.pilObject.size[0], + height = self.pilObject.size[1], ) self.basicImage = wrappedImage - except IOError: raise - raise errors.ImageError_Parsing( utils.ImageErrorCodes.INVALID_FILETYPE ) - - except errors.ImageError , e: - raise + raise errors.ImageError_Parsing(utils.ImageErrorCodes.INVALID_FILETYPE) - except Exception as e : + except errors.ImageError, e: raise + except Exception as e: + raise - def resize( self, instructions_dict, FilelikePreference=None, ): + def resize(self, instructions_dict, FilelikePreference=None, ): """this does the heavy lifting - + be warned - this uses a bit of memory! - + 1. we operate on a copy of the pilObject via cStringIo - ( which is already a copy of the original ) + (which is already a copy of the original) 2. we save to another new cStringIO 'file' valid `constraint-method` for `instructions_dict` 'fit-within' - Resizes item to fit within the bounding box , on both height - and width. This resulting image will be the size of the + Resizes item to fit within the bounding box, on both height + and width. This resulting image will be the size of the bounding box or smaller. 'fit-within:crop-to' - resizes the item along whichever axis ensures the bounding box - is 100% full, then crops. This resulting image will be the + resizes the item along whichever axis ensures the bounding box + is 100% full, then crops. This resulting image will be the size of the bounding box. - + 'fit-within:ensure-width' - resizes item to fit within the bounding box, scaling height - to ensure 100% width. This resulting image will be the size of + resizes item to fit within the bounding box, scaling height + to ensure 100% width. This resulting image will be the size of the bounding box. 'fit-within:ensure-height' - resizes item to fit within the bounding box, scaling width to - ensure 100% height. This resulting image will be the size of + resizes item to fit within the bounding box, scaling width to + ensure 100% height. This resulting image will be the size of the bounding box. 'smallest:ensure-minimum' - useful for things like og:image where you want at least a 200px - image. + useful for things like og:image where you want at least a 200px + image. 'exact:no-resize' - don't scale! raises an error if a scale must be made. this is a + don't scale! raises an error if a scale must be made. this is a convenience for just saving/re-encoding files. i.e. 100x100 must receive an image that is 100x100 'exact:proportion' - tries to scale the image to an exact size. raises an error if + tries to scale the image to an exact size. raises an error if it can't. Usually this is used to resample a 1:1 image, however this might be used to drop an image to a specific proportion. - i.e. 300x400 can scale to 30x40, 300x400 but not 30x50 - + i.e. 300x400 can scale to 30x40, 300x400 but not 30x50 + `FilelikePreference` - default preference for file-like objects - - """ if FilelikePreference is None: @@ -367,7 +365,7 @@ def resize( self, instructions_dict, FilelikePreference=None, ): resized_image = self.pilObject.copy() if resized_image.palette: resized_image = resized_image.convert() - + constraint_method = 'fit-within' if 'constraint-method' in instructions_dict: constraint_method = instructions_dict['constraint-method'] @@ -375,25 +373,25 @@ def resize( self, instructions_dict, FilelikePreference=None, ): # t_ = target # i_ = image / real - ( i_w , i_h ) = self.pilObject.size + (i_w, i_h) = self.pilObject.size t_w = instructions_dict['width'] t_h = instructions_dict['height'] - + crop = () - # notice that we only scale DOWN ( ie: check that t_x < i_x + # notice that we only scale DOWN (ie: check that t_x < i_x - if constraint_method in ( 'fit-within' , 'fit-within:crop-to' ): + if constraint_method in ('fit-within', 'fit-within:crop-to'): # figure out the proportions proportion_w = 1 proportion_h = 1 - if t_w < i_w : - proportion_w = t_w / i_w - if t_h < i_h : - proportion_h = t_h / i_h - + if t_w < i_w: + proportion_w = t_w / i_w + if t_h < i_h: + proportion_h = t_h / i_h + if constraint_method == 'fit-within': # peg to the SMALLEST proportion so the entire image fits if proportion_w < proportion_h: @@ -401,8 +399,8 @@ def resize( self, instructions_dict, FilelikePreference=None, ): elif proportion_h < proportion_w: proportion_w = proportion_h # figure out the resizes! - t_w = int ( i_w * proportion_w ) - t_h = int ( i_h * proportion_h ) + t_w = int(i_w * proportion_w) + t_h = int(i_h * proportion_h) elif constraint_method == 'fit-within:crop-to': # peg so the smallest dimension fills the canvas, then crop the rest. @@ -410,17 +408,17 @@ def resize( self, instructions_dict, FilelikePreference=None, ): proportion_h = proportion_w elif proportion_h > proportion_w: proportion_w = proportion_h - + # note what we want to crop to crop_w = t_w crop_h = t_h - + # figure out the resizes! - t_w = int ( i_w * proportion_w ) - t_h = int ( i_h * proportion_h ) - - if ( crop_w != t_w ) or ( crop_h != t_h ): - + t_w = int(i_w * proportion_w) + t_h = int(i_h * proportion_h) + + if (crop_w != t_w) or (crop_h != t_h): + # support_hack_against_artifacting handles an issue where .thumbnail makes stuff look like shit # except we're not using .thumbnail anymore; we're using resize directly support_hack_against_artifacting = USE_THUMBNAIL @@ -429,103 +427,103 @@ def resize( self, instructions_dict, FilelikePreference=None, ): t_w += 1 if t_h < i_h: t_h += 1 - - ( x0, y0 , x1 , y1 )= ( 0 , 0 , t_w , t_h ) - - if t_w > crop_w : - x0 = int( ( t_w / 2 ) - ( crop_w / 2 ) ) + + (x0, y0, x1, y1) = (0, 0, t_w, t_h) + + if t_w > crop_w: + x0 = int((t_w / 2) - (crop_w / 2)) x1 = x0 + crop_w - - if t_h > crop_h : - y0 = int( ( t_h / 2 ) - ( crop_h / 2 ) ) + + if t_h > crop_h: + y0 = int((t_h / 2) - (crop_h / 2)) y1 = y0 + crop_h - - crop = ( x0 , y0 , x1 , y1 ) + + crop = (x0, y0, x1, y1) elif constraint_method == 'fit-within:ensure-width': proportion = 1 - if t_w < i_w : - proportion = t_w / i_w - t_h = int ( i_h * proportion ) + if t_w < i_w: + proportion = t_w / i_w + t_h = int(i_h * proportion) elif constraint_method == 'fit-within:ensure-height': proportion = 1 - if t_h < i_h : - proportion = t_h / i_h - t_w = int ( i_w * proportion ) - + if t_h < i_h: + proportion = t_h / i_h + t_w = int(i_w * proportion) elif constraint_method == 'smallest:ensure-minimum': - ## useful for things like og:image where you want at least a 200px image - + # useful for things like og:image where you want at least a 200px image + # figure out the proportions - proportion_w = t_w / i_w - proportion_h = t_h / i_h - + proportion_w = t_w / i_w + proportion_h = t_h / i_h + # we don't want to scale up... - if ( proportion_h > 1 or proportion_w > 1 ) : + if (proportion_h > 1 or proportion_w > 1): proportion_h = 1 proportion_w = 1 - + use_scale = 'h' scale_factor = proportion_h - if proportion_w > proportion_h : + if proportion_w > proportion_h: use_scale = 'w' scale_factor = proportion_w - + t_h = int(i_h * scale_factor) t_w = int(i_w * scale_factor) - elif constraint_method == 'exact:proportion': proportion_w = 1 proportion_h = 1 - if t_w < i_w : - proportion_w = t_w / i_w - if t_h < i_h : - proportion_h = t_h / i_h - if ( proportion_w != proportion_h ) : - raise errors.ImageError_ResizeError( 'item can not be scaled to exact size' ) + if t_w < i_w: + proportion_w = t_w / i_w + if t_h < i_h: + proportion_h = t_h / i_h + if (proportion_w != proportion_h): + raise errors.ImageError_ResizeError('item can not be scaled to exact size') elif constraint_method == 'exact:no-resize': - if ( t_w != i_w ) or ( t_h != i_h ) : - raise errors.ImageError_ResizeError( 'item is not exact size' ) + if (t_w != i_w) or (t_h != i_h): + raise errors.ImageError_ResizeError('item is not exact size') else: - raise errors.ImageError_ResizeError( 'Invalid constraint-method for size recipe: "%s"' % constraint_method ) + raise errors.ImageError_ResizeError('Invalid constraint-method for size recipe: "%s"' % constraint_method) - - if ( i_w != t_w ) or ( i_h != t_h ) : - if USE_THUMBNAIL : - ## the thumbnail is faster , but has been looking uglier in recent versions - resized_image.thumbnail( [ t_w , t_h ] , Image.ANTIALIAS ) + if (i_w != t_w) or (i_h != t_h): + if USE_THUMBNAIL: + # the thumbnail is faster, but has been looking uglier in recent versions + resized_image.thumbnail([t_w, t_h], Image.ANTIALIAS) else: - resized_image = resized_image.resize( ( t_w, t_h, ) , Image.ANTIALIAS ) - + resized_image = resized_image.resize((t_w, t_h, ), Image.ANTIALIAS) + if len(crop): resized_image = resized_image.crop(crop) resized_image.load() - + format = 'JPEG' if 'format' in instructions_dict: format = instructions_dict['format'].upper() - + pil_options = {} - if format in ( 'JPEG' , 'PDF' , ) : - for i in ( 'quality', 'optimize', 'progressive' ): + if format in ('JPEG', 'PDF', ): + for i in ('quality', 'optimize', 'progressive'): k = 'save_%s' % i if k in instructions_dict: pil_options[i] = instructions_dict[k] elif format == 'PNG': - for i in ( 'optimize', 'transparency' , 'bits', 'dictionary' ): + for i in ('optimize', 'transparency', 'bits', 'dictionary'): k = 'save_%s' % i if k in instructions_dict: pil_options[i] = instructions_dict[k] - ## save the image ! + # save the image ! resized_image_file = FilelikePreference() - resized_image.save( resized_image_file , format , **pil_options ) - - return ResizedImage( resized_image_file , format=format , - width=resized_image.size[0] , height=resized_image.size[1] ) - + resized_image.save(resized_image_file, format, **pil_options) + + return ResizedImage( + resized_image_file, + format=format, + width=resized_image.size[0], + height=resized_image.size[1] + ) diff --git a/imagehelper/resizer.py b/imagehelper/resizer.py index 52b336e..6db212b 100644 --- a/imagehelper/resizer.py +++ b/imagehelper/resizer.py @@ -9,15 +9,16 @@ from . import utils + class ResizerConfig(object): """ResizerFactory allows you to specify what/how to resize. - - You could subclass this configuator - just instantiate the object with - `is_subclass = True` + + You could subclass this configuator - just instantiate the object with + `is_subclass = True` to preserve your vars, or configure one on the fly with __init__() `resizesSchema` - a dict in this format: - { 'size_name' : { + { 'size_name': { 'width': 120, 'height': 120, 'constraint-method': 'fit-within', @@ -26,12 +27,12 @@ class ResizerConfig(object): 'suffix': 't1', 'format':'JPEG', }, - 'other_size_name' : {...}, + 'other_size_name': {...}, } - - `selected_resizes` : an array of size names ( see above ) t + + `selected_resizes`: an array of size names (see above) t o be resized - + width* in pixels @@ -43,19 +44,19 @@ class ResizerConfig(object): constraint-method see below for valid constraint methods - - + + save_ keys prepended with `save_` are stripped of "save_" and are then - passed on to PIL as kwargs. - warning: different formats accept different arguments. view the + passed on to PIL as kwargs. + warning: different formats accept different arguments. view the code in 'resize' to see what works. valid constraint methods: - + see `imagehelper.image_wrapper.ImageWrapper().resize()` for full details - + 'exact:no-resize' 'exact:proportion' 'fit-within' @@ -63,7 +64,7 @@ class ResizerConfig(object): 'fit-within:ensure-height' 'fit-within:ensure-width' 'smallest:ensure-minimum' - + `optimize` - True / False @@ -72,14 +73,15 @@ class ResizerConfig(object): selected_resizes = None optimize_original = None optimize_resized = None - - def __init__( self, resizesSchema=None, selected_resizes=None, - is_subclass=False, optimize_original=None, optimize_resized=None - ): + + def __init__( + self, resizesSchema=None, selected_resizes=None, + is_subclass=False, optimize_original=None, optimize_resized=None + ): if not is_subclass: self.resizesSchema = resizesSchema - if selected_resizes is None : - if resizesSchema is not None : + if selected_resizes is None: + if resizesSchema is not None: self.selected_resizes = resizesSchema.keys() else: self.selected_resizes = selected_resizes @@ -88,29 +90,27 @@ def __init__( self, resizesSchema=None, selected_resizes=None, class ResizerFactory(object): - """This is a conveniece Factory to store application configuration + """This is a conveniece Factory to store application configuration options. - + You can create a single ResizerFactory in your application, then just use it to continually resize images. Factories have no state, they - simply hold configuration information, so they're threadsafe. + simply hold configuration information, so they're threadsafe. """ - resizerConfig= None - + resizerConfig = None - def __init__( self , resizerConfig=None ): + def __init__(self, resizerConfig=None): """ args `resizerConfig` a resizer.ResizerConfig instance """ self.resizerConfig = resizerConfig - - def resizer( self , imagefile=None , file_b64=None ): + def resizer(self, imagefile=None, file_b64=None): """Returns a resizer object; optionally with an imagefile. This does not resize. - + This is useful for validating a file for it's ability to be resized. args @@ -120,20 +120,19 @@ def resizer( self , imagefile=None , file_b64=None ): file cgi.fi """ - resizer = Resizer( resizerConfig=self.resizerConfig ) - if imagefile is not None and file_b64 is not None : + resizer = Resizer(resizerConfig=self.resizerConfig) + if imagefile is not None and file_b64 is not None: raise ValueError("Only pass in `imagefile` or `file_b64`") - if ( imagefile is not None ) or ( file_b64 is not None) : - resizer.register_image_file( imagefile=imagefile , file_b64=file_b64 ) + if (imagefile is not None) or (file_b64 is not None): + resizer.register_image_file(imagefile=imagefile, file_b64=file_b64) return resizer - class ResizerResultset(object): resized = None original = None - - def __init__( self , resized , original=None ): + + def __init__(self, resized, original=None): self.resized = resized self.original = original @@ -144,121 +143,131 @@ class Resizer(object): _resizerConfig = None _resizerResultset = None _image = None - - def __init__( self , resizerConfig=None ): + + def __init__(self, resizerConfig=None): self._resizerConfig = resizerConfig self._resizerResultset = None self._wrappedImage = None - - - def register_image_file( self, imagefile=None, imageWrapper=None, file_b64=None, optimize_original=None ): + def register_image_file( + self, + imagefile = None, + imageWrapper = None, + file_b64 = None, + optimize_original = None, + ): """registers a file to be resized - - if we pass in cgi.FieldStorage , it seems to bool() to None even when there is a value + + if we pass in cgi.FieldStorage, it seems to bool() to None even when there is a value the workaround (grr) is to check against None - + """ - - if self._wrappedImage is not None : + + if self._wrappedImage is not None: raise errors.ImageError_DuplicateAction("We already have registered a file.") - - if ( imagefile is None ) and ( imageWrapper is None ) and ( file_b64 is None ): + + if (imagefile is None) and (imageWrapper is None) and (file_b64 is None): raise errors.ImageError_ConfigError("Must submit either imagefile /or/ imageWrapper /or/ file_b64") - if ( imagefile is not None ) and ( imageWrapper is not None ) and ( file_b64 is not None) : + if (imagefile is not None) and (imageWrapper is not None) and (file_b64 is not None): raise errors.ImageError_ConfigError("Submit only imagefile /or/ imageWrapper /or/ file_b64") - if file_b64 is not None : - imagefile = utils.b64_decode_to_file( file_b64 ) - - if imagefile is not None : - self._wrappedImage = image_wrapper.ImageWrapper( imagefile = imagefile ) + if file_b64 is not None: + imagefile = utils.b64_decode_to_file(file_b64) + + if imagefile is not None: + self._wrappedImage = image_wrapper.ImageWrapper(imagefile = imagefile) elif imageWrapper is not None: - if not isinstance( imageWrapper, image_wrapper.ImageWrapper ): + if not isinstance(imageWrapper, image_wrapper.ImageWrapper): raise errors.ImageError_ConfigError("imageWrapper must be of type `imaage_wrapper.ImageWrapper`") self._wrappedImage = imageWrapper if optimize_original is None: - if self._resizerConfig : + if self._resizerConfig: optimize_original = self._resizerConfig.optimize_original else: raise ValueError("no optimize_original and no self._resizerConfig") if optimize_original: - print "optimizing original" self._wrappedImage.basicImage.optimize() - - def resize( self , imagefile=None, imageWrapper=None, file_b64=None, resizesSchema=None, selected_resizes=None, optimize_original=None, optimize_resized=None ): + def resize( + self, + imagefile = None, + imageWrapper = None, + file_b64 = None, + resizesSchema = None, + selected_resizes = None, + optimize_original = None, + optimize_resized = None, + ): """ Returns a dict of resized images calls self.register_image_file() if needed - - this resizes the images. + + this resizes the images. it returns the images and updates the internal dict. - + the internal dict will have an @archive object as well """ if resizesSchema is None: - if self._resizerConfig : + if self._resizerConfig: resizesSchema = self._resizerConfig.resizesSchema else: raise ValueError("no resizesSchema and no self._resizerConfig") if optimize_original is None: - if self._resizerConfig : + if self._resizerConfig: optimize_original = self._resizerConfig.optimize_original else: raise ValueError("no optimize_original and no self._resizerConfig") if optimize_resized is None: - if self._resizerConfig : + if self._resizerConfig: optimize_resized = self._resizerConfig.optimize_resized else: raise ValueError("no optimize_resized and no self._resizerConfig") if selected_resizes is None: - if self._resizerConfig : + if self._resizerConfig: selected_resizes = self._resizerConfig.selected_resizes else: raise ValueError("no selected_resizes and no self._resizerConfig") - + if not len(resizesSchema.keys()): raise errors.ImageError_ConfigError("We have no resizesSchema... error") if not len(selected_resizes): raise errors.ImageError_ConfigError("We have no selected_resizes... error") - if ( imagefile is not None ) or ( imageWrapper is not None ) or ( file_b64 is not None ): - self.register_image_file( imagefile=imagefile, imageWrapper=imageWrapper, file_b64=file_b64, optimize_original=optimize_original ) + if (imagefile is not None) or (imageWrapper is not None) or (file_b64 is not None): + self.register_image_file(imagefile=imagefile, imageWrapper=imageWrapper, file_b64=file_b64, optimize_original=optimize_original) + + if not self._wrappedImage: + raise errors.ImageError_ConfigError("Please pass in a `imagefile` if you have not set an imageFileObject yet") - if not self._wrappedImage : - raise errors.ImageError_ConfigError("Please pass in a `imagefile` if you have not set an imageFileObject yet") - # we'll stash the items here - resized= {} + resized = {} for size in selected_resizes: if size[0] == "@": raise errors.ImageError_ConfigError("@ is a reserved initial character for image sizes") - - ## ImageWrapper.resize returns a ResizedImage that has attributes `.resized_image`, `image_format` - resized[ size ]= self._wrappedImage.resize( resizesSchema[ size ] ) - if optimize_resized : - resized[ size ].optimize() - + + # ImageWrapper.resize returns a ResizedImage that has attributes `.resized_image`, `image_format` + resized[size] = self._wrappedImage.resize(resizesSchema[size]) + if optimize_resized: + resized[size].optimize() + resizerResultset = ResizerResultset( - resized = resized , - original = self._wrappedImage.get_original() , - ) + resized = resized, + original = self._wrappedImage.get_original(), + ) self._resizerResultset = resizerResultset return resizerResultset - - def fake_resize( self , original_filename , selected_resizes=None ): + def fake_resize(self, original_filename, selected_resizes=None): - if not self._resizerConfig : + if not self._resizerConfig: raise ValueError("fake_resultset requires an instance configured with resizerConfig") resizesSchema = self._resizerConfig.resizesSchema @@ -272,23 +281,21 @@ def fake_resize( self , original_filename , selected_resizes=None ): raise errors.ImageError_ConfigError("We have no selected_resizes... error") # we'll stash the items here - resized= {} + resized = {} for size in selected_resizes: if size[0] == "@": raise errors.ImageError_ConfigError("@ is a reserved initial character for image sizes") - - resized[ size ]= True - + + resized[size] = True + resizerResultset = ResizerResultset( - resized = resized , - original = image_wrapper.FakedOriginal( original_filename = original_filename ) , + resized = resized, + original = image_wrapper.FakedOriginal(original_filename = original_filename), ) self._resizerResultset = resizerResultset return resizerResultset - - def get_original( self ): + def get_original(self): """get the original image, which may have data for us""" return self._wrappedImage.get_original() - diff --git a/imagehelper/s3.py b/imagehelper/s3.py index 52a21a9..24e9b4c 100644 --- a/imagehelper/s3.py +++ b/imagehelper/s3.py @@ -10,14 +10,14 @@ import boto.s3.bucket except: boto = None - - -def check_archive_original( resizerResultset , archive_original=None ): + + +def check_archive_original(resizerResultset, archive_original=None): """do we want to archive the original? - + `resizerResultset` object of `resizer.Resultset` - + `archive_original` should we archive original? `None` (default) @@ -25,29 +25,28 @@ def check_archive_original( resizerResultset , archive_original=None ): archive if resizerResultset.original `True` explict. - archive resizerResultset.original; + archive resizerResultset.original; raise error if missing `False` explicit. do not archive. """ - if archive_original is False : + if archive_original is False: return False - elif archive_original is None : - if resizerResultset.original : + elif archive_original is None: + if resizerResultset.original: return True return False - elif archive_original is True : - if not resizerResultset.original : - raise ValueError("""Missing resizerResultset.original for + elif archive_original is True: + if not resizerResultset.original: + raise ValueError("""Missing resizerResultset.original for explicit archiving""") return True - class S3Config(object): """Configuration info for amazon s3 services""" key_public = None @@ -57,17 +56,17 @@ class S3Config(object): bucket_public_headers = None bucket_archive_headers = None archive_original = None - - def __init__(\ - self, - key_public = None , - key_private = None , - bucket_public_name = None , - bucket_archive_name = None , - bucket_public_headers = None , - bucket_archive_headers = None , - archive_original = None , - ): + + def __init__( + self, + key_public = None, + key_private = None, + bucket_public_name = None, + bucket_archive_name = None, + bucket_public_headers = None, + bucket_archive_headers = None, + archive_original = None, + ): self.key_public = key_public self.key_private = key_private self.bucket_public_name = bucket_public_name @@ -78,11 +77,11 @@ def __init__(\ class S3Logger(object): - """The s3 save method will log to this logger on uploads and deletes. - Any object offering these methods can be replaced; + """The s3 save method will log to this logger on uploads and deletes. + Any object offering these methods can be replaced; This is only illustrative.""" - def log_upload( self, bucket_name=None, key=None , file_size=None , file_md5=None , ): + def log_upload(self, bucket_name=None, key=None, file_size=None, file_md5=None, ): """args: `self` `bucket_name` @@ -96,7 +95,7 @@ def log_upload( self, bucket_name=None, key=None , file_size=None , file_md5=Non """ pass - def log_delete( self, bucket_name=None, key=None ): + def log_delete(self, bucket_name=None, key=None): """args: `self` `bucket_name` @@ -113,20 +112,18 @@ class S3ManagerFactory(object): _s3Config = None _s3Logger = None - def __init__( self , s3Config=None , s3Logger=None , resizerConfig=None ): + def __init__(self, s3Config=None, s3Logger=None, resizerConfig=None): self._s3Config = s3Config self._s3Logger = s3Logger self._resizerConfig = resizerConfig - + def s3_manager(self): """generate and return a new S3Manager instance""" - return S3Manager( s3Config=self._s3Config , s3Logger=self._s3Logger , resizerConfig=self._resizerConfig ) + return S3Manager(s3Config=self._s3Config, s3Logger=self._s3Logger, resizerConfig=self._resizerConfig) def s3_simple_access(self): """generate and return a new S3SimpleAccess instance""" - return S3SimpleAccess( s3Config=self._s3Config , s3Logger=self._s3Logger , resizerConfig=self._resizerConfig ) - - + return S3SimpleAccess(s3Config=self._s3Config, s3Logger=self._s3Logger, resizerConfig=self._resizerConfig) class _S3CoreManager(object): @@ -136,66 +133,63 @@ class _S3CoreManager(object): _s3Connection = None _s3Logger = None _s3_buckets = None - + s3headers_public_default = None s3headers_archive_default = None filename_template = "%(guid)s-%(suffix)s.%(format)s" filename_template_archive = "%(guid)s.%(format)s" - @property def s3_connection(self): """property that memoizes the connection""" if self._s3Connection is None: - self._s3Connection = boto.connect_s3( self._s3Config.key_public , self._s3Config.key_private ) + self._s3Connection = boto.connect_s3(self._s3Config.key_public, self._s3Config.key_private) return self._s3Connection - @property - def s3_buckets( self ): + def s3_buckets(self): """property that memoizes the s3 buckets""" if self._s3_buckets is None: # memoize the buckets - # create our bucket list + # create our bucket list s3_buckets = {} # @public and @archive are special - bucket_public = boto.s3.bucket.Bucket( connection=self.s3_connection , name=self._s3Config.bucket_public_name ) + bucket_public = boto.s3.bucket.Bucket(connection=self.s3_connection, name=self._s3Config.bucket_public_name) s3_buckets[self._s3Config.bucket_public_name] = bucket_public s3_buckets['@public'] = bucket_public - if self._s3Config.bucket_archive_name : - bucket_archive = boto.s3.bucket.Bucket( connection=self.s3_connection , name=self._s3Config.bucket_archive_name ) + if self._s3Config.bucket_archive_name: + bucket_archive = boto.s3.bucket.Bucket(connection=self.s3_connection, name=self._s3Config.bucket_archive_name) s3_buckets[self._s3Config.bucket_archive_name] = bucket_archive s3_buckets['@archive'] = bucket_archive # look through our selected sizes - if self._resizerConfig : + if self._resizerConfig: for size in self._resizerConfig.selected_resizes: if size[0] == "@": raise errors.ImageError_ConfigError("@ is a reserved initial character for image sizes") if 's3_bucket_public' in self._resizerConfig.resizesSchema[size]: bucket_name = self._resizerConfig.resizesSchema[size]['s3_bucket_public'] - if bucket_name not in s3_buckets : - s3_buckets[bucket_name] = boto.s3.bucket.Bucket( connection=self.s3_connection , name=bucket_name ) - + if bucket_name not in s3_buckets: + s3_buckets[bucket_name] = boto.s3.bucket.Bucket(connection=self.s3_connection, name=bucket_name) + # store the buckets self._s3_buckets = s3_buckets # return the memoized buckets - return self._s3_buckets - + return self._s3_buckets - def s3_delete( self , s3_uploads ): + def s3_delete(self, s3_uploads): """workhorse for deletion - - `s3_uploads` + + `s3_uploads` `dict` - format = - s3_uploads[size] = ( target_filename , bucket_name ) - + format = + s3_uploads[size] = (target_filename, bucket_name) + """ # setup the s3 connection @@ -204,58 +198,59 @@ def s3_delete( self , s3_uploads ): for size in s3_uploads.keys(): # grab the stash - ( target_filename , bucket_name ) = s3_uploads[size] + (target_filename, bucket_name) = s3_uploads[size] # active bucket bucket = s3_buckets[bucket_name] - + # delete it - log.debug( "going to delete %s from %s" % (target_filename,bucket_name) ) + log.debug("going to delete %s from %s" % + (target_filename, bucket_name)) bucket.delete_key(target_filename) # external logging if self._s3Logger: - self._s3Logger.log_delete( bucket_name=bucket_name , - key=target_filename , ) - + self._s3Logger.log_delete( + bucket_name=bucket_name, + key=target_filename, + ) # internal cleanup del s3_uploads[size] return s3_uploads - + class S3Manager(_S3CoreManager): """`S3Manager` handles all the actual uploading and deleting""" - def __init__( self , s3Config=None , s3Logger=None , resizerConfig=None ): - if not resizerConfig : + def __init__(self, s3Config=None, s3Logger=None, resizerConfig=None): + if not resizerConfig: raise ValueError("""`S3Manager` requires a `resizerConfig` which contains the resize recipes. these are needed for generating filenames.""") self._s3Config = s3Config self._s3Logger = s3Logger self._resizerConfig = resizerConfig - ## - ## generate the default headers - ## + # ## + # generate the default headers + # ## # public and archive get different acls / content-types - self.s3headers_public_default = { 'x-amz-acl' : 'public-read' } + self.s3headers_public_default = {'x-amz-acl': 'public-read'} if self._s3Config.bucket_public_headers: for k in self._s3Config.bucket_public_headers: - self.s3headers_public_default[k]= self._s3Config.bucket_public_headers[k] + self.s3headers_public_default[k] = self._s3Config.bucket_public_headers[k] self.s3headers_archive_default = {} if self._s3Config.bucket_archive_headers: for k in self._s3Config.bucket_archive_headers: - self.s3headers_archive_default[k]= self._s3Config.bucket_archive_headers[k] + self.s3headers_archive_default[k] = self._s3Config.bucket_archive_headers[k] - - def _validate__selected_resizes( self , resizerResultset , selected_resizes ): + def _validate__selected_resizes(self, resizerResultset, selected_resizes): """shared validation returns `dict` selected_resizes ARGS `resizerResultset` - `resizer.ResizerResultset` object + `resizer.ResizerResultset` object `resized` - dict of images that were resized `original ` - original file @@ -268,44 +263,43 @@ def _validate__selected_resizes( self , resizerResultset , selected_resizes ): if selected_resizes is None: selected_resizes = resizerResultset.resized.keys() - for k in selected_resizes : + for k in selected_resizes: - if k not in resizerResultset.resized : + if k not in resizerResultset.resized: raise errors.ImageError_ConfigError("selected size is not resizerResultset.resized (%s)" % k) - - if k not in self._resizerConfig.resizesSchema : + + if k not in self._resizerConfig.resizesSchema: raise errors.ImageError_ConfigError("selected size is not self._resizerConfig.resizesSchema (%s)" % k) - + # exist early for invalid sizes - if ( k[0] == "@" ) : + if (k[0] == "@"): raise errors.ImageError_ConfigError("@ is a reserved initial character for image sizes (%s)" % k) - - return selected_resizes + return selected_resizes - def generate_filenames( self , resizerResultset , guid , selected_resizes=None , archive_original=None ): + def generate_filenames(self, resizerResultset, guid, selected_resizes=None, archive_original=None): """ - generates the filenames s3 would save to; + generates the filenames s3 would save to; this is useful for planning/testing or deleting old files Returns a `dict` of target filenames keys = resized size - values = tuple ( target_filename , bucket_name ) - + values = tuple (target_filename, bucket_name) + `resizerResultset` - a `resizer.ResizerResultset` object + a `resizer.ResizerResultset` object `resized` - dict of images that were resized `original ` - original file `guid` a `uuid` or similar name that forms the basis for storage the guid is passed into the template in self._resizerConfig - + `selected_resizes` default = `None` -- all keys of resizerResultset.resized a `list` of keys to save we default to saving all the resized images - + `archive_original` default = `None` should we archive the original ? @@ -313,7 +307,7 @@ def generate_filenames( self , resizerResultset , guid , selected_resizes=None , """ if guid is None: - raise errors.ImageError_ArgsError("""You must supply a `guid` for + raise errors.ImageError_ArgsError("""You must supply a `guid` for the image. this is used for filename templating""") # default to the resized images @@ -322,16 +316,16 @@ def generate_filenames( self , resizerResultset , guid , selected_resizes=None , # quickly validate selected_resizes = self._validate__selected_resizes( - resizerResultset , selected_resizes ) + resizerResultset, selected_resizes) # init our return dict filename_mapping = {} for size in selected_resizes: - + instructions = self._resizerConfig.resizesSchema[size] - # calc vars for filename templating + # calc vars for filename templating filename_template = self.filename_template suffix = size if 'filename_template' in instructions: @@ -340,49 +334,48 @@ def generate_filenames( self , resizerResultset , guid , selected_resizes=None , suffix = instructions['suffix'] # generate the filename - target_filename = filename_template % {\ - 'guid' : guid , - 'suffix' : suffix , - 'format' : utils.PIL_type_to_standardized( instructions['format'] ) - } + target_filename = filename_template % { + 'guid': guid, + 'suffix': suffix, + 'format': utils.PIL_type_to_standardized(instructions['format']) + } # figure out the bucketname bucket_name = self._s3Config.bucket_public_name - if 's3_bucket_public' in instructions : + if 's3_bucket_public' in instructions: bucket_name = instructions['s3_bucket_public'] - - filename_mapping[ size ] = ( target_filename , bucket_name ) - - if check_archive_original( resizerResultset , archive_original=archive_original ) : + + filename_mapping[size] = (target_filename, bucket_name) + + if check_archive_original(resizerResultset, archive_original=archive_original): filename_template_archive = self.filename_template_archive - target_filename = filename_template_archive % {\ - 'guid' : guid , - 'format' : utils.PIL_type_to_standardized( resizerResultset.original.format ) - } + target_filename = filename_template_archive % { + 'guid': guid, + 'format': utils.PIL_type_to_standardized(resizerResultset.original.format) + } bucket_name = self._s3Config.bucket_archive_name - filename_mapping["@archive"] = ( target_filename , bucket_name ) + filename_mapping["@archive"] = (target_filename, bucket_name) - ## return the filemapping + # return the filemapping return filename_mapping - - def s3_save( self , resizerResultset , guid , selected_resizes=None , archive_original=None ): + def s3_save(self, resizerResultset, guid, selected_resizes=None, archive_original=None): """ Returns a dict of resized images calls self.register_image_file() if needed - - this resizes the images. + + this resizes the images. it returns the images and updates the internal dict. - + `resizerResultset` - a `resizer.ResizerResultset` object + a `resizer.ResizerResultset` object `resized` - dict of images that were resized `original ` - original file `guid` a `uuid` or similar name that forms the basis for storage the guid is passed to the template in `self.generate_filenames` - + `selected_resizes` default = `None` -- all keys of resizerResultset.resized a `list` of keys to save @@ -390,7 +383,7 @@ def s3_save( self , resizerResultset , guid , selected_resizes=None , archive_or if you don't want to resize any images: pass in an empty list -- [] passing in `None` will run the default images - + `archive_original` default = `None` should we archive the original ? @@ -398,160 +391,159 @@ def s3_save( self , resizerResultset , guid , selected_resizes=None , archive_or """ if guid is None: - raise errors.ImageError_ArgsError("""You must supply a `guid` for + raise errors.ImageError_ArgsError("""You must supply a `guid` for the image. this is used""") - + # quickly validate - selected_resizes = self._validate__selected_resizes( - resizerResultset , selected_resizes ) - + selected_resizes = self._validate__selected_resizes( + resizerResultset, selected_resizes) + # setup the s3 connection s3_buckets = self.s3_buckets # and then we have the bucketed filenames... - target_filenames = self.generate_filenames( resizerResultset , guid , - selected_resizes=selected_resizes , - archive_original=archive_original ) - - # log uploads for removal/tracking and return + target_filenames = self.generate_filenames( + resizerResultset, + guid, + selected_resizes=selected_resizes, + archive_original=archive_original + ) + + # log uploads for removal/tracking and return s3_uploads = {} try: # and then we upload... for size in selected_resizes: - - ( target_filename , bucket_name ) = target_filenames[ size ] - bucket = s3_buckets[ bucket_name ] - log.debug("Uploading %s to %s " % ( target_filename , bucket )) + (target_filename, bucket_name) = target_filenames[size] + bucket = s3_buckets[bucket_name] + + log.debug("Uploading %s to %s " % (target_filename, bucket)) # generate the headers _s3_headers = self.s3headers_public_default.copy() - _s3_headers['Content-Type'] = utils.PIL_type_to_content_type( resizerResultset.resized[size].format ) + _s3_headers['Content-Type'] = utils.PIL_type_to_content_type(resizerResultset.resized[size].format) if 's3_headers' in self._resizerConfig.resizesSchema[size]: for k in self._resizerConfig.resizesSchema[size]['s3_headers']: - _s3_headers[k]= self._resizerConfig.resizesSchema[size]['s3_headers'][k] + _s3_headers[k] = self._resizerConfig.resizesSchema[size]['s3_headers'][k] - # upload - s3_key = boto.s3.key.Key( bucket ) + # upload + s3_key = boto.s3.key.Key(bucket) s3_key.key = target_filename - s3_key.set_contents_from_string( resizerResultset.resized[size].file.getvalue() , headers=_s3_headers ) - + s3_key.set_contents_from_string(resizerResultset.resized[size].file.getvalue(), headers=_s3_headers) # log for removal/tracking & return - s3_uploads[size] = ( target_filename , bucket_name ) + s3_uploads[size] = (target_filename, bucket_name) # log to external plugin too if self._s3Logger: - self._s3Logger.log_upload( bucket_name = bucket_name , - key = target_filename , - file_size = resizerResultset.resized[size].file_size , - file_md5 = resizerResultset.resized[size].file_md5 , + self._s3Logger.log_upload( + bucket_name = bucket_name, + key = target_filename, + file_size = resizerResultset.resized[size].file_size, + file_md5 = resizerResultset.resized[size].file_md5, ) - if '@archive' in target_filenames : - + if '@archive' in target_filenames: + size = "@archive" - ( target_filename , bucket_name ) = target_filenames[ size ] - bucket = s3_buckets[ bucket_name ] + (target_filename, bucket_name) = target_filenames[size] + bucket = s3_buckets[bucket_name] - log.debug("Uploading %s to %s " % ( target_filename , bucket_name )) + log.debug("Uploading %s to %s " % (target_filename, bucket_name)) - # calculate the headers ; + # calculate the headers ; # no need to set acl, its going to be owner-only by default _s3_headers = self.s3headers_archive_default.copy() - _s3_headers['Content-Type'] = utils.PIL_type_to_content_type( resizerResultset.original.format ) + _s3_headers['Content-Type'] = utils.PIL_type_to_content_type(resizerResultset.original.format) - # upload - s3_key_original = boto.s3.key.Key( bucket ) + # upload + s3_key_original = boto.s3.key.Key(bucket) s3_key_original.key = target_filename - s3_key_original.set_contents_from_string( resizerResultset.original.file.getvalue() , headers=_s3_headers ) + s3_key_original.set_contents_from_string(resizerResultset.original.file.getvalue(), headers=_s3_headers) # log for removal/tracking & return - s3_uploads[size] = ( target_filename , bucket_name ) + s3_uploads[size] = (target_filename, bucket_name) # log to external plugin too if self._s3Logger: - self._s3Logger.log_upload( bucket_name = bucket_name , - key = target_filename , - file_size = resizerResultset.original.file_size , - file_md5 = resizerResultset.original.file_md5 , + self._s3Logger.log_upload( + bucket_name = bucket_name, + key = target_filename, + file_size = resizerResultset.original.file_size, + file_md5 = resizerResultset.original.file_md5, ) - - except Exception as e : + + except Exception as e: # if we have ANY issues, we want to delete everything from amazon s3. otherwise this stuff is just hiding up there log.debug("Error uploading... rolling back s3 items") - s3_uploads = self.s3_delete( s3_uploads ) + s3_uploads = self.s3_delete(s3_uploads) raise - raise ImageError_S3Upload('error uploading') - - return s3_uploads - - + raise errors.ImageError_S3Upload('error uploading') + return s3_uploads class S3SimpleAccess(_S3CoreManager): - def __init__( self , s3Config=None , s3Logger=None , resizerConfig=None ): + def __init__(self, s3Config=None, s3Logger=None, resizerConfig=None): self._s3Config = s3Config self._s3Logger = s3Logger self._resizerConfig = resizerConfig - ## - ## generate the default headers - ## + # ## + # ## generate the default headers + # ## # public and archive get different acls / content-types - self.s3headers_public_default = { 'x-amz-acl' : 'public-read' } + self.s3headers_public_default = {'x-amz-acl': 'public-read'} if self._s3Config.bucket_public_headers: for k in self._s3Config.bucket_public_headers: - self.s3headers_public_default[k]= self._s3Config.bucket_public_headers[k] + self.s3headers_public_default[k] = self._s3Config.bucket_public_headers[k] self.s3headers_archive_default = {} if self._s3Config.bucket_archive_headers: for k in self._s3Config.bucket_archive_headers: - self.s3headers_archive_default[k]= self._s3Config.bucket_archive_headers[k] - - - def s3_file_upload( self , bucket_name , filename , wrappedFile , upload_type="public"): - if upload_type not in ( "public", "archive"): + self.s3headers_archive_default[k] = self._s3Config.bucket_archive_headers[k] + + def s3_file_upload(self, bucket_name, filename, wrappedFile, upload_type="public"): + if upload_type not in ("public", "archive"): raise ValueError("upload_type must be `public` or `archive`") s3_buckets = self.s3_buckets s3_uploads = {} try: - - bucket = s3_buckets[ bucket_name ] - log.debug("Uploading %s to %s " % ( filename , bucket_name )) + bucket = s3_buckets[bucket_name] + + log.debug("Uploading %s to %s " % (filename, bucket_name)) - # calculate the headers ; + # calculate the headers ; # no need to set acl, its going to be owner-only by default _s3_headers = self.s3headers_public_default.copy() - _s3_headers['Content-Type'] = utils.PIL_type_to_content_type( wrappedFile.format ) + _s3_headers['Content-Type'] = utils.PIL_type_to_content_type(wrappedFile.format) - # upload - s3_key_original = boto.s3.key.Key( bucket ) + # upload + s3_key_original = boto.s3.key.Key(bucket) s3_key_original.key = filename - s3_key_original.set_contents_from_string( wrappedFile.file.getvalue() , headers=_s3_headers ) + s3_key_original.set_contents_from_string(wrappedFile.file.getvalue(), headers=_s3_headers) # log for removal/tracking & return - s3_uploads = self.simple_uploads_mapping( bucket_name , filename ) + s3_uploads = self.simple_uploads_mapping(bucket_name, filename) # log to external plugin too if self._s3Logger: - self._s3Logger.log_upload( bucket_name = bucket_name , - key = filename , - file_size = wrappedFile.file_size , - file_md5 = wrappedFile.file_md5 , + self._s3Logger.log_upload( + bucket_name = bucket_name, + key = filename, + file_size = wrappedFile.file_size, + file_md5 = wrappedFile.file_md5, ) - + return s3_uploads except: raise - - def simple_uploads_mapping( self , bucket_name , filename ) : + + def simple_uploads_mapping(self, bucket_name, filename): s3_uploads = {} - s3_uploads[ "%s||%s" % (bucket_name,filename,) ] = ( filename , bucket_name ) + s3_uploads["%s||%s" % (bucket_name, filename, )] = (filename, bucket_name) return s3_uploads - - diff --git a/imagehelper/utils.py b/imagehelper/utils.py index cee7a7f..4d8d077 100644 --- a/imagehelper/utils.py +++ b/imagehelper/utils.py @@ -17,21 +17,20 @@ class ImageErrorCodes(object): INVALID_OTHER = 2 NO_IMAGE = 3 MISSING_FILE = 4 - UNSUPPORTED_IMAGE_CLASS = 5 ## Must be cgi.FieldStorage or file + UNSUPPORTED_IMAGE_CLASS = 5 # Must be cgi.FieldStorage or file INVALID_REBUILD = 6 MISSING_FILENAME_METHOD = 7 - -_PIL_type_to_content_type= { +_PIL_type_to_content_type = { 'gif': 'image/gif', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', - 'pdf':'application/pdf', + 'pdf': 'application/pdf', 'png': 'image/png', } -_PIL_type_to_standardized= { +_PIL_type_to_standardized = { 'gif': 'gif', 'jpg': 'jpg', 'jpeg': 'jpg', @@ -39,7 +38,7 @@ class ImageErrorCodes(object): 'png': 'png', } -_standardized_to_PIL_type= { +_standardized_to_PIL_type = { 'gif': 'GIF', 'jpg': 'JPEG', 'jpeg': 'JPEG', @@ -48,35 +47,37 @@ class ImageErrorCodes(object): } -def PIL_type_to_content_type( ctype ): +def PIL_type_to_content_type(ctype): ctype = ctype.lower() if ctype in _PIL_type_to_content_type: - return _PIL_type_to_content_type[ ctype ] + return _PIL_type_to_content_type[ctype] raise ValueError('invalid ctype') -def PIL_type_to_standardized( ctype ): + +def PIL_type_to_standardized(ctype): ctype = ctype.lower() if ctype in _PIL_type_to_standardized: - return _PIL_type_to_standardized[ ctype ] + return _PIL_type_to_standardized[ctype] raise ValueError('invalid ctype') -def PIL_type_to_extension( ctype ): + +def PIL_type_to_extension(ctype): ctype = ctype.lower() if ctype in _PIL_type_to_standardized: - return _PIL_type_to_standardized[ ctype ] + return _PIL_type_to_standardized[ctype] raise ValueError('invalid ctype') -def standardized_to_PIL_type( ctype ): + +def standardized_to_PIL_type(ctype): ctype = ctype.lower() if ctype in _standardized_to_PIL_type: - return _standardized_to_PIL_type[ ctype ] + return _standardized_to_PIL_type[ctype] raise ValueError('invalid ctype') - -def file_size( fileobj ): +def file_size(fileobj): """what's the size of the object?""" - fileobj.seek(0,os.SEEK_END) + fileobj.seek(0, os.SEEK_END) sized = fileobj.tell() fileobj.seek(0) return sized @@ -86,8 +87,8 @@ def file_md5(fileobj): fileobj.seek(0) md5 = hashlib.md5() block_size = md5.block_size * 128 - for chunk in iter( lambda: fileobj.read(block_size),b''): - md5.update(chunk) + for chunk in iter(lambda: fileobj.read(block_size), b''): + md5.update(chunk) fileobj.seek(0) return md5.hexdigest() @@ -108,7 +109,3 @@ def b64_decode_to_file(coded_string): fileobj.write(decoded_data) fileobj.seek(0) return fileobj - - - - diff --git a/setup.py b/setup.py index 98500c2..0874242 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ import os from setuptools import setup + def get_docs(): result = [] in_docs = False @@ -17,9 +18,11 @@ def get_docs(): f.close() return '\n'.join(result) + requires = [ "envoy", - ] +] + setup( name='imagehelper', @@ -40,6 +43,6 @@ def get_docs(): "Topic :: Software Development :: Libraries :: Python Modules", "Development Status :: 4 - Beta", ], - tests_require = requires, - install_requires = requires, + tests_require = requires, + install_requires = requires, ) diff --git a/tests.py b/tests.py index 438534f..22335a7 100644 --- a/tests.py +++ b/tests.py @@ -5,29 +5,29 @@ import cStringIO -resizesSchema= { +resizesSchema = { 'thumb1': { 'width': 120, 'height': 120, 'save_quality': 50, 'suffix': 't1', - 'format':'JPEG', + 'format': 'JPEG', 'constraint-method': 'fit-within', 'filename_template': '%(guid)s.%(format)s', - 's3_headers': { 'x-amz-acl' : 'public-read' } + 's3_headers': {'x-amz-acl': 'public-read'} }, 't2': { 'width': 120, 'height': 120, 'save_quality': 50, 'suffix': 't2', - 'format':'PDF', + 'format': 'PDF', 'constraint-method': 'fit-within:ensure-width', }, 'thumb3': { 'width': 120, 'height': 120, - 'format':'GIF', + 'format': 'GIF', 'constraint-method': 'fit-within:ensure-height', }, 't4': { @@ -36,17 +36,19 @@ 'save_optimize': True, 'filename_template': '%(guid)s---%(suffix)s.%(format)s', 'suffix': 't4', - 'format':'PNG', + 'format': 'PNG', 'constraint-method': 'fit-within:crop-to', }, } -selected_resizes = ['thumb1','t2','thumb3','t4'] +selected_resizes = ['thumb1', 't2', 'thumb3', 't4'] _img = None + + def get_imagefile(): global _img - if _img is None: - img = open('tests/henry.jpg','r') + if _img is None: + img = open('tests/henry.jpg', 'r') img.seek(0) data = img.read() img.close() @@ -60,30 +62,31 @@ def get_imagefile(): def newS3Config(): Config = ConfigParser.ConfigParser() Config.read('aws.cfg') - AWS_KEY_PUBLIC = Config.get('aws','AWS_KEY_PUBLIC') - AWS_KEY_SECRET = Config.get('aws','AWS_KEY_SECRET') - AWS_BUCKET_PUBLIC = Config.get('aws','AWS_BUCKET_PUBLIC') - AWS_BUCKET_SECRET = Config.get('aws','AWS_BUCKET_SECRET') - AWS_BUCKET_ALT = Config.get('aws','AWS_BUCKET_ALT') - - s3Config= imagehelper.s3.S3Config( + AWS_KEY_PUBLIC = Config.get('aws', 'AWS_KEY_PUBLIC') + AWS_KEY_SECRET = Config.get('aws', 'AWS_KEY_SECRET') + AWS_BUCKET_PUBLIC = Config.get('aws', 'AWS_BUCKET_PUBLIC') + AWS_BUCKET_SECRET = Config.get('aws', 'AWS_BUCKET_SECRET') + AWS_BUCKET_ALT = Config.get('aws', 'AWS_BUCKET_ALT') + + s3Config = imagehelper.s3.S3Config( key_public = AWS_KEY_PUBLIC, key_private = AWS_KEY_SECRET, bucket_public_name = AWS_BUCKET_PUBLIC, bucket_archive_name = AWS_BUCKET_SECRET, - bucket_public_headers = { 'x-amz-acl' : 'public-read' }, - bucket_archive_headers = { }, + bucket_public_headers = {'x-amz-acl': 'public-read'}, + bucket_archive_headers = {}, archive_original = True ) - + return s3Config - -def newResizerConfig( optimize_original=True , optimize_resized=True ): - resizerConfig = imagehelper.resizer.ResizerConfig(\ - resizesSchema = resizesSchema , - selected_resizes = selected_resizes , - optimize_original = optimize_original , - optimize_resized = optimize_resized , + + +def newResizerConfig(optimize_original=True, optimize_resized=True): + resizerConfig = imagehelper.resizer.ResizerConfig( + resizesSchema = resizesSchema, + selected_resizes = selected_resizes, + optimize_original = optimize_original, + optimize_resized = optimize_resized, ) return resizerConfig @@ -93,100 +96,97 @@ def newS3Logger(): return s3Logger +class CustomS3Logger(imagehelper.s3.S3Logger): -class CustomS3Logger( imagehelper.s3.S3Logger ): - def log_upload( self, bucket_name=None, key=None , file_size=None , file_md5=None ): + def log_upload(self, bucket_name=None, key=None, file_size=None, file_md5=None): print "CustomS3Logger.log_upload" - print "\t %s , %s , %s , %s" % ( bucket_name , key , file_size , file_md5 ) - def log_delete( self, bucket_name=None, key=None ): - print "CustomS3Logger.log_delete" - print "\t %s , %s" % ( bucket_name , key ) + print "\t %s, %s, %s, %s" % (bucket_name, key, file_size, file_md5) + def log_delete(self, bucket_name=None, key=None): + print "CustomS3Logger.log_delete" + print "\t %s, %s" % (bucket_name, key) -class TestResize( unittest.TestCase ): +class TestResize(unittest.TestCase): def test_direct_resize(self): - + # new resizer config resizerConfig = newResizerConfig() - + # build a new resizer - resizer = imagehelper.resizer.Resizer( resizerConfig=resizerConfig ) + resizer = imagehelper.resizer.Resizer(resizerConfig=resizerConfig) # try to register the image - resizer.register_image_file( imagefile=get_imagefile(), ) - + resizer.register_image_file(imagefile=get_imagefile(), ) + try: # resize the image # this should fail, because we don't want to risk changing the image before registering - results = resizer.resize( imagefile=get_imagefile() ) - except imagehelper.errors.ImageError_DuplicateAction : + results = resizer.resize(imagefile=get_imagefile()) + except imagehelper.errors.ImageError_DuplicateAction: # expected! pass - + # build a new resizer - resizer = imagehelper.resizer.Resizer( resizerConfig=resizerConfig ) + resizer = imagehelper.resizer.Resizer(resizerConfig=resizerConfig) # resize the image - resizedImages = resizer.resize( imagefile=get_imagefile() ) - + resizedImages = resizer.resize(imagefile=get_imagefile()) -class TestS3( unittest.TestCase ): +class TestS3(unittest.TestCase): def test_s3_factory(self): - # generate the configs + # generate the configs resizerConfig = newResizerConfig() s3Config = newS3Config() s3Logger = newS3Logger() - + # generate the factory - s3ManagerFactory = imagehelper.s3.S3ManagerFactory( s3Config=s3Config , s3Logger=s3Logger , resizerConfig=resizerConfig ) - + s3ManagerFactory = imagehelper.s3.S3ManagerFactory(s3Config=s3Config, s3Logger=s3Logger, resizerConfig=resizerConfig) + # grab a manager s3Manager = s3ManagerFactory.s3_manager() - + # make sure we generated a manager - assert isinstance( s3Manager , imagehelper.s3.S3Manager ) + assert isinstance(s3Manager, imagehelper.s3.S3Manager) # inspect the manager to ensure it is set up correctly - assert s3Manager._s3Config == s3Config - assert s3Manager._s3Logger == s3Logger - assert s3Manager._resizerConfig == resizerConfig - - + assert s3Manager._s3Config == s3Config + assert s3Manager._s3Logger == s3Logger + assert s3Manager._resizerConfig == resizerConfig def test_s3(self): - + # new resizer config resizerConfig = newResizerConfig() # build a factory - resizerFactory = imagehelper.resizer.ResizerFactory( resizerConfig=resizerConfig ) - + resizerFactory = imagehelper.resizer.ResizerFactory(resizerConfig=resizerConfig) + # grab a resizer resizer = resizerFactory.resizer() - + # resize ! - resizedImages = resizer.resize( imagefile=get_imagefile() ) + resizedImages = resizer.resize(imagefile=get_imagefile()) # new s3 config s3Config = newS3Config() # new s3 logger - if False : + if False: s3Logger = imagehelper.s3.S3Logger() else: s3Logger = CustomS3Logger() # upload the resized items - uploader = imagehelper.s3.S3Manager( s3Config=s3Config , resizerConfig=resizerConfig , s3Logger=s3Logger ) - + uploader = imagehelper.s3.S3Manager(s3Config=s3Config, resizerConfig=resizerConfig, s3Logger=s3Logger) + guid = "123" - uploaded = uploader.s3_save( resizedImages , guid ) - deleted = uploader.s3_delete( uploaded ) + uploaded = uploader.s3_save(resizedImages, guid) + deleted = uploader.s3_delete(uploaded) -class TestResizingMethods( unittest.TestCase ): +class TestResizingMethods(unittest.TestCase): def test_fit_within(self): method = 'fit-within' @@ -195,30 +195,29 @@ def test_fit_within(self): 'width': 120, 'height': 120, 'save_optimize': True, - 'format':'PNG', + 'format': 'PNG', 'constraint-method': method, }, } - - ## what do we expect ? - expected_original_wh = ( 1200 , 1600 ) - expected_resized_wh = ( 90 , 120 ) + + # what do we expect ? + expected_original_wh = (1200, 1600) + expected_resized_wh = (90, 120) r = imagehelper.resizer.Resizer() - results = r.resize( imagefile=get_imagefile(), resizesSchema=schema, selected_resizes=('test',), optimize_original=False, optimize_resized=True, ) - - ## what do we have ? - actual_original_wh = ( results.original.width , results.original.height ) - actual_resized_wh = ( results.resized['test'].width , results.resized['test'].height ) + results = r.resize(imagefile=get_imagefile(), resizesSchema=schema, selected_resizes=('test', ), optimize_original=False, optimize_resized=True, ) + + # what do we have ? + actual_original_wh = (results.original.width, results.original.height) + actual_resized_wh = (results.resized['test'].width, results.resized['test'].height) - ## assert the original matches + # assert the original matches assert expected_original_wh[0] == actual_original_wh[0] assert expected_original_wh[1] == actual_original_wh[1] - ## assert the resize matches + # assert the resize matches assert expected_resized_wh[0] == actual_resized_wh[0] assert expected_resized_wh[1] == actual_resized_wh[1] - def test_fit_within_crop_to(self): method = 'fit-within:crop-to' @@ -227,31 +226,30 @@ def test_fit_within_crop_to(self): 'width': 120, 'height': 120, 'save_optimize': True, - 'format':'PNG', + 'format': 'PNG', 'constraint-method': method, }, } - - ## what do we expect ? - expected_original_wh = ( 1200 , 1600 ) - expected_resized_wh = ( 120 , 120 ) + + # what do we expect ? + expected_original_wh = (1200, 1600) + expected_resized_wh = (120, 120) r = imagehelper.resizer.Resizer() - results = r.resize( imagefile=get_imagefile() , resizesSchema=schema , selected_resizes=('test',), optimize_original=False, optimize_resized=True ) - - ## what do we have ? - actual_original_wh = ( results.original.width , results.original.height ) - actual_resized_wh = ( results.resized['test'].width , results.resized['test'].height ) + results = r.resize(imagefile=get_imagefile(), resizesSchema=schema, selected_resizes=('test', ), optimize_original=False, optimize_resized=True) + + # what do we have ? + actual_original_wh = (results.original.width, results.original.height) + actual_resized_wh = (results.resized['test'].width, results.resized['test'].height) - ## assert the original matches + # assert the original matches assert expected_original_wh[0] == actual_original_wh[0] assert expected_original_wh[1] == actual_original_wh[1] - ## assert the resize matches + # assert the resize matches assert expected_resized_wh[0] == actual_resized_wh[0] assert expected_resized_wh[1] == actual_resized_wh[1] - def test_fit_within_ensure_width(self): method = 'fit-within:ensure-width' schema = { @@ -259,30 +257,29 @@ def test_fit_within_ensure_width(self): 'width': 120, 'height': 120, 'save_optimize': True, - 'format':'PNG', + 'format': 'PNG', 'constraint-method': method, }, } - - ## what do we expect ? - expected_original_wh = ( 1200 , 1600 ) - expected_resized_wh = ( 120 , 160 ) + + # what do we expect ? + expected_original_wh = (1200, 1600) + expected_resized_wh = (120, 160) r = imagehelper.resizer.Resizer() - results = r.resize( imagefile=get_imagefile() , resizesSchema=schema , selected_resizes=('test',), optimize_original=False, optimize_resized=True ) - - ## what do we have ? - actual_original_wh = ( results.original.width , results.original.height ) - actual_resized_wh = ( results.resized['test'].width , results.resized['test'].height ) + results = r.resize(imagefile=get_imagefile(), resizesSchema=schema, selected_resizes=('test', ), optimize_original=False, optimize_resized=True) + + # what do we have ? + actual_original_wh = (results.original.width, results.original.height) + actual_resized_wh = (results.resized['test'].width, results.resized['test'].height) - ## assert the original matches + # assert the original matches assert expected_original_wh[0] == actual_original_wh[0] assert expected_original_wh[1] == actual_original_wh[1] - ## assert the resize matches + # assert the resize matches assert expected_resized_wh[0] == actual_resized_wh[0] assert expected_resized_wh[1] == actual_resized_wh[1] - def test_fit_within_ensure_height(self): method = 'fit-within:ensure-height' @@ -291,31 +288,30 @@ def test_fit_within_ensure_height(self): 'width': 120, 'height': 120, 'save_optimize': True, - 'format':'PNG', + 'format': 'PNG', 'constraint-method': method, }, } - - ## what do we expect ? - expected_original_wh = ( 1200 , 1600 ) - expected_resized_wh = ( 90 , 120 ) + + # what do we expect ? + expected_original_wh = (1200, 1600) + expected_resized_wh = (90, 120) r = imagehelper.resizer.Resizer() - results = r.resize( imagefile=get_imagefile() , resizesSchema=schema , selected_resizes=('test',), optimize_original=False, optimize_resized=True ) - - ## what do we have ? - actual_original_wh = ( results.original.width , results.original.height ) - actual_resized_wh = ( results.resized['test'].width , results.resized['test'].height ) + results = r.resize(imagefile=get_imagefile(), resizesSchema=schema, selected_resizes=('test', ), optimize_original=False, optimize_resized=True) + + # what do we have ? + actual_original_wh = (results.original.width, results.original.height) + actual_resized_wh = (results.resized['test'].width, results.resized['test'].height) - ## assert the original matches + # assert the original matches assert expected_original_wh[0] == actual_original_wh[0] assert expected_original_wh[1] == actual_original_wh[1] - ## assert the resize matches + # assert the resize matches assert expected_resized_wh[0] == actual_resized_wh[0] assert expected_resized_wh[1] == actual_resized_wh[1] - def test_fit_within_smallest_ensure_minimum(self): method = 'smallest:ensure-minimum' schema = { @@ -323,31 +319,30 @@ def test_fit_within_smallest_ensure_minimum(self): 'width': 120, 'height': 120, 'save_optimize': True, - 'format':'PNG', + 'format': 'PNG', 'constraint-method': method, }, } - - ## what do we expect ? - expected_original_wh = ( 1200 , 1600 ) - expected_resized_wh = ( 120 , 160 ) + + # what do we expect ? + expected_original_wh = (1200, 1600) + expected_resized_wh = (120, 160) r = imagehelper.resizer.Resizer() - results = r.resize( imagefile=get_imagefile() , resizesSchema=schema , selected_resizes=('test',), optimize_original=False, optimize_resized=True ) - - ## what do we have ? - actual_original_wh = ( results.original.width , results.original.height ) - actual_resized_wh = ( results.resized['test'].width , results.resized['test'].height ) + results = r.resize(imagefile=get_imagefile(), resizesSchema=schema, selected_resizes=('test', ), optimize_original=False, optimize_resized=True) + + # what do we have ? + actual_original_wh = (results.original.width, results.original.height) + actual_resized_wh = (results.resized['test'].width, results.resized['test'].height) - ## assert the original matches + # assert the original matches assert expected_original_wh[0] == actual_original_wh[0] assert expected_original_wh[1] == actual_original_wh[1] - ## assert the resize matches + # assert the resize matches assert expected_resized_wh[0] == actual_resized_wh[0] assert expected_resized_wh[1] == actual_resized_wh[1] - def test_fit_within_exact_no_resize(self): method = 'exact:no-resize' schema = { @@ -355,31 +350,30 @@ def test_fit_within_exact_no_resize(self): 'width': 1200, 'height': 1600, 'save_optimize': True, - 'format':'PNG', + 'format': 'PNG', 'constraint-method': method, }, } - - ## what do we expect ? - expected_original_wh = ( 1200 , 1600 ) - expected_resized_wh = ( 1200 , 1600 ) + + # what do we expect ? + expected_original_wh = (1200, 1600) + expected_resized_wh = (1200, 1600) r = imagehelper.resizer.Resizer() - results = r.resize( imagefile=get_imagefile() , resizesSchema=schema , selected_resizes=('test',), optimize_original=False, optimize_resized=True ) + results = r.resize(imagefile=get_imagefile(), resizesSchema=schema, selected_resizes=('test', ), optimize_original=False, optimize_resized=True) - ## what do we have ? - actual_original_wh = ( results.original.width , results.original.height ) - actual_resized_wh = ( results.resized['test'].width , results.resized['test'].height ) + # what do we have ? + actual_original_wh = (results.original.width, results.original.height) + actual_resized_wh = (results.resized['test'].width, results.resized['test'].height) - ## assert the original matches + # assert the original matches assert expected_original_wh[0] == actual_original_wh[0] assert expected_original_wh[1] == actual_original_wh[1] - ## assert the resize matches + # assert the resize matches assert expected_resized_wh[0] == actual_resized_wh[0] assert expected_resized_wh[1] == actual_resized_wh[1] - def test_fit_within_exact_proportion(self): method = 'exact:proportion' schema = { @@ -387,29 +381,26 @@ def test_fit_within_exact_proportion(self): 'width': 240, 'height': 320, 'save_optimize': True, - 'format':'PNG', + 'format': 'PNG', 'constraint-method': method, }, } - - ## what do we expect ? - expected_original_wh = ( 1200 , 1600 ) - expected_resized_wh = ( 240 , 320 ) + + # what do we expect ? + expected_original_wh = (1200, 1600) + expected_resized_wh = (240, 320) r = imagehelper.resizer.Resizer() - results = r.resize( imagefile=get_imagefile() , resizesSchema=schema , selected_resizes=('test',), optimize_original=False, optimize_resized=True ) + results = r.resize(imagefile=get_imagefile(), resizesSchema=schema, selected_resizes=('test', ), optimize_original=False, optimize_resized=True) - ## what do we have ? - actual_original_wh = ( results.original.width , results.original.height ) - actual_resized_wh = ( results.resized['test'].width , results.resized['test'].height ) + # what do we have ? + actual_original_wh = (results.original.width, results.original.height) + actual_resized_wh = (results.resized['test'].width, results.resized['test'].height) - ## assert the original matches + # assert the original matches assert expected_original_wh[0] == actual_original_wh[0] assert expected_original_wh[1] == actual_original_wh[1] - ## assert the resize matches + # assert the resize matches assert expected_resized_wh[0] == actual_resized_wh[0] assert expected_resized_wh[1] == actual_resized_wh[1] - - -