From e3a2bb255a40cd10dab5ddd25185765622503e54 Mon Sep 17 00:00:00 2001 From: Oleksandr Yizchak Sanin Date: Tue, 19 May 2026 00:19:34 +0200 Subject: [PATCH 1/4] Replace BaseException with Exception across codebase (#7401) Catching BaseException inadvertently suppresses KeyboardInterrupt, SystemExit, and GeneratorExit, which should nearly always propagate. All 17 occurrences across monai/ and tests/ are replaced with Exception, which is the appropriate base class for catchable errors. Signed-off-by: Oleksandr Sanin --- monai/__init__.py | 2 +- monai/apps/auto3dseg/data_analyzer.py | 4 ++-- monai/apps/auto3dseg/ensemble_builder.py | 2 +- monai/apps/auto3dseg/utils.py | 2 +- monai/apps/detection/metrics/coco.py | 2 +- monai/apps/nnunet/nnunetv2_runner.py | 4 ++-- monai/config/deviceconfig.py | 2 +- monai/data/__init__.py | 2 +- monai/inferers/inferer.py | 2 +- monai/utils/tf32.py | 4 ++-- tests/apps/detection/networks/test_retinanet.py | 4 ++-- tests/networks/nets/test_resnet.py | 2 +- tests/test_utils.py | 2 +- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/monai/__init__.py b/monai/__init__.py index d92557a8e1..45e15ddcd9 100644 --- a/monai/__init__.py +++ b/monai/__init__.py @@ -131,7 +131,7 @@ def filter(self, record): # workaround related to https://github.com/Project-MONAI/MONAI/issues/7575 if hasattr(torch.cuda.device_count, "cache_clear"): torch.cuda.device_count.cache_clear() -except BaseException: +except Exception: from .utils.misc import MONAIEnvVars if MONAIEnvVars.debug(): diff --git a/monai/apps/auto3dseg/data_analyzer.py b/monai/apps/auto3dseg/data_analyzer.py index 15e56abfea..30824e200f 100644 --- a/monai/apps/auto3dseg/data_analyzer.py +++ b/monai/apps/auto3dseg/data_analyzer.py @@ -341,7 +341,7 @@ def _get_all_case_stats( _label_argmax = True # track if label is argmaxed batch_data[self.label_key] = label.to(device) d = summarizer(batch_data) - except BaseException as err: + except Exception as err: if "image_meta_dict" in batch_data.keys(): filename = batch_data["image_meta_dict"][ImageMetaKey.FILENAME_OR_OBJ] else: @@ -357,7 +357,7 @@ def _get_all_case_stats( label = torch.argmax(label, dim=0) if label.shape[0] > 1 else label[0] batch_data[self.label_key] = label.to("cpu") d = summarizer(batch_data) - except BaseException as err: + except Exception as err: logger.info(f"Unable to process data {filename} on {device}. {err}") continue else: diff --git a/monai/apps/auto3dseg/ensemble_builder.py b/monai/apps/auto3dseg/ensemble_builder.py index e574baf7c8..eaf1f14c7f 100644 --- a/monai/apps/auto3dseg/ensemble_builder.py +++ b/monai/apps/auto3dseg/ensemble_builder.py @@ -216,7 +216,7 @@ def __call__(self, pred_param: dict | None = None) -> list: if "image_save_func" in param: try: ensemble_preds = self.ensemble_pred(preds, sigmoid=sigmoid) - except BaseException: + except Exception: ensemble_preds = self.ensemble_pred([_.to("cpu") for _ in preds], sigmoid=sigmoid) res = img_saver(ensemble_preds) # res is the path to the saved results diff --git a/monai/apps/auto3dseg/utils.py b/monai/apps/auto3dseg/utils.py index fbf9dc101c..1946bb718a 100644 --- a/monai/apps/auto3dseg/utils.py +++ b/monai/apps/auto3dseg/utils.py @@ -59,7 +59,7 @@ def import_bundle_algo_history( if best_metric is None: try: best_metric = algo.get_score() - except BaseException: + except Exception: pass is_trained = best_metric is not None diff --git a/monai/apps/detection/metrics/coco.py b/monai/apps/detection/metrics/coco.py index d1347d76b8..b8de0eb104 100644 --- a/monai/apps/detection/metrics/coco.py +++ b/monai/apps/detection/metrics/coco.py @@ -541,7 +541,7 @@ def _compute_stats_single_threshold( for save_idx, array_index in enumerate(inds): precision[save_idx] = pr[array_index] th_scores[save_idx] = dt_scores_sorted[array_index] - except BaseException: + except Exception: pass return recall, np.array(precision), np.array(th_scores) diff --git a/monai/apps/nnunet/nnunetv2_runner.py b/monai/apps/nnunet/nnunetv2_runner.py index 98b265cbbb..4828ccb56e 100644 --- a/monai/apps/nnunet/nnunetv2_runner.py +++ b/monai/apps/nnunet/nnunetv2_runner.py @@ -200,7 +200,7 @@ def __init__( from nnunetv2.utilities.dataset_name_id_conversion import maybe_convert_to_dataset_name self.dataset_name = maybe_convert_to_dataset_name(int(self.dataset_name_or_id)) - except BaseException: + except Exception: logger.warning( f"Dataset with name/ID: {self.dataset_name_or_id} cannot be found in the record. " "Please ignore the message above if you are running the pipeline from a fresh start. " @@ -278,7 +278,7 @@ def convert_dataset(self): num_input_channels=num_input_channels, output_datafolder=raw_data_foldername, ) - except BaseException as err: + except Exception as err: logger.warning(f"Input config may be incorrect. Detail info: error/exception message is:\n {err}") return diff --git a/monai/config/deviceconfig.py b/monai/config/deviceconfig.py index aa1f2a0b53..5800651e59 100644 --- a/monai/config/deviceconfig.py +++ b/monai/config/deviceconfig.py @@ -120,7 +120,7 @@ def print_config(file=sys.stdout): def _dict_append(in_dict, key, fn): try: in_dict[key] = fn() if callable(fn) else fn - except BaseException: + except Exception: in_dict[key] = "UNKNOWN for given OS" diff --git a/monai/data/__init__.py b/monai/data/__init__.py index 5e367cc297..971d5121f7 100644 --- a/monai/data/__init__.py +++ b/monai/data/__init__.py @@ -118,7 +118,7 @@ from .wsi_datasets import MaskedPatchWSIDataset, PatchWSIDataset, SlidingPatchWSIDataset from .wsi_reader import BaseWSIReader, CuCIMWSIReader, OpenSlideWSIReader, TiffFileWSIReader, WSIReader -with contextlib.suppress(BaseException): +with contextlib.suppress(Exception): from multiprocessing.reduction import ForkingPickler def _rebuild_meta(cls, storage, dtype, metadata): diff --git a/monai/inferers/inferer.py b/monai/inferers/inferer.py index eea573609c..ee94b1ebdb 100644 --- a/monai/inferers/inferer.py +++ b/monai/inferers/inferer.py @@ -545,7 +545,7 @@ def __init__( ) if cache_roi_weight_map and self.roi_weight_map is None: warnings.warn("cache_roi_weight_map=True, but cache is not created. (dynamic roi_size?)") - except BaseException as e: + except Exception as e: raise RuntimeError( f"roi size {self.roi_size}, mode={mode}, sigma_scale={sigma_scale}, device={device}\n" "Seems to be OOM. Please try smaller patch size or mode='constant' instead of mode='gaussian'." diff --git a/monai/utils/tf32.py b/monai/utils/tf32.py index ad5918a34a..db5e05279e 100644 --- a/monai/utils/tf32.py +++ b/monai/utils/tf32.py @@ -41,7 +41,7 @@ def has_ampere_or_later() -> bool: major, _ = pynvml.nvmlDeviceGetCudaComputeCapability(handle) if major >= 8: return True - except BaseException: + except Exception: pass finally: pynvml.nvmlShutdown() @@ -71,7 +71,7 @@ def detect_default_tf32() -> bool: may_enable_tf32 = True return may_enable_tf32 - except BaseException: + except Exception: from monai.utils.misc import MONAIEnvVars if MONAIEnvVars.debug(): diff --git a/tests/apps/detection/networks/test_retinanet.py b/tests/apps/detection/networks/test_retinanet.py index 3f4721a755..eb491361ea 100644 --- a/tests/apps/detection/networks/test_retinanet.py +++ b/tests/apps/detection/networks/test_retinanet.py @@ -140,7 +140,7 @@ def test_retina_shape(self, model, input_param, input_shape): def test_script(self, model, input_param, input_shape): try: idx = int(self.id().split("test_script_")[-1]) - except BaseException: + except Exception: idx = 0 idx %= 3 # test whether support torchscript @@ -174,7 +174,7 @@ def test_script(self, model, input_param, input_shape): def test_onnx(self, model, input_param, input_shape): try: idx = int(self.id().split("test_onnx_")[-1]) - except BaseException: + except Exception: idx = 0 idx %= 3 # test whether support torchscript diff --git a/tests/networks/nets/test_resnet.py b/tests/networks/nets/test_resnet.py index 371ec89682..241f57c78d 100644 --- a/tests/networks/nets/test_resnet.py +++ b/tests/networks/nets/test_resnet.py @@ -249,7 +249,7 @@ def tearDown(self): if os.path.exists(self.tmp_ckpt_filename): try: os.remove(self.tmp_ckpt_filename) - except BaseException: + except Exception: pass @parameterized.expand(TEST_CASES) diff --git a/tests/test_utils.py b/tests/test_utils.py index 27af61cefe..05f7cb88d9 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -241,7 +241,7 @@ def is_tf32_env(): a_full = torch.randn(1024, 1024, dtype=torch.double, device="cuda", generator=g_gpu) b_full = torch.randn(1024, 1024, dtype=torch.double, device="cuda", generator=g_gpu) _tf32_enabled = (a_full.float() @ b_full.float() - a_full @ b_full).abs().max().item() > 0.001 # 0.1713 - except BaseException: + except Exception: pass print(f"tf32 enabled: {_tf32_enabled}") return _tf32_enabled From 37db0adf747f10d304da9d379fe035b5f08e5a5f Mon Sep 17 00:00:00 2001 From: Oleksandr Yizchak Sanin Date: Tue, 19 May 2026 00:48:46 +0200 Subject: [PATCH 2/4] Fix RandGridDistortiond converting entire dict when transform is skipped (#8604) When _do_transform is False, RandGridDistortiond called convert_to_tensor on the entire data dictionary, which converted non-image metadata values (integers, strings) into tensors. This caused AttributeError ('int' object has no attribute 'numel') during DataLoader collation. Only convert the transform's target keys instead of the whole dict, consistent with how the transform handles its keys in the normal (do_transform=True) path. Signed-off-by: Oleksandr Sanin --- monai/transforms/spatial/dictionary.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 51ad0435fc..6fcb2465f3 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -2305,8 +2305,9 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc d = dict(data) self.randomize(None) if not self._do_transform: - out: dict[Hashable, torch.Tensor] = convert_to_tensor(d, track_meta=get_track_meta()) - return out + for key in self.key_iterator(d): + d[key] = convert_to_tensor(d[key], track_meta=get_track_meta()) + return d first_key: Hashable = self.first_key(d) if first_key == (): From a4cefe7a4465af26b0c5d661012dfa30b004a64e Mon Sep 17 00:00:00 2001 From: Oleksandr Yizchak Sanin Date: Tue, 19 May 2026 09:29:35 +0200 Subject: [PATCH 3/4] DCO Remediation Commit for Oleksandr Yizchak Sanin I, Oleksandr Yizchak Sanin , hereby add my Signed-off-by to this commit: 37db0adf747f10d304da9d379fe035b5f08e5a5f Signed-off-by: Oleksandr Yizchak Sanin From 117d65bd5ac0aef3750305d2cb5327c78ab1ada6 Mon Sep 17 00:00:00 2001 From: Oleksandr Sanin Date: Tue, 19 May 2026 10:24:08 +0200 Subject: [PATCH 4/4] Fix mypy error: add type annotation for convert_to_tensor return value Add explicit dict[Hashable, torch.Tensor] type annotation to the convert_to_tensor call in the first_key == () branch to satisfy mypy's no-any-return check. Signed-off-by: Oleksandr Yizchak Sanin --- monai/transforms/spatial/dictionary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 6fcb2465f3..543d875c4e 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -2311,7 +2311,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torc first_key: Hashable = self.first_key(d) if first_key == (): - out = convert_to_tensor(d, track_meta=get_track_meta()) + out: dict[Hashable, torch.Tensor] = convert_to_tensor(d, track_meta=get_track_meta()) return out if isinstance(d[first_key], MetaTensor) and d[first_key].pending_operations: # type: ignore warnings.warn(f"data['{first_key}'] has pending operations, transform may return incorrect results.")