diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..f0da052 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,8 @@ +[run] +omit = + */site-packages/* + */distutils/* + tests/* + setup.py + mlchain/__main__.py +concurrency = multiprocessing \ No newline at end of file diff --git a/.gitignore b/.gitignore index 27ed906..12db4de 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ mlchain.egg-info .pyc test/* .pytest_cache -**/.DS_Store \ No newline at end of file +**/.DS_Store +htmlcov +.coverage +.coverage.* \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index cd5ed41..36a4761 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,7 @@ jobs: - stage: build name: "Build on Python 3.7 MacOS" os: osx - language: minimal + language: shell install: - pip3 install -U scikit-build awscli pip - pip3 install -U -r requirements.txt @@ -78,6 +78,7 @@ jobs: script: - pip install . - python -m coverage run --source=. -m unittest discover + - python -m coverage combine - python -m coverage report after_success: - codecov @@ -93,6 +94,7 @@ jobs: script: - pip install . - python -m coverage run --source=. -m unittest discover + - python -m coverage combine - python -m coverage report after_success: - codecov @@ -101,13 +103,14 @@ jobs: - stage: test name: "Test on Python 3.7 MacOS" os: osx - language: minimal + language: shell install: - pip3 install -U scikit-build pytest coverage codecov pip - pip3 install -U attrs script: - pip3 install . - python3 -m coverage run --source=. -m unittest discover + - python3 -m coverage combine - python3 -m coverage report after_success: - codecov @@ -125,6 +128,7 @@ jobs: script: - python -m pip install . - python -m coverage run --source=. -m unittest discover + - python -m coverage combine - python -m coverage report after_success: - codecov @@ -142,4 +146,4 @@ jobs: secure: lEK5DTFYmn2vkeP8OrG8CPgUOH/PvhO76OO5F2/K3HbQxJZLH3Vsmzk/mRsY1pIC52XpGLDSg/8d62V9bu2WNReLRQJZ1zIgKSswvf4USrsd0axGwIbJyuX+vr81/x4j4rQr3ohfb2zOIit7JoDrRugwBICSAEukNfXoOZdN6wVn4zpLsW/bdlrNlIsNmhzUBKfurRMPEqsSE1Bq2dDGmyd4KNiZlaJF4PEgNQHfV9qwW2+j/ky4ulCzFgIfxKUpIfvUPFN3Uw3HdaJAaOH6h+S84hvi30xwD8bT8os990fK0zZ/sW5e0ogRckmkGh3jDJcXzyCPetAABMkUjwTrIkehQ2I2QhT49V0+Qqq3A3iJFAGxufl3HEYNY4ZVxo7PXMBIkmA6TdKomhPLh1nKeQTaFomSvoY/Usnc5pF1Va5vhHUz2mGeUgnbR0kQPN2d3yF6hVvl2TnM4Ml0YK2sQa4og4xYOouBGqJsATTZ7OEdG/Iha1KpQRwkHaWs+FShGs1UL0UZO5+FYVSd6va3i1IPxZLoZzJBPscclKBJbrtfup7cWRO2LEeS1uzS0NEKGTmrZLmOW8m9o0Pt+F61pU8meG3O+n0CxYGrytUSNvCrMA0+ZT9DY0hF9Q08w7AASG+zZVarwhnFDakeNq91Z1GLGxB+mD3vIa5mslZGYMw= on: tags: true - distributions: "sdist bdist_wheel" \ No newline at end of file + distributions: "sdist bdist_wheel" \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index ec95e9d..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2018 Techainer - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index 2ea1b5d..8005087 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include LICENSE *.rst *.toml *.yml *.yaml +include LICENSE *.rst *.md *.toml *.yml *.yaml include requirements.txt graft .github diff --git a/README.rst b/README.rst deleted file mode 100644 index 4b4a136..0000000 --- a/README.rst +++ /dev/null @@ -1,3 +0,0 @@ -Mlchain helps AI developers to easily run, deploy and monitor AI models and Workflows without having Devops skills. - -This Mlchain Python library lets you launch models and do many tasks with Mlchain Platform. Please sign up your account on Mlchain before working with it. \ No newline at end of file diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..24f3088 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,9 @@ +coverage: + status: + project: # settings affecting project coverage + default: + target: auto # auto % coverage target + threshold: 5% # allow for 5% reduction of coverage without failing + + # do not run coverage on patch nor changes + patch: false \ No newline at end of file diff --git a/mlchain/__init__.py b/mlchain/__init__.py index 33dd70d..d100a4e 100644 --- a/mlchain/__init__.py +++ b/mlchain/__init__.py @@ -1,5 +1,5 @@ # Parameters of MLchain -__version__ = "0.1.6" +__version__ = "0.1.7" HOST = "https://www.api.mlchain.ml" WEB_HOST = HOST API_ADDRESS = HOST @@ -11,11 +11,4 @@ from .context import mlchain_context from .base.exceptions import * -from .config import mlconfig - -try: - import torch - - torch.set_num_thread(1) -except Exception as e: - pass +from .config import mlconfig \ No newline at end of file diff --git a/mlchain/cli/init.py b/mlchain/cli/init.py index 9917024..a29c381 100644 --- a/mlchain/cli/init.py +++ b/mlchain/cli/init.py @@ -14,4 +14,5 @@ def init_command(file): logger.warning("File {} exists. Please change name file".format(file)) else: with open(file, 'wb') as fp: - fp.write(open(os.path.join(root_path, 'config.yaml'), 'rb').read()) + with open(os.path.join(root_path, 'config.yaml'), 'rb') as fr: + fp.write(fr.read()) diff --git a/mlchain/cli/main.py b/mlchain/cli/main.py index 2d98267..da865fc 100644 --- a/mlchain/cli/main.py +++ b/mlchain/cli/main.py @@ -17,22 +17,21 @@ def get_version(ctx, param, value): if not value or ctx.resilient_parsing: return - message = "Python %(python)s\nMlChain %(mlchain)s\nFlask %(flask)s\nQuart %(quart)s\nGrpc %(grpc)s" + message = "Python %(python)s\nMlChain %(mlchain)s\nFlask %(flask)s\nQuart %(quart)s\n" click.echo( message % { "python": platform.python_version(), "mlchain": __version__, "flask": flask.__version__, - "quart": quart.__version__, - "grpc": grpc.__version__ + "quart": quart.__version__ }, color=ctx.color, ) ctx.exit() -def main(as_module=False): +def main(as_module=False, is_testing=False): version_option = click.Option( ["--version"], help="Show the mlchain version", @@ -46,8 +45,9 @@ def main(as_module=False): cli.add_command(init_command) cli.add_command(artifact_command) cli.add_command(serve_command) + if is_testing: + return cli cli.main(args=sys.argv[1:], prog_name="python -m mlchain" if as_module else None) - if __name__ == "__main__": - main(as_module=True) + cli = main(as_module=True) diff --git a/mlchain/cli/run.py b/mlchain/cli/run.py index 8993589..a8c5c65 100644 --- a/mlchain/cli/run.py +++ b/mlchain/cli/run.py @@ -131,11 +131,13 @@ def run_command(entry_file, host, port, bind, wrapper, server, workers, config, bind = None bind = mlconfig.get_value(bind, config, 'bind', []) wrapper = mlconfig.get_value(wrapper, config, 'wrapper', None) - workers = mlconfig.get_value(workers, config, 'workers', None) - if workers is None: - workers = 1 - else: - workers = int(workers) + if wrapper == 'gunicorn' and os.name == 'nt': + logger.warning('Gunicorn warper are not supported on Windows. Switching to None instead.') + wrapper = None + workers = mlconfig.get_value(workers, config['gunicorn'], 'workers', None) + if workers is None and 'hypercorn' in config.keys(): + workers = mlconfig.get_value(workers, config['hypercorn'], 'workers', None) + workers = int(workers) if workers is not None else 1 name = mlconfig.get_value(name, config, 'name', None) cors = mlconfig.get_value(None, config, 'cors', False) @@ -262,7 +264,7 @@ def load(self): static_url_path=static_url_path, static_folder=static_folder, template_folder=template_folder) - app.run(host, port, bind=bind, cors=cors, workers=workers, + app.run(host, port, bind=bind, cors=cors, gunicorn=False, hypercorn=True, **config.get('hypercorn', {}), model_id=model_id) app = get_model(entry_file) diff --git a/mlchain/client/__init__.py b/mlchain/client/__init__.py index fc18f1b..51ee43e 100644 --- a/mlchain/client/__init__.py +++ b/mlchain/client/__init__.py @@ -7,8 +7,6 @@ class Client(HttpClient, GrpcClient): def __init__(self, api_key=None, api_address=None, serializer='json', timeout=5 * 60, headers=None, type='http', name: str = "", version: str = "", check_status=False): - logger.warn("mlchain.client.Client is deprecated and will be remove in the next version. " - "Please use mlchain.client.HttpModel instead") assert isinstance(type, str), "type model must be a string" self._api_key = api_key self._api_address = api_address @@ -28,7 +26,7 @@ def __init__(self, api_key=None, api_address=None, serializer='json', timeout=5 raise Exception("type must be http or grpc") def model(self, name: str = "", version: str = "", check_status=False): - logger.warn( + logger.warning( "function .model is deprecated and will be remove in the next version") if self._type.lower() == 'http': return HttpClient(api_key=self._api_key, api_address=self._api_address, serializer=self._serializer, diff --git a/mlchain/config.py b/mlchain/config.py index 71448a5..954257a 100644 --- a/mlchain/config.py +++ b/mlchain/config.py @@ -43,7 +43,7 @@ def from_json(self, path): def from_yaml(self, path): import yaml - self.update(yaml.load(open(path))) + self.update(yaml.load(open(path), Loader=yaml.FullLoader)) def update(self, data): for k, v in data.items(): @@ -134,12 +134,14 @@ def load_config(data): def load_json(path): import json - return json.load(open(path, encoding='utf-8')) + with open(path, encoding='utf-8') as f: + return json.load(f) def load_yaml(path): import yaml - return yaml.load(open(path)) + with open(path) as f: + return yaml.load(f, Loader=yaml.FullLoader) def load_file(path): diff --git a/mlchain/server/static/mlchain_fig_1.png b/mlchain/server/static/mlchain_fig_1.png new file mode 100644 index 0000000..bdbaeea Binary files /dev/null and b/mlchain/server/static/mlchain_fig_1.png differ diff --git a/mlchain/server/static/mlchain_logo.png b/mlchain/server/static/mlchain_logo.png new file mode 100644 index 0000000..125743f Binary files /dev/null and b/mlchain/server/static/mlchain_logo.png differ diff --git a/mlchain/server/templates/home.html b/mlchain/server/templates/home.html index b71b5f5..673765a 100644 --- a/mlchain/server/templates/home.html +++ b/mlchain/server/templates/home.html @@ -1,392 +1,472 @@ - +
- - - - -How to build better AI?
-Using MlChain now!
- - - - More detail - -Fully support with popular libraries and application integration.
-
-
-
-
Easy to track and organize your team, work and expense.
-
-