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
7 changes: 7 additions & 0 deletions monai/transforms/lazy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ def resample(data: torch.Tensor, matrix: NdarrayOrTensor, kwargs: dict | None =
}
ndim = len(matrix) - 1
img = convert_to_tensor(data=data, track_meta=monai.data.get_track_meta())
if not (torch.is_floating_point(img) or torch.is_complex(img)):
warnings.warn(
f"Lazy resampling computes in floating point and converts the input of dtype {img.dtype} to "
"float32; the original data type is not preserved. For integer data such as label maps, set "
"`lazy=False` for the affected transforms (or cast back afterwards) if the data type must be preserved.",
stacklevel=2,
)
Comment on lines +195 to +201

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
git ls-files | grep -F 'monai/transforms/lazy/utils.py' || true

echo "== relevant lines =="
sed -n '160,215p' monai/transforms/lazy/utils.py | cat -n

echo "== surrounding function/docs =="
sed -n '1,130p' monai/transforms/lazy/utils.py | cat -n

echo "== imports in file =="
sed -n '1,40p' monai/transforms/lazy/utils.py | cat -n

echo "== warnings usages in file =="
rg -n "warnings\.warn|stacklevel|category=UserWarning|UserWarning" monai/transforms/lazy/utils.py

echo "== ruff availability/config references =="
(git ls-files | grep -E '(^|/)(pyproject\.toml|ruff\.toml|\.ruff\.toml)$' && rg -n '(^tool\.ruff|B028|PERF|warn|stacklevel)' pyproject.toml ruff.toml .ruff.toml 2>/dev/null || true) || true

Repository: Project-MONAI/MONAI

Length of output: 11556


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== resample def and full relevant section =="
rg -n -A 70 -B 20 "^def resample" monai/transforms/lazy/utils.py

echo "== pyproject ruff config =="
sed -n '45,80p' pyproject.toml | cat -n

echo "== Python warnings default stacklevel behavior probe =="
python3 - <<'PY'
import warnings

def f():
    warnings.warn("no stacklevel")
    warnings.warn("with stacklevel=2", stacklevel=2)

try:
    import ruff
except Exception as e:
    print("ruff import unavailable:", type(e).__name__, e)

f()
PY

Repository: Project-MONAI/MONAI

Length of output: 6928


Set an explicit caller-facing warning category and stack level.

warnings.warn defaults to UserWarning, but the dtype-conversion warning should pass it explicitly. Add stacklevel=2 so the message points to the caller instead of this helper.

Proposed fix
         warnings.warn(
             f"Lazy resampling computes in floating point and converts the input of dtype {img.dtype} to "
-            "float32; the original data type is not preserved. For integer data such as label maps, set "
-            "`lazy=False` for the affected transforms (or cast back afterwards) if the data type must be preserved."
+            "float32; the original data type is not preserved. For integer data such as label maps, set "
+            "`lazy=False` for the affected transforms (or cast back afterwards) if the data type must be preserved.",
+            category=UserWarning,
+            stacklevel=2,
         )
📝 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
if not (torch.is_floating_point(img) or torch.is_complex(img)):
warnings.warn(
f"Lazy resampling computes in floating point and converts the input of dtype {img.dtype} to "
"float32; the original data type is not preserved. For integer data such as label maps, set "
"`lazy=False` for the affected transforms (or cast back afterwards) if the data type must be preserved."
)
if not (torch.is_floating_point(img) or torch.is_complex(img)):
warnings.warn(
f"Lazy resampling computes in floating point and converts the input of dtype {img.dtype} to "
"float32; the original data type is not preserved. For integer data such as label maps, set "
"`lazy=False` for the affected transforms (or cast back afterwards) if the data type must be preserved.",
category=UserWarning,
stacklevel=2,
)
🧰 Tools
🪛 Ruff (0.16.0)

[warning] 196-196: No explicit stacklevel keyword argument found

Set stacklevel=2

