Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/cli upd #187

Merged
merged 26 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
77d3985
fix: make description optional
IgnacioHeredia Apr 26, 2024
47440d9
feat: allow multiple input files
IgnacioHeredia May 6, 2024
28f2127
refactor: remove unneeded check
IgnacioHeredia May 14, 2024
4fa01aa
feat: allow linebreaks in help messages
IgnacioHeredia May 14, 2024
e610071
feat: add required info to help message
IgnacioHeredia May 14, 2024
c4b9a57
feat: add types to help message
IgnacioHeredia May 14, 2024
f52101d
feat: properly support lists and dicts in argparse
IgnacioHeredia May 16, 2024
216f71a
style: organize `_fields_to_dict` code
IgnacioHeredia May 16, 2024
cb1d579
feat: support bools
IgnacioHeredia May 17, 2024
13e6105
style: fix flake8
IgnacioHeredia May 17, 2024
53a4c32
style: fix black
IgnacioHeredia May 17, 2024
60257d1
Merge branch 'master' into fix/cli
alvarolopez Jun 7, 2024
c51c3b4
fix: studying apispec for fields conversion
vykozlov Jul 15, 2024
12a1d46
fix: store BytesIO object in file
vykozlov Jul 15, 2024
1d9facd
fix: prepare deepaas-cli for the release 2.x
vykozlov Aug 26, 2024
35033e4
fix: merge from releases/2.x, replace return with log.info()
vykozlov Aug 26, 2024
8b71eaf
fix: merge from releases/2.x, replace return with log.info()
vykozlov Aug 26, 2024
ce1a46a
fix: merge from releases/2.x, replace return with log.info()
vykozlov Aug 26, 2024
a0a1780
fix: remove W-I-P test for cli
vykozlov Aug 26, 2024
330db84
tests: move to Python 3.12
alvarolopez Aug 27, 2024
56138d1
style: make black happy
alvarolopez Aug 27, 2024
4c9c5de
fix: remove unused imports
alvarolopez Aug 27, 2024
363766a
style: remove commented code
alvarolopez Aug 27, 2024
6d870d9
fix: remove unused parameter in function
alvarolopez Aug 27, 2024
32a3f52
fix: merge ifs statements
alvarolopez Aug 27, 2024
e8ed37c
tests: move to AI4OS Hub CI Images
alvarolopez Aug 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .sqa/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3.6"

services:
DEEPaaS-testing:
image: "indigodatacloud/ci-images:python3.11"
image: "ai4oshub/ci-images:python3.12"
hostname: "deepaas-testing"
volumes:
- type: bind
Expand Down
160 changes: 80 additions & 80 deletions deepaas/cmd/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@
import argparse
import ast
import deepaas
import io
import json
import mimetypes
import multiprocessing as mp
import os
import re
import shutil
import sys
import tempfile
import uuid

from datetime import datetime
from marshmallow import fields
from marshmallow import fields # marshmallow
from oslo_config import cfg
from oslo_log import log

Expand All @@ -41,6 +41,8 @@

CONF = config.CONF

LOG = log.getLogger(__name__)

debug_cli = False

# Not all types are covered! If not listed, the type is 'str'
Expand Down Expand Up @@ -125,7 +127,10 @@ def _fields_to_dict(fields_in):
else:
val_help += f"\nType: {param['type'].__name__}"
if val_type is fields.Field:
val_help += " (filepath)"
if val.metadata.get("type", "") == "file":
val_help += " (filepath)"
else:
val_help += " (WARNING: original type fields.Field)"

# Infer "required"
param["required"] = val.required
Expand All @@ -147,14 +152,28 @@ def _fields_to_dict(fields_in):
return dict_out


# Function to detect file arguments
def _get_file_args(fields_in):
"""Function to retrieve a list of file-type fields
:param fields_in: mashmallow fields
:return: list
"""
file_fields = []
for k, v in fields_in.items():
if (type(v) is fields.Field) and (v.metadata.get("type", "") == "file"):
file_fields.append(k)

