Skip to content

Raise exception when user-specified reader package is not installed#8771

Open
haoyu-haoyu wants to merge 1 commit intoProject-MONAI:devfrom
haoyu-haoyu:fix/raise-exception-when-specified-reader-not-installed
Open

Raise exception when user-specified reader package is not installed#8771
haoyu-haoyu wants to merge 1 commit intoProject-MONAI:devfrom
haoyu-haoyu:fix/raise-exception-when-specified-reader-not-installed

Conversation

@haoyu-haoyu
Copy link

Fixes #7437

Description

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.

Before (current behavior):

# User specifies ITKReader but itk is not installed
loader = LoadImage(reader="ITKReader")
# Only a warning is printed, then PILReader is used silently
# This can lead to unexpected results

After (new behavior):

# User specifies ITKReader but itk is not installed
loader = LoadImage(reader="ITKReader")
# OptionalImportError is raised immediately with a clear message:
# "Required package for reader ITKReader is not installed..."

Default behavior (reader=None) remains unchanged — missing optional packages are still handled gracefully with debug logging, and the system falls back to available readers as before.

Changes

  1. monai/transforms/io/array.py: Changed warnings.warn() to raise OptionalImportError() in the user-specified reader registration path (lines 212-216).

  2. tests/transforms/test_load_image.py: Added TestLoadImageReaderNotInstalled test class with:

    • test_specified_reader_not_installed_raises: Direct test (runs when itk is NOT installed)
    • test_specified_reader_not_installed_raises_mocked: Mock-based test that always runs, simulating a reader whose package is not installed

Testing

  • New unit tests added
  • Existing tests unaffected (default reader=None path unchanged)

Signed-off-by: haoyu-haoyu haoyu-haoyu@users.noreply.github.com

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 <haoyu-haoyu@users.noreply.github.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 11, 2026

📝 Walkthrough

Walkthrough

Changes LoadImage to raise OptionalImportError instead of warning when a specified reader package is unavailable, with error messages directing users to install the required package. Adds test coverage for this new exception-based behavior through direct and mocked reader scenarios.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly and specifically describes the main change: raising an exception when a user-specified reader package is not installed.
Description check ✅ Passed PR description includes fixes reference, clear before/after behavior, specific file changes, test additions, and testing confirmation matching the template.
Linked Issues check ✅ Passed Changes fully implement the requirement from issue #7437: raising OptionalImportError when a user-specified reader is not installed, with new tests validating this behavior.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #7437: exception raising in LoadImage, test coverage, and related documentation/behavior updates with no extraneous modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/transforms/test_load_image.py (2)

513-514: Assert the error text too.

These pass on any OptionalImportError, including one bubbling up from deeper in reader init. assertRaisesRegex on the reader name or install hint would lock in the new contract.

Patch
-        with self.assertRaises(OptionalImportError):
+        with self.assertRaisesRegex(OptionalImportError, r"ITKReader|install"):
             LoadImage(reader="ITKReader")
@@
-            with self.assertRaises(OptionalImportError):
+            with self.assertRaisesRegex(OptionalImportError, r"MockMissingReader|install"):
                 LoadImage(reader="MockMissingReader")

