Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions monai/transforms/io/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,7 @@ def __init__(
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."
)
raise
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())
Expand Down
26 changes: 22 additions & 4 deletions tests/data/test_init_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

from monai.data import ITKReader, NibabelReader, NrrdReader, NumpyReader, PILReader, PydicomReader
from monai.transforms import LoadImage, LoadImaged
from monai.utils import MetaKeys
from monai.utils import MetaKeys, OptionalImportError, optional_import
from tests.test_utils import SkipIfNoModule


Expand All @@ -30,9 +30,27 @@ def test_load_image(self):
self.assertIsInstance(instance1, LoadImage)
self.assertIsInstance(instance2, LoadImage)

for r in ["NibabelReader", "PILReader", "ITKReader", "NumpyReader", "NrrdReader", "PydicomReader", None]:
inst = LoadImaged("image", reader=r)
self.assertIsInstance(inst, LoadImaged)
optional_readers = {
"NibabelReader": "nibabel",
"PILReader": "PIL",
"ITKReader": "itk",
"NrrdReader": "nrrd",
"PydicomReader": "pydicom",
}
for r, module in optional_readers.items():
with self.subTest(reader=r):
_, has_module = optional_import(module, allow_namespace_pkg=module in ("itk", "nrrd"))
if has_module:
inst = LoadImaged("image", reader=r)
self.assertIsInstance(inst, LoadImaged)
else:
with self.assertRaises(OptionalImportError):
LoadImaged("image", reader=r)

inst = LoadImaged("image", reader="NumpyReader")
self.assertIsInstance(inst, LoadImaged)
inst = LoadImaged("image", reader=None)
self.assertIsInstance(inst, LoadImaged)

@SkipIfNoModule("nibabel")
@SkipIfNoModule("cupy")
Expand Down
57 changes: 55 additions & 2 deletions tests/transforms/test_load_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import shutil
import tempfile
import unittest
import warnings
from pathlib import Path
from unittest.mock import patch

import nibabel as nib
import numpy as np
Expand All @@ -24,11 +26,11 @@
from PIL import Image

from monai.apps import download_and_extract
from monai.data import NibabelReader, PydicomReader
from monai.data import ImageReader, NibabelReader, PydicomReader
from monai.data.meta_obj import get_track_meta, set_track_meta
from monai.data.meta_tensor import MetaTensor
from monai.transforms import LoadImage
from monai.utils import optional_import
from monai.utils import OptionalImportError, optional_import
from tests.test_utils import SkipIfNoModule, assert_allclose, skip_if_downloading_fails, testing_data_config

itk, has_itk = optional_import("itk", allow_namespace_pkg=True)
Expand All @@ -52,6 +54,38 @@ def get_data(self, _obj):
return np.zeros((1, 1, 1)), {"name": "my test"}


class _MissingDependencyReader(ImageReader):
"""a test reader that simulates a missing optional dependency"""

def __init__(self):
raise OptionalImportError("mock missing dependency")

def verify_suffix(self, _filename):
return True

def read(self, _data, **_kwargs):
return None

def get_data(self, _img):
return np.zeros((1, 1)), {}


class _FallbackReader(ImageReader):
"""a test reader that should not be used after an explicit reader import failure"""

read_called = False

def verify_suffix(self, _filename):
return True

def read(self, data, **_kwargs):
type(self).read_called = True
return data

def get_data(self, _img):
return np.zeros((1, 1)), {"name": "fallback"}


TEST_CASE_1 = [{}, ["test_image.nii.gz"], (128, 128, 128)]

TEST_CASE_2 = [{}, ["test_image.nii.gz"], (128, 128, 128)]
Expand Down Expand Up @@ -184,6 +218,25 @@ def get_data(self, _obj):
TESTS_META.append([{"reader": "ITKReader", "fallback_only": False}, (128, 128, 128), track_meta])


class TestLoadImageReaderSelection(unittest.TestCase):
def test_explicit_string_reader_missing_dependency_raises(self):
"""test explicitly requested string readers don't fall back when their dependency is missing"""
_FallbackReader.read_called = False
readers = {"missingreader": _MissingDependencyReader, "fallbackreader": _FallbackReader}
with patch("monai.transforms.io.array.SUPPORTED_READERS", readers):
loader = LoadImage()
self.assertEqual(len(loader.readers), 1)
self.assertIsInstance(loader.readers[0], _FallbackReader)

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
with self.assertRaises(OptionalImportError):
LoadImage(reader="missingreader")
Comment thread
coderabbitai[bot] marked this conversation as resolved.

self.assertEqual(len(caught), 0)
self.assertFalse(_FallbackReader.read_called)


@unittest.skipUnless(has_itk, "itk not installed")
class TestLoadImage(unittest.TestCase):
@classmethod
Expand Down