From 1e6dd49cd60422cc3cf5d83c4a12764ae384e696 Mon Sep 17 00:00:00 2001 From: SexyERIC0723 Date: Wed, 11 Mar 2026 17:27:08 +0000 Subject: [PATCH] Raise exception when user-specified reader package is not installed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a user explicitly specifies a reader (e.g., reader="ITKReader") but the required package is not installed, LoadImage now raises an OptionalImportError instead of silently falling back to another reader with only a warning. This makes the behavior explicit: if the user specifically requested a reader, they should be informed immediately that it cannot be used, rather than having the system silently use a different reader which may produce unexpected results. Default behavior (reader=None) remains unchanged — missing optional packages are still handled gracefully with debug logging. Fixes Project-MONAI#7437 Signed-off-by: haoyu-haoyu Signed-off-by: SexyERIC0723 --- monai/transforms/io/array.py | 9 ++++--- tests/data/test_init_reader.py | 10 ++++++-- tests/transforms/test_load_image.py | 40 +++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/monai/transforms/io/array.py b/monai/transforms/io/array.py index 0628a7fbc4..e950df6121 100644 --- a/monai/transforms/io/array.py +++ b/monai/transforms/io/array.py @@ -209,10 +209,11 @@ def __init__( the_reader = look_up_option(_r.lower(), SUPPORTED_READERS) try: self.register(the_reader(*args, **kwargs)) - except OptionalImportError: - warnings.warn( - f"required package for reader {_r} is not installed, or the version doesn't match requirement." - ) + except OptionalImportError as e: + raise OptionalImportError( + f"Required package for reader {_r} is not installed, or the version doesn't match requirement. " + f"Please install the required package to use {_r}." + ) from e except TypeError: # the reader doesn't have the corresponding args/kwargs warnings.warn(f"{_r} is not supported with the given parameters {args} {kwargs}.") self.register(the_reader()) diff --git a/tests/data/test_init_reader.py b/tests/data/test_init_reader.py index 4170412207..7728650609 100644 --- a/tests/data/test_init_reader.py +++ b/tests/data/test_init_reader.py @@ -15,6 +15,7 @@ from monai.data import ITKReader, NibabelReader, NrrdReader, NumpyReader, PILReader, PydicomReader from monai.transforms import LoadImage, LoadImaged +from monai.utils import OptionalImportError from tests.test_utils import SkipIfNoModule @@ -26,8 +27,13 @@ def test_load_image(self): self.assertIsInstance(instance2, LoadImage) for r in ["NibabelReader", "PILReader", "ITKReader", "NumpyReader", "NrrdReader", "PydicomReader", None]: - inst = LoadImaged("image", reader=r) - self.assertIsInstance(inst, LoadImaged) + try: + inst = LoadImaged("image", reader=r) + self.assertIsInstance(inst, LoadImaged) + except OptionalImportError: + # Reader's backend package is not installed — expected in + # minimal-dependency environments after the fix for #7437. + pass @SkipIfNoModule("nibabel") @SkipIfNoModule("cupy") diff --git a/tests/transforms/test_load_image.py b/tests/transforms/test_load_image.py index 031e38272e..35121283b4 100644 --- a/tests/transforms/test_load_image.py +++ b/tests/transforms/test_load_image.py @@ -498,5 +498,45 @@ def test_correct(self, input_param, expected_shape, track_meta): self.assertFalse(hasattr(r, "affine")) +class TestLoadImageReaderNotInstalled(unittest.TestCase): + """Test that specifying a reader whose required package is not installed raises an error. + + Addresses https://github.com/Project-MONAI/MONAI/issues/7437 + """ + + @unittest.skipIf(has_itk, "test requires itk to NOT be installed") + def test_specified_reader_not_installed_raises(self): + """When a user explicitly specifies a reader that is not installed, LoadImage should raise + an OptionalImportError instead of silently falling back to another reader.""" + from monai.utils import OptionalImportError + + with self.assertRaises(OptionalImportError): + LoadImage(reader="ITKReader") + + def test_specified_reader_not_installed_raises_mocked(self): + """Mock test to verify OptionalImportError is raised (not just warned) when a user-specified + reader's required package is not installed.""" + from unittest.mock import patch + + from monai.utils import OptionalImportError + + _original = __import__("monai.transforms.io.array", fromlist=["optional_import"]).optional_import + + def _mock_optional_import(module, name="", *args, **kwargs): + if name == "MockMissingReader": + # Return a class that raises OptionalImportError on instantiation, + # simulating a reader whose backend package is not installed + class _Unavailable: + def __init__(self, *a, **kw): + raise OptionalImportError("mock package is not installed") + + return _Unavailable, True + return _original(module, *args, name=name, **kwargs) + + with patch("monai.transforms.io.array.optional_import", side_effect=_mock_optional_import): + with self.assertRaises(OptionalImportError): + LoadImage(reader="MockMissingReader") + + if __name__ == "__main__": unittest.main()