Also applies to: 537-538

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/transforms/test_load_image.py` around lines 513 - 514, The test
currently uses assertRaises(OptionalImportError) when constructing
LoadImage(reader="ITKReader"); replace it with
assertRaisesRegex(OptionalImportError, r"(ITKReader|install.*ITK|itk)") to
ensure the raised OptionalImportError specifically mentions the reader name or
the install hint; do the same change for the other occurrence
(LoadImage(reader="ITKReader") around lines 537-538) so the tests fail only when
the expected import hint is present rather than any unrelated
OptionalImportError.

501-518: Use Google-style docstrings on the new test definitions.

The docstrings are present, but they do not follow the required Google-style sections. As per coding guidelines, "Docstrings should be present for all definition which describe each variable, return value, and raised exception in the appropriate section of the Google-style of docstrings."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/transforms/test_load_image.py` around lines 501 - 518, Update the
docstrings in class TestLoadImageReaderNotInstalled for the methods
test_specified_reader_not_installed_raises and
test_specified_reader_not_installed_raises_mocked to use Google-style
docstrings: include a short summary, an Args: section if the test takes
parameters (omit if none), a Returns: section (state that it returns None), and
a Raises: section documenting that OptionalImportError is expected when calling
LoadImage(reader="ITKReader"); reference the test names and the
LoadImage/OptionalImportError symbols so the intent is clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@monai/transforms/io/array.py`:
- Around line 212-216: The except block currently discards the original
OptionalImportError context; change the handler to capture the exception (e.g.,
except OptionalImportError as e:) and re-raise the new OptionalImportError with
exception chaining (raise OptionalImportError(... ) from e) so the original
traceback is preserved; reference the caught OptionalImportError and the reader
identifier _r when updating the except block in monai/transforms/io/array.py.

---

Nitpick comments:
In `@tests/transforms/test_load_image.py`:
- Around line 513-514: The test currently uses assertRaises(OptionalImportError)
when constructing LoadImage(reader="ITKReader"); replace it with
assertRaisesRegex(OptionalImportError, r"(ITKReader|install.*ITK|itk)") to
ensure the raised OptionalImportError specifically mentions the reader name or
the install hint; do the same change for the other occurrence
(LoadImage(reader="ITKReader") around lines 537-538) so the tests fail only when
the expected import hint is present rather than any unrelated
OptionalImportError.
- Around line 501-518: Update the docstrings in class
TestLoadImageReaderNotInstalled for the methods
test_specified_reader_not_installed_raises and
test_specified_reader_not_installed_raises_mocked to use Google-style
docstrings: include a short summary, an Args: section if the test takes
parameters (omit if none), a Returns: section (state that it returns None), and
a Raises: section documenting that OptionalImportError is expected when calling
LoadImage(reader="ITKReader"); reference the test names and the
LoadImage/OptionalImportError symbols so the intent is clear.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1553a00f-91e7-46a5-9318-e9d99eafc257

📥 Commits

Reviewing files that changed from the base of the PR and between daaedaa and 1b6730f.

📒 Files selected for processing (2)
  • monai/transforms/io/array.py
  • tests/transforms/test_load_image.py

Comment on lines 212 to 216
except OptionalImportError:
warnings.warn(
f"required package for reader {_r} is not installed, or the version doesn't match requirement."
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}."
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n monai/transforms/io/array.py | sed -n '210,220p'

Repository: Project-MONAI/MONAI

Length of output: 813


🏁 Script executed:

cat -n monai/utils/module.py | sed -n '395,415p'

Repository: Project-MONAI/MONAI

Length of output: 1076


🏁 Script executed:

cat -n monai/transforms/io/array.py | sed -n '190,230p'

Repository: Project-MONAI/MONAI

Length of output: 2458


🏁 Script executed:

# Check for similar exception patterns in the file
rg -n "except OptionalImportError" monai/transforms/io/array.py

# Check if there are other exception re-raise patterns in the file with chaining
rg -n "raise.*from" monai/transforms/io/array.py

Repository: Project-MONAI/MONAI

Length of output: 154


🏁 Script executed:

# Check if there are other OptionalImportError re-raises in the codebase with chaining
rg -A 3 "except OptionalImportError" monai/ -t py | head -40

# Check if exception chaining is used elsewhere in the file
rg "raise.*from" monai/transforms/io/array.py -B 2 -A 2

Repository: Project-MONAI/MONAI

Length of output: 1379


🏁 Script executed:

# Check Python version targeted by the project to confirm exception chaining is supported
rg "python_requires" setup.py setup.cfg pyproject.toml -A 1

# Look at the OptionalImportError definition
rg -A 5 "class OptionalImportError" monai/

# Check if there are tests for this specific code path
fd test.*\.py -x rg -l "ArrayReader\|register.*OptionalImportError" {} +

Repository: Project-MONAI/MONAI

Length of output: 33546


🏁 Script executed:

# Check if there are tests for the ArrayReader register functionality
find . -name "*test*array*" -o -name "*test*reader*" | grep -i transform | head -10

Repository: Project-MONAI/MONAI

Length of output: 111


🏁 Script executed:

# Search for test files related to array I/O or ArrayReader
rg -l "ArrayReader\|register.*reader" tests/ -t py | head -5

# Also check if there are any existing tests for exception chaining patterns
rg "from err\|from e\>" tests/ -t py | head -20

Repository: Project-MONAI/MONAI

Length of output: 163


🏁 Script executed:

# Search for exception chaining patterns in tests
rg "raise.*from" tests/ -t py | head -20

# Look for ArrayReader tests specifically
rg "ArrayReader" tests/ -t py -l

Repository: Project-MONAI/MONAI

Length of output: 712


Add exception chaining to preserve the original error context.

The caught OptionalImportError at line 212 is replaced without preserving its cause. Use exception chaining to maintain the original traceback for debugging.

Patch
-                except OptionalImportError:
-                    raise OptionalImportError(
+                except OptionalImportError as err:
+                    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 err
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
except OptionalImportError:
warnings.warn(
f"required package for reader {_r} is not installed, or the version doesn't match requirement."
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}."
)
except OptionalImportError as err:
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 err
🧰 Tools
🪛 Ruff (0.15.5)

[warning] 213-216: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


[warning] 213-216: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/transforms/io/array.py` around lines 212 - 216, The except block
currently discards the original OptionalImportError context; change the handler
to capture the exception (e.g., except OptionalImportError as e:) and re-raise
the new OptionalImportError with exception chaining (raise
OptionalImportError(... ) from e) so the original traceback is preserved;
reference the caught OptionalImportError and the reader identifier _r when
updating the except block in monai/transforms/io/array.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Raise the exception when LoadImage has a reader specified but it is not installed

1 participant