async def __call_sync(self, task, outputs, idx, limiter, max_retries=1, pass_fail_job=False): if limiter is not None: async with limiter: for retry_idx in range(max_retries): try: outputs[idx] = task() self.update_progress_bar() return None except Exception: if retry_idx == max_retries - 1 and not pass_fail_job: with except_handler(): raise AssertionError( "ERROR in {}th task.\n {1}".format( idx, format_exc( name="mlchain.workflows.parallel") )) if retry_idx < 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"))) else: for retry_idx in range(max_retries): try: outputs[idx] = task() self.update_progress_bar() return None except Exception: if retry_idx == max_retries - 1 and not pass_fail_job: with except_handler(): raise AssertionError("ERROR in {}th task\n".format( idx, format_exc(name="mlchain.workflows.parallel"))) if retry_idx < 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: {0}".format( format_exc(name="mlchain.workflows.parallel"))) self.update_progress_bar()
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 download_bytes(self, key, bucket_name=None, use_basename=True): """ Download a file from S3. Args: key: `str`. S3 key that will point to the file. local_path: `str`. the path to download to. bucket_name: `str`. Name of the bucket in which to store the file. use_basename: `bool`. whether or not to use the basename of the key. """ if not bucket_name: bucket_name = self.bucket try: f = BytesIO() self.client.download_fileobj(bucket_name, key, f) return f.getvalue() except Exception as e: logger.error(str(e)) return None
def call(self, request, context): header = request.header function_name = request.function_name args = request.args kwargs = request.kwargs serializer = self.get_serializer(header.serializer) headers = request.headers uid = str(uuid4()) mlchain_context.set(headers) mlchain_context['MLCHAIN_CONTEXT_ID'] = uid args = serializer.decode(args) kwargs = serializer.decode(kwargs) func = self.model.get_function(function_name) kwargs = self.get_kwargs(func, *args, **kwargs) kwargs = self._normalize_kwargs_to_valid_format(kwargs, func) try: start = time.time() output = self.model.call_function(function_name, None, **kwargs) duration = time.time() - start output = { 'output': output, 'time': duration, 'api_version': self.version, 'mlchain_version': mlchain.__version__ } except MlChainError as ex: err = ex.msg logger.error("code: {0} msg: {1}".format(ex.code, ex.msg)) output = { 'error': err, 'time': 0, 'code': ex.code, 'api_version': self.version, 'mlchain_version': mlchain.__version__ } except Exception as ex: output = { 'output': str(ex), 'time': 0, 'api_version': self.version, 'mlchain_version': mlchain.__version__ } return mlchain_pb2.Output(output=serializer.encode(output))
def download_cv2(self, key, bucket_name=None): """ Download a file from S3. Args: key: `str`. S3 key that will point to the file. local_path: `str`. the path to download to. bucket_name: `str`. Name of the bucket in which to store the file. use_basename: `bool`. whether or not to use the basename of the key. """ if not bucket_name: bucket_name = self.bucket try: f = BytesIO() self.client.download_fileobj(bucket_name, key, f) cv2 = import_cv2() return cv2.imdecode( np.asarray(bytearray(f.getvalue()), dtype="uint8"), cv2.IMREAD_COLOR) except Exception as e: logger.error(str(e)) return None
def download_file(self, key, local_path, bucket_name=None, use_basename=False): """ Download a file from S3. Args: key: `str`. S3 key that will point to the file. local_path: `str`. the path to download to. bucket_name: `str`. Name of the bucket in which to store the file. use_basename: `bool`. whether or not to use the basename of the key. """ if not bucket_name: bucket_name = self.bucket local_path = os.path.abspath(local_path) if use_basename: local_path = os.path.join( local_path, os.path.basename(key)) # append_basename(local_path, key) dir_name = os.path.dirname(local_path) if not os.path.exists(dir_name): os.makedirs(dir_name) try: self.client.download_file( bucket_name, key, local_path, Callback=ProgressPercentage( key, self.client.head_object(Bucket=bucket_name, Key=key)["ContentLength"])) return True except Exception as e: logger.error(str(e)) return False
def exception_handle(type, value, traceback): logger.error(format_exc(tb=traceback, exception=value))
def run(self, host='127.0.0.1', port=8080, bind=None, cors=False, cors_resources={}, cors_allow_origins='*', gunicorn=False, debug=False, use_reloader=False, workers=1, timeout=60, keepalive=10, max_requests=0, threads=1, worker_class='gthread', umask='0', ngrok=False, model_id=None, **kwargs): """ Run a server from a Python class :model: Your model class :host: IP address you want to start server :port: Port to start server at :bind: Gunicorn: The socket to bind. A list of string or string of the form: HOST, HOST:PORT, unix:PATH, fd://FD. An IP is a valid HOST. :deny_all_function: Default is False, which enable all function except function with @except_serving or function in blacklist, True is deny all and you could use with whitelist :blacklist: All listing function name here won't be served :whitelist: Served all function name inside whitelist :cors: Enable CORS or not :cors_resources: Config Resources of flask-cors :cors_allow_origins: Allow host of cors :gunicorn: Run with Gunicorn or not :debug: Debug or not :use_reloader: Default False, which is using 1 worker in debug instead of 2 :workers: Number of workers to run Gunicorn :timeout: Timeout of each request :keepalive: The number of seconds to wait for requests on a Keep-Alive connection. :threads: The number of worker threads for handling requests. Be careful, threads would break your result if it is bigger than 1 :worker_class: The type of workers to use. :max_requests: Max Request to restart Gunicorn Server, default is 0 which means no restart :umask: A bit mask for the file mode on files written by Gunicorn. :kwargs: Other Gunicorn options """ try: self.register_swagger() except Exception as ex: logger.error("Can't register swagger with error {0}".format(ex)) if ngrok: from pyngrok import ngrok as pyngrok endpoint = pyngrok.connect(port=port) logger.info("Ngrok url: {0}".format(endpoint)) os.environ['NGROK_URL'] = endpoint else: endpoint = os.environ.get('NGROK_URL') try: register_autofrontend(model_id=model_id, serve_model=self.model, version=self.version, endpoint=endpoint) except Exception as ex: logger.error("Can't register autofrontend with error {0}".format(ex)) if cors: CORS(self.app, resources=cors_resources, origins=cors_allow_origins) if not gunicorn: if bind is not None: if isinstance(bind, str): bind = [bind] if isinstance(bind, list): for ip_port in bind: if re.match(r'(localhost:|((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|:)){4})\d+', ip_port): logger.warning("Using host and port in bind to runserver") host, port = ip_port.split(":") logger.info("-" * 80) logger.info("Served model with Flask at host={0}, port={1}".format(host, port)) logger.info("Debug = {}".format(debug)) logger.info("-" * 80) self.app.run(host=host, port=port, debug=debug, use_reloader=use_reloader, threaded=threads > 1) else: # Process bind, host, port if isinstance(bind, str): bind = [bind] bind_host_port = '%s:%s' % (host, port) if bind is None: bind = [bind_host_port] logger.info("-" * 80) logger.info("Served model with Flask and Gunicorn at bind={}".format(bind)) logger.info("Number of workers: {}".format(workers)) logger.info("Number of threads: {}".format(threads)) logger.info("API timeout: {}".format(timeout)) logger.info("Debug = {}".format(debug)) logger.info("-" * 80) loglevel = kwargs.get('loglevel', 'warning' if debug else 'info') GunicornWrapper(self.app, bind=bind, workers=workers, timeout=timeout, keepalive=keepalive, max_requests=max_requests, loglevel=loglevel, worker_class=worker_class, threads=threads, umask=umask, **kwargs).run()