From 44558d1ede316fb652a135173f5d18e56686cf50 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Fri, 27 Aug 2021 11:34:02 +0700 Subject: [PATCH 01/17] Increase MAX_CONTENT_LENGTH to 1GB --- mlchain/__init__.py | 2 +- mlchain/server/flask_server.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mlchain/__init__.py b/mlchain/__init__.py index de9101d..9bc06d1 100644 --- a/mlchain/__init__.py +++ b/mlchain/__init__.py @@ -7,7 +7,7 @@ ) # Parameters of MLchain -__version__ = "0.2.2" +__version__ = "0.2.3" HOST = "https://www.api.mlchain.ml" WEB_HOST = HOST API_ADDRESS = HOST diff --git a/mlchain/server/flask_server.py b/mlchain/server/flask_server.py index 9eb6b24..5c5120f 100644 --- a/mlchain/server/flask_server.py +++ b/mlchain/server/flask_server.py @@ -255,7 +255,7 @@ def __init__(self, model: ServeModel, name=None, version='0.0', static_url_path=static_url_path) self.app.url_map.strict_slashes = False - self.app.config['MAX_CONTENT_LENGTH'] = 200 * 1024 * 1024 + self.app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 1024 self.app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True self.converter = Converter(FileStorage, self._get_file_name, self._get_data) diff --git a/setup.py b/setup.py index cbeb5fd..edb243e 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import pathlib import os from setuptools import setup, find_packages -__version__ = "0.2.2" +__version__ = "0.2.3" project = "mlchain" From 2a6066e78bddf9be6d73cbff6f31eb328d1359ed Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Tue, 31 Aug 2021 13:25:28 +0700 Subject: [PATCH 02/17] Default starlette -> flask, add parallel sync to compatible with gevent --- mlchain/__init__.py | 2 +- mlchain/cli/mlconfig.yaml | 13 ++- mlchain/workflows_sync/__init__.py | 2 + mlchain/workflows_sync/parallel_sync.py | 116 ++++++++++++++++++++++++ mlchain/workflows_sync/task.py | 30 ++++++ requirements.txt | 3 +- setup.py | 2 +- 7 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 mlchain/workflows_sync/__init__.py create mode 100644 mlchain/workflows_sync/parallel_sync.py create mode 100644 mlchain/workflows_sync/task.py diff --git a/mlchain/__init__.py b/mlchain/__init__.py index 9bc06d1..0d542e5 100644 --- a/mlchain/__init__.py +++ b/mlchain/__init__.py @@ -7,7 +7,7 @@ ) # Parameters of MLchain -__version__ = "0.2.3" +__version__ = "0.2.4" HOST = "https://www.api.mlchain.ml" WEB_HOST = HOST API_ADDRESS = HOST diff --git a/mlchain/cli/mlconfig.yaml b/mlchain/cli/mlconfig.yaml index a80cfe6..0a9beca 100644 --- a/mlchain/cli/mlconfig.yaml +++ b/mlchain/cli/mlconfig.yaml @@ -8,7 +8,7 @@ host: 0.0.0.0 # Host of service port: 8001 # Port service # Server config -server: starlette # Option flask or starlette or grpc +server: flask # Option flask or starlette or grpc wrapper: gunicorn # Option None or gunicorn cors: true # Auto enable CORS cors_allow_origins: # Allow origins for CORS @@ -20,10 +20,13 @@ template_folder: # template folder for TemplateResponse # Gunicorn config - Use gunicorn for general case gunicorn: - timeout: 200 # The requests will be maximum 200 seconds in default, then when the requests is done, the worker will be restarted - max_requests: 0 # Maximum serving requests until workers restart to handle over memory in Python - workers: 1 # Number of duplicate workers - threads: 1 # Number of simultaneous threads in workers + timeout: 200 # The requests will be maximum 200 seconds in default, then when the requests is done, the worker will be restarted + max_requests: 0 # Maximum serving requests until workers restart to handle over memory in Python + workers: 1 # Number of duplicate workers + threads: 1 # Number of simultaneous threads in workers + worker_class: gthread # The base worker_class, can use gevent (For better IO) or uvicorn.workers.UvicornWorker (starlette - For Async) + max_requests_jitter: 50 # Restart worker different time + accesslog: mlchain-server.log # Log file for gunicorn bind: - 'unix:/tmp/gunicorn.sock' # Using sock to make gunicorn faster diff --git a/mlchain/workflows_sync/__init__.py b/mlchain/workflows_sync/__init__.py new file mode 100644 index 0000000..3fac3ca --- /dev/null +++ b/mlchain/workflows_sync/__init__.py @@ -0,0 +1,2 @@ +from .parallel_sync import Parallel +from .task import Task diff --git a/mlchain/workflows_sync/parallel_sync.py b/mlchain/workflows_sync/parallel_sync.py new file mode 100644 index 0000000..cd99e2d --- /dev/null +++ b/mlchain/workflows_sync/parallel_sync.py @@ -0,0 +1,116 @@ +import os +from multiprocessing.pool import ThreadPool +from mlchain.base.log import format_exc, except_handler, logger +from typing import List + +class TrioProgress: + def __init__(self, total, notebook_mode=False, **kwargs): + if notebook_mode: # pragma: no cover + from tqdm.notebook import tqdm + else: + from tqdm import tqdm + + self.tqdm = tqdm(total=total, **kwargs) + self.count = 0 + self.total = total + + def task_processed(self): + self.tqdm.update(1) + self.count += 1 + if self.count == self.total: + self.tqdm.close() + +class Parallel: + """ + Build a collection of tasks to be executed in parallel + :tasks: List of [Task, function] items + :max_threads: Maximum Threads for this Parallel + :max_retries: Maximum retry time when a task fail + :pass_fail_job: Pass or Raise error when a task run fail + :verbose: Print error or not + """ + + def __init__( + self, + tasks: List, + max_threads: int = 10, + max_retries: int = 0, + pass_fail_job: bool = False, + verbose: bool = True, + ): + """ + :tasks: [Task, function] items + :max_threads: Maximum threads to Parallel, max_threads=0 means no limitation + :max_retries: How many time retry when job fail + :pass_fail_job: No exeption when a job fail + :verbose: Verbose or not + """ + + assert isinstance(tasks, list) and all( + callable(task) for task in tasks + ), "You have to transfer a list of callable instances or mlchain.Task" + self.tasks = tasks + if max_threads == -1: + max_threads = 100 + elif max_threads == 0: + max_threads = os.cpu_count() + self.max_threads = max(0, max_threads) + + self.max_retries = max(max_retries + 1, 1) + self.pass_fail_job = pass_fail_job + self.verbose = verbose + self.show_progress_bar = False + self.progress_bar = None + + def update_progress_bar(self): + if self.show_progress_bar: + self.progress_bar.task_processed() + + def exec_task(self, task, idx=None): + for retry_idx in range(self.max_retries): + try: + output = task.exec() + self.update_progress_bar() + return output + except Exception as ex: + if retry_idx == self.max_retries - 1 and not self.pass_fail_job: + return ex + if retry_idx < self.max_retries - 1 or not self.verbose: + logger.error( + "PARALLEL ERROR in {0}th task and retry task, " + "run times = {1}".format(idx, retry_idx + 1) + ) + else: + logger.debug( + "PASSED PARALLEL ERROR in {}th task:".format(idx, format_exc(name="mlchain.workflows.parallel")) + ) + return None + + def run(self, progress_bar: bool = False, notebook_mode: bool = False): + """ + When you run parallel in root, please use this function + :progress_bar: Use tqdm to show the progress of calling Parallel + :notebook_mode: Put it to true if run mlchain inside notebook + """ + pool = ThreadPool(max(1, self.max_threads)) + if progress_bar: + self.show_progress_bar = True + self.progress_bar = TrioProgress( + total=len(self.tasks), notebook_mode=notebook_mode + ) + + async_result = [ + pool.apply_async(self.exec_task, args=[task, idx]) + for idx, task in enumerate(self.tasks) + ] + + results = [] + for result in async_result: + output = result.get() + if isinstance(output, Exception): + pool.terminate() + pool.close() + raise output + results.append(output) + pool.close() + return results diff --git a/mlchain/workflows_sync/task.py b/mlchain/workflows_sync/task.py new file mode 100644 index 0000000..2231855 --- /dev/null +++ b/mlchain/workflows_sync/task.py @@ -0,0 +1,30 @@ +from mlchain import mlchain_context + +class Task: + """ + This class wrap a function to a Task + :func_: Function + """ + + def __init__(self, func_, *args, **kwargs): + assert callable(func_), 'You have to transfer a callable instance and its params' + self.func_ = func_ + self.args = args + self.kwargs = kwargs + self.span = None + self.context = mlchain_context.copy() + + def exec(self): + return self.func_(*self.args, **self.kwargs) + + def __call__(self): + """ + Task's process code + """ + return self.func_(*self.args, **self.kwargs) + + def __enter__(self): + mlchain_context.update(self.context) + + def __exit__(self, exc_type, exc_val, exc_tb): + pass \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 80cd43a..f935ed5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,10 @@ attrs>=20.3.0 blosc>=1.10.4; sys_platform != 'win32' -Click>=8.0.1 +Click>=7.1.2 Flask==1.1.2 Flask-Cors>=3.0.9 gunicorn>=20.1.0 +gevent>=21.8.0 Jinja2>=2.11.2 MarkupSafe>=1.1.1 msgpack==1.0.2 diff --git a/setup.py b/setup.py index edb243e..bbbf28b 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import pathlib import os from setuptools import setup, find_packages -__version__ = "0.2.3" +__version__ = "0.2.4" project = "mlchain" From 83bd9cf10e14cd05afd10609becf01b656d7a644 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Tue, 7 Sep 2021 15:33:38 +0700 Subject: [PATCH 03/17] Handle ast.literal_eval by using json.loads --- mlchain/base/converter.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/mlchain/base/converter.py b/mlchain/base/converter.py index 3f64478..1197aa8 100644 --- a/mlchain/base/converter.py +++ b/mlchain/base/converter.py @@ -31,6 +31,17 @@ def import_cv2(): import cv2 as cv cv2 = cv +def ast_json_parse_string(value: str): + try: + l = ast.literal_eval(value) + return l + except Exception as ex: + try: + l = json.loads(value) + return l + except Exception as ex1: + raise MLChainAssertionError("Can't convert {0} to Python list, dict, set. Please check the variable {1}".format(value, mlchain_context.CONVERT_VARIABLE)) + def bytes2ndarray(value: bytes) -> np.ndarray: import_cv2() nparr = np.fromstring(value, np.uint8) @@ -81,9 +92,10 @@ def str2ndarray(value: str) -> np.ndarray: pass try: + l = ast_json_parse_string(value) + # If it is a string array - import ast - arr = np.array(ast.literal_eval(value)) + arr = np.array(l) if arr is not None: return arr except: @@ -118,25 +130,24 @@ def str2bool(value: str) -> bool: def str2list(value: str) -> List: - try: - l = ast.literal_eval(value) - return l - except: + try: + l = ast_json_parse_string(value) + return l + except Exception as ex: return [value] - def str2dict(value: str) -> dict: try: - l = ast.literal_eval(value) + l = ast_json_parse_string(value) return l - except: + except Exception as ex: raise MLChainAssertionError("Can't convert {0} to dict. Please check the variable {1}".format(value, mlchain_context.CONVERT_VARIABLE)) def str2set(value: str) -> set: try: - l = ast.literal_eval(value) + l = ast_json_parse_string(value) return l - except: + except Exception as ex: raise MLChainAssertionError("Can't convert {0} to set. Please check the variable {1}".format(value, mlchain_context.CONVERT_VARIABLE)) def str2bytes(value: str) -> bytes: @@ -501,9 +512,10 @@ def str2pil(value: str) -> Image.Image: pass try: + l = ast_json_parse_string(value) + # If it is a string array - import ast - return Image.fromarray(ast.literal_eval(value)) + return Image.fromarray(l) except: raise MLChainAssertionError( "There's no way to convert to PIL Image with variable {0}. Please check the variable {1}".format(value, mlchain_context.CONVERT_VARIABLE)) From 0b27acd3a1392f5855a2fb00c44115de3ad0996c Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Wed, 8 Sep 2021 22:23:20 +0700 Subject: [PATCH 04/17] Add Background Sync --- mlchain/__init__.py | 2 +- mlchain/workflows_sync/__init__.py | 5 +- mlchain/workflows_sync/background.py | 117 ++++++++++++++++++ .../{parallel_sync.py => parallel.py} | 0 setup.py | 2 +- 5 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 mlchain/workflows_sync/background.py rename mlchain/workflows_sync/{parallel_sync.py => parallel.py} (100%) diff --git a/mlchain/__init__.py b/mlchain/__init__.py index 0d542e5..8d93916 100644 --- a/mlchain/__init__.py +++ b/mlchain/__init__.py @@ -7,7 +7,7 @@ ) # Parameters of MLchain -__version__ = "0.2.4" +__version__ = "0.2.5" HOST = "https://www.api.mlchain.ml" WEB_HOST = HOST API_ADDRESS = HOST diff --git a/mlchain/workflows_sync/__init__.py b/mlchain/workflows_sync/__init__.py index 3fac3ca..0211dd8 100644 --- a/mlchain/workflows_sync/__init__.py +++ b/mlchain/workflows_sync/__init__.py @@ -1,2 +1,3 @@ -from .parallel_sync import Parallel -from .task import Task +from .background import Background +from .parallel import Parallel +from .task import Task \ No newline at end of file diff --git a/mlchain/workflows_sync/background.py b/mlchain/workflows_sync/background.py new file mode 100644 index 0000000..7c884cb --- /dev/null +++ b/mlchain/workflows_sync/background.py @@ -0,0 +1,117 @@ +import inspect +import time +from threading import Thread, Event +from .task import Task +from datetime import timedelta +from concurrent.futures import ThreadPoolExecutor +import logging +import traceback + +class BackgroundTask(Thread): + def __init__(self, interval, task, max_repeat, callback=None, max_thread:int=1, pass_fail_job:bool=False): + assert callable(task) + + Thread.__init__(self) + self.stopped = Event() + self.is_done = False + self.interval = interval + self.task = task + self.max_repeat = max_repeat + self.callback = callback + self.output = None + self.pool_limit = ThreadPoolExecutor(max_workers=max_thread) + self.pass_fail_job = pass_fail_job + + if callback is not None: + self.pool_limit_callback = ThreadPoolExecutor(max_workers=1) + + def stop(self): + self.stopped.set() + self.join() + + def get_output(self, task, *args, **kwargs): + try: + self.output = task(*args, **kwargs) + except Exception as ex: + self.output = ("MLCHAIN_BACKGROUND_ERROR", traceback.format_exc()) + self.call_the_callback() + + def call_the_callback(self): + if self.callback: + self.pool_limit_callback.submit(self.callback) + + if isinstance(self.output, tuple) and len(self.output) == 2 and self.output[0] == "MLCHAIN_BACKGROUND_ERROR": + if self.pass_fail_job: + logging.error("BACKGROUND CALL ERROR: {0}".format(self.output[1])) + else: + raise Exception("BACKGROUND CALL ERROR: {0}".format(self.output[1])) + + def run(self): + if self.interval is not None: + count_repeat = 0 + while (self.max_repeat < 0 or count_repeat < self.max_repeat) \ + and (not self.stopped.wait(self.interval.total_seconds())): + + if isinstance(type(self.task), Task) \ + or issubclass(type(self.task), Task): + self.pool_limit.submit(self.get_output, self.task.func_, *self.task.args, **self.task.kwargs) + else: + self.pool_limit.submit(self.get_output, self.task) + count_repeat += 1 + else: + if isinstance(type(self.task), Task) \ + or issubclass(type(self.task), Task): + self.pool_limit.submit(self.get_output, self.task.func_, *self.task.args, **self.task.kwargs) + else: + self.pool_limit.submit(self.get_output, self.task) + + self.pool_limit.shutdown(wait=True) + self.is_done = True + + if isinstance(self.output, tuple) and len(self.output) == 2 and self.output[0] == "MLCHAIN_BACKGROUND_ERROR": + if self.pass_fail_job: + logging.error("BACKGROUND CALL ERROR: {0}".format(self.output[1])) + else: + raise Exception("BACKGROUND CALL ERROR: {0}".format(self.output[1])) + + if self.callback is not None: + self.pool_limit_callback.shutdown(wait=True) + self.is_done = True + + def wait(self, interval: float = 0.1): + while not self.is_done: + time.sleep(interval) + return self.output + + def wait(self, interval: float = 0.1): + while not self.is_done: + time.sleep(interval) + return self.output + +class Background: + """ + Run a task in background using Threading.Event + :task: [Task, function] item + :interval: timedelta or float seconds + """ + + def __init__(self, task, interval:float=None, max_repeat:int=-1, callback=None): + assert callable(task), 'You have to transfer a callable instance or an mlchain.Task' + assert (max_repeat > 0 and interval is not None and interval > 0) or max_repeat == -1, "interval need to be set when max_repeat > 0" + assert callback is None or callable(callback), "callback need to be callable" + + if interval is not None: + if isinstance(interval, int) or isinstance(interval, float): + interval = timedelta(seconds = interval) + + self.task = task + self.interval = interval + self.max_repeat = max_repeat + self.callback = callback + + def run(self, max_thread:int=1, pass_fail_job:bool=False): + task = BackgroundTask(interval=self.interval, task=self.task, + max_repeat=self.max_repeat, callback=self.callback, max_thread=max_thread, pass_fail_job=pass_fail_job) + task.start() + + return task \ No newline at end of file diff --git a/mlchain/workflows_sync/parallel_sync.py b/mlchain/workflows_sync/parallel.py similarity index 100% rename from mlchain/workflows_sync/parallel_sync.py rename to mlchain/workflows_sync/parallel.py diff --git a/setup.py b/setup.py index bbbf28b..ce6cd5f 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import pathlib import os from setuptools import setup, find_packages -__version__ = "0.2.4" +__version__ = "0.2.5" project = "mlchain" From 0fa4e33672aa6442ccd18953ae053aff550b911c Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 18:21:24 +0700 Subject: [PATCH 05/17] Update requirements --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index f935ed5..b371d72 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,13 @@ attrs>=20.3.0 blosc>=1.10.4; sys_platform != 'win32' Click>=7.1.2 -Flask==1.1.2 +Flask==1.1.4 Flask-Cors>=3.0.9 gunicorn>=20.1.0 gevent>=21.8.0 Jinja2>=2.11.2 MarkupSafe>=1.1.1 -msgpack==1.0.2 +msgpack==1.0.3 numpy<1.20; python_version == '3.6' numpy<=1.20.3; python_version >= '3.7' opencv-python>=4.1.2.30 @@ -22,7 +22,7 @@ uvicorn[standard]>=0.14.0 uvloop==0.14.0; sys_platform != 'win32' and python_version == '3.6' uvloop>=0.14.0; sys_platform != 'win32' and python_version >= '3.7' Werkzeug>=1.0.1 -httpx==0.18.2 +httpx==0.22.0 grpcio protobuf>=3.10.0 boto3>=1.16.43 From f2406cd68092648f8d49f2a7fa81ef27bb54dd45 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 18:33:02 +0700 Subject: [PATCH 06/17] Add itsdangerous and remove python-Levenshtein --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b371d72..17308a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ attrs>=20.3.0 blosc>=1.10.4; sys_platform != 'win32' Click>=7.1.2 +itsdangerous==1.1.0 Flask==1.1.4 Flask-Cors>=3.0.9 gunicorn>=20.1.0 @@ -32,5 +33,4 @@ fuzzywuzzy>=0.18.0 GPUtil>=1.4.0 tqdm pyngrok>=5.0.1 -python-Levenshtein pynvml \ No newline at end of file From 7283ef4c6f25487909feb0bcf199a3ad6c082ab6 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 18:39:48 +0700 Subject: [PATCH 07/17] Fixed Click version for Flask 1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 17308a3..2af5dc0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ attrs>=20.3.0 blosc>=1.10.4; sys_platform != 'win32' -Click>=7.1.2 +Click==7.1.2 itsdangerous==1.1.0 Flask==1.1.4 Flask-Cors>=3.0.9 From a435bedbc9c40397dcc1315bf6acae9b9e4ebb97 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 18:47:25 +0700 Subject: [PATCH 08/17] Remove Werkzeug due to Flask 1.1.4 error --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2af5dc0..4a2fa41 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,7 +22,6 @@ urllib3>=1.26.2 uvicorn[standard]>=0.14.0 uvloop==0.14.0; sys_platform != 'win32' and python_version == '3.6' uvloop>=0.14.0; sys_platform != 'win32' and python_version >= '3.7' -Werkzeug>=1.0.1 httpx==0.22.0 grpcio protobuf>=3.10.0 From c17b5c9463e5ad468c262c03b95f93574f9c7b35 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 18:55:40 +0700 Subject: [PATCH 09/17] Remove Jinja2 due to Flask 1.1.4 --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4a2fa41..55ff38e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,6 @@ Flask==1.1.4 Flask-Cors>=3.0.9 gunicorn>=20.1.0 gevent>=21.8.0 -Jinja2>=2.11.2 MarkupSafe>=1.1.1 msgpack==1.0.3 numpy<1.20; python_version == '3.6' From 0c9e31aceebc0bf8972e5e95c70129e705abd926 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 18:59:14 +0700 Subject: [PATCH 10/17] Fixed Jinja2 and Werkzeug --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index 55ff38e..d118dac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ Flask==1.1.4 Flask-Cors>=3.0.9 gunicorn>=20.1.0 gevent>=21.8.0 +Jinja2==2.11.3 MarkupSafe>=1.1.1 msgpack==1.0.3 numpy<1.20; python_version == '3.6' @@ -21,6 +22,7 @@ urllib3>=1.26.2 uvicorn[standard]>=0.14.0 uvloop==0.14.0; sys_platform != 'win32' and python_version == '3.6' uvloop>=0.14.0; sys_platform != 'win32' and python_version >= '3.7' +Werkzeug==1.0.1 httpx==0.22.0 grpcio protobuf>=3.10.0 From 035ee27524fc2214b67051450bf9759fb46a4b4f Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 19:04:35 +0700 Subject: [PATCH 11/17] Fixed h11 issue --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index d118dac..48a7d1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ attrs>=20.3.0 blosc>=1.10.4; sys_platform != 'win32' Click==7.1.2 itsdangerous==1.1.0 +h11==0.12.0 Flask==1.1.4 Flask-Cors>=3.0.9 gunicorn>=20.1.0 From 28d80a4ae151024b41f7cd3559fda5b1d2743ad4 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 19:27:06 +0700 Subject: [PATCH 12/17] Replace fuzzywuzzy by thefuzz and drop support python 3.6 --- .github/workflows/ci.yml | 2 +- mlchain/base/serve_model.py | 2 +- mlchain/server/base.py | 2 +- requirements.txt | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9c2c9a..2472aac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: [3.7, 3.8, 3.9] if: "!contains(github.event.head_commit.message, 'ci skip')" diff --git a/mlchain/base/serve_model.py b/mlchain/base/serve_model.py index c7e39b6..a178246 100644 --- a/mlchain/base/serve_model.py +++ b/mlchain/base/serve_model.py @@ -5,7 +5,7 @@ import types from mlchain.context import mlchain_context from .exceptions import MLChainAssertionError, MlChainError, MLChain404Error -from fuzzywuzzy import process as fuzzywuzzy_process +from thefuzz import process as fuzzywuzzy_process def non_thread(timeout=-1): if timeout is None or (isinstance(timeout, (float, int)) and timeout <= 0): diff --git a/mlchain/server/base.py b/mlchain/server/base.py index 1a6af19..62417af 100644 --- a/mlchain/server/base.py +++ b/mlchain/server/base.py @@ -3,7 +3,7 @@ import warnings from inspect import signature, _empty from collections import defaultdict -from fuzzywuzzy.fuzz import ratio +from thefuzz.fuzz import ratio from mlchain.base import ServeModel from mlchain.base.log import logger from mlchain.base.serializer import JsonSerializer, MsgpackSerializer, MsgpackBloscSerializer diff --git a/requirements.txt b/requirements.txt index 48a7d1f..4bd6837 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,8 +29,8 @@ grpcio protobuf>=3.10.0 boto3>=1.16.43 pyyaml>=5.3.1 -sentry-sdk[flask]>=1.1.0 -fuzzywuzzy>=0.18.0 +sentry-sdk>=1.5.7 +thefuzz GPUtil>=1.4.0 tqdm pyngrok>=5.0.1 From 217618cde770dff7fc7e50c415c2d66b17bcfeb6 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 19:34:38 +0700 Subject: [PATCH 13/17] Fixed MarkupSafe --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 4bd6837..2b43dc8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ Flask==1.1.4 Flask-Cors>=3.0.9 gunicorn>=20.1.0 gevent>=21.8.0 +MarkupSafe==2.0.1 Jinja2==2.11.3 MarkupSafe>=1.1.1 msgpack==1.0.3 From 0c084cddf214d5025b041e45811b3263006b9692 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 19:37:44 +0700 Subject: [PATCH 14/17] Remove reduntdant MarkupSafe>=1.1.1 --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2b43dc8..02762ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,6 @@ gunicorn>=20.1.0 gevent>=21.8.0 MarkupSafe==2.0.1 Jinja2==2.11.3 -MarkupSafe>=1.1.1 msgpack==1.0.3 numpy<1.20; python_version == '3.6' numpy<=1.20.3; python_version >= '3.7' From 1241ae63e04522df59e6c43f1549d6a3d54c63f8 Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Thu, 10 Mar 2022 19:40:27 +0700 Subject: [PATCH 15/17] Re-update sentry-sdk[flask] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 02762ce..6178584 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,7 +29,7 @@ grpcio protobuf>=3.10.0 boto3>=1.16.43 pyyaml>=5.3.1 -sentry-sdk>=1.5.7 +sentry-sdk[flask]>=1.5.7 thefuzz GPUtil>=1.4.0 tqdm From 34628eecfc7fa8c376c0cd09093e7e182c379b4c Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Fri, 11 Mar 2022 08:20:56 +0700 Subject: [PATCH 16/17] Does not use opencv-python 4.5 because of failed coverage test --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6178584..fdfac55 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ Jinja2==2.11.3 msgpack==1.0.3 numpy<1.20; python_version == '3.6' numpy<=1.20.3; python_version >= '3.7' -opencv-python>=4.1.2.30 +opencv-python>=4.1.2.30, <4.5 Pillow>=8.0.1 starlette[full]>=0.14.2 requests>=2.25.1 From 90de92867692502795dca208bf339e47ef57c64a Mon Sep 17 00:00:00 2001 From: Hoang Viet Date: Mon, 14 Mar 2022 16:12:48 +0700 Subject: [PATCH 17/17] Handle fix starlette version from typing_extensions import ParamSpec --- mlchain/__init__.py | 2 +- requirements.txt | 4 ++-- setup.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mlchain/__init__.py b/mlchain/__init__.py index 3c978ab..fcee9b4 100644 --- a/mlchain/__init__.py +++ b/mlchain/__init__.py @@ -7,7 +7,7 @@ ) # Parameters of MLchain -__version__ = "0.2.5" +__version__ = "0.2.6" HOST = "https://www.api.mlchain.ml" WEB_HOST = HOST diff --git a/requirements.txt b/requirements.txt index fdfac55..22dd09e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,13 +14,13 @@ numpy<1.20; python_version == '3.6' numpy<=1.20.3; python_version >= '3.7' opencv-python>=4.1.2.30, <4.5 Pillow>=8.0.1 -starlette[full]>=0.14.2 +starlette[full]==0.19.0 requests>=2.25.1 six>=1.13.0 toml>=0.10.0 trio>=0.19.0 urllib3>=1.26.2 -uvicorn[standard]>=0.14.0 +uvicorn[standard]==0.17.6 uvloop==0.14.0; sys_platform != 'win32' and python_version == '3.6' uvloop>=0.14.0; sys_platform != 'win32' and python_version >= '3.7' Werkzeug==1.0.1 diff --git a/setup.py b/setup.py index a069e42..5b42119 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import pathlib import os from setuptools import setup, find_packages -__version__ = "0.2.5" +__version__ = "0.2.6" project = "mlchain"