diff --git a/monai/bundle/__init__.py b/monai/bundle/__init__.py index fa139bb6017..b550dc8c93b 100644 --- a/monai/bundle/__init__.py +++ b/monai/bundle/__init__.py @@ -24,6 +24,7 @@ init_bundle, load, run, + run_workflow, trt_export, verify_metadata, verify_net_in_out, diff --git a/monai/bundle/__main__.py b/monai/bundle/__main__.py index aa0ed20ef5a..e143a5c7edd 100644 --- a/monai/bundle/__main__.py +++ b/monai/bundle/__main__.py @@ -11,7 +11,16 @@ from __future__ import annotations -from monai.bundle.scripts import ckpt_export, download, init_bundle, run, trt_export, verify_metadata, verify_net_in_out +from monai.bundle.scripts import ( + ckpt_export, + download, + init_bundle, + run, + run_workflow, + trt_export, + verify_metadata, + verify_net_in_out, +) if __name__ == "__main__": from monai.utils import optional_import diff --git a/monai/bundle/scripts.py b/monai/bundle/scripts.py index af2419a9a97..6a607a28365 100644 --- a/monai/bundle/scripts.py +++ b/monai/bundle/scripts.py @@ -636,7 +636,7 @@ def run( args_file: a JSON or YAML file to provide default values for `runner_id`, `meta_file`, `config_file`, `logging`, and override pairs. so that the command line inputs can be simplified. override: id-value pairs to override or add the corresponding config content. - e.g. ``--net#input_chns 42``. + e.g. ``--net#input_chns 42``, ``--net %/data/other.json#net_arg``. """ @@ -664,7 +664,8 @@ def run( logging_file="configs/logging.conf", tracking=None, ) - workflow = ConfigWorkflow( + run_workflow( + workflow=ConfigWorkflow, config_file=config_file_, meta_file=meta_file_, logging_file=logging_file_, @@ -674,9 +675,6 @@ def run( tracking=tracking_, **_args, ) - workflow.initialize() - workflow.run() - workflow.finalize() def run_workflow(workflow: str | BundleWorkflow | None = None, args_file: str | None = None, **kwargs: Any) -> None: @@ -688,23 +686,10 @@ def run_workflow(workflow: str | BundleWorkflow | None = None, args_file: str | .. code-block:: bash # Execute this module as a CLI entry with default ConfigWorkflow: - python -m monai.bundle run --meta_file --config_file - - # Override config values at runtime by specifying the component id and its new value: - python -m monai.bundle run --net#input_chns 1 ... - - # Override config values with another config file `/path/to/another.json`: - python -m monai.bundle run --net %/path/to/another.json ... - - # Override config values with part content of another config file: - python -m monai.bundle run --net %/data/other.json#net_arg ... - - # Set default args of `run` in a JSON / YAML file, help to record and simplify the command line. - # Other args still can override the default args at runtime: - python -m monai.bundle run --args_file "/workspace/data/args.json" --config_file + python -m monai.bundle run_workflow --meta_file --config_file # Set the workflow to other customized BundleWorkflow subclass: - python -m monai.bundle run --workflow CustomizedWorkflow ... + python -m monai.bundle run_workflow --workflow CustomizedWorkflow ... Args: workflow: specified bundle workflow name, should be a string or class, default to "ConfigWorkflow". @@ -716,17 +701,17 @@ def run_workflow(workflow: str | BundleWorkflow | None = None, args_file: str | _args = _update_args(args=args_file, workflow=workflow, **kwargs) _log_input_summary(tag="run", args=_args) - (workflow_name,) = _pop_args(_args, workflow=ConfigWorkflow) + (workflow_name,) = _pop_args(_args, workflow=ConfigWorkflow) # the default workflow name is "ConfigWorkflow" if isinstance(workflow_name, str): workflow_class, has_built_in = optional_import("monai.bundle", name=f"{workflow_name}") # search built-in if not has_built_in: workflow_class = locate(f"{workflow_name}") # search dotted path if workflow_class is None: - raise ValueError(f"can not locate specified workflow class: {workflow_name}.") + raise ValueError(f"cannot locate specified workflow class: {workflow_name}.") elif issubclass(workflow_name, BundleWorkflow): workflow_class = workflow_name else: - raise ValueError(f"`workflow` must be the bundle workflow class name, but got: {workflow_name}.") + raise ValueError(f"argument `workflow` must be the bundle workflow class name or type, got: {workflow_name}.") workflow_ = workflow_class(**_args) workflow_.initialize() diff --git a/tests/test_integration_bundle_run.py b/tests/test_integration_bundle_run.py index 1bb06046cd9..8287d4aeb24 100644 --- a/tests/test_integration_bundle_run.py +++ b/tests/test_integration_bundle_run.py @@ -62,8 +62,9 @@ def test_tiny(self): }, f, ) - cmd = ["coverage", "run", "-m", "monai.bundle", "run", "--run_id", "training", "--config_file", config_file] - command_line_tests(cmd) + cmd = ["coverage", "run", "-m", "monai.bundle"] + command_line_tests(cmd + ["run", "training", "--config_file", config_file]) + command_line_tests(cmd + ["run_workflow", "--run_id", "training", "--config_file", config_file]) @parameterized.expand([TEST_CASE_1, TEST_CASE_2]) def test_shape(self, config_file, expected_shape): @@ -136,13 +137,13 @@ def test_shape(self, config_file, expected_shape): # test the saved execution configs self.assertTrue(len(glob(f"{tempdir}/config_*.json")), 2) - def test_customize_workflow(self): + def test_customized_workflow(self): expected_shape = (64, 64, 64) test_image = np.random.rand(*expected_shape) filename = os.path.join(self.data_dir, "image.nii") nib.save(nib.Nifti1Image(test_image, np.eye(4)), filename) - cmd = "-m fire monai.bundle.scripts run --workflow tests.nonconfig_workflow.NonConfigWorkflow" + cmd = "-m fire monai.bundle.scripts run_workflow --workflow tests.nonconfig_workflow.NonConfigWorkflow" cmd += f" --filename {filename} --output_dir {self.data_dir}" command_line_tests(["coverage", "run"] + cmd.split(" ")) loader = LoadImage(image_only=True)