(B028)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@monai/transforms/lazy/utils.py` around lines 195 - 200, Update the
warnings.warn call in the lazy resampling dtype-check block to explicitly use
UserWarning and set stacklevel=2, preserving the existing warning message and
condition.

Source: Linters/SAST tools

init_affine = monai.data.to_affine_nd(ndim, img.affine)
spatial_size = kwargs.get(LazyAttr.SHAPE, None)
out_spatial_size = img.peek_pending_shape() if spatial_size is None else spatial_size
Expand Down
16 changes: 16 additions & 0 deletions tests/transforms/functional/test_resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from __future__ import annotations

import unittest
import warnings

import torch
from parameterized import parameterized
Expand Down Expand Up @@ -45,6 +46,21 @@ def test_resample_function_impl(self, img, matrix, expected):
out_1 = resample(img, matrix, {"lazy_resample_mode": "other value", "lazy_dtype": torch.float})
self.assertIs(out.dtype, out_1.dtype) # testing dtype in different lazy_resample_mode

def test_resample_warns_on_non_float_dtype(self):
"""Lazy resampling upcasts non-floating-point inputs to float32; the user should be warned (see issue #6713)."""
img = convert_to_tensor(get_arange_img((3, 3)), dtype=torch.uint8)
with self.assertWarns(Warning):
out = resample(img, torch.eye(3), {"lazy_resample_mode": "auto"})
self.assertIs(out.dtype, torch.float32)
Comment on lines +49 to +54

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the test file and inspect relevant test/source behavior.
if [ -f tests/transforms/functional/test_resample.py ]; then
  echo "== test file snippet =="
  cat -n tests/transforms/functional/test_resample.py | sed -n '1,100p'
else
  echo "tests/transforms/functional/test_resample.py not found"
  fd -i 'test_resample\.py'
fi

echo "== resample definitions/usages matching warning contract =="
rg -n "resample|lazy_resample_mode|converts the input|UserWarning|Warning" tests/transforms/functional/test_resample.py -S || true

echo "== likely implementation files =="
rg -n "def resample|class .*Resample|lazy_resample_mode|converts the input|UserWarning" -S --glob '*.py' | head -n 120

Repository: Project-MONAI/MONAI

Length of output: 4348


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
fd -i 'functional\.py$' monai/transforms/lazy || true
fd -i 'lazy.*\.py$' monai/transforms || true
rg -n "def resample\b" --glob '*.py' | head -n 80
echo "== warning strings =="
rg -n "converts the input|dtype torch\.(uint8|float)|dtype .*float32|lazy_dtype|lazy_resample_mode" --glob '*.py' | head -n 200

echo "== inspect candidate implementation =="
while IFS= read -r f; do
  echo "--- $f ---"
  ast-grep outline "$f" --match 'resample' --view expanded || true
  cat -n "$f" | sed -n '1,220p'
done < <(fd -i 'functional\.py$' monai/transforms/lazy)

Repository: Project-MONAI/MONAI

Length of output: 215


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== resample definitions/usages =="
rg -n "def resample\b|class .*Resample|resample\b" --glob '*.py' | head -n 200 || true
echo "== warning strings and lazy options =="
rg -n "converts the input|lazy_dtype|lazy_resample_mode|warn\(|warnings\.warn|UserWarning|Warning" --glob '*.py' | head -n 250 || true

echo "== inspect monai/transforms/lazy/functional.py =="
wc -l monai/transforms/lazy/functional.py
cat -n monai/transforms/lazy/functional.py | sed -n '1,260p'

echo "== Python unittest assertWarns semantics probe =="
python3 - <<'PY'
import warnings, unittest

class Bad(unittest.TestCase):
    def pass_warning(self):
        with warnings.catch_warnings():
            warnings.simplefilter("error")
            with self.assertWarns(Warning):
                warnings.warn("hello", DeprecationWarning)
            with self.assertWarnsRegex(Warning, "hello"):
                warnings.warn("hello", DeprecationWarning)
try:
    Bad().pass_warning()
    print("both accept unrelated warning subtype under current semantics")
except Exception as e:
    print(type(e).__name__, e)
PY

Repository: Project-MONAI/MONAI

Length of output: 191


🏁 Script executed:

#!/bin/bash
set -u

echo "== locate functional.py =="
fd -a 'functional.py$' . | sed 's#^\./##' | grep 'monai/transforms/lazy/functional.py' || true

echo "== resample references =="
rg -n "def resample\b|lazy_dtype|lazy_resample_mode|warnings\.warn|UserWarning|Warning" -S --glob '*.py' . | head -n 300

echo "== inspect monai/transforms/lazy/functional.py =="
if [ -f monai/transforms/lazy/functional.py ]; then
  wc -l monai/transforms/lazy/functional.py
  cat -n monai/transforms/lazy/functional.py | sed -n '1,260p'
else
  echo "monai/transforms/lazy/functional.py missing"
fi

Repository: Project-MONAI/MONAI

Length of output: 47659


🏁 Script executed:

#!/bin/bash
set -u

python3 - <<'PY'
import ast
path = "monai/transforms/lazy/utils.py"
with open(path) as f:
    tree = ast.parse(f.read())

for node in ast.walk(tree):
    if isinstance(node, ast.FunctionDef) and node.name == "resample":
        file = node.lineno; col = node.col_offset
        print(f"resample starts line {file} col {col}")
        print(ast.get_source_segment(open(path).read(), node))
        break
else:
    print("resample function not found")

with open(path) as f:
    for i, line in enumerate(f, start=1):
        if 140 <= i <= 220:
            print(f"{i}: {line.rstrip()}")
PY

Repository: Project-MONAI/MONAI

Length of output: 9441


Assert the warning category and message.

self.assertWarns(Warning) only checks for any Warning subclass, while resample() emits a UserWarning with the dtype conversion message. Use assertWarnsRegex(UserWarning, ...) with a regex that covers the emitted text.

Proposed test change
-        with self.assertWarns(Warning):
+        with self.assertWarnsRegex(
+            UserWarning, r"converts the input of dtype torch\.uint8 to float32"
+        ):
             out = resample(img, torch.eye(3), {"lazy_resample_mode": "auto"})
📝 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
def test_resample_warns_on_non_float_dtype(self):
"""Lazy resampling upcasts non-floating-point inputs to float32; the user should be warned (see issue #6713)."""
img = convert_to_tensor(get_arange_img((3, 3)), dtype=torch.uint8)
with self.assertWarns(Warning):
out = resample(img, torch.eye(3), {"lazy_resample_mode": "auto"})
self.assertIs(out.dtype, torch.float32)
def test_resample_warns_on_non_float_dtype(self):
"""Lazy resampling upcasts non-floating-point inputs to float32; the user should be warned (see issue `#6713`)."""
img = convert_to_tensor(get_arange_img((3, 3)), dtype=torch.uint8)
with self.assertWarnsRegex(
UserWarning, r"converts the input of dtype torch\.uint8 to float32"
):
out = resample(img, torch.eye(3), {"lazy_resample_mode": "auto"})
self.assertIs(out.dtype, torch.float32)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/transforms/functional/test_resample.py` around lines 49 - 54, Update
test_resample_warns_on_non_float_dtype to assert the specific UserWarning
category and match the emitted dtype-conversion message with
assertWarnsRegex(UserWarning, ...), while preserving the existing resample call
and float32 dtype assertion.

Source: Path instructions

self.assertIs(img.dtype, torch.uint8) # the input tensor itself is not mutated

def test_resample_no_warning_for_float_dtype(self):
"""Float32 inputs do not trigger the lazy resampling dtype warning."""
img = convert_to_tensor(get_arange_img((3, 3)), dtype=torch.float32)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
with warnings.catch_warnings():
warnings.simplefilter("error") # turn any warning into an error
resample(img, torch.eye(3), {"lazy_resample_mode": "auto"})


if __name__ == "__main__":
unittest.main()
Loading