return file_fields


# Function to get a model object
def _get_model_name(model_name=None):
"""Function to get model_obj from the name of the model.
def _get_model_name():
"""Return et mode name and model fron configured model.

In case of error, prints the list of available models
:param model_name: name of the model
:return: model object
:return: mode name, model object
"""

model_name = CONF.model_name
models = loading.get_available_models("v2")
if model_name:
model_obj = models.get(model_name)
Expand All @@ -180,22 +199,8 @@ def _get_model_name(model_name=None):
sys.exit(1)


def _get_file_args(fields_in):
"""Function to retrieve a list of file-type fields
:param fields_in: mashmallow fields
:return: list
"""
file_fields = []
for k, v in fields_in.items():
if type(v) is fields.Field:
file_fields.append(k)
return file_fields


# Get the model name
model_name = CONF.model_name

model_name, model_obj = _get_model_name(model_name)
model_name, model_obj = _get_model_name()

# use deepaas.model.v2.wrapper.ModelWrapper(). deepaas>1.2.1dev4
# model_obj = v2_wrapper.ModelWrapper(name=model_name,
Expand Down Expand Up @@ -291,12 +296,8 @@ def help_formatter(prog):
),
]


CONF = cfg.CONF
CONF.register_cli_opts(cli_opts)

LOG = log.getLogger(__name__)


# store DEEPAAS_METHOD output in a file
def _store_output(results, out_file):
Expand All @@ -309,14 +310,10 @@ def _store_output(results, out_file):
if not os.path.exists(out_path): # Create path if does not exist
os.makedirs(out_path)

f = open(out_file, "w+")
f.write(results)
f.close()

LOG.info("[INFO, Output] Output is saved in {}".format(out_file))
with open(out_file, "w+") as f:
f.write(results)


# async def main():
def main():
"""Executes model's methods with corresponding parameters"""

Expand All @@ -327,59 +324,60 @@ def main():
log.set_defaults(default_log_levels=log.get_default_log_levels())

CONF(sys.argv[1:], project="deepaas", version=deepaas.extract_version())

log.setup(CONF, "deepaas-cli")

LOG.info("[INFO, Method] {} was called.".format(CONF.methods.name))
LOG.info(f"{CONF.methods.name} was called.")

# put all variables in dict, makes life easier...
conf_vars = vars(CONF._namespace)

if CONF.deepaas_with_multiprocessing:
mp.set_start_method("spawn", force=True)

# Create file wrapper for file args (if provided)
for farg in file_args[CONF.methods.name]:
if getattr(CONF.methods, farg, None):
fpath = conf_vars[farg]

# create tmp file as later it supposed
# to be deleted by the application
temp = tempfile.NamedTemporaryFile()
temp.close()
# copy original file into tmp file
with open(fpath, "rb") as f:
with open(temp.name, "wb") as f_tmp:
for line in f:
f_tmp.write(line)

# create file object
file_type = mimetypes.MimeTypes().guess_type(fpath)[0]
file_obj = v2_wrapper.UploadedFile(
name="data",
filename=temp.name,
content_type=file_type,
original_filename=fpath,
)
# re-write parameter in conf_vars
conf_vars[farg] = file_obj
# Create file wrapper for file args, ONLY for "predict()" and "train()"
if CONF.methods.name == "predict" or CONF.methods.name == "train":
# Create file wrapper for file args (if provided!)
for farg in file_args[CONF.methods.name]:
if getattr(CONF.methods, farg, None):
fpath = conf_vars[farg]

# create tmp file as later it supposed
# to be deleted by the application
temp = tempfile.NamedTemporaryFile()
temp.close()
# copy original file into tmp file
with open(fpath, "rb") as f:
with open(temp.name, "wb") as f_tmp:
for line in f:
f_tmp.write(line)

# create file object
file_type = mimetypes.MimeTypes().guess_type(fpath)[0]
file_obj = v2_wrapper.UploadedFile(
name="data",
filename=temp.name,
content_type=file_type,
original_filename=fpath,
)
# re-write parameter in conf_vars
conf_vars[farg] = file_obj

# debug of input parameters
LOG.debug("[DEBUG provided options, conf_vars]: {}".format(conf_vars))
LOG.debug("[provided options, conf_vars]: {}".format(conf_vars))

if CONF.methods.name == "get_metadata":
meta = model_obj.get_metadata()
meta_json = json.dumps(meta)
LOG.debug("[DEBUG, get_metadata, Output]: {}".format(meta_json))
LOG.debug("[get_metadata]: {}".format(meta_json))
if CONF.deepaas_method_output:
_store_output(meta_json, CONF.deepaas_method_output)

return meta_json
LOG.info(f"return: {meta_json}")

elif CONF.methods.name == "warm":
# await model_obj.warm()
model_obj.warm()
LOG.info("[INFO, warm] Finished warm() method")
LOG.info("return: Finished warm() method")

elif CONF.methods.name == "predict":
# call predict method
Expand Down Expand Up @@ -409,25 +407,25 @@ def main():
): # noqa: W503
out_file = out_file + extension
LOG.warn(
"[WARNING] You are trying to store {} "
"You are trying to store {} "
"type data in the file "
"with {} extension!\n"
"===================> "
"New output is {}".format(extension, out_extension, out_file)
)
if extension == ".json" or extension is None:
results_json = json.dumps(task)
LOG.debug("[DEBUG, predict, Output]: {}".format(results_json))
f = open(out_file, "w+")
f.write(results_json)
f.close()
LOG.debug("[predict]: {}".format(results_json))
_store_output(results_json, out_file)
elif type(task) is io.BytesIO:
with open(out_file, "wb") as f:
f.write(task.getbuffer())
else:
out_results = task.name
shutil.copy(out_results, out_file)

LOG.info("[INFO, Output] Output is saved in {}".format(out_file))
LOG.info(f"Output of type type({task}), {task}")

return task
LOG.info(f"return: Output is saved in {out_file}")
else:
LOG.info(f"return: {task}")

elif CONF.methods.name == "train":
train_vars = _get_subdict(conf_vars, train_args)
Expand All @@ -448,19 +446,21 @@ def main():
# we assume that train always returns JSON
ret["result"]["output"] = task
results_json = json.dumps(ret)
LOG.info("[INFO] Elapsed time: %s", str(end - start))
LOG.debug("[DEBUG, train, Output]: {}".format(results_json))
LOG.info("Elapsed time: %s", str(end - start))
LOG.debug("[train]: {}".format(results_json))
if CONF.deepaas_method_output:
_store_output(results_json, CONF.deepaas_method_output)
return results_json
LOG.info(f"return: Output is saved in {CONF.deepaas_method_output}")
else:
LOG.info(f"return: {results_json}")

else:
LOG.warn("[WARNING] No Method was requested! Return get_metadata()")
LOG.warn("No Method was requested! Return get_metadata()")
meta = model_obj.get_metadata()
meta_json = json.dumps(meta)
LOG.debug("[DEBUG, get_metadata, Output]: {}".format(meta_json))
LOG.debug("[get_metadata]: {}".format(meta_json))

return meta_json
LOG.info(f"return: {meta_json}")


if __name__ == "__main__":
Expand Down
6 changes: 3 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ python =
3.8: py38
3.9: py39
3.10: py310
3.11: py311, flake8, black, bandit, pip-missing-reqs, pypi
3.12: py312
3.11: py311
3.12: py312, flake8, black, bandit, pip-missing-reqs, pypi

[base]
python = python3.11
python = python3.12
package = deepaas

[pytest]
Expand Down
Loading