From c22fdbb36ae20caea6b90584972b56704521562a Mon Sep 17 00:00:00 2001 From: Abubakar Abid Date: Tue, 19 Feb 2019 00:26:09 -0800 Subject: [PATCH] added requirements to push package --- .idea/workspace.xml | 193 ++++++++++-------- gradio.py => build/lib/gradio/__init__.py | 6 +- inputs.py => build/lib/gradio/inputs.py | 2 +- .../lib/gradio/networking.py | 0 outputs.py => build/lib/gradio/outputs.py | 0 .../lib/gradio/preprocessing_utils.py | 0 dist/gradio-0.1.0-py3-none-any.whl | Bin 0 -> 6799 bytes dist/gradio-0.1.0.tar.gz | Bin 0 -> 5397 bytes gradio.egg-info/PKG-INFO | 11 + gradio.egg-info/SOURCES.txt | 12 ++ gradio.egg-info/dependency_links.txt | 1 + gradio.egg-info/requires.txt | 6 + gradio.egg-info/top_level.txt | 1 + gradio/__init__.py | 131 ++++++++++++ gradio/inputs.py | 94 +++++++++ gradio/networking.py | 76 +++++++ gradio/outputs.py | 63 ++++++ gradio/preprocessing_utils.py | 53 +++++ script.py | 12 -- setup.py | 24 +++ 20 files changed, 581 insertions(+), 104 deletions(-) rename gradio.py => build/lib/gradio/__init__.py (98%) rename inputs.py => build/lib/gradio/inputs.py (98%) rename networking.py => build/lib/gradio/networking.py (100%) rename outputs.py => build/lib/gradio/outputs.py (100%) rename preprocessing_utils.py => build/lib/gradio/preprocessing_utils.py (100%) create mode 100644 dist/gradio-0.1.0-py3-none-any.whl create mode 100644 dist/gradio-0.1.0.tar.gz create mode 100644 gradio.egg-info/PKG-INFO create mode 100644 gradio.egg-info/SOURCES.txt create mode 100644 gradio.egg-info/dependency_links.txt create mode 100644 gradio.egg-info/requires.txt create mode 100644 gradio.egg-info/top_level.txt create mode 100644 gradio/__init__.py create mode 100644 gradio/inputs.py create mode 100644 gradio/networking.py create mode 100644 gradio/outputs.py create mode 100644 gradio/preprocessing_utils.py delete mode 100644 script.py create mode 100644 setup.py diff --git a/.idea/workspace.xml b/.idea/workspace.xml index c13e3cff68..3887798abf 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,15 +2,12 @@ - - - - - - - - - + + + + + + - - + + - - + + - + - + - + - - + + - - + + - - + + + + + + + + + + - - + + + + + + @@ -168,41 +177,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -223,9 +197,6 @@ - clear - ctx - clear-b css/ communica communicate @@ -253,6 +224,9 @@ 6002 INITIAL_WEBSOCKET_PORT print( + inputs. + subprocess + psutil 400 @@ -296,6 +270,9 @@ @@ -335,7 +312,7 @@ - + @@ -388,7 +365,7 @@ - + @@ -519,13 +496,6 @@ - - - - - - - @@ -533,13 +503,6 @@ - - - - - - - @@ -577,8 +540,6 @@ - - @@ -600,13 +561,69 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gradio.py b/build/lib/gradio/__init__.py similarity index 98% rename from gradio.py rename to build/lib/gradio/__init__.py index a9d1ee16c4..683e35061b 100644 --- a/gradio.py +++ b/build/lib/gradio/__init__.py @@ -3,9 +3,9 @@ import websockets import nest_asyncio import webbrowser from bs4 import BeautifulSoup -import inputs -import outputs -import networking +from gradio import inputs +from gradio import outputs +from gradio import networking nest_asyncio.apply() diff --git a/inputs.py b/build/lib/gradio/inputs.py similarity index 98% rename from inputs.py rename to build/lib/gradio/inputs.py index de4d101e21..bbc482de24 100644 --- a/inputs.py +++ b/build/lib/gradio/inputs.py @@ -3,7 +3,7 @@ import base64 from PIL import Image from io import BytesIO import numpy as np -import preprocessing_utils +from gradio import preprocessing_utils class AbstractInput(ABC): """ diff --git a/networking.py b/build/lib/gradio/networking.py similarity index 100% rename from networking.py rename to build/lib/gradio/networking.py diff --git a/outputs.py b/build/lib/gradio/outputs.py similarity index 100% rename from outputs.py rename to build/lib/gradio/outputs.py diff --git a/preprocessing_utils.py b/build/lib/gradio/preprocessing_utils.py similarity index 100% rename from preprocessing_utils.py rename to build/lib/gradio/preprocessing_utils.py diff --git a/dist/gradio-0.1.0-py3-none-any.whl b/dist/gradio-0.1.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..14d001d0743e0135aeac758736522cf6715de953 GIT binary patch literal 6799 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 literal 0 HcmV?d00001 diff --git a/gradio.egg-info/PKG-INFO b/gradio.egg-info/PKG-INFO new file mode 100644 index 0000000000..81c54d5ad1 --- /dev/null +++ b/gradio.egg-info/PKG-INFO @@ -0,0 +1,11 @@ +Metadata-Version: 1.0 +Name: gradio +Version: 0.1.0 +Summary: Python library for easily interacting with trained machine learning models +Home-page: https://github.com/abidlabs/gradio +Author: Abubakar Abid +Author-email: a12d@stanford.edu +License: UNKNOWN +Description: UNKNOWN +Keywords: machine learning,visualization,reproducibility +Platform: UNKNOWN diff --git a/gradio.egg-info/SOURCES.txt b/gradio.egg-info/SOURCES.txt new file mode 100644 index 0000000000..083d66bd06 --- /dev/null +++ b/gradio.egg-info/SOURCES.txt @@ -0,0 +1,12 @@ +README.md +setup.py +gradio/__init__.py +gradio/inputs.py +gradio/networking.py +gradio/outputs.py +gradio/preprocessing_utils.py +gradio.egg-info/PKG-INFO +gradio.egg-info/SOURCES.txt +gradio.egg-info/dependency_links.txt +gradio.egg-info/requires.txt +gradio.egg-info/top_level.txt \ No newline at end of file diff --git a/gradio.egg-info/dependency_links.txt b/gradio.egg-info/dependency_links.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/gradio.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/gradio.egg-info/requires.txt b/gradio.egg-info/requires.txt new file mode 100644 index 0000000000..0d1880790e --- /dev/null +++ b/gradio.egg-info/requires.txt @@ -0,0 +1,6 @@ +numpy +websockets +nest_asyncio +beautifulsoup4Pillow +requests +psutil diff --git a/gradio.egg-info/top_level.txt b/gradio.egg-info/top_level.txt new file mode 100644 index 0000000000..25aceddaba --- /dev/null +++ b/gradio.egg-info/top_level.txt @@ -0,0 +1 @@ +gradio diff --git a/gradio/__init__.py b/gradio/__init__.py new file mode 100644 index 0000000000..683e35061b --- /dev/null +++ b/gradio/__init__.py @@ -0,0 +1,131 @@ +import asyncio +import websockets +import nest_asyncio +import webbrowser +from bs4 import BeautifulSoup +from gradio import inputs +from gradio import outputs +from gradio import networking + +nest_asyncio.apply() + +LOCALHOST_IP = '127.0.0.1' +INITIAL_WEBSOCKET_PORT = 9200 +TRY_NUM_PORTS = 100 + + +class Interface(): + """ + """ + build_template_path = 'templates/tmp_html.html' + + def __init__(self, input, output, model, model_type, preprocessing_fn=None, postprocessing_fn=None): + """ + :param model_type: what kind of trained model, can be 'keras' or 'sklearn'. + :param model_obj: the model object, such as a sklearn classifier or keras model. + :param model_params: additional model parameters. + """ + self.input_interface = inputs.registry[input](preprocessing_fn) + self.output_interface = outputs.registry[output](postprocessing_fn) + self.model_type = model_type + self.model_obj = model + + def _build_template(self): + input_template_path = self.input_interface._get_template_path() + output_template_path = self.output_interface._get_template_path() + input_page = open(input_template_path) + output_page = open(output_template_path) + input_soup = BeautifulSoup(input_page.read(), features="html.parser") + output_soup = BeautifulSoup(output_page.read(), features="html.parser") + + all_io_url = 'templates/all_io.html' + all_io_page = open(all_io_url) + all_io_soup = BeautifulSoup(all_io_page.read(), features="html.parser") + input_tag = all_io_soup.find("div", {"id": "input"}) + output_tag = all_io_soup.find("div", {"id": "output"}) + + input_tag.replace_with(input_soup) + output_tag.replace_with(output_soup) + + f = open(self.build_template_path, "w") + f.write(str(all_io_soup.prettify)) + return self.build_template_path + + def _set_socket_url_in_js(self, socket_url): + with open('js/all-io.js') as fin: + lines = fin.readlines() + lines[0] = 'var NGROK_URL = "{}"\n'.format(socket_url.replace('http', 'ws')) + + with open('js/all-io.js', 'w') as fout: + for line in lines: + fout.write(line) + + def _set_socket_port_in_js(self, socket_port): + with open('js/all-io.js') as fin: + lines = fin.readlines() + lines[1] = 'var SOCKET_PORT = {}\n'.format(socket_port) + + with open('js/all-io.js', 'w') as fout: + for line in lines: + fout.write(line) + + def predict(self, array): + if self.model_type=='sklearn': + return self.model_obj.predict(array) + elif self.model_type=='keras': + return self.model_obj.predict(array) + elif self.model_type=='func': + return self.model_obj(array) + else: + raise ValueError('model_type must be one of: "sklearn" or "keras" or "func".') + + async def communicate(self, websocket, path): + """ + Method that defines how this interface communicates with the websocket. + :param websocket: a Websocket object used to communicate with the interface frontend + :param path: ignored + """ + while True: + try: + msg = await websocket.recv() + processed_input = self.input_interface._pre_process(msg) + prediction = self.predict(processed_input) + processed_output = self.output_interface._post_process(prediction) + await websocket.send(str(processed_output)) + except websockets.exceptions.ConnectionClosed: + pass + + def launch(self, share_link=True): + """ + Standard method shared by interfaces that launches a websocket at a specified IP address. + """ + networking.kill_processes([4040, 4041]) + server_port = networking.start_simple_server() + path_to_server = 'http://localhost:{}/'.format(server_port) + path_to_template = self._build_template() + + ports_in_use = networking.get_ports_in_use() + for i in range(TRY_NUM_PORTS): + if not ((INITIAL_WEBSOCKET_PORT + i) in ports_in_use): + break + else: + raise OSError("All ports from {} to {} are in use. Please close a port.".format( + INITIAL_WEBSOCKET_PORT, INITIAL_WEBSOCKET_PORT + TRY_NUM_PORTS)) + + start_server = websockets.serve(self.communicate, LOCALHOST_IP, INITIAL_WEBSOCKET_PORT + i) + self._set_socket_port_in_js(INITIAL_WEBSOCKET_PORT + i) + + if share_link: + site_ngrok_url = networking.setup_ngrok(server_port) + socket_ngrok_url = networking.setup_ngrok(INITIAL_WEBSOCKET_PORT, api_url=networking.NGROK_TUNNELS_API_URL2) + self._set_socket_url_in_js(socket_ngrok_url) + print("NOTE: Gradio is in beta stage, please report all bugs to: a12d@stanford.edu") + print("Model available locally at: {}".format(path_to_server + path_to_template)) + print("Model available publicly for 8 hours at: {}".format(site_ngrok_url + '/' + path_to_template)) + asyncio.get_event_loop().run_until_complete(start_server) + try: + asyncio.get_event_loop().run_forever() + except RuntimeError: # Runtime errors are thrown in jupyter notebooks because of async. + pass + + webbrowser.open(path_to_server + path_to_template) diff --git a/gradio/inputs.py b/gradio/inputs.py new file mode 100644 index 0000000000..bbc482de24 --- /dev/null +++ b/gradio/inputs.py @@ -0,0 +1,94 @@ +from abc import ABC, abstractmethod +import base64 +from PIL import Image +from io import BytesIO +import numpy as np +from gradio import preprocessing_utils + +class AbstractInput(ABC): + """ + An abstract class for defining the methods that all gradio inputs should have. + When this is subclassed, it is automatically added to the registry + """ + + def __init__(self, preprocessing_fn=None): + if preprocessing_fn is not None: + self._pre_process = preprocessing_fn + super().__init__() + + @abstractmethod + def _get_template_path(self): + """ + All interfaces should define a method that returns the path to its template. + """ + pass + + @abstractmethod + def _pre_process(self): + """ + All interfaces should define a method that returns the path to its template. + """ + pass + + +class Sketchpad(AbstractInput): + + def _get_template_path(self): + return 'templates/sketchpad_input.html' + + def _pre_process(self, imgstring): + """ + """ + content = imgstring.split(';')[1] + image_encoded = content.split(',')[1] + body = base64.decodebytes(image_encoded.encode('utf-8')) + im = Image.open(BytesIO(base64.b64decode(image_encoded))).convert('L') + im = preprocessing_utils.resize_and_crop(im, (28, 28)) + array = np.array(im).flatten().reshape(1, 28, 28, 1) + return array + + +class Webcam(AbstractInput): + + def _get_template_path(self): + return 'templates/webcam_input.html' + + def _pre_process(self, imgstring): + """ + """ + content = imgstring.split(';')[1] + image_encoded = content.split(',')[1] + body = base64.decodebytes(image_encoded.encode('utf-8')) + im = Image.open(BytesIO(base64.b64decode(image_encoded))).convert('L') + im = preprocessing_utils.resize_and_crop(im, (48, 48)) + array = np.array(im).flatten().reshape(1, 48, 48, 1) + return array + +class Textbox(AbstractInput): + + def _get_template_path(self): + return 'templates/textbox_input.html' + + def _pre_process(self, text): + """ + """ + return text + +class ImageUpload(AbstractInput): + + def _get_template_path(self): + return 'templates/image_upload_input.html' + + def _pre_process(self, imgstring): + """ + """ + content = imgstring.split(';')[1] + image_encoded = content.split(',')[1] + body = base64.decodebytes(image_encoded.encode('utf-8')) + im = Image.open(BytesIO(base64.b64decode(image_encoded))).convert('L') + im = preprocessing_utils.resize_and_crop(im, (48, 48)) + array = np.array(im).flatten().reshape(1, 48, 48, 1) + return array + + +registry = {cls.__name__.lower(): cls for cls in AbstractInput.__subclasses__()} diff --git a/gradio/networking.py b/gradio/networking.py new file mode 100644 index 0000000000..bb0e01586b --- /dev/null +++ b/gradio/networking.py @@ -0,0 +1,76 @@ +import subprocess +import requests +import zipfile +import io +import sys +import os +from psutil import process_iter, AccessDenied +from signal import SIGTERM # or SIGKILL + +INITIAL_PORT_VALUE = 7860 +TRY_NUM_PORTS = 100 +LOCALHOST_PREFIX = 'localhost:' +NGROK_TUNNELS_API_URL = "http://localhost:4040/api/tunnels" # TODO(this should be captured from output) +NGROK_TUNNELS_API_URL2 = "http://localhost:4041/api/tunnels" # TODO(this should be captured from output) + +NGROK_ZIP_URLS = { + "linux": "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip", + "darwin": "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-amd64.zip", + "win32": "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-windows-amd64.zip", +} + + +def get_ports_in_use(): + ports_in_use = [] + for proc in process_iter(): + for conns in proc.connections(kind='inet'): + ports_in_use.append(conns.laddr.port) + return ports_in_use + + +def start_simple_server(): + # TODO(abidlabs): increment port number until free port is found + ports_in_use = get_ports_in_use() + for i in range(TRY_NUM_PORTS): + if not((INITIAL_PORT_VALUE + i) in ports_in_use): + break + else: + raise OSError("All ports from {} to {} are in use. Please close a port.".format( + INITIAL_PORT_VALUE, INITIAL_PORT_VALUE + TRY_NUM_PORTS)) + subprocess.Popen(['python', '-m', 'http.server', str(INITIAL_PORT_VALUE + i)]) + return INITIAL_PORT_VALUE + i + + +def download_ngrok(): + try: + zip_file_url = NGROK_ZIP_URLS[sys.platform] + except KeyError: + print("Sorry, we don't currently support your operating system, please leave us a note on GitHub, and we'll look into it!") + return + + r = requests.get(zip_file_url) + z = zipfile.ZipFile(io.BytesIO(r.content)) + z.extractall() + + +def setup_ngrok(local_port, api_url=NGROK_TUNNELS_API_URL): + if not(os.path.isfile('ngrok.exe')): + download_ngrok() + subprocess.Popen(['ngrok', 'http', str(local_port)]) + r = requests.get(api_url) + for tunnel in r.json()['tunnels']: + if LOCALHOST_PREFIX + str(local_port) in tunnel['config']['addr']: + return tunnel['public_url'] + raise RuntimeError("Not able to retrieve ngrok public URL") + + +def kill_processes(process_ids): + for proc in process_iter(): + for conns in proc.connections(kind='inet'): + if conns.laddr.port in process_ids: + try: + proc.send_signal(SIGTERM) # or SIGKILL + except AccessDenied: + print("Unable to kill process running on port {}, please kill manually.".format(conns.laddr.port)) + + diff --git a/gradio/outputs.py b/gradio/outputs.py new file mode 100644 index 0000000000..9a0e5b6fc0 --- /dev/null +++ b/gradio/outputs.py @@ -0,0 +1,63 @@ +from abc import ABC, abstractmethod +import numpy as np + +class AbstractOutput(ABC): + """ + An abstract class for defining the methods that all gradio inputs should have. + When this is subclassed, it is automatically added to the registry + """ + + def __init__(self, postprocessing_fn=None): + """ + """ + if postprocessing_fn is not None: + self._post_process = postprocessing_fn + super().__init__() + + @abstractmethod + def _get_template_path(self): + """ + All interfaces should define a method that returns the path to its template. + """ + pass + + @abstractmethod + def _post_process(self): + """ + All interfaces should define a method that returns the path to its template. + """ + pass + + +class Class(AbstractOutput): + + def _get_template_path(self): + return 'templates/class_output.html' + + def _post_process(self, prediction): + """ + """ + if isinstance(prediction, np.ndarray): + prediction = prediction.squeeze() + if prediction.size == 1: + return prediction + else: + return prediction.argmax() + elif isinstance(prediction, str): + return prediction + else: + raise ValueError("Unable to post-process model prediction.") + + +class Textbox(AbstractOutput): + + def _get_template_path(self): + return 'templates/textbox_output.html' + + def _post_process(self, prediction): + """ + """ + return prediction + + +registry = {cls.__name__.lower(): cls for cls in AbstractOutput.__subclasses__()} diff --git a/gradio/preprocessing_utils.py b/gradio/preprocessing_utils.py new file mode 100644 index 0000000000..bcdc4302d0 --- /dev/null +++ b/gradio/preprocessing_utils.py @@ -0,0 +1,53 @@ +from PIL import Image + + +def resize_and_crop(img, size, crop_type='top'): + """ + Resize and crop an image to fit the specified size. + args: + img_path: path for the image to resize. + modified_path: path to store the modified image. + size: `(width, height)` tuple. + crop_type: can be 'top', 'middle' or 'bottom', depending on this + value, the image will cropped getting the 'top/left', 'midle' or + 'bottom/rigth' of the image to fit the size. + raises: + Exception: if can not open the file in img_path of there is problems + to save the image. + ValueError: if an invalid `crop_type` is provided. + """ + # Get current and desired ratio for the images + img_ratio = img.size[0] / float(img.size[1]) + ratio = size[0] / float(size[1]) + # The image is scaled/cropped vertically or horizontally depending on the ratio + if ratio > img_ratio: + img = img.resize((size[0], size[0] * img.size[1] / img.size[0]), + Image.ANTIALIAS) + # Crop in the top, middle or bottom + if crop_type == 'top': + box = (0, 0, img.size[0], size[1]) + elif crop_type == 'middle': + box = (0, (img.size[1] - size[1]) / 2, img.size[0], (img.size[1] + size[1]) / 2) + elif crop_type == 'bottom': + box = (0, img.size[1] - size[1], img.size[0], img.size[1]) + else: + raise ValueError('ERROR: invalid value for crop_type') + img = img.crop(box) + elif ratio < img_ratio: + img = img.resize((size[1] * img.size[0] // img.size[1], size[1]), + Image.ANTIALIAS) + # Crop in the top, middle or bottom + if crop_type == 'top': + box = (0, 0, size[0], img.size[1]) + elif crop_type == 'middle': + box = ((img.size[0] - size[0]) / 2, 0, (img.size[0] + size[0]) / 2, img.size[1]) + elif crop_type == 'bottom': + box = (img.size[0] - size[0], 0, img.size[0], img.size[1]) + else: + raise ValueError('ERROR: invalid value for crop_type') + img = img.crop(box) + else: + img = img.resize((size[0], size[1]), + Image.ANTIALIAS) + # If the scale is the same, we do not need to crop + return img diff --git a/script.py b/script.py deleted file mode 100644 index 8b4896706a..0000000000 --- a/script.py +++ /dev/null @@ -1,12 +0,0 @@ -from bs4 import BeautifulSoup - -sketchpad_url = 'templates/sketchpad_input.html' -all_io_url = 'templates/all_io.html' -sketchpad_page = open(sketchpad_url) -all_io_page = open(all_io_url) -sketchpad_soup = BeautifulSoup(sketchpad_page.read()) -all_io_soup = BeautifulSoup(all_io_page.read()) -input_tag = all_io_soup.find("div", {"id": "input"}) -input_tag.replace_with(sketchpad_soup) -f = open("templates/tmp_html.html", "w") -f.write(str(all_io_soup.prettify)) \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000000..ddbb12cdd3 --- /dev/null +++ b/setup.py @@ -0,0 +1,24 @@ +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + +setup( + name = 'gradio', + version = '0.1.0', + description = 'Python library for easily interacting with trained machine learning models', + author = 'Abubakar Abid', + author_email = 'a12d@stanford.edu', + url = 'https://github.com/abidlabs/gradio', + packages=['gradio'], + keywords = ['machine learning', 'visualization', 'reproducibility'], + install_requires=[ + 'numpy', + 'websockets', + 'nest_asyncio', + 'beautifulsoup4' + 'Pillow', + 'requests', + 'psutil', + ], +)