# Author: Milos Buncic
# Date: 2017/10/14
# Description: Image modifier function (output: byte stream)
# Supported parameters:
#   url, width, height, scale, gray, invert, flip, mirror and fmt

import os
import sys
import json
import requests
from uuid import uuid4
from io import BytesIO
from PIL import Image, ImageOps


def json2dict(data):
  """
  Convert json to dict (output: dict)
  """
  try:
    return json.loads(data)
  except Exception as e:
    print('error: {}'.format(e), file=sys.stderr)
    sys.exit()


def get_img(url, timeout=60):
  """
  Make request and return binary content
  """
  try:
    return requests.get(url, timeout=timeout).content
  except Exception as e:
    print('error: {}'.format(e), file=sys.stderr)
    sys.exit()


def imgmod(image, **parms):
  """
  Image modifier (output: byte stream)
  """
  try:
    # Open and resize the image
    img = Image.open(BytesIO(image))
    if parms['width'] and parms['height']:
      newimg = img.resize((int(parms['width']), int(parms['height'])))
    elif parms['width']:
      newimg = img.resize((int(parms['width']), img.height))
    elif parms['height']:
      newimg = img.resize((img.width, int(parms['height'])))
    else:
      newimg = img.resize([
          int(size * float(parms['scale']))
          for size in img.size
        ])

    # Make image black and white
    if parms['gray']:
      newimg = ImageOps.grayscale(newimg)

    # Invert (negate) the image
    if parms['invert']:
      newimg = ImageOps.invert(newimg)

    # Flip the image vertically (top to bottom)
    if parms['flip']:
      newimg = ImageOps.flip(newimg)

    # Flip the image horizontally (left to right)
    if parms['mirror']:
      newimg = ImageOps.mirror(newimg)

    # Send image as byte stream on stdout
    parms['fmt'] = 'jpeg' if parms['fmt'].lower() == 'jpg' else parms['fmt']
    filename = '/dev/shm/{}.{}'.format(
        uuid4(), parms['fmt']
      ).replace('-', '')
    newimg.save(filename, parms['fmt'])
    with open(filename, 'rb') as f:
      sys.stdout.buffer.write(f.read())
  except Exception as e:
    print('error: {}'.format(e), file=sys.stderr)
  finally:
    try:
      # Cleanup
      newimg.close()
      img.close()
      os.remove(filename)
    except:
      pass


def handle(data, **parms):
  """
  Caller function (will be called by run.py)
  """
  # Collect parms from stdin (json), if they don't exist, try query strings
  parms = json2dict(data) if data else parms

  cfg_parms = [
    'url',
    'width',
    'height',
    'scale',
    'gray',
    'invert',
    'flip',
    'mirror',
    'fmt'
  ]

  # Collect defined and set default values for supported parameters
  for p in cfg_parms:
    if p in ['url', 'width', 'height']:
      parms[p] = parms.get(p)
    elif p == 'scale':
      parms[p] = parms.get(p) if parms.get(p) else 1.0
    elif p == 'fmt':
      parms[p] = parms.get(p) if parms.get(p) else 'jpeg'
    else:
      # gray, invert, flip, mirror
      parms[p] = True if parms.get(p) == 'true' else False

  # Start modifying the image
  imgmod(get_img(parms['url']), **parms)

@@ -240,31 +240,8 @@
 sts%0A
-from uuid import uuid4%0A
 from
@@ -1742,18 +1742,18 @@
  stream 
+t
 o
-n
  stdout%0A
@@ -1837,97 +1837,27 @@
     
-filename = '/dev/shm/%7B%7D.%7B%7D'.format(%0A        uuid4(), parms%5B'fmt'%5D%0A      ).replace('-', ''
+out_file = BytesIO(
 )%0A  
@@ -1870,23 +1870,23 @@
 mg.save(
-filenam
+out_fil
 e, parms
@@ -1902,42 +1902,25 @@
     
-with open(filename, 'rb') as f:%0A  
+out_file.seek(0)%0A
     
@@ -1943,17 +1943,24 @@
 r.write(
-f
+out_file
 .read())
@@ -2121,25 +2121,22 @@
    o
-s.remove(filename
+ut_file.close(
 )%0A  

Use memory files with BytesIO