Executing app locally¶
Executing and Inspecting App in Jupyter Notebook¶
You can define Operators and Application in Jupyter Notebook.
from typing import Any, List, Union
import monai.deploy.core as md # 'md' stands for MONAI Deploy (or can use 'core' instead)
from monai.deploy.core import (Application, DataPath, ExecutionContext, Image,
InputContext, IOType, Operator, OutputContext)
# @md.input("", DataPath, IOType.DISK)
@md.output("list", List[Union[str,int]], IOType.IN_MEMORY)
@md.env(pip_packages=["numpy"])
class TaskA(Operator):
def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):
input_path = op_input.get().path # input path (current path). not used here.
model_path = context.models.get().path # model path. This would be "" if the path (default: 'models') doesn't exist.
op_output.set(["A", 1])
@md.input("list", List[Union[str,int]], IOType.IN_MEMORY)
# @md.output("", DataPath, IOType.DISK)
@md.env(pip_packages=["scikit-image"])
class TaskB(Operator):
def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):
input_data = op_input.get()
print(input_data)
output_path = op_output.get().path # output path (current path). not used here.
@md.env(pip_packages=["Pillow"])
class App(Application):
def compose(self):
taskA = TaskA()
taskB = TaskB()
self.add_flow(taskA, taskB)
Once an Application class (App
) is defined, you can instentiate the application and execute with Application.run() method.
At least one parameter (whose value is not None
or ""
) needs to be specified to execute the application inside Jupyter Notebook.
Since the above example doesn’t use input or output paths, we specify "."
to locate current path.
(If we specify other path for output
and the path doesn’t exist, the output path would be created during the execution.)
app = App()
print(app.context) # print app context
app.run(input=".", output=".") # override 'input_path' and 'output_path' in app.context
AppContext(graph=nx_digraph, input_path=input, output_path=output, model_path=models, workdir=, datastore=memory, executor=single_process_executor, resource=Resource(cpu=0, memory=0, gpu=0))
Going to initiate execution of operator TaskA
Executing operator TaskA (Process ID: 9471, Operator ID: ab200dcf-833b-40f0-a61c-884793b2d9b4)
Done performing execution of operator TaskA
Going to initiate execution of operator TaskB
Executing operator TaskB (Process ID: 9471, Operator ID: e2db0e3f-76a1-43c1-bb60-26fd7500db22)
['A', 1]
Done performing execution of operator TaskB
app.context # 'input_path' and 'output_path' are now overwritten
AppContext(graph=nx_digraph, input_path=., output_path=., model_path=models, workdir=, datastore=memory, executor=single_process_executor, resource=Resource(cpu=0, memory=0, gpu=0))
You can access Graph object through Application.graph
graph = app.graph
print(f"root_operators: {list(graph.get_root_operators())}")
print(f"operators: {list(graph.get_operators())}")
worklist = list(graph.gen_worklist())
print(f"worklist: {worklist}")
print(f"graph.is_root(worklist[0]): {graph.is_root(worklist[0])}")
print(f"graph.is_leaf(worklist[1]): {graph.is_leaf(worklist[1])}")
root_operators: [<__main__.TaskA object at 0x7fc749451358>]
operators: [<__main__.TaskA object at 0x7fc749451358>, <__main__.TaskB object at 0x7fc749451438>]
worklist: [<__main__.TaskA object at 0x7fc749451358>, <__main__.TaskB object at 0x7fc749451438>]
graph.is_root(worklist[0]): True
graph.is_leaf(worklist[1]): True
Executing Application through CLI¶
Once the application is verified inside Jupyter notebook, we can write the whole application as a file(app.py
) with the following lines at the end of the file:
if __name__ == "__main__":
App(do_run=True)
Above lines are needed to execute the application code by using python
interpreter.
%%writefile app.py
from typing import Any, List, Union
import monai.deploy.core as md # 'md' stands for MONAI Deploy (or can use 'core' instead)
from monai.deploy.core import (Application, DataPath, ExecutionContext, Image,
InputContext, IOType, Operator, OutputContext)
# @md.input("", DataPath, IOType.DISK)
@md.output("list", List[Union[str,int]], IOType.IN_MEMORY)
@md.env(pip_packages=["numpy"])
class TaskA(Operator):
def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):
input_path = op_input.get().path # input path (current path). not used here.
model_path = context.models.get().path # model path. This would be "" if the path (default: 'models') doesn't exist.
op_output.set(["A", 1])
@md.input("list", List[Union[str,int]], IOType.IN_MEMORY)
# @md.output("", DataPath, IOType.DISK)
@md.env(pip_packages=["scikit-image"])
class TaskB(Operator):
def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):
input_data = op_input.get()
print(input_data)
output_path = op_output.get().path # output path (current path). not used here.
@md.env(pip_packages=["Pillow"])
class App(Application):
def compose(self):
taskA = TaskA()
taskB = TaskB()
self.add_flow(taskA, taskB)
if __name__ == "__main__":
App(do_run=True)
Overwriting app.py
You can load the Application object from file through monai.deploy.utils.importutil.get_application()
method.
For the Application object, you can get the package information through Application.get_package_info() method
import json
from monai.deploy.utils.importutil import get_application
app = get_application("app.py") # the parameter can be a main application file or an application folder
print(json.dumps(app.get_package_info(), indent=2))
{
"app-name": "App",
"app-version": "0.0.0",
"sdk-version": "0.1.0-rc.2+1.g217d6b1.dirty",
"command": "python3 -u /opt/monai/app/app.py",
"resource": {
"cpu": 0,
"gpu": 0,
"memory": "0Mi"
},
"models": [],
"pip-packages": [
"pillow",
"numpy",
"scikit-image"
]
}
Let’s run the application through CLI
!python app.py --input . --output .
Going to initiate execution of operator TaskA
Executing operator TaskA (Process ID: 9567, Operator ID: cc8b6114-7bab-4d80-9e82-0c6c42493986)
Done performing execution of operator TaskA
Going to initiate execution of operator TaskB
Executing operator TaskB (Process ID: 9567, Operator ID: acd67d8b-fb1a-4642-aa65-a32a411f7174)
['A', 1]
Done performing execution of operator TaskB
monai-deploy exec¶
Above command is same with the following command line:
!monai-deploy exec app.py --input . --output .
Going to initiate execution of operator TaskA
Executing operator TaskA (Process ID: 9724, Operator ID: 2c697da1-831f-49ce-84b1-6451413ce367)
Done performing execution of operator TaskA
Going to initiate execution of operator TaskB
Executing operator TaskB (Process ID: 9724, Operator ID: a1c07a95-a5fd-4d58-809e-e4f2fbc101ce)
['A', 1]
Done performing execution of operator TaskB
monai-deploy exec --help
Output:
usage: monai-deploy exec [-h] [-l {DEBUG,INFO,WARN,ERROR,CRITICAL}]
[--input INPUT] [--output OUTPUT] [--model MODEL]
[--workdir WORKDIR] [--graph {nx_digraph}]
[--datastore {memory}]
[--executor {single_process_executor}]
[remaining [remaining ...]]
positional arguments:
remaining
optional arguments:
-h, --help show this help message and exit
-l {DEBUG,INFO,WARN,ERROR,CRITICAL}, --log-level {DEBUG,INFO,WARN,ERROR,CRITICAL}
Set the logging level (default: INFO)
--input INPUT, -i INPUT
Path to input folder/file (default: input)
--output OUTPUT, -o OUTPUT
Path to output folder (default: output)
--model MODEL, -m MODEL
Path to model(s) folder/file (default: models)
--workdir WORKDIR, -w WORKDIR
Path to workspace folder (default: A temporary
'.monai_workdir' folder in the current folder)
--graph {nx_digraph} Set Graph engine (default: nx_digraph)
--datastore {memory} Set Datastore (default: memory)
--executor {single_process_executor}
Set Executor (default: single_process_executor)