Skip to content

Commit a759ba3

Browse files
author
Cipher
committed
fix: handle OptionalImportError for class-based reader instantiation
The exception handling for missing optional packages only wrapped string-based reader imports (LoadImage(reader='ITKReader')). When users passed a class directly (LoadImage(reader=ITKReader)), OptionalImportError would leak through. Update the class instantiation path (elif inspect.isclass(_r):) to: 1. Catch OptionalImportError during _r(*args, **kwargs) call 2. Re-raise as RuntimeError with consistent message 3. Use exception chaining (from e) for debugging 4. Handle TypeError with warn-and-retry (same as string path) 5. Extract class name safely for error message Now both LoadImage(reader='ITKReader') and LoadImage(reader=ITKReader) produce consistent RuntimeError when the optional package is missing. Add test_explicit_class_reader_not_installed_raises_runtime_error to verify the class path is properly handled. Signed-off-by: Cipher <cipher@openclaw.ai>
1 parent 13a87bd commit a759ba3

2 files changed

Lines changed: 21 additions & 1 deletion

File tree

monai/transforms/io/array.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,18 @@ def __init__(
219219
warnings.warn(f"{_r} is not supported with the given parameters {args} {kwargs}.")
220220
self.register(the_reader())
221221
elif inspect.isclass(_r):
222-
self.register(_r(*args, **kwargs))
222+
try:
223+
self.register(_r(*args, **kwargs))
224+
except OptionalImportError as e:
225+
reader_name = getattr(_r, "__name__", str(_r))
226+
raise RuntimeError(
227+
f"The required package for reader '{reader_name}' is not installed, or the version doesn't match "
228+
f"the requirement. If you want to use '{reader_name}', please install the required package. "
229+
f"If you want to use an alternative reader, do not specify the `reader` argument."
230+
) from e
231+
except TypeError: # the reader doesn't have the corresponding args/kwargs
232+
warnings.warn(f"{_r.__name__} is not supported with the given parameters {args} {kwargs}.")
233+
self.register(_r())
223234
else:
224235
self.register(_r) # reader instance, ignoring the constructor args/kwargs
225236
return

tests/transforms/test_load_image.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,15 @@ def test_explicit_reader_not_installed_raises_runtime_error(self):
514514
self.assertIn("ITKReader", str(ctx.exception))
515515
self.assertIn("not installed", str(ctx.exception))
516516

517+
def test_explicit_class_reader_not_installed_raises_runtime_error(self):
518+
"""When user passes a class reader whose package is missing, RuntimeError is raised (not OptionalImportError)."""
519+
# This tests the class path (not string path) to ensure consistent behavior
520+
with patch("monai.data.ITKReader.__init__", side_effect=OptionalImportError("itk")):
521+
with self.assertRaises(RuntimeError) as ctx:
522+
LoadImage(reader=ITKReader)
523+
self.assertIn("ITKReader", str(ctx.exception))
524+
self.assertIn("not installed", str(ctx.exception))
525+
517526
def test_unspecified_reader_falls_back_silently(self):
518527
"""When no reader is specified, missing optional readers should be silently skipped (no exception)."""
519528
# Force the fallback path by simulating missing optional dependencies.

0 commit comments

Comments
 (0)