From 8a13215e248d0b3caa04269b2b1bf70d16d9b1c7 Mon Sep 17 00:00:00 2001 From: VedhSontha Date: Fri, 12 Jun 2026 01:07:46 +0530 Subject: [PATCH 1/2] Raise exception for uninstalled LoadImage reader and print writer pkg suggestion --- monai/data/image_writer.py | 20 ++++++++++++++++++-- monai/transforms/io/array.py | 13 +++++++++---- tests/data/test_image_rw.py | 14 ++++++++++++-- tests/data/test_init_reader.py | 23 +++++++++++++++++++++-- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/monai/data/image_writer.py b/monai/data/image_writer.py index cc6cdcdead..47e811def8 100644 --- a/monai/data/image_writer.py +++ b/monai/data/image_writer.py @@ -107,16 +107,32 @@ def resolve_writer(ext_name, error_if_not_found=True) -> Sequence: fmt = fmt[1:] avail_writers = [] default_writers = SUPPORTED_WRITERS.get(EXT_WILDCARD, ()) + import re + errors = [] for _writer in look_up_option(fmt, SUPPORTED_WRITERS, default=default_writers): try: _writer() # this triggers `monai.utils.module.require_pkg` to check the system availability avail_writers.append(_writer) - except OptionalImportError: + except OptionalImportError as e: + errors.append(str(e)) continue except Exception: # other writer init errors indicating it exists avail_writers.append(_writer) if not avail_writers and error_if_not_found: - raise OptionalImportError(f"No ImageWriter backend found for {fmt}.") + required_pkgs = [] + for err in errors: + match = re.search(r"required package `([^`]+)`", err) + if match: + pkg = match.group(1) + pkg = "pillow" if pkg == "PIL" else pkg + pkg = "pynrrd" if pkg == "nrrd" else pkg + if pkg not in required_pkgs: + required_pkgs.append(pkg) + + msg = f"No ImageWriter backend found for {fmt}." + if required_pkgs: + msg += f" Please install: {' or '.join(required_pkgs)}." + raise OptionalImportError(msg) writer_tuple = ensure_tuple(avail_writers) SUPPORTED_WRITERS[fmt] = writer_tuple return writer_tuple diff --git a/monai/transforms/io/array.py b/monai/transforms/io/array.py index aadd96763d..ef9fa52a07 100644 --- a/monai/transforms/io/array.py +++ b/monai/transforms/io/array.py @@ -209,13 +209,18 @@ def __init__( the_reader = look_up_option(_r.lower(), SUPPORTED_READERS) try: self.register(the_reader(*args, **kwargs)) - except OptionalImportError: - warnings.warn( + except OptionalImportError as e: + raise OptionalImportError( f"required package for reader {_r} is not installed, or the version doesn't match requirement." - ) + ) 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()) + try: + self.register(the_reader()) + except OptionalImportError as e: + raise OptionalImportError( + f"required package for reader {_r} is not installed, or the version doesn't match requirement." + ) from e elif inspect.isclass(_r): self.register(_r(*args, **kwargs)) else: diff --git a/tests/data/test_image_rw.py b/tests/data/test_image_rw.py index d90c1c8571..96224f4cec 100644 --- a/tests/data/test_image_rw.py +++ b/tests/data/test_image_rw.py @@ -139,8 +139,18 @@ def test_rgb(self, reader, writer): class TestRegRes(unittest.TestCase): def test_0_default(self): self.assertTrue(len(resolve_writer(".png")) > 0, "has png writer") - self.assertTrue(len(resolve_writer(".nrrd")) > 0, "has nrrd writer") - self.assertTrue(len(resolve_writer("unknown")) > 0, "has writer") + _, has_nibabel = optional_import("nibabel") + _, has_itk = optional_import("itk", allow_namespace_pkg=True) + if has_nibabel or has_itk: + self.assertTrue(len(resolve_writer(".nrrd")) > 0, "has nrrd writer") + self.assertTrue(len(resolve_writer("unknown")) > 0, "has writer") + else: + with self.assertRaises(OptionalImportError) as ctx: + resolve_writer(".nrrd") + self.assertIn("Please install: itk or nibabel.", str(ctx.exception)) + with self.assertRaises(OptionalImportError) as ctx: + resolve_writer("unknown") + self.assertIn("Please install: itk or nibabel.", str(ctx.exception)) register_writer("unknown1", lambda: (_ for _ in ()).throw(OptionalImportError)) with self.assertRaises(OptionalImportError): resolve_writer("unknown1") diff --git a/tests/data/test_init_reader.py b/tests/data/test_init_reader.py index 169fd20a5f..94e9ad55b0 100644 --- a/tests/data/test_init_reader.py +++ b/tests/data/test_init_reader.py @@ -29,9 +29,28 @@ def test_load_image(self): self.assertIsInstance(instance1, LoadImage) self.assertIsInstance(instance2, LoadImage) + from monai.utils import optional_import, OptionalImportError + pkg_map = { + "NibabelReader": "nibabel", + "PILReader": "PIL", + "ITKReader": "itk", + "NrrdReader": "nrrd", + "NumpyReader": "numpy", + "PydicomReader": "pydicom", + } for r in ["NibabelReader", "PILReader", "ITKReader", "NumpyReader", "NrrdReader", "PydicomReader", None]: - inst = LoadImaged("image", reader=r) - self.assertIsInstance(inst, LoadImaged) + if r is None: + inst = LoadImaged("image", reader=r) + self.assertIsInstance(inst, LoadImaged) + continue + + _, has_pkg = optional_import(pkg_map[r]) + if has_pkg: + inst = LoadImaged("image", reader=r) + self.assertIsInstance(inst, LoadImaged) + else: + with self.assertRaises(OptionalImportError): + LoadImaged("image", reader=r) @SkipIfNoModule("nibabel") @SkipIfNoModule("cupy") From 5673b525cb218e2ca91000c098dc360abc313d39 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:43:43 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/data/image_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/data/image_writer.py b/monai/data/image_writer.py index 47e811def8..49ecaed02b 100644 --- a/monai/data/image_writer.py +++ b/monai/data/image_writer.py @@ -128,7 +128,7 @@ def resolve_writer(ext_name, error_if_not_found=True) -> Sequence: pkg = "pynrrd" if pkg == "nrrd" else pkg if pkg not in required_pkgs: required_pkgs.append(pkg) - + msg = f"No ImageWriter backend found for {fmt}." if required_pkgs: msg += f" Please install: {' or '.join(required_pkgs)}."