From faa2e698960d73c79620903b97914a227b1f48d8 Mon Sep 17 00:00:00 2001 From: Raphael Malikian Date: Fri, 19 Jun 2026 00:53:32 -0700 Subject: [PATCH 1/3] fix: add missing stacklevel=2 to warnings.warn() in losses/ Add stacklevel=2 to 28 warnings.warn() calls across 9 loss modules so that warnings correctly point to the user's calling code instead of MONAI internal code. Affected files: - dice.py (7 instances) - tversky.py (3 instances) - mcc_loss.py (3 instances) - focal_loss.py (3 instances) - hausdorff_loss.py (3 instances) - unified_focal_loss.py (3 instances) - spatial_mask.py (3 instances) - perceptual.py (2 instances) - adversarial_loss.py (1 instance) Also fixes a typo in perceptual.py: 'supp, ort' -> 'support'. Fixes #8929 Signed-off-by: Raphael Malikian --- monai/losses/adversarial_loss.py | 2 +- monai/losses/dice.py | 14 +++++++------- monai/losses/focal_loss.py | 4 ++-- monai/losses/hausdorff_loss.py | 6 +++--- monai/losses/mcc_loss.py | 6 +++--- monai/losses/perceptual.py | 4 ++-- monai/losses/spatial_mask.py | 6 +++--- monai/losses/tversky.py | 6 +++--- monai/losses/unified_focal_loss.py | 6 +++--- 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/monai/losses/adversarial_loss.py b/monai/losses/adversarial_loss.py index b2c27a41ee..d83e5bafc9 100644 --- a/monai/losses/adversarial_loss.py +++ b/monai/losses/adversarial_loss.py @@ -130,7 +130,7 @@ def forward( warnings.warn( "Variable target_is_real has been set to False, but for_discriminator is set" "to False. To optimise a generator, target_is_real must be set to True." - ) + , stacklevel=2) if not isinstance(input, list): input = [input] diff --git a/monai/losses/dice.py b/monai/losses/dice.py index b4558f930c..2c4010176a 100644 --- a/monai/losses/dice.py +++ b/monai/losses/dice.py @@ -156,7 +156,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: n_pred_ch = input.shape[1] if self.softmax: if n_pred_ch == 1: - warnings.warn("single channel prediction, `softmax=True` ignored.") + warnings.warn("single channel prediction, `softmax=True` ignored.", stacklevel=2) else: input = torch.softmax(input, 1) @@ -165,13 +165,13 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: if self.to_onehot_y: if n_pred_ch == 1: - warnings.warn("single channel prediction, `to_onehot_y=True` ignored.") + warnings.warn("single channel prediction, `to_onehot_y=True` ignored.", stacklevel=2) else: target = one_hot(target, num_classes=n_pred_ch) if not self.include_background: if n_pred_ch == 1: - warnings.warn("single channel prediction, `include_background=False` ignored.") + warnings.warn("single channel prediction, `include_background=False` ignored.", stacklevel=2) else: # if skipping background, removing first channel target = target[:, 1:] @@ -405,7 +405,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: n_pred_ch = input.shape[1] if self.softmax: if n_pred_ch == 1: - warnings.warn("single channel prediction, `softmax=True` ignored.") + warnings.warn("single channel prediction, `softmax=True` ignored.", stacklevel=2) else: input = torch.softmax(input, 1) @@ -414,13 +414,13 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: if self.to_onehot_y: if n_pred_ch == 1: - warnings.warn("single channel prediction, `to_onehot_y=True` ignored.") + warnings.warn("single channel prediction, `to_onehot_y=True` ignored.", stacklevel=2) else: target = one_hot(target, num_classes=n_pred_ch) if not self.include_background: if n_pred_ch == 1: - warnings.warn("single channel prediction, `include_background=False` ignored.") + warnings.warn("single channel prediction, `include_background=False` ignored.", stacklevel=2) else: # if skipping background, removing first channel target = target[:, 1:] @@ -987,7 +987,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: if self.to_onehot_y: n_pred_ch = input.shape[1] if n_pred_ch == 1: - warnings.warn("single channel prediction, `to_onehot_y=True` ignored.") + warnings.warn("single channel prediction, `to_onehot_y=True` ignored.", stacklevel=2) else: target = one_hot(target, num_classes=n_pred_ch) dice_loss = self.dice(input, target) diff --git a/monai/losses/focal_loss.py b/monai/losses/focal_loss.py index 7773cbdc9a..b6d10c711c 100644 --- a/monai/losses/focal_loss.py +++ b/monai/losses/focal_loss.py @@ -146,13 +146,13 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: if self.to_onehot_y: if n_pred_ch == 1: - warnings.warn("single channel prediction, `to_onehot_y=True` ignored.") + warnings.warn("single channel prediction, `to_onehot_y=True` ignored.", stacklevel=2) else: target = one_hot(target, num_classes=n_pred_ch) if not self.include_background: if n_pred_ch == 1: - warnings.warn("single channel prediction, `include_background=False` ignored.") + warnings.warn("single channel prediction, `include_background=False` ignored.", stacklevel=2) else: # if skipping background, removing first channel target = target[:, 1:] diff --git a/monai/losses/hausdorff_loss.py b/monai/losses/hausdorff_loss.py index 680ff7bc82..d10fdb9fd5 100644 --- a/monai/losses/hausdorff_loss.py +++ b/monai/losses/hausdorff_loss.py @@ -154,7 +154,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: n_pred_ch = input.shape[1] if self.softmax: if n_pred_ch == 1: - warnings.warn("single channel prediction, `softmax=True` ignored.") + warnings.warn("single channel prediction, `softmax=True` ignored.", stacklevel=2) else: input = torch.softmax(input, 1) @@ -163,13 +163,13 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: if self.to_onehot_y: if n_pred_ch == 1: - warnings.warn("single channel prediction, `to_onehot_y=True` ignored.") + warnings.warn("single channel prediction, `to_onehot_y=True` ignored.", stacklevel=2) else: target = one_hot(target, num_classes=n_pred_ch) if not self.include_background: if n_pred_ch == 1: - warnings.warn("single channel prediction, `include_background=False` ignored.") + warnings.warn("single channel prediction, `include_background=False` ignored.", stacklevel=2) else: # If skipping background, removing first channel target = target[:, 1:] diff --git a/monai/losses/mcc_loss.py b/monai/losses/mcc_loss.py index ac2877e5f7..17323f8941 100644 --- a/monai/losses/mcc_loss.py +++ b/monai/losses/mcc_loss.py @@ -133,7 +133,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: n_pred_ch = input.shape[1] if self.softmax: if n_pred_ch == 1: - warnings.warn("single channel prediction, `softmax=True` ignored.") + warnings.warn("single channel prediction, `softmax=True` ignored.", stacklevel=2) else: input = torch.softmax(input, 1) @@ -142,13 +142,13 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: if self.to_onehot_y: if n_pred_ch == 1: - warnings.warn("single channel prediction, `to_onehot_y=True` ignored.") + warnings.warn("single channel prediction, `to_onehot_y=True` ignored.", stacklevel=2) else: target = one_hot(target, num_classes=n_pred_ch) if not self.include_background: if n_pred_ch == 1: - warnings.warn("single channel prediction, `include_background=False` ignored.") + warnings.warn("single channel prediction, `include_background=False` ignored.", stacklevel=2) else: target = target[:, 1:] input = input[:, 1:] diff --git a/monai/losses/perceptual.py b/monai/losses/perceptual.py index 635a3e75ce..d948dacfd5 100644 --- a/monai/losses/perceptual.py +++ b/monai/losses/perceptual.py @@ -112,7 +112,7 @@ def __init__( ) if not channel_wise: warnings.warn( - "MedicalNet networks supp, ort channel-wise loss. Consider setting channel_wise=True.", stacklevel=2 + "MedicalNet networks support channel-wise loss. Consider setting channel_wise=True.", stacklevel=2 ) # Channel-wise only for MedicalNet @@ -128,7 +128,7 @@ def __init__( # raise a warning that this may change the default cache dir for all torch.hub calls warnings.warn( f"Setting cache_dir to {cache_dir}, this may change the default cache dir for all torch.hub calls." - ) + , stacklevel=2) self.spatial_dims = spatial_dims self.perceptual_function: nn.Module diff --git a/monai/losses/spatial_mask.py b/monai/losses/spatial_mask.py index 0f823410dd..dc6b06f264 100644 --- a/monai/losses/spatial_mask.py +++ b/monai/losses/spatial_mask.py @@ -55,16 +55,16 @@ def forward(self, input: torch.Tensor, target: torch.Tensor, mask: torch.Tensor mask: the shape should be B1H[WD] or 11H[WD]. """ if mask is None: - warnings.warn("No mask value specified for the MaskedLoss.") + warnings.warn("No mask value specified for the MaskedLoss.", stacklevel=2) return self.loss(input, target) if input.dim() != mask.dim(): - warnings.warn(f"Dim of input ({input.shape}) is different from mask ({mask.shape}).") + warnings.warn(f"Dim of input ({input.shape}) is different from mask ({mask.shape}).", stacklevel=2) if input.shape[0] != mask.shape[0] and mask.shape[0] != 1: raise ValueError(f"Batch size of mask ({mask.shape}) must be one or equal to input ({input.shape}).") if target.dim() > 1: if mask.shape[1] != 1: raise ValueError(f"Mask ({mask.shape}) must have only one channel.") if input.shape[2:] != mask.shape[2:]: - warnings.warn(f"Spatial size of input ({input.shape}) is different from mask ({mask.shape}).") + warnings.warn(f"Spatial size of input ({input.shape}) is different from mask ({mask.shape}).", stacklevel=2) return self.loss(input * mask, target * mask) diff --git a/monai/losses/tversky.py b/monai/losses/tversky.py index 154f34c526..5db4025be0 100644 --- a/monai/losses/tversky.py +++ b/monai/losses/tversky.py @@ -118,7 +118,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: n_pred_ch = input.shape[1] if self.softmax: if n_pred_ch == 1: - warnings.warn("single channel prediction, `softmax=True` ignored.") + warnings.warn("single channel prediction, `softmax=True` ignored.", stacklevel=2) else: input = torch.softmax(input, 1) @@ -127,13 +127,13 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: if self.to_onehot_y: if n_pred_ch == 1: - warnings.warn("single channel prediction, `to_onehot_y=True` ignored.") + warnings.warn("single channel prediction, `to_onehot_y=True` ignored.", stacklevel=2) else: target = one_hot(target, num_classes=n_pred_ch) if not self.include_background: if n_pred_ch == 1: - warnings.warn("single channel prediction, `include_background=False` ignored.") + warnings.warn("single channel prediction, `include_background=False` ignored.", stacklevel=2) else: # if skipping background, removing first channel target = target[:, 1:] diff --git a/monai/losses/unified_focal_loss.py b/monai/losses/unified_focal_loss.py index 745513fec0..98dbf124c6 100644 --- a/monai/losses/unified_focal_loss.py +++ b/monai/losses/unified_focal_loss.py @@ -58,7 +58,7 @@ def forward(self, y_pred: torch.Tensor, y_true: torch.Tensor) -> torch.Tensor: if self.to_onehot_y: if n_pred_ch == 1: - warnings.warn("single channel prediction, `to_onehot_y=True` ignored.") + warnings.warn("single channel prediction, `to_onehot_y=True` ignored.", stacklevel=2) else: y_true = one_hot(y_true, num_classes=n_pred_ch) @@ -122,7 +122,7 @@ def forward(self, y_pred: torch.Tensor, y_true: torch.Tensor) -> torch.Tensor: if self.to_onehot_y: if n_pred_ch == 1: - warnings.warn("single channel prediction, `to_onehot_y=True` ignored.") + warnings.warn("single channel prediction, `to_onehot_y=True` ignored.", stacklevel=2) else: y_true = one_hot(y_true, num_classes=n_pred_ch) @@ -223,7 +223,7 @@ def forward(self, y_pred: torch.Tensor, y_true: torch.Tensor) -> torch.Tensor: n_pred_ch = y_pred.shape[1] if self.to_onehot_y: if n_pred_ch == 1: - warnings.warn("single channel prediction, `to_onehot_y=True` ignored.") + warnings.warn("single channel prediction, `to_onehot_y=True` ignored.", stacklevel=2) else: y_true = one_hot(y_true, num_classes=n_pred_ch) From fd98601a954e7e62f607006eb0d0dabc233c7904 Mon Sep 17 00:00:00 2001 From: Raphael Malikian Date: Fri, 19 Jun 2026 02:03:44 -0700 Subject: [PATCH 2/3] fix: consistent formatting for stacklevel=2 in perceptual.py and adversarial_loss.py Address CodeRabbit review: move stacklevel parameter to same line as closing quote for consistent formatting across the PR. Signed-off-by: Raphael Malikian --- monai/losses/adversarial_loss.py | 5 +++-- monai/losses/perceptual.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/monai/losses/adversarial_loss.py b/monai/losses/adversarial_loss.py index d83e5bafc9..8be05bab89 100644 --- a/monai/losses/adversarial_loss.py +++ b/monai/losses/adversarial_loss.py @@ -129,8 +129,9 @@ def forward( target_is_real = True # With generator, we always want this to be true! warnings.warn( "Variable target_is_real has been set to False, but for_discriminator is set" - "to False. To optimise a generator, target_is_real must be set to True." - , stacklevel=2) + "to False. To optimise a generator, target_is_real must be set to True.", + stacklevel=2, + ) if not isinstance(input, list): input = [input] diff --git a/monai/losses/perceptual.py b/monai/losses/perceptual.py index d948dacfd5..8ebb0f4879 100644 --- a/monai/losses/perceptual.py +++ b/monai/losses/perceptual.py @@ -127,8 +127,9 @@ def __init__( torch.hub.set_dir(cache_dir) # raise a warning that this may change the default cache dir for all torch.hub calls warnings.warn( - f"Setting cache_dir to {cache_dir}, this may change the default cache dir for all torch.hub calls." - , stacklevel=2) + f"Setting cache_dir to {cache_dir}, this may change the default cache dir for all torch.hub calls.", + stacklevel=2, + ) self.spatial_dims = spatial_dims self.perceptual_function: nn.Module From 42a67da24d28d4279aafbc363b6fd273b3e57de4 Mon Sep 17 00:00:00 2001 From: rtmalikian Date: Fri, 19 Jun 2026 09:45:57 -0700 Subject: [PATCH 3/3] fix: run black formatting on losses/ files Signed-off-by: rtmalikian --- monai/losses/spatial_mask.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/monai/losses/spatial_mask.py b/monai/losses/spatial_mask.py index dc6b06f264..ba91c22fde 100644 --- a/monai/losses/spatial_mask.py +++ b/monai/losses/spatial_mask.py @@ -66,5 +66,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor, mask: torch.Tensor if mask.shape[1] != 1: raise ValueError(f"Mask ({mask.shape}) must have only one channel.") if input.shape[2:] != mask.shape[2:]: - warnings.warn(f"Spatial size of input ({input.shape}) is different from mask ({mask.shape}).", stacklevel=2) + warnings.warn( + f"Spatial size of input ({input.shape}) is different from mask ({mask.shape}).", stacklevel=2 + ) return self.loss(input * mask, target * mask)