From 9efd54d000d5efd325cda3b752d36651d1796884 Mon Sep 17 00:00:00 2001 From: Felix Schnabel Date: Fri, 6 Jan 2023 16:02:25 +0100 Subject: [PATCH] 5804 Simplify the configparser usage to get parsed attributes easily (#5813) part of #5804. ### Description This adds a simple attribute access for ConfigParser so ```python from monai.bundle import ConfigParser parser = ConfigParser({"a":"b"}) a = parser.get_parsed_content("a") ``` can be rewritten to ```python from monai.bundle import ConfigParser parser = ConfigParser({"a":"b"}) a = parser.a ``` This only works for variables/methods that are not included in ConfigParser so e.g. `parser.config` would still get the whole config. This PR only supports shallow attribute accesses, since the returned value will be a `dict` where you need to use `["key"]` to get the content. ### Types of changes - [x] Non-breaking change (fix or new feature that would not break existing functionality). - [x] New tests added to cover the changes. - [x] Quick tests passed locally by running `./runtests.sh --quick --unittests --disttests`. Signed-off-by: Felix Schnabel --- docs/source/bundle.rst | 1 + monai/bundle/config_parser.py | 13 +++++++++++++ tests/test_auto3dseg_ensemble.py | 2 +- tests/test_config_parser.py | 16 ++++++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/docs/source/bundle.rst b/docs/source/bundle.rst index 0977788ded..a429f446e9 100644 --- a/docs/source/bundle.rst +++ b/docs/source/bundle.rst @@ -32,6 +32,7 @@ Model Bundle --------------- .. autoclass:: ConfigParser :members: + :special-members: `Scripts` --------- diff --git a/monai/bundle/config_parser.py b/monai/bundle/config_parser.py index d57238cfaa..f1970b3758 100644 --- a/monai/bundle/config_parser.py +++ b/monai/bundle/config_parser.py @@ -116,6 +116,19 @@ def __init__( def __repr__(self): return f"{self.config}" + def __getattr__(self, id): + """ + Get the parsed result of ``ConfigItem`` with the specified ``id`` + with default arguments (e.g. ``lazy=True``, ``instantiate=True`` and ``eval_expr=True``). + + Args: + id: id of the ``ConfigItem``. + + See also: + :py:meth:`get_parsed_content` + """ + return self.get_parsed_content(id) + def __getitem__(self, id: Union[str, int]): """ Get the config by id. diff --git a/tests/test_auto3dseg_ensemble.py b/tests/test_auto3dseg_ensemble.py index c1ffbd254a..d33e20b07d 100644 --- a/tests/test_auto3dseg_ensemble.py +++ b/tests/test_auto3dseg_ensemble.py @@ -139,7 +139,7 @@ def test_ensemble(self) -> None: builder = AlgoEnsembleBuilder(history, data_src_cfg) builder.set_ensemble_method(AlgoEnsembleBestN(n_best=1)) ensemble = builder.get_ensemble() - pred_param["network#init_filter"] = 8 # segresnet + pred_param["network#init_filters"] = 8 # segresnet preds = ensemble(pred_param) self.assertTupleEqual(preds[0].shape, (2, 24, 24, 24)) diff --git a/tests/test_config_parser.py b/tests/test_config_parser.py index d02a05c914..305dae13e2 100644 --- a/tests/test_config_parser.py +++ b/tests/test_config_parser.py @@ -256,6 +256,22 @@ def test_pdb(self): with self.assertRaisesRegex(RuntimeError, ".*bdb.BdbQuit.*"): case_pdb() + def test_get_via_attributes(self): + config = { + "A": {"B": {"C": 1}}, + "my_dims": 2, + "dims_1": "$@my_dims + 1", + "patch_size": [8, 8], + "transform": {"_target_": "Lambda", "func": "$lambda x: x.reshape((1, *@patch_size))"}, + } + parser = ConfigParser(config=config) + self.assertEqual(parser.A, {"B": {"C": 1}}) + self.assertEqual(parser.dims_1, 3) + + trans = parser.transform + result = trans(np.ones(64)) + self.assertTupleEqual(result.shape, (1, 8, 8)) + if __name__ == "__main__": unittest.main()