Skip to content

Commit 494c2dc

Browse files
Kirscherclaude
andcommitted
Report a missing nnunetv2 as an optional dependency in nnUNetV2Runner
`nnUNetV2Runner.__init__` imports `nnunetv2.configuration` directly, so constructing the runner without nnunetv2 installed raises a bare ModuleNotFoundError rather than MONAI's OptionalImportError. The dataset lookup just above it is wrapped in a broad `except Exception`, which swallows that same ImportError first and logs Dataset with name/ID: 123 cannot be found in the record. ... please check your input_config. so the reported cause is the user's configuration, not the missing package. Decorate the class with `@require_pkg(pkg_name="nnunetv2")`, as is done for other optional dependencies (ITKReader, NibabelReader, ...). The failure now names the package and links the installation docs, and the misleading dataset warning is no longer emitted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Kirscher <tristan.kirscher@gmail.com>
1 parent 87060c4 commit 494c2dc

2 files changed

Lines changed: 60 additions & 1 deletion

File tree

monai/apps/nnunet/nnunetv2_runner.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from monai.apps.nnunet.utils import NNUNETMode as M
2424
from monai.apps.nnunet.utils import analyze_data, create_new_data_copy, create_new_dataset_json
2525
from monai.bundle import ConfigParser
26-
from monai.utils import ensure_tuple, optional_import
26+
from monai.utils import ensure_tuple, optional_import, require_pkg
2727
from monai.utils.misc import run_cmd
2828

2929
load_pickle, _ = optional_import("batchgenerators.utilities.file_and_folder_operations", name="load_pickle")
@@ -38,6 +38,7 @@
3838
DATASET_ID_FORMAT = r"Dataset[0-9]{3}|[0-9]+" # regex format for a valid nnUnet dataset name
3939

4040

41+
@require_pkg(pkg_name="nnunetv2")
4142
class nnUNetV2Runner: # noqa: N801
4243
"""
4344
``nnUNetV2Runner`` provides an interface in MONAI to use `nnU-Net` V2 library to analyze, train, and evaluate
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright (c) MONAI Consortium
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
# Unless required by applicable law or agreed to in writing, software
7+
# distributed under the License is distributed on an "AS IS" BASIS,
8+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
# See the License for the specific language governing permissions and
10+
# limitations under the License.
11+
12+
from __future__ import annotations
13+
14+
import os
15+
import tempfile
16+
import unittest
17+
from unittest import mock
18+
19+
from monai.apps.nnunet.nnunetv2_runner import nnUNetV2Runner
20+
from monai.utils import OptionalImportError
21+
22+
23+
class TestnnUNetV2RunnerOptionalImport(unittest.TestCase):
24+
"""``nnUNetV2Runner`` requires the optional ``nnunetv2`` package to be installed."""
25+
26+
def setUp(self) -> None:
27+
self.test_dir = tempfile.TemporaryDirectory()
28+
test_path = self.test_dir.name
29+
self.input_config = {
30+
"dataset_name_or_id": "123",
31+
"dataroot": os.path.join(test_path, "data"),
32+
"datalist": os.path.join(test_path, "lists", "task4.json"),
33+
"work_dir": os.path.join(test_path, "work"),
34+
"nnunet_raw": os.path.join(test_path, "nnUNet_raw"),
35+
"nnunet_preprocessed": os.path.join(test_path, "nnUNet_preprocessed"),
36+
"nnunet_results": os.path.join(test_path, "nnUNet_results"),
37+
}
38+
39+
def test_missing_nnunetv2_raises_optional_import_error(self) -> None:
40+
"""A missing ``nnunetv2`` must be reported as such, not as a bare ``ModuleNotFoundError``."""
41+
with mock.patch("monai.utils.module.optional_import", return_value=(None, False)):
42+
with self.assertRaises(OptionalImportError) as context:
43+
nnUNetV2Runner(input_config=dict(self.input_config))
44+
self.assertIn("nnunetv2", str(context.exception))
45+
46+
def test_missing_nnunetv2_does_not_warn_about_the_dataset(self) -> None:
47+
"""The dataset lookup warning must not fire when the real cause is the missing package."""
48+
with mock.patch("monai.utils.module.optional_import", return_value=(None, False)):
49+
with self.assertNoLogs("monai.apps.nnunet.nnunetv2_runner", level="WARNING"):
50+
with self.assertRaises(OptionalImportError):
51+
nnUNetV2Runner(input_config=dict(self.input_config))
52+
53+
def tearDown(self) -> None:
54+
self.test_dir.cleanup()
55+
56+
57+
if __name__ == "__main__":
58+
unittest.main()

0 commit comments

Comments
 (0)