Skip to content

Improve test coverage for torch integration#928

Open
jhelferty-nv wants to merge 45 commits into
shader-slang:mainfrom
jhelferty-nv:fix-782
Open

Improve test coverage for torch integration#928
jhelferty-nv wants to merge 45 commits into
shader-slang:mainfrom
jhelferty-nv:fix-782

Conversation

@jhelferty-nv

@jhelferty-nv jhelferty-nv commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Improve overall test coverage from 73.8% to 82.8% (+9.0 pp), focusing on
torch integration paths on both the Python and C++ sides.

Python coverage: 78.7% → 82.9% (+4.2 pp, 264 newly covered lines)
C++ coverage: 69.2% → 82.7% (+13.5 pp, 1050 newly covered lines)

New and extended test files cover:

  • DiffPair factory paths, property access, repr, and error guards
  • TorchTensorMarshall creation, dtype conversion, and validation
  • torch bridge fallback mode error paths
  • torch autograd backward pass creating output DiffPairs
  • torch raw dispatch and rare scalar type output (uint8, int8, int16, bool)
  • torch benchmark smoke tests (slangpy_torch C API and libtorch paths)
  • Tensor/TensorView/DiffTensorView forward paths and gradient handling
  • ValueRef inout for scalars, vectors, and matrices (with padding)
  • DispatchData error paths, writer callbacks, and cache hits
  • Module load_from_source, require_struct, find_function_in_struct
  • ByteAddressBuffer and RWByteAddressBuffer read/write
  • StridedBufferView error guards, contiguity, and negative indexing
  • PackedArg property access and repr
  • C++ repr coverage for FunctionNode, SlangType, and texture shapes
  • Image loading across channel counts and flip_y

Bug fixes included:

  • Fix flip_y producing non-contiguous array in types/common.py
    (np.flipud result rejected by copy_from_numpy)

Bugs found and filed as separate issues:

All tests avoid direct Native object construction where possible, using
Python-level APIs (diff_pair(), pack(), backward(), func.map()) to
exercise the underlying C++ code paths naturally.

Fixes #782
Fixes #783

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes

    • Ensure image-loading returns C-contiguous arrays; improve CUDA interop synchronization and lifetime handling to prevent async-use and memory corruption.
  • Tests

    • Large expansion of automated tests: buffers, buffer-views/indexing, NDBuffers, tensors/tensorviews, packing/marshalling, textures/descriptors, shader dispatch/return types, image loading, and extensive CUDA & PyTorch integration (autograd, dtypes, benchmarks).
  • Chores

    • Broadened stability/regression coverage for interoperability and edge-case error paths.

The generated Slang struct for DiffPairMarshall had two bugs preventing
scalar backward passes from working:

1. Used bare `load`/`store` method names instead of `__slangpy_load`/
   `__slangpy_store` (the convention used by all SlangPy Slang types).

2. Generated `__slangpy_load` that output `DifferentialPair<T>` instead
   of `T`, causing a type mismatch with the trampoline temporaries.
   The methods were also not `[Differentiable]`, so `bwd_diff` could
   not propagate gradients through them.

The fix rewrites `generate_differential_pair` to follow the same pattern
as `DiffTensor`: the struct's load/store methods operate on the primal
type and, when gradients are needed, use `[Differentiable]` methods with
`[BackwardDerivative]` custom backward functions that explicitly
read/write the derivative buffer.

Removes the "Awaiting auto-diff changes" skip markers from
test_call_with_diff_scalars and test_call_with_diff_pairs, which now
pass on both Vulkan and CUDA.

Made-with: Cursor
- test_ndbuffer.py: 21 tests covering NDBuffer construction (empty,
  zeros, from_numpy, element_count, empty_like, zeros_like),
  data transfer (copy_from_numpy, to_numpy, clear), views, broadcasting,
  validation errors, and end-to-end Slang function dispatch with
  NDBufferMarshall (1D/2D add, scalar broadcast, read-only pass,
  StructuredBuffer pass, return type, int dtype).

- test_diffpair_type.py: 19 tests covering DiffPair data class
  construction (defaults, None handling, needs_grad, int types),
  get/set by PrimType, slangpy_signature, and both factory functions
  (diffPair, floatDiffPair).

Made-with: Cursor
)

- test_torch_mixed.py: 7 CUDA tests covering mixed torch.Tensor +
  NDBuffer argument combinations, torch+scalar broadcast, torch return
  values, 2D torch dispatch, and pack() with torch.Tensor (exercises
  TorchTensorMarshall.build_shader_object path).

Made-with: Cursor
shader-slang#782)

Tests for bridge_fallback.py error/guard paths, torchtensormarshall.py
DiffPair factory and error paths, builtin/tensor.py grad validation and
properties, and builtin/value.py return types and reduce_type via torch
pipelines.

Made-with: Cursor
shader-slang#782)

- Fix non-contiguous array bug in load_buffer_data_from_image when
  flip_y=True (np.flipud returns a view; add contiguity guard)
- Add test_load_image.py: covers Tensor.load_from_image channel variants,
  flip_y, scale/offset, greyscale, and linearize flags
- Add test_torch_dtypes.py: covers create_output branches for int32,
  int64, float64 in slangpytorchtensor.cpp
- Add test_descriptor_marshall.py: covers DescriptorHandle low-level API
  and DescriptorMarshall error paths
- Extend test_tensor.py: covers __str__, detach(), deprecated Tensor.numpy(),
  deprecated element_count param, and empty() error paths
- Extend test_tensorview.py: covers slangpy.Tensor through TensorView and
  scalar-to-vector TensorView<float2> resolution (tensorcommon.py)
- Extend test_difftensorview.py: covers slangpy.Tensor through
  DiffTensorView forward path (tensorcommon.py)

Made-with: Cursor
…edbuffer (shader-slang#782)

- test_raw_dispatch: error paths (return value, out param, no params,
  wrong param name), writer tuple via .write(), dispatch cache hit
- test_return_types: inout scalar/vector/matrix ValueRef exercising
  padding/unpadding paths in valueref.py
- test_modules: function.py error paths (set, write, return_type, as_struct)
- test_buffers: ByteAddressBuffer read-only and RWByteAddressBuffer write

Made-with: Cursor
…er-slang#782)

Move tests from test_cpp_coverage_tier1.py and test_cpp_coverage_torch.py
into existing files by topic: repr tests to test_tostring, texture shape to
test_textures, Shape/SignatureBuilder to test_shape, buffer view indexing to
test_buffer_views, torch dtypes to test_torch_dtypes, raw dispatch to
test_raw_dispatch, DiffPair to test_torch_marshall_gaps, and benchmarks
to a new standalone test_torch_benchmarks.py.

Made-with: Cursor
…slang#782)

Replace direct DescriptorMarshall construction with indirect triggers:
reduce_type error via func.map(tex=(0,)), init error by passing a
DescriptorHandle to a function whose module lacks slangpy import.

Made-with: Cursor
Replace direct TensorMarshall construction with indirect triggers:
- Grad dtype mismatch errors via Tensor.with_grads() + function call
- Writable check via read-only tensor with grad_in + function call
- Properties test via pack() instead of direct construction
- Rename create_tensor_marshall test for consistency

Made-with: Cursor
…ackward pass (shader-slang#782)

- Use diff_pair() wrapper in 6 tests that used NativeTorchTensorDiffPair
  with is_input=True (the default)
- Rewrite is_input=False test as actual backward pass test that triggers
  output diff pairs naturally through autograd
- Rewrite grad-only factory test to use diff_pair(None, grad)
- Add comment to test_slangtype_repr_no_reflection explaining why
  NativeSlangType is constructed directly

Made-with: Cursor
…hader-slang#782)

Use pack(module, diff_pair(primal, grad)) which internally constructs
a BindContext and calls build_shader_object, instead of constructing
these native objects directly.

Made-with: Cursor
The NotImplementedError in TorchTensorMarshall.build_shader_object is an
intentional stub — the C++ autograd path handles gradients directly and
bypasses build_shader_object entirely. No value in covering a single
raise line for a path that was never needed.

Made-with: Cursor
shader-slang#782)

Line numbers in the target source files will drift over time, making
these references misleading rather than helpful.

Made-with: Cursor
…PackedArg properties (shader-slang#782)

- point_to shape/element_stride mismatch errors
- Indexing: illegal argument type, too many indices
- copy_from_numpy non-contiguous source rejection
- clear() with explicit command encoder
- is_contiguous singleton dimension skip
- maybe_pad_data float3 padding (skips: stride=12 on current hw)
- PackedArg .python/.shader_object/.python_object property access

Made-with: Cursor
…der-slang#782)

Move tests from test_torch_marshall_gaps.py into test_torchintegration.py
(factory paths, properties, error guards, nanobind bindings) and
test_torch_autograd_workflows.py (backward pass DiffPair output).
Broaden parametrization from CUDA-only to DEVICE_TYPES and switch
dispatch tests to get_torch_device() for Vulkan interop support.

Made-with: Cursor
@jhelferty-nv jhelferty-nv requested a review from a team as a code owner April 9, 2026 20:37
@jhelferty-nv jhelferty-nv requested review from bmillsNV and removed request for a team April 9, 2026 20:37
@coderabbitai

coderabbitai Bot commented Apr 9, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds extensive pytest coverage (many new CUDA/PyTorch tests) across torch integration, buffer/tensor marshalling, NDBuffer/BufferView, image loading, module/function reflection, and error paths. Introduces C++ changes to track an interop CUDA stream, allocate/keepalive interop device buffers, and wait for GPU submits; ensures image-loaded arrays are C-contiguous.

Changes

Cohort / File(s) Summary
Torch & Torch-integration tests
slangpy/tests/slangpy_tests/test_torch_autograd_workflows.py, slangpy/tests/slangpy_tests/test_torch_benchmarks.py, slangpy/tests/slangpy_tests/test_torch_dtypes.py, slangpy/tests/slangpy_tests/test_torch_mixed.py, slangpy/tests/slangpy_tests/test_torchintegration.py, slangpy/tests/slangpy_tests/test_value_torch_pipeline.py
Added many CUDA/PyTorch tests covering autograd/backward, benchmarks, dtype mapping, DiffPair/TorchTensorMarshall behavior, marshalling/pack paths, reprs and error cases; files gated for macOS/PyTorch/CUDA where appropriate.
Bridge fallback & Tensor marshall gaps
slangpy/tests/slangpy_tests/test_bridge_fallback_gaps.py, slangpy/tests/slangpy_tests/test_tensor_marshall_gaps.py
New tests targeting bridge_fallback APIs and tensor marshall gaps: extract_tensor_info/get_signature, _CudaBufferView interface, copy_to/from_buffer error and size checks, create_empty/create_zeros_like, stream helper, and negative cases (many xfail markers).
TensorView / DiffTensorView
slangpy/tests/slangpy_tests/test_tensorview.py, slangpy/tests/slangpy_tests/test_difftensorview.py
CUDA-only tests exercising TensorView/DiffTensorView copy/resolve flows using slangpy.Tensor inputs/outputs and numeric round-trips.
Buffer views / Buffers / NDBuffer
slangpy/tests/slangpy_tests/test_buffer_views.py, slangpy/tests/slangpy_tests/test_buffers.py, slangpy/tests/slangpy_tests/test_ndbuffer.py
Expanded BufferView indexing/contiguity/error tests, byte-address read/write shader tests, NDBuffer construction/cloning/views/broadcasts, copy/clear/contiguity edge cases, and end-to-end shader executions.
Descriptor / Marshalling / Value types
slangpy/tests/slangpy_tests/test_descriptor_marshall.py, slangpy/tests/slangpy_tests/test_diffpair_type.py, slangpy/tests/slangpy_tests/test_return_types.py
DescriptorHandle marshalling tests (bindless gating), DiffPair type/factory coverage, inout ValueRef tests for scalar/vector/matrix, marshaller properties, packing and error cases.
Module / Function / Repr / Reflection
slangpy/tests/slangpy_tests/test_modules.py, slangpy/tests/slangpy_tests/test_tostring.py
New tests for Module/FunctionNode APIs, find/require behaviors, reprs for functions/packed args, SignatureBuilder.bytes checks, and reflection edge cases.
Image & Texture tests
slangpy/tests/slangpy_tests/test_load_image.py, slangpy/tests/slangpy_tests/test_textures.py
GPU image-loading tests across pixel formats and options (flip_y, scale/offset, grayscale, linearize) and get_texture_shape tests for texture kinds and arrays.
Tensor & Shape behavior, raw dispatch
slangpy/tests/slangpy_tests/test_tensor.py, slangpy/tests/slangpy_tests/test_shape.py, slangpy/tests/slangpy_tests/test_raw_dispatch.py
Added Tensor API tests (str, detach, deprecation warnings, validation), Shape equality/SignatureBuilder tests, and raw dispatch validation and reuse tests (including a Torch dispatch case).
Small runtime changes (Python & C/C++)
slangpy/types/common.py, src/sgl/utils/slangpy.h, src/slangpy_ext/utils/slangpy.cpp, src/slangpy_ext/utils/slangpytorchtensor.cpp
Python: ensure returned image buffer arrays are C-contiguous. C++: CallContext now tracks an interop CUDA stream; NativeCallData::exec prefers that stream for temporary submits and waits for submit completion when readback is needed; torch marshalling allocates zeroed interop device buffers when needed, wires TensorView data/strides to interop buffers, marks the current PyTorch CUDA stream on first interop use, and retains interop buffers in read_back to extend lifetime across async GPU work.

Sequence Diagram(s)

sequenceDiagram
    participant PyTest as Python Test
    participant TorchBridge as Torch Bridge
    participant CallCtx as CallContext
    participant NativeCall as NativeCallData
    participant Device as GPU Device

    PyTest->>TorchBridge: call function with torch.Tensor (may be CUDA)
    TorchBridge->>CallCtx: mark_interop_cuda_stream(current PyTorch CUDA stream)
    TorchBridge->>NativeCall: prepare marshall, allocate interop buffer if needed
    NativeCall->>Device: submit_command_buffer(using opts.cuda_stream or CallCtx.interop_cuda_stream)
    Device-->>NativeCall: submit_id (non-zero)
    NativeCall->>Device: wait_for_submit(submit_id) [if readback required]
    Device-->>PyTest: results returned (tensor/ndbuffer)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • bmillsNV
  • ccummingsNV
  • tdavidovicNV

Poem

🐰
I hopped through buffers, tensors, and streams,
Wrote tests that chase compiler dreams,
I packed a value and marked a stream,
Kept CUDA buffers safe ‘til they gleam—
Hooray, the tests all sparkle clean! 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Improve test coverage for torch integration' accurately summarizes the main objective of the changeset, which is to expand test coverage for torch-related functionality.
Linked Issues check ✅ Passed The changeset comprehensively addresses both linked issues: #782 (Python-side torch coverage) and #783 (C++-side torch coverage) through extensive test additions and bug fixes targeting torch integration paths.
Out of Scope Changes check ✅ Passed All changes are aligned with the stated objectives. Changes include test additions, one targeted bug fix (flip_y contiguity), and C++ instrumentation for torch integration coverage—all within scope of issues #782 and #783.

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


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.

@jhelferty-nv jhelferty-nv changed the title Improve test coverage for torch integration (#782, #783) Improve test coverage for torch integration Apr 9, 2026
Made-with: Cursor

# Conflicts:
#	slangpy/tests/slangpy_tests/test_torch_autograd_workflows.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (11)
slangpy/tests/slangpy_tests/test_return_types.py (1)

211-214: Consider asserting the full matrix result, not only two elements.

A full-array comparison would better catch accidental writes in padding/unpadding paths.

Proposed assertion refinement
     modify_mat(v)
     result = v.value.to_numpy()
-    assert abs(result[0][0] - 11.0) < 1e-5
-    assert abs(result[1][1] - 5.0) < 1e-5
+    expected = orig.copy()
+    expected[0][0] += 10.0
+    np.testing.assert_allclose(result, expected, rtol=0.0, atol=1e-5)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slangpy/tests/slangpy_tests/test_return_types.py` around lines 211 - 214, The
test currently checks only two elements of the matrix returned from
v.value.to_numpy(), which can miss padding/unpadding bugs; update the assertions
to compare the entire numpy array returned by to_numpy() against the expected
full matrix (e.g., build the expected 2x2 matrix and use a full-array comparison
such as numpy.testing.assert_allclose or an elementwise allclose assertion) so
the test fails on any unexpected differences rather than only at [0][0] and
[1][1].
slangpy/tests/slangpy_tests/test_tensorview.py (1)

279-293: Consolidate duplicated slangpy.Tensor copy test.

test_tensorview_copy_tensor overlaps with existing test_tensorview_copy_slangpy_tensor (same setup/path/assertion intent). Keeping both adds maintenance and CI time without new behavioral coverage; consider merging into one canonical test.

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

In `@slangpy/tests/slangpy_tests/test_tensorview.py` around lines 279 - 293, The
two tests test_tensorview_copy_tensor and test_tensorview_copy_slangpy_tensor
duplicate setup and assertions for module.copy_tensorview (using
Tensor.from_numpy, Tensor.zeros and output_tensor.to_numpy), so remove one and
keep a single canonical test to cover the behavior across DEVICE_TYPES;
specifically, delete test_tensorview_copy_tensor and ensure
test_tensorview_copy_slangpy_tensor parametrizes DEVICE_TYPES, constructs input
via Tensor.from_numpy, output via Tensor.zeros, calls
module.copy_tensorview(input, output) and asserts np.allclose(result, data,
atol=1e-5) so no coverage is lost.
slangpy/tests/slangpy_tests/test_shape.py (1)

161-161: Consider adding type annotations on the new test method signatures.

Both new methods currently leave arguments unannotated. If you’re enforcing the repository-wide rule consistently, annotate self and return type.

♻️ Suggested update
-    def test_equality_incompatible_type(self):
+    def test_equality_incompatible_type(self: "TestShapeOperations") -> None:
         """Test equality returns False for non-Shape/non-list types."""
         s = Shape([1, 2, 3])
         assert s != "not a shape"
         assert s != 42
         assert s != 3.14
...
-    def test_bytes_property(self):
+    def test_bytes_property(self: "TestSignatureBuilder") -> None:
         """SignatureBuilder.bytes returns raw bytes matching str encoding."""
         sb = SignatureBuilder()

As per coding guidelines **/*.py: Use type annotations for all Python function arguments.

Also applies to: 463-463

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

In `@slangpy/tests/slangpy_tests/test_shape.py` at line 161, The test method
test_equality_incompatible_type (and the other new test at the referenced
location) lack type annotations; update their signatures to annotate self as
"self: TestCase" (or appropriate test class type) and add a return annotation of
"-> None" so both methods read e.g. def test_equality_incompatible_type(self:
TestCase) -> None:, ensuring consistency with the repository's function argument
annotations.
slangpy/tests/slangpy_tests/test_textures.py (1)

620-620: Add explicit type annotations to the new helper/test function signatures.

These new Python function arguments are currently untyped, which breaks the repository typing rule.

♻️ Proposed fix
-def _create_simple_texture(device, tex_type, width=16, height=16, depth=16, array_length=1):
+def _create_simple_texture(
+    device: object,
+    tex_type: TextureType,
+    width: int = 16,
+    height: int = 16,
+    depth: int = 16,
+    array_length: int = 1,
+) -> Texture:
@@
-def test_get_texture_shape(device_type, tex_type, expected_shape):
+def test_get_texture_shape(
+    device_type: DeviceType, tex_type: TextureType, expected_shape: tuple[int, ...]
+) -> None:
@@
-def test_get_texture_shape_array_types(device_type):
+def test_get_texture_shape_array_types(device_type: DeviceType) -> None:

As per coding guidelines: **/*.py: Use type annotations for all Python function arguments.

Also applies to: 652-652, 661-661

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

In `@slangpy/tests/slangpy_tests/test_textures.py` at line 620, Add explicit type
annotations to the new test helper signatures: annotate _create_simple_texture
parameters as device: Any, tex_type: Any, width: int = 16, height: int = 16,
depth: int = 16, array_length: int = 1 and give the function an explicit return
type (e.g., Any or the concrete texture type used in tests). Import Any from
typing if needed. Apply the same pattern to the other new helper/test functions
in the same file that lack annotations so all function arguments and return
types are explicitly typed.
slangpy/tests/slangpy_tests/test_bridge_fallback_gaps.py (1)

98-103: Add non-contiguous/storage-offset round-trip cases for copy helpers.

Current success-path tests cover contiguous tensors only. Adding transposed/sliced tensors would better protect documented fallback behavior for non-contiguous layouts.

♻️ Suggested test additions
 class TestCopyToBuffer:
@@
     `@pytest.mark.skipif`(not torch.cuda.is_available(), reason="CUDA not available")
     def test_copy_round_trip(self):
         src = torch.tensor([1.0, 2.0, 3.0], device="cuda", dtype=torch.float32)
         dest = torch.zeros(3, device="cuda", dtype=torch.float32)
         bf.copy_to_buffer(src, dest.data_ptr(), dest.numel() * dest.element_size())
         assert torch.allclose(src, dest)
+
+    `@pytest.mark.skipif`(not torch.cuda.is_available(), reason="CUDA not available")
+    def test_copy_round_trip_non_contiguous(self):
+        base = torch.arange(12, device="cuda", dtype=torch.float32).reshape(3, 4)
+        src = base.t()  # non-contiguous view
+        dest = torch.empty_like(src.contiguous())
+        bf.copy_to_buffer(src, dest.data_ptr(), dest.numel() * dest.element_size())
+        assert torch.allclose(src, dest)
@@
 class TestCopyFromBuffer:
@@
     `@pytest.mark.skipif`(not torch.cuda.is_available(), reason="CUDA not available")
     def test_copy_round_trip(self):
         src = torch.tensor([4.0, 5.0, 6.0], device="cuda", dtype=torch.float32)
         dest = torch.zeros(3, device="cuda", dtype=torch.float32)
         bf.copy_from_buffer(dest, src.data_ptr(), src.numel() * src.element_size())
         assert torch.allclose(src, dest)
+
+    `@pytest.mark.skipif`(not torch.cuda.is_available(), reason="CUDA not available")
+    def test_copy_round_trip_storage_offset_view(self):
+        src = torch.arange(8, device="cuda", dtype=torch.float32)[2:]  # storage_offset > 0
+        dest_base = torch.zeros(10, device="cuda", dtype=torch.float32)
+        dest = dest_base[1:7]  # non-zero storage offset
+        bf.copy_from_buffer(dest, src.data_ptr(), src.numel() * src.element_size())
+        assert torch.allclose(src, dest)

Based on learnings: "Test files must run torch bridge tests in both native and fallback modes via fixture, covering CPU/CUDA tensors, various dtypes, non-contiguous layouts, transposed tensors, and storage offsets".

Also applies to: 119-123

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

In `@slangpy/tests/slangpy_tests/test_bridge_fallback_gaps.py` around lines 98 -
103, The test_copy_round_trip only exercises contiguous tensors; add cases
exercising non-contiguous layouts and storage-offsets so bf.copy_to_buffer and
the corresponding copy-back logic are validated for transposed/sliced views and
tensors with non-zero storage_offset. Update test_copy_round_trip (and the
similar case at lines 119-123) to parametrize or add subtests that create src as
transposed (e.g., src.t()), sliced strided views, and tensors made via offsets
(e.g., a slice of a larger tensor) and then perform bf.copy_to_buffer(src,
dest.data_ptr(), src.numel() * src.element_size()) and assert
torch.allclose(src, dest.view_as(src) or dest_contiguous_view) for each layout;
ensure the existing test fixture that runs bridge tests in both native and
fallback modes covers these new cases so both paths are exercised.
slangpy/tests/slangpy_tests/test_tensor_marshall_gaps.py (1)

169-173: Make this xfail strict so fixed bugs don’t get silently ignored.

Using strict=True turns unexpected passes into a visible signal to remove/update the marker.

Suggested tweak
 `@pytest.mark.xfail`(
     reason="TensorMarshall.build_shader_object derivative path references 'primal' field "
     "that doesn't exist on the shader object type; likely needs DiffTensor infrastructure",
     raises=RuntimeError,
+    strict=True,
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slangpy/tests/slangpy_tests/test_tensor_marshall_gaps.py` around lines 169 -
173, The xfail marker on the test in test_tensor_marshall_gaps.py should be made
strict so unexpected passes fail the test suite: update the pytest.mark.xfail
decorator invocation used on that test to include strict=True (i.e.,
pytest.mark.xfail(..., strict=True)) while keeping the existing reason and
raises arguments intact so the test will surface when the underlying bug is
fixed.
slangpy/tests/slangpy_tests/test_load_image.py (1)

97-103: Rename unused variable to indicate intentional discard.

The expected variable is unpacked but never used since this test only validates shape properties for 2-channel images.

Suggested fix
 def test_load_two_channel(device, rg_image):
     """Load a 2-channel image and verify it produces float2 tensor."""
-    path, expected = rg_image
+    path, _ = rg_image
     tensor = spy.Tensor.load_from_image(device, path)
     result = tensor.to_numpy()
     assert result.ndim == 3
     assert result.shape[2] == 2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slangpy/tests/slangpy_tests/test_load_image.py` around lines 97 - 103, The
test_load_two_channel function unpacks rg_image into path and expected but never
uses expected; rename the unused variable (expected) to _expected or simply _ to
indicate intentional discard. Update the tuple unpacking in
test_load_two_channel (where it currently reads "path, expected") to use "path,
_expected" or "path, _" so the test intent is clear and linter warnings about
unused variables are avoided.
slangpy/tests/slangpy_tests/test_value_torch_pipeline.py (2)

207-248: Use raw strings for regex patterns.

Same linter warning as in other test files.

Suggested fix
 `@pytest.mark.parametrize`("device_type", CUDA_TYPES)
 def test_scalar_reduce_type_error(device_type: DeviceType):
     """ScalarMarshall.reduce_type raises for dimensions > 0."""
     from slangpy.bindings.typeregistry import PYTHON_TYPES

     device = helpers.get_device(device_type)
     func = helpers.create_function_from_module(device, "add_scalar", ADD_SCALAR_SHADER)
     layout = func.module.layout

     marshall = PYTHON_TYPES[float](layout, 1.0)
-    with pytest.raises(ValueError, match="[Cc]annot reduce"):
+    with pytest.raises(ValueError, match=r"[Cc]annot reduce"):
         marshall.reduce_type(None, 1)
     with pytest.raises(ValueError, match=r"[Cc]annot reduce"):
         marshall.reduce_type(None, 2)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slangpy/tests/slangpy_tests/test_value_torch_pipeline.py` around lines 207 -
248, The pytest.raises calls in test_scalar_reduce_type_error and
test_vector_reduce_type use normal string regex literals for the match argument;
change those match arguments to raw string literals (prefix with r) so linter
regex warnings go away—i.e., update the match="[Cc]annot reduce" occurrences
passed to pytest.raises in these tests (where marshall.reduce_type is invoked)
to match=r"[Cc]annot reduce".

158-178: Add assertion or note the test purpose is exercising code path only.

In test_pack_float2_vector, the result variable is assigned but never used. If the test's purpose is solely to exercise VectorMarshall.build_shader_object without validating output, consider either:

  1. Adding a minimal assertion (e.g., assert result is None or result is not None)
  2. Prefixing with _ to indicate intentional discard

In test_pack_scalar_value, the test correctly asserts packed_scalar is not None.

Suggested fix for consistency
 `@pytest.mark.parametrize`("device_type", CUDA_TYPES)
 def test_pack_float2_vector(device_type: DeviceType):
     """pack() a float2 vector value, exercising VectorMarshall.build_shader_object."""
     device = helpers.get_device(device_type)
     func = helpers.create_function_from_module(device, "scale_vec", SCALE_VECTOR_SHADER)

     v = slangpy.math.float2(3.0, 4.0)
     packed = pack(func.module, v)
     out_buf = slangpy.types.NDBuffer.zeros(device, shape=(1,), dtype=float)
-    result = func(packed, 2.0, out_buf)
+    func(packed, 2.0, out_buf)  # Exercise VectorMarshall.build_shader_object
+    # Output validation not needed; test exercises the code path
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slangpy/tests/slangpy_tests/test_value_torch_pipeline.py` around lines 158 -
178, The test test_pack_float2_vector assigns result = func(packed, 2.0,
out_buf) but never uses it; either assert a trivial condition on result or mark
it as intentionally discarded to avoid unused-variable warnings and clarify
intent. Update test_pack_float2_vector to either add a minimal assertion (e.g.,
assert result is not None or assert result is None depending on expected
behavior) or rename the variable to _result to indicate the call is only
exercising VectorMarshall.build_shader_object via pack(func.module, v) and func
invocation.
slangpy/tests/slangpy_tests/test_torchintegration.py (2)

914-921: Use raw string for regex pattern.

Same issue as above - the character classes work but should use a raw string prefix.

Suggested fix
 def test_torch_cpu_tensor_rejected(device_type: DeviceType):
     """Non-CUDA torch tensors are rejected by write_shader_cursor_pre_dispatch."""
     device = helpers.get_torch_device(device_type)
     func = helpers.create_function_from_module(device, "identity", IDENTITY_SRC)

     cpu_tensor = torch.tensor([1.0, 2.0], dtype=torch.float32)
-    with pytest.raises(Exception, match="[Cc][Uu][Dd][Aa]"):
+    with pytest.raises(Exception, match=r"[Cc][Uu][Dd][Aa]"):
         func(cpu_tensor)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slangpy/tests/slangpy_tests/test_torchintegration.py` around lines 914 - 921,
The pytest.raises call in test_torch_cpu_tensor_rejected uses a non-raw regex
string for match="[Cc][Uu][Dd][Aa]"; change it to a raw string
(match=r"[Cc][Uu][Dd][Aa]") so backslashes and escapes in the regex are
interpreted correctly by Python; update the match argument in
test_torch_cpu_tensor_rejected accordingly.

874-891: Use raw strings for regex patterns with character classes.

The match= patterns contain character classes [Uu] that work correctly but trigger linter warnings. Using raw strings makes the intent clearer and is a Python best practice for regex patterns.

Suggested fix
 `@pytest.mark.parametrize`("device_type", DEVICE_TYPES)
 def test_factory_unsupported_torch_dtype_raises(device_type: DeviceType):
     """Passing a tensor with unsupported dtype raises ValueError."""
     layout = _get_layout(device_type)
     t = torch.tensor([1.0 + 2.0j], dtype=torch.complex64, device="cuda")
-    with pytest.raises(ValueError, match="[Uu]nsupported"):
+    with pytest.raises(ValueError, match=r"[Uu]nsupported"):
         ttm.create_torch_tensor_marshall(layout, t)


 `@pytest.mark.parametrize`("device_type", DEVICE_TYPES)
 def test_diffpair_factory_unsupported_dtype_raises(device_type: DeviceType):
     """DiffPair factory raises for unsupported torch dtype."""
     layout = _get_layout(device_type)
     primal = torch.tensor([1.0 + 2.0j], dtype=torch.complex64, device="cuda")
     grad = torch.tensor([0.0 + 0.0j], dtype=torch.complex64, device="cuda")
     pair = diff_pair(primal, grad)
-    with pytest.raises(ValueError, match="[Uu]nsupported"):
+    with pytest.raises(ValueError, match=r"[Uu]nsupported"):
         ttm.create_torch_tensor_marshall(layout, pair)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slangpy/tests/slangpy_tests/test_torchintegration.py` around lines 874 - 891,
The pytest assertions in test_factory_unsupported_torch_dtype_raises and
test_diffpair_factory_unsupported_dtype_raises use match="[Uu]nsupported" which
triggers linter warnings; update both calls to use raw string literals for the
regex (e.g. change match="[Uu]nsupported" to match=r"[Uu]nsupported") so the
regex is a raw string while keeping the same pattern and behavior in
ttm.create_torch_tensor_marshall tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@slangpy/tests/slangpy_tests/test_buffer_views.py`:
- Around line 329-343: The test test_copy_from_torch_cpu_fallback is only
skipping on macOS but still calls helpers.get_device(DeviceType.cuda), which
fails when CUDA is not available on non-macOS runners; change the test to detect
CUDA availability after importing torch and skip if CUDA isn’t available (e.g.,
call torch.cuda.is_available() and pytest.skip when False) or catch the error
from helpers.get_device(DeviceType.cuda) and pytest.skip, so the test only
proceeds when a CUDA device can actually be acquired.

In `@slangpy/tests/slangpy_tests/test_raw_dispatch.py`:
- Around line 218-220: The nested callback function writer has untyped
parameters; add type annotations for its arguments (cursor and k_value) in the
writer definition (e.g., def writer(cursor: CursorType, k_value: int):),
importing or aliasing the appropriate CursorType from the test utilities or
falling back to typing.Any if a concrete type isn't exported; ensure the
annotation follows the repo's typing style and update imports if needed.

In `@slangpy/tests/slangpy_tests/test_torch_dtypes.py`:
- Around line 18-67: The tests lack Python type annotations on fixture and test
function arguments; update the fixture and tests to add types: annotate the
fixture function device() with a return type (e.g., -> spy.DeviceType or the
correct device type used by get_device), and annotate each test argument — e.g.,
test_torch_output_int32(device: spy.DeviceType), test_torch_output_int64(device:
spy.DeviceType), test_torch_output_float64(device: spy.DeviceType), and
test_torch_output_rare_scalar(device: spy.DeviceType, slang_func: str,
torch_dtype: torch.dtype) — so all function arguments (device, slang_func,
torch_dtype) have explicit type annotations matching the repo typing
conventions.

---

Nitpick comments:
In `@slangpy/tests/slangpy_tests/test_bridge_fallback_gaps.py`:
- Around line 98-103: The test_copy_round_trip only exercises contiguous
tensors; add cases exercising non-contiguous layouts and storage-offsets so
bf.copy_to_buffer and the corresponding copy-back logic are validated for
transposed/sliced views and tensors with non-zero storage_offset. Update
test_copy_round_trip (and the similar case at lines 119-123) to parametrize or
add subtests that create src as transposed (e.g., src.t()), sliced strided
views, and tensors made via offsets (e.g., a slice of a larger tensor) and then
perform bf.copy_to_buffer(src, dest.data_ptr(), src.numel() *
src.element_size()) and assert torch.allclose(src, dest.view_as(src) or
dest_contiguous_view) for each layout; ensure the existing test fixture that
runs bridge tests in both native and fallback modes covers these new cases so
both paths are exercised.

In `@slangpy/tests/slangpy_tests/test_load_image.py`:
- Around line 97-103: The test_load_two_channel function unpacks rg_image into
path and expected but never uses expected; rename the unused variable (expected)
to _expected or simply _ to indicate intentional discard. Update the tuple
unpacking in test_load_two_channel (where it currently reads "path, expected")
to use "path, _expected" or "path, _" so the test intent is clear and linter
warnings about unused variables are avoided.

In `@slangpy/tests/slangpy_tests/test_return_types.py`:
- Around line 211-214: The test currently checks only two elements of the matrix
returned from v.value.to_numpy(), which can miss padding/unpadding bugs; update
the assertions to compare the entire numpy array returned by to_numpy() against
the expected full matrix (e.g., build the expected 2x2 matrix and use a
full-array comparison such as numpy.testing.assert_allclose or an elementwise
allclose assertion) so the test fails on any unexpected differences rather than
only at [0][0] and [1][1].

In `@slangpy/tests/slangpy_tests/test_shape.py`:
- Line 161: The test method test_equality_incompatible_type (and the other new
test at the referenced location) lack type annotations; update their signatures
to annotate self as "self: TestCase" (or appropriate test class type) and add a
return annotation of "-> None" so both methods read e.g. def
test_equality_incompatible_type(self: TestCase) -> None:, ensuring consistency
with the repository's function argument annotations.

In `@slangpy/tests/slangpy_tests/test_tensor_marshall_gaps.py`:
- Around line 169-173: The xfail marker on the test in
test_tensor_marshall_gaps.py should be made strict so unexpected passes fail the
test suite: update the pytest.mark.xfail decorator invocation used on that test
to include strict=True (i.e., pytest.mark.xfail(..., strict=True)) while keeping
the existing reason and raises arguments intact so the test will surface when
the underlying bug is fixed.

In `@slangpy/tests/slangpy_tests/test_tensorview.py`:
- Around line 279-293: The two tests test_tensorview_copy_tensor and
test_tensorview_copy_slangpy_tensor duplicate setup and assertions for
module.copy_tensorview (using Tensor.from_numpy, Tensor.zeros and
output_tensor.to_numpy), so remove one and keep a single canonical test to cover
the behavior across DEVICE_TYPES; specifically, delete
test_tensorview_copy_tensor and ensure test_tensorview_copy_slangpy_tensor
parametrizes DEVICE_TYPES, constructs input via Tensor.from_numpy, output via
Tensor.zeros, calls module.copy_tensorview(input, output) and asserts
np.allclose(result, data, atol=1e-5) so no coverage is lost.

In `@slangpy/tests/slangpy_tests/test_textures.py`:
- Line 620: Add explicit type annotations to the new test helper signatures:
annotate _create_simple_texture parameters as device: Any, tex_type: Any, width:
int = 16, height: int = 16, depth: int = 16, array_length: int = 1 and give the
function an explicit return type (e.g., Any or the concrete texture type used in
tests). Import Any from typing if needed. Apply the same pattern to the other
new helper/test functions in the same file that lack annotations so all function
arguments and return types are explicitly typed.

In `@slangpy/tests/slangpy_tests/test_torchintegration.py`:
- Around line 914-921: The pytest.raises call in test_torch_cpu_tensor_rejected
uses a non-raw regex string for match="[Cc][Uu][Dd][Aa]"; change it to a raw
string (match=r"[Cc][Uu][Dd][Aa]") so backslashes and escapes in the regex are
interpreted correctly by Python; update the match argument in
test_torch_cpu_tensor_rejected accordingly.
- Around line 874-891: The pytest assertions in
test_factory_unsupported_torch_dtype_raises and
test_diffpair_factory_unsupported_dtype_raises use match="[Uu]nsupported" which
triggers linter warnings; update both calls to use raw string literals for the
regex (e.g. change match="[Uu]nsupported" to match=r"[Uu]nsupported") so the
regex is a raw string while keeping the same pattern and behavior in
ttm.create_torch_tensor_marshall tests.

In `@slangpy/tests/slangpy_tests/test_value_torch_pipeline.py`:
- Around line 207-248: The pytest.raises calls in test_scalar_reduce_type_error
and test_vector_reduce_type use normal string regex literals for the match
argument; change those match arguments to raw string literals (prefix with r) so
linter regex warnings go away—i.e., update the match="[Cc]annot reduce"
occurrences passed to pytest.raises in these tests (where marshall.reduce_type
is invoked) to match=r"[Cc]annot reduce".
- Around line 158-178: The test test_pack_float2_vector assigns result =
func(packed, 2.0, out_buf) but never uses it; either assert a trivial condition
on result or mark it as intentionally discarded to avoid unused-variable
warnings and clarify intent. Update test_pack_float2_vector to either add a
minimal assertion (e.g., assert result is not None or assert result is None
depending on expected behavior) or rename the variable to _result to indicate
the call is only exercising VectorMarshall.build_shader_object via
pack(func.module, v) and func invocation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 764d58db-5f86-456f-9ff8-1a7f153ab973

📥 Commits

Reviewing files that changed from the base of the PR and between 514bdf7 and 8fba9b5.

📒 Files selected for processing (26)
  • slangpy/builtin/diffpair.py
  • slangpy/tests/slangpy_tests/test_bridge_fallback_gaps.py
  • slangpy/tests/slangpy_tests/test_buffer_views.py
  • slangpy/tests/slangpy_tests/test_buffers.py
  • slangpy/tests/slangpy_tests/test_descriptor_marshall.py
  • slangpy/tests/slangpy_tests/test_differential_function_call.py
  • slangpy/tests/slangpy_tests/test_diffpair_type.py
  • slangpy/tests/slangpy_tests/test_difftensorview.py
  • slangpy/tests/slangpy_tests/test_load_image.py
  • slangpy/tests/slangpy_tests/test_modules.py
  • slangpy/tests/slangpy_tests/test_ndbuffer.py
  • slangpy/tests/slangpy_tests/test_raw_dispatch.py
  • slangpy/tests/slangpy_tests/test_return_types.py
  • slangpy/tests/slangpy_tests/test_shape.py
  • slangpy/tests/slangpy_tests/test_tensor.py
  • slangpy/tests/slangpy_tests/test_tensor_marshall_gaps.py
  • slangpy/tests/slangpy_tests/test_tensorview.py
  • slangpy/tests/slangpy_tests/test_textures.py
  • slangpy/tests/slangpy_tests/test_torch_autograd_workflows.py
  • slangpy/tests/slangpy_tests/test_torch_benchmarks.py
  • slangpy/tests/slangpy_tests/test_torch_dtypes.py
  • slangpy/tests/slangpy_tests/test_torch_mixed.py
  • slangpy/tests/slangpy_tests/test_torchintegration.py
  • slangpy/tests/slangpy_tests/test_tostring.py
  • slangpy/tests/slangpy_tests/test_value_torch_pipeline.py
  • slangpy/types/common.py
💤 Files with no reviewable changes (1)
  • slangpy/tests/slangpy_tests/test_differential_function_call.py

Comment thread slangpy/tests/slangpy_tests/test_buffer_views.py
Comment thread slangpy/tests/slangpy_tests/test_raw_dispatch.py Outdated
Comment thread slangpy/tests/slangpy_tests/test_torch_dtypes.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (3)
slangpy/tests/slangpy_tests/test_difftensorview.py (1)

51-65: Consolidate duplicate copy coverage and avoid cache-coupled device selection

This test overlaps heavily with test_difftensorview_copy_slangpy_tensor (Lines 136-147). Also, Line 55 reacquires a device instead of using module.device, which adds unnecessary cache-coupling risk.

♻️ Suggested minimal cleanup in this segment
 `@pytest.mark.parametrize`("device_type", DEVICE_TYPES)
 def test_difftensorview_copy_tensor(device_type: DeviceType):
     """Test copy_difftensorview with slangpy.Tensor — exercises tensorcommon resolve_types for DiffTensorView."""
     module = load_module(device_type)
-    device = helpers.get_device(type=device_type)
 
     data = np.array([1.0, 2.0, 3.0, 4.0, 5.0], dtype=np.float32)
-    input_tensor = Tensor.from_numpy(device, data)
-    output_tensor = Tensor.zeros(device, (5,), "float")
+    input_tensor = Tensor.from_numpy(module.device, data)
+    output_tensor = Tensor.zeros(module.device, (5,), "float")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slangpy/tests/slangpy_tests/test_difftensorview.py` around lines 51 - 65, The
test test_difftensorview_copy_tensor duplicates coverage from
test_difftensorview_copy_slangpy_tensor and unnecessarily reacquires a device
via helpers.get_device(device_type); update the test to reuse the device
provided by load_module (use module.device) to avoid cache-coupled device
selection and remove or consolidate redundant assertions with
test_difftensorview_copy_slangpy_tensor; specifically, in
test_difftensorview_copy_tensor replace the helpers.get_device call with device
= module.device and keep the same tensor creation/copy/assert logic or delete
this test and ensure the remaining test covers both Tensor and slangpy.Tensor
copy paths across DEVICE_TYPES.
slangpy/tests/slangpy_tests/test_torchintegration.py (2)

981-983: Avoid broad Exception in guard-path tests.

Use a specific exception type (ValueError, TypeError, or SlangPyError) so these tests fail if the wrong error path triggers.

✅ Suggested diff (example)
-    with pytest.raises(Exception, match="shape"):
+    with pytest.raises((ValueError, TypeError), match="shape"):
         func(bad)

@@
-    with pytest.raises(Exception, match=r"[Cc][Uu][Dd][Aa]"):
+    with pytest.raises((ValueError, TypeError), match=r"[Cc][Uu][Dd][Aa]"):
         func(cpu_tensor)

As per coding guidelines: "Python-layer errors should raise standard Python exceptions (ValueError, TypeError, or SlangPyError)".

Also applies to: 992-993

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

In `@slangpy/tests/slangpy_tests/test_torchintegration.py` around lines 981 - 983,
Replace the broad pytest.raises(Exception, ...) assertions in the torch
integration tests with a specific exception type so incorrect error paths are
caught; update the two occurrences where the test calls pytest.raises(Exception,
match="shape") around the call to func(bad) (and the similar case at lines
992-993) to use the appropriate specific exception (ValueError, TypeError, or
SlangPyError) that the code is expected to raise for invalid tensor shapes.

794-810: Strengthen assertions in new DiffPair execution tests.

assert result is not None is too weak for regression detection. Assert expected value/shape (and gradient where relevant) to ensure these paths verify behavior, not just non-crash execution.

Based on learnings: "Applies to slangpy/tests/slangpy_tests/test_torchintegration.py : End-to-end torch integration tests must cover: scalars/vectors/arrays/generics, extra vectorization dims, return modes (return/pass/out), autograd forward+backward, cache reuse, and structured types".

Also applies to: 813-823

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

In `@slangpy/tests/slangpy_tests/test_torchintegration.py` around lines 794 - 810,
The test test_diffpair_read_signature uses a weak assertion; strengthen it by
asserting the actual tensor value and shape returned by func when passed
diff_pair(primal, grad) and also verify autograd behavior by calling backward on
a suitable scalar or sum of result and asserting that primal.grad (or the paired
grad) matches expected gradients; update assertions in the related test block
(the similar test around the next diff pair section) likewise to check
result.shape, numeric contents (e.g., squared values for "square"), and that
gradients equal the expected tensor (ones), referencing
test_diffpair_read_signature, diff_pair, func, primal, grad, and result to
locate and modify the assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@slangpy/tests/slangpy_tests/test_torchintegration.py`:
- Line 950: The tests in test_torchintegration.py use pytest.raises(...,
match="...") with regex patterns that should be raw strings; update the
pytest.raises calls (the one shown and the other occurrences around the
pytest.raises usages at the nearby assertions) to use raw string literals
(prefix the pattern with r, e.g., r"[Uu]nsupported") so regex metacharacters are
interpreted correctly and to satisfy Ruff RUF043; locate the pytest.raises calls
in the test_torchintegration.py file (the test function(s) invoking
pytest.raises) and change their match arguments to raw strings.

---

Nitpick comments:
In `@slangpy/tests/slangpy_tests/test_difftensorview.py`:
- Around line 51-65: The test test_difftensorview_copy_tensor duplicates
coverage from test_difftensorview_copy_slangpy_tensor and unnecessarily
reacquires a device via helpers.get_device(device_type); update the test to
reuse the device provided by load_module (use module.device) to avoid
cache-coupled device selection and remove or consolidate redundant assertions
with test_difftensorview_copy_slangpy_tensor; specifically, in
test_difftensorview_copy_tensor replace the helpers.get_device call with device
= module.device and keep the same tensor creation/copy/assert logic or delete
this test and ensure the remaining test covers both Tensor and slangpy.Tensor
copy paths across DEVICE_TYPES.

In `@slangpy/tests/slangpy_tests/test_torchintegration.py`:
- Around line 981-983: Replace the broad pytest.raises(Exception, ...)
assertions in the torch integration tests with a specific exception type so
incorrect error paths are caught; update the two occurrences where the test
calls pytest.raises(Exception, match="shape") around the call to func(bad) (and
the similar case at lines 992-993) to use the appropriate specific exception
(ValueError, TypeError, or SlangPyError) that the code is expected to raise for
invalid tensor shapes.
- Around line 794-810: The test test_diffpair_read_signature uses a weak
assertion; strengthen it by asserting the actual tensor value and shape returned
by func when passed diff_pair(primal, grad) and also verify autograd behavior by
calling backward on a suitable scalar or sum of result and asserting that
primal.grad (or the paired grad) matches expected gradients; update assertions
in the related test block (the similar test around the next diff pair section)
likewise to check result.shape, numeric contents (e.g., squared values for
"square"), and that gradients equal the expected tensor (ones), referencing
test_diffpair_read_signature, diff_pair, func, primal, grad, and result to
locate and modify the assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c48ddbab-8a3b-4046-a926-b5c5e60eab5b

📥 Commits

Reviewing files that changed from the base of the PR and between 8fba9b5 and 53bb8a2.

📒 Files selected for processing (3)
  • slangpy/tests/slangpy_tests/test_difftensorview.py
  • slangpy/tests/slangpy_tests/test_torch_autograd_workflows.py
  • slangpy/tests/slangpy_tests/test_torchintegration.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • slangpy/tests/slangpy_tests/test_torch_autograd_workflows.py

Comment thread slangpy/tests/slangpy_tests/test_torchintegration.py Outdated
@jhelferty-nv

Copy link
Copy Markdown
Contributor Author

Coverage details

All coverage data collected on a machine with an NVIDIA GeForce GTX 960 (Maxwell, SM 5.2). Some code paths are unreachable on this hardware — see "Not testable on current hardware" and "Follow-up tasks" below.

Overall summary (weighted by LOC)

Before After Delta
Python 78.7% (5,249 / 6,672) 82.9% (5,513 / 6,648) +4.2 pp
C++ 69.2% (4,821 / 6,967) 82.7% (5,359 / 6,479) +13.5 pp
Overall 73.8% (10,070 / 13,639) 82.8% (10,872 / 13,127) +9.0 pp
  • Uncovered lines: 3,569 → 2,255 (−1,314)
  • Total LOC decreased by 512 due to upstream refactoring between measurements

Python — implementation files (non-test, non-benchmark)

File Before After Δ Notes
__init__.py 74% (31) 74% (31)
bindings/__init__.py 100% (6) 100% (6)
bindings/boundvariable.py 85% (297) 85% (297)
bindings/boundvariableruntime.py 100% (27) 100% (27)
bindings/codegen.py 88% (138) 90% (138) +2
bindings/marshall.py 88% (52) 90% (52) +2
bindings/typeregistry.py 78% (18) 89% (18) +11
builtin/__init__.py 100% (14) 100% (14)
builtin/accelerationstructure.py 92% (26) 92% (26)
builtin/array.py 87% (122) 87% (122)
builtin/descriptor.py 42% (40) 58% (40) +16
builtin/diffpair.py 27% (101) 95% (107) +68 PR #917
builtin/ndbuffer.py 43% (191) 48% (191) +5 Being removed
builtin/numpy.py 91% (58) 91% (58)
builtin/range.py 91% (35) 91% (35)
builtin/resourceview.py 100% (15) 100% (15)
builtin/struct.py 92% (65) 92% (65)
builtin/structuredbuffer.py 81% (63) 83% (63) +2
builtin/tensor.py 78% (86) 90% (86) +12
builtin/tensorcommon.py 81% (187) 84% (187) +3
builtin/texture.py 84% (180) 84% (180)
builtin/value.py 74% (248) 86% (248) +12
builtin/valueref.py 88% (155) 88% (155)
core/__init__.py 100% (0) 100% (0)
core/calldata.py 84% (267) 85% (267) +1 Moving to C++ (#691)
core/callsignature.py 89% (107) 89% (107)
core/dispatchdata.py 86% (120) 92% (120) +6
core/enums.py 100% (9) 100% (9)
core/function.py 87% (342) 91% (305) +4
core/generator.py 90% (322) 90% (322)
core/instance.py 93% (68) 93% (68)
core/jupyter.py 0% (143) 0% (143) Untestable headless
core/logging.py 91% (134) 91% (134)
core/module.py 84% (122) 90% (127) +6
core/native.py 100% (1) 100% (1)
core/packedarg.py 90% (20) 90% (20)
core/shapes.py 57% (7) 57% (7) Dead code
core/struct.py 91% (94) 91% (94)
core/utils.py 44% (120) 44% (120) ~50 lines dead code
experimental/diffinstancelist.py 77% (26) 77% (26)
experimental/gridarg.py 95% (60) 95% (60)
reflection/lookup.py 82% (80) 85% (80) +3
reflection/reflectiontypes.py 83% (925) 84% (925) +1
reflection/typeresolution.py 98% (212) 98% (212)
reflection/vectorize.py 91% (205) 91% (205)
testing/crashpad.py 20% (122) 20% (122) Test infra
testing/helpers.py 64% (211) 81% (211) +17 Test infra
testing/plugin.py 85% (59) 85% (59) Test infra
torchintegration/__init__.py 100% (4) 100% (4)
torchintegration/autogradhook.py 100% (19) 100% (19)
torchintegration/bridge_fallback.py 78% (69) 99% (69) +21
torchintegration/detection.py 91% (34) 91% (34)
torchintegration/torchtensormarshall.py 86% (139) 95% (139) +9
types/__init__.py 100% (9) 100% (9)
types/buffer.py 80% (93) 80% (93) NDBuffer; being removed
types/callidarg.py 86% (37) 86% (37)
types/common.py 15% (34) 92% (36) +77
types/diffpair.py 100% (21) 100% (21)
types/helpers.py 52% (33) 52% (33) Dead code
types/randfloatarg.py 98% (45) 98% (45)
types/tensor.py 77% (110) 94% (110) +17
types/threadidarg.py 97% (29) 97% (29)
types/valueref.py 100% (12) 100% (12)
types/wanghasharg.py 98% (53) 98% (53)

C++ — slangpy_ext/utils (.cpp files, raw gcov)

File Before After Δ Notes
slangpy.cpp 88.2% (922/1045) 88.2% (922/1045)
slangpyfunction.cpp 89.2% (148/166) 89.2% (148/166)
slangpytorchtensor.cpp 73.0% (330/452) ~86.7% (392/452) +13.7 After accumulating new test data
slangpytensor.cpp 78.6% (272/346) 79.2% (274/346) +0.6
slangpybuffer.cpp 80.1% (157/196) 80.1% (157/196)
slangpystridedbufferview.cpp 89.9% (328/365) 89.9% (328/365)
slangpyresources.cpp 75.4% (144/191) 76.4% (146/191) +1.0
slangpypackedarg.cpp 65.2% (15/23) 65.2% (15/23)
slangpyvalue.cpp 100% (27/27) 100% (27/27)
torch_bridge.cpp 41.9% (57/136) 90.4% (123/136) +48.5 Before was measurement artifact
texture_loader.cpp 81.4% (48/59) 91.5% (54/59) +10.1
crashpad.cpp 100% (8/8) 100% (8/8)
renderdoc.cpp 100% (9/9) 100% (9/9)
tev.cpp 100% (30/30) 100% (30/30)

Header coverage (raw gcov, largest TU shown):

File Before After Δ Notes
slangpy.h 71.0% (213/300) 72.7% (218/300) +1.7
slangpyfunction.h 75.7% (53/70) 75.7% (53/70)
slangpybuffer.h 90.5% (19/21) 90.5% (19/21)
slangpytensor.h 86.1% (31/36) 86.1% (31/36)
slangpytorchtensor.h 56.0% (14/25) 56.0% (14/25)
slangpyresources.h 72.0% (18/25) 88.0% (22/25) +16.0
torch_bridge.h 79.7% (51/64) 3.7% (7/187) Line count grew; TU changed

C++ — slangpy_ext/device (.cpp files, raw gcov)

Nanobind wrappers around the sgl device layer. Many files show different line counts due to upstream changes.

File Before After Δ Notes
command.cpp 25.7% (115/448) 95.1% (391/411) +69.4 Line count changed upstream
device.cpp 66.3% (419/632) 79.9% (397/497) +13.6 Line count changed
resource.cpp 47.0% (183/389) 84.6% (314/371) +37.6 Line count changed
reflection.cpp 97.4% (234/240) new
raytracing.cpp 62.0% (116/187) 64.1% (182/284) +2.1 Line count grew
types.cpp 76.5% (182/238) new
shader.cpp 67.0% (555/828) 78.6% (125/159) +11.6 Line count changed upstream
shader_cursor.cpp 61.8% (209/338) 82.7% (43/52) +20.9 Line count changed
buffer_cursor.cpp 47.1% (138/293) 56.7% (72/127) +9.6 Line count changed
pipeline.cpp 35.3% (53/150) 64.4% (58/90) +29.1 Line count changed
input_layout.cpp 81.6% (40/49) new
native_handle.cpp 88.9% (16/18) new
kernel.cpp 90.6% (29/32) new
query.cpp 76.9% (20/26) new
surface.cpp 69.1% (29/42) new
sampler.cpp 57.1% (24/42) new
fence.cpp 70.8% (17/24) new
formats.cpp 100% (30/30) new
device_child.cpp 100% (7/7) new
shader_object.cpp 63.2% (60/95) 100% (3/3) Line count changed upstream

C++ — slangpy_torch

File Coverage Notes
torch_bridge_impl.cpp 75.4% (156/207) First measurement with coverage-instrumented build

Refactoring principles

All tests were audited for direct use of Native C++ objects. Where possible, tests were rewritten to trigger the same C++ code paths through Python-level APIs:

  • DescriptorMarshallfunc.map() and func() on modules without import "slangpy" to trigger error paths indirectly
  • TensorMarshallTensor.with_grads() + functional calls, pack() for property access
  • NativeTorchTensorDiffPairdiff_pair() wrapper and result.backward() to naturally create is_input=False pairs
  • BindContext/CallModepack() public API

Remaining direct Native usage (with justifying comments in tests):

  • NativeSlangType in test_tostring.py — child class SlangType always requires TypeReflection; no public API produces one with type_reflection=None
  • NativeFunctionNode in test_tostring.pyFunctionNode.__repr__ delegates to NativeFunctionNode.__repr__; tested via FunctionNode

Dead code identified

File Function/Class Reason
core/shapes.py check_concrete() Never called anywhere
core/utils.py try_find_type_decl, parse_generic_signature_tree, etc. ~50 lines, never called
builtin/ndbuffer.py BaseNDBufferMarshall Superseded by native C++ impl
types/helpers.py Array path in resolve_vector_generator_type No caller passes support_array=True
types/tensor.py if dtype is None check in empty() resolve_element_type raises first
slangpytensor.cpp read_calldata, extract_shape, extract_strides, validate_tensor_shape, write_tensor_fields_from_pointer No-op stubs or never called; torch tensors use NativeTorchTensorMarshall
slangpybuffer.cpp NativeNDBufferMarshall::read_calldata No-op stub
slangpy.cpp read_call_data_post_dispatch Call site commented out; replaced by read_back list
slangpystridedbufferview.cpp scalartype_to_dtype none_/void_ cases Can't be buffer element types
torchtensormarshall.py TorchTensorMarshall.build_shader_object gradient path Intentional stub; autograd handled in C++
slangpytorchtensor.cpp NativeTorchTensorDiffPair::read_signature Virtual override unreachable after PR #872 SignatureBuffer optimization. Filed as #923

Bugs found

File Description Status
types/common.py flip_y=True produces non-contiguous array; copy_from_numpy rejects it Fixed in this PR
builtin/descriptor.py Constructor type mismatch: TypeReflection vs NativeSlangType Documented
slangpytensor.cpp slangpy.Tensor + DiffTensorView backward pass crashes with CUDA_ERROR_ILLEGAL_ADDRESS Filed as #919 (fixed in #920)
builtin/descriptor.py Hardcoded StructuredBuffer<Unknown> type prevents texture/sampler descriptor handles Filed as #922
slangpytorchtensor.cpp NativeTorchTensorDiffPair::read_signature bypassed by PR #872 SignatureBuffer optimization Filed as #923

Remaining gaps — skipped with justification

File Coverage Why skipped
builtin/ndbuffer.py 48% Being replaced by Tensor (#548, #582)
types/buffer.py 80% NDBuffer-related, being removed
core/calldata.py 85% Being moved to C++ (#691)
core/jupyter.py 0% Headless test environment, requires Jupyter kernel
core/utils.py 44% ~50 lines confirmed dead code
core/shapes.py 57% check_concrete() is dead code
builtin/descriptor.py 58% Speculative infrastructure with zero external consumers. Blocked by #922
builtin/texture.py 84% Remaining gaps require hardware not available (cube maps, MSAA) or are error guards
slangpytorchtensor.cpp (partial) ~87% Remaining 60 lines: interop paths (~30, needs non-CUDA), read_signature (14, dead code #923), float16 (3, GTX 960 limitation), defensive guards (~10), gcov artifacts (~3)

Follow-up tasks (for other environments)

Task Environment needed Lines
D3D12/Vulkan interop paths in slangpytorchtensor.cpp D3D12 or Vulkan primary GPU + CUDA PyTorch ~25
Metal element_byte_stride in slangpytensor.cpp macOS with Metal backend ~4
DiffPair read_calldata without primal in slangpytorchtensor.cpp D3D12/Vulkan (interop path needed) ~15
NativeDescriptorMarshall write path in slangpyresources.cpp GPU with bindless feature. Blocked by #922 ~14

Tests that directly create torch tensors on CUDA now skip when CUDA
is not available. Fixes failures on non-CUDA runners (e.g. macOS).

Made-with: Cursor
Prefix match patterns containing regex metacharacters with r to satisfy
Ruff RUF043 and make regex intent explicit.

Made-with: Cursor
DescriptorHandle type resolution is broken (resolves to
StructuredBuffer<Unknown> instead of the actual descriptor type),
causing all four tests to fail with ResolveException or wrong error
messages. Mark as strict xfail so they surface when shader-slang#922 is fixed.

Made-with: Cursor
Made-with: Cursor

# Conflicts:
#	slangpy/tests/slangpy_tests/test_torchintegration.py
Replace non-ASCII glyphs (arrows, em-dashes) with ASCII equivalents
to pass check-ascii-source pre-commit hook. Call func(pair) twice in
test_diffpair_get_shape_grad_only to exercise the second-dispatch
regression path from shader-slang#929. Apply clang-format.

Made-with: Cursor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/slangpy_ext/utils/slangpy.cpp (1)

977-1010: ⚠️ Potential issue | 🟡 Minor

Use ASCII-only text in the new execution comments.

check-ascii-source is failing on this hunk. The and characters in Lines 978-1007 need ASCII replacements before this can merge.

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

In `@src/slangpy_ext/utils/slangpy.cpp` around lines 977 - 1010, The comment text
contains non-ASCII characters (the bidirectional arrow and em dash) that trigger
check-ascii-source; replace the non-ASCII characters in the new execution
comments surrounding the submit/cleanup logic (near temp_command_encoder,
submit_command_buffer, command_encoder, read_back, wait_for_submit and
DeviceType::cuda) with ASCII equivalents (e.g., use "<->" or "->" for ↔ and "-"
for —) so all comments are plain ASCII; keep the explanatory intent and
punctuation but only swap those characters in the comment strings.
src/slangpy_ext/utils/slangpytorchtensor.cpp (1)

627-632: ⚠️ Potential issue | 🟠 Major

Use the same CUDA stream for the memset and the recorded interop sync.

When context->cuda_stream() is unset, Line 631 enqueues memset_device_async() on the default stream, but Line 632 records PyTorch's current stream. submit_command_buffer() will then synchronize graphics work against a different stream than the one that actually zeroed the buffer.

Suggested fix
         void* cuda_ptr = interop_buffer->cuda_memory();
         if (cuda_ptr && buffer_size > 0) {
-            CUstream stream = context->cuda_stream().is_valid()
-                ? reinterpret_cast<CUstream>(context->cuda_stream().value())
-                : nullptr;
-            cuda::memset_device_async(static_cast<uint8_t*>(cuda_ptr), 0, buffer_size, stream);
-            mark_interop_stream(info.device_index);
+            void* stream_ptr = context->cuda_stream().is_valid()
+                ? reinterpret_cast<void*>(context->cuda_stream().value())
+                : TorchBridge::instance().get_current_cuda_stream(info.device_index);
+            cuda::memset_device_async(
+                static_cast<uint8_t*>(cuda_ptr),
+                0,
+                buffer_size,
+                reinterpret_cast<CUstream>(stream_ptr)
+            );
+            context->mark_interop_cuda_stream(
+                NativeHandle(rhi::NativeHandle(rhi::NativeHandleType::CUstream, reinterpret_cast<uint64_t>(stream_ptr)))
+            );
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/slangpy_ext/utils/slangpytorchtensor.cpp` around lines 627 - 632, The
memset is enqueued on a stream computed from context->cuda_stream(), but
mark_interop_stream() records PyTorch's current stream separately so they may
differ; change the code in slangpytorchtensor.cpp so you compute a single
CUstream variable (use context->cuda_stream() when set, otherwise obtain
PyTorch's current CUDA stream for info.device_index), pass that stream into
cuda::memset_device_async(...) and ensure mark_interop_stream(...) records that
same stream (e.g., add/use an overload or change mark_interop_stream to accept
the CUstream or a wrapper) so the memset and the interop sync use the identical
CUDA stream.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/sgl/utils/slangpy.h`:
- Around line 430-441: The comments around mark_interop_cuda_stream and
interop_cuda_stream include non-ASCII punctuation (specifically the ↔ and —
characters); update those comment lines to use ASCII-only equivalents (e.g.,
replace ↔ with "<->" or "↔" -> "to" and replace — with "--" or a simple hyphen)
so that check-ascii-source passes while leaving the function names and
m_interop_cuda_stream usage unchanged.

In `@src/slangpy_ext/utils/slangpytorchtensor.cpp`:
- Around line 579-583: The comment block describing CUDA stream recording
contains non-ASCII punctuation (the ↔ arrow and the — em-dash); update that
comment to use ASCII-only characters (e.g. replace ↔ with <-> or <- -> and
replace — with -- or a single hyphen) in the comment that references exec(),
submit_command_buffer(), copy_to_buffer, and memset_device_async so
check-ascii-source passes.
- Around line 493-503: When offsets.is_tensorview and interop_buffer is present
the code rebuilds TensorViewData for a TensorView/DiffTensorView which are
CUDA-only; add a guard that rejects or skips the interop-buffer path for
TensorView types when the runtime/backend is not CUDA (i.e., detect backend from
the interop_buffer or runtime context and if not CUDA, do not populate
TensorViewData from interop_buffer and return an error or fallback), keeping the
existing TensorViewData population for true CUDA interop only; reference
offsets.is_tensorview, TensorViewData, interop_buffer, and the existing stride
logic (make_contiguous_strides, apply_broadcast_stride_zeroing,
binding->transform(), context->call_shape()) to locate and gate the change.

---

Outside diff comments:
In `@src/slangpy_ext/utils/slangpy.cpp`:
- Around line 977-1010: The comment text contains non-ASCII characters (the
bidirectional arrow and em dash) that trigger check-ascii-source; replace the
non-ASCII characters in the new execution comments surrounding the
submit/cleanup logic (near temp_command_encoder, submit_command_buffer,
command_encoder, read_back, wait_for_submit and DeviceType::cuda) with ASCII
equivalents (e.g., use "<->" or "->" for ↔ and "-" for —) so all comments are
plain ASCII; keep the explanatory intent and punctuation but only swap those
characters in the comment strings.

In `@src/slangpy_ext/utils/slangpytorchtensor.cpp`:
- Around line 627-632: The memset is enqueued on a stream computed from
context->cuda_stream(), but mark_interop_stream() records PyTorch's current
stream separately so they may differ; change the code in slangpytorchtensor.cpp
so you compute a single CUstream variable (use context->cuda_stream() when set,
otherwise obtain PyTorch's current CUDA stream for info.device_index), pass that
stream into cuda::memset_device_async(...) and ensure mark_interop_stream(...)
records that same stream (e.g., add/use an overload or change
mark_interop_stream to accept the CUstream or a wrapper) so the memset and the
interop sync use the identical CUDA stream.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c5d0a818-5550-4f40-b965-96cc6ff0d5de

📥 Commits

Reviewing files that changed from the base of the PR and between 2862919 and ca92e87.

📒 Files selected for processing (4)
  • slangpy/tests/slangpy_tests/test_torchintegration.py
  • src/sgl/utils/slangpy.h
  • src/slangpy_ext/utils/slangpy.cpp
  • src/slangpy_ext/utils/slangpytorchtensor.cpp

Comment thread src/sgl/utils/slangpy.h Outdated
Comment thread src/slangpy_ext/utils/slangpytorchtensor.cpp Outdated
Comment thread src/slangpy_ext/utils/slangpytorchtensor.cpp Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
slangpy/tests/slangpy_tests/test_load_image.py (1)

105-112: ⚠️ Potential issue | 🟡 Minor

Assert two-channel pixel values, not just shape.

expected is still unused, so channel-order/value regressions can pass undetected. Please compare full tensor contents here.

Suggested patch
 def test_load_two_channel(device, rg_image):
     """Load a 2-channel image and verify it produces float2 tensor."""
     path, expected = rg_image
     tensor = spy.Tensor.load_from_image(device, path)
     result = tensor.to_numpy()
     assert result.ndim == 3
     assert result.shape[2] == 2
+    assert result.shape == expected.shape
+    assert np.allclose(result, expected, atol=1e-5)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@slangpy/tests/slangpy_tests/test_load_image.py` around lines 105 - 112, The
test_load_two_channel currently only checks shape; update it to assert pixel
values match the fixture by using the expected value from rg_image: after
loading with spy.Tensor.load_from_image and converting to numpy (result),
compare result to expected (or expected.astype/result dtype) using an
elementwise comparison (e.g., numpy.testing.assert_allclose or equality) so
channel order and values are validated; reference symbols:
test_load_two_channel, rg_image, spy.Tensor.load_from_image, result, expected.
🧹 Nitpick comments (1)
slangpy/tests/slangpy_tests/test_torchintegration.py (1)

798-804: Rename this test to match what it currently validates.

The function name says read_signature, but the docstring explicitly says this path is not covered yet. A smoke-oriented name would prevent coverage confusion.

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

In `@slangpy/tests/slangpy_tests/test_torchintegration.py` around lines 798 - 804,
Rename the test function test_diffpair_read_signature to reflect it only
exercises the forward pass (e.g., test_diffpair_forward_pass or
test_diffpair_forward_smoke), update the function name declaration accordingly,
and keep the existing docstring and body unchanged; locate the test function
symbol test_diffpair_read_signature in the file and perform the rename so test
discovery and coverage correctly reflect what the test validates.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@slangpy/tests/slangpy_tests/test_load_image.py`:
- Around line 27-145: Update missing type annotations: annotate the fixtures and
test function parameters and returns — change image_dir() -> str; rgb_image(),
rgba_image(), grey_image(), rg_image() -> Tuple[str, np.ndarray]; and annotate
all test functions' parameters (e.g. test_load_rgb(device, rgb_image) to
test_load_rgb(device: spy.Device, rgb_image: Tuple[str, np.ndarray])) and
similarly for test_load_rgba, test_load_greyscale_single_channel,
test_load_two_channel, test_load_flip_y, test_load_scale_offset,
test_load_greyscale_flag, test_load_linearize; add necessary imports from typing
(Tuple) and use the spy.Device type for the device parameter if available.
Ensure signatures exactly match symbols: image_dir, rgb_image, rgba_image,
grey_image, rg_image, and all test_* functions.

In `@slangpy/tests/slangpy_tests/test_torchintegration.py`:
- Around line 33-35: Tests create CUDA tensors at collection time causing
failures on CPU-only environments; add a module-level skip so collection is
aborted cleanly when CUDA isn't available. At the top of the test module (where
requires_cuda is defined), set a module-wide marker like pytestmark =
pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available")
or call pytest.skip("CUDA not available", allow_module_level=True) early in the
module; this ensures the parametrize that builds CUDA tensors (the CUDA-related
parametrization in test_torchintegration.py) won't execute during collection.
- Around line 999-1011: Replace the broad Exception assertions with the concrete
exception types raised by the code: change the pytest.raises(Exception,
match="shape") around the call to func(bad) to pytest.raises(ValueError,
match="shape") (this covers the shape-check in slangpy/types/tensor.py), and
change the pytest.raises(Exception, match=r"[Cc][Uu][Dd][Aa]") around
func(cpu_tensor) in test_torch_cpu_tensor_rejected to
pytest.raises(RuntimeError, match=r"[Cc][Uu][Dd][Aa]") (this aligns with the
RuntimeError raised by the torch fallback in
slangpy/torchintegration/bridge_fallback.py); keep the same match strings and
the same `func`/`cpu_tensor` call sites created via
helpers.create_function_from_module.

---

Duplicate comments:
In `@slangpy/tests/slangpy_tests/test_load_image.py`:
- Around line 105-112: The test_load_two_channel currently only checks shape;
update it to assert pixel values match the fixture by using the expected value
from rg_image: after loading with spy.Tensor.load_from_image and converting to
numpy (result), compare result to expected (or expected.astype/result dtype)
using an elementwise comparison (e.g., numpy.testing.assert_allclose or
equality) so channel order and values are validated; reference symbols:
test_load_two_channel, rg_image, spy.Tensor.load_from_image, result, expected.

---

Nitpick comments:
In `@slangpy/tests/slangpy_tests/test_torchintegration.py`:
- Around line 798-804: Rename the test function test_diffpair_read_signature to
reflect it only exercises the forward pass (e.g., test_diffpair_forward_pass or
test_diffpair_forward_smoke), update the function name declaration accordingly,
and keep the existing docstring and body unchanged; locate the test function
symbol test_diffpair_read_signature in the file and perform the rename so test
discovery and coverage correctly reflect what the test validates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ee84df3f-8ac3-4ddf-982c-8aa93204bb5e

📥 Commits

Reviewing files that changed from the base of the PR and between ca92e87 and 4266e05.

📒 Files selected for processing (7)
  • slangpy/tests/slangpy_tests/test_load_image.py
  • slangpy/tests/slangpy_tests/test_raw_dispatch.py
  • slangpy/tests/slangpy_tests/test_torch_dtypes.py
  • slangpy/tests/slangpy_tests/test_torchintegration.py
  • src/sgl/utils/slangpy.h
  • src/slangpy_ext/utils/slangpy.cpp
  • src/slangpy_ext/utils/slangpytorchtensor.cpp
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/slangpy_ext/utils/slangpy.cpp
  • slangpy/tests/slangpy_tests/test_torch_dtypes.py
  • src/sgl/utils/slangpy.h
  • src/slangpy_ext/utils/slangpytorchtensor.cpp
  • slangpy/tests/slangpy_tests/test_raw_dispatch.py

Comment on lines +27 to +145
def image_dir():
d = os.path.join(tempfile.gettempdir(), "slangpy_test_images")
os.makedirs(d, exist_ok=True)
return d


def _save_image(path: str, data: np.ndarray, pixel_format: spy.Bitmap.PixelFormat):
b = spy.Bitmap(data, pixel_format=pixel_format)
b.write(path)


@pytest.fixture(scope="module")
def rgb_image(image_dir):
data = np.array(
[[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]], [[0.7, 0.8, 0.9], [1.0, 0.0, 0.5]]], dtype=np.float32
)
path = os.path.join(image_dir, "test_rgb.exr")
_save_image(path, data, spy.Bitmap.PixelFormat.rgb)
return path, data


@pytest.fixture(scope="module")
def rgba_image(image_dir):
data = np.array(
[
[[0.1, 0.2, 0.3, 1.0], [0.4, 0.5, 0.6, 0.8]],
[[0.7, 0.8, 0.9, 0.5], [1.0, 0.0, 0.5, 0.2]],
],
dtype=np.float32,
)
path = os.path.join(image_dir, "test_rgba.exr")
_save_image(path, data, spy.Bitmap.PixelFormat.rgba)
return path, data


@pytest.fixture(scope="module")
def grey_image(image_dir):
data = np.array([[0.1, 0.4], [0.7, 1.0]], dtype=np.float32)
path = os.path.join(image_dir, "test_grey.exr")
_save_image(path, data, spy.Bitmap.PixelFormat.y)
return path, data


@pytest.fixture(scope="module")
def rg_image(image_dir):
data = np.array([[[0.1, 0.2], [0.4, 0.5]], [[0.7, 0.8], [1.0, 0.0]]], dtype=np.float32)
path = os.path.join(image_dir, "test_rg.exr")
_save_image(path, data, spy.Bitmap.PixelFormat.ya)
return path, data


def test_load_rgb(device, rgb_image):
"""Load a 3-channel RGB image via Tensor.load_from_image."""
path, expected = rgb_image
tensor = spy.Tensor.load_from_image(device, path)
result = tensor.to_numpy()
assert result.shape == expected.shape
assert np.allclose(result, expected, atol=1e-5)


def test_load_rgba(device, rgba_image):
"""Load a 4-channel RGBA image."""
path, expected = rgba_image
tensor = spy.Tensor.load_from_image(device, path)
result = tensor.to_numpy()
assert result.shape == expected.shape
assert np.allclose(result, expected, atol=1e-5)


def test_load_greyscale_single_channel(device, grey_image):
"""Load a single-channel image."""
path, expected = grey_image
tensor = spy.Tensor.load_from_image(device, path)
result = tensor.to_numpy()
assert result.shape == expected.shape
assert np.allclose(result, expected, atol=1e-5)


def test_load_two_channel(device, rg_image):
"""Load a 2-channel image and verify it produces float2 tensor."""
path, expected = rg_image
tensor = spy.Tensor.load_from_image(device, path)
result = tensor.to_numpy()
assert result.ndim == 3
assert result.shape[2] == 2


def test_load_flip_y(device, rgb_image):
"""Load with flip_y=True — rows should be vertically reversed."""
path, expected = rgb_image
tensor = spy.Tensor.load_from_image(device, path, flip_y=True)
result = tensor.to_numpy()
assert result.shape == expected.shape
assert np.allclose(result, np.flipud(expected), atol=1e-5)


def test_load_scale_offset(device, rgb_image):
"""Load with scale and offset applied."""
path, expected = rgb_image
tensor = spy.Tensor.load_from_image(device, path, scale=2.0, offset=0.5)
result = tensor.to_numpy()
assert np.allclose(result, expected * 2.0 + 0.5, atol=1e-5)


def test_load_greyscale_flag(device, rgb_image):
"""Load an RGB image with grayscale=True to force single-channel output."""
path, _ = rgb_image
tensor = spy.Tensor.load_from_image(device, path, grayscale=True)
result = tensor.to_numpy()
assert result.ndim == 2


def test_load_linearize(device, rgb_image):
"""Load with linearize=True to disable sRGB gamma correction."""
path, _ = rgb_image
tensor = spy.Tensor.load_from_image(device, path, linearize=True)
result = tensor.to_numpy()
assert result.shape[0] == 2 and result.shape[1] == 2

Copy link
Copy Markdown

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:

#!/bin/bash
# Read-only verification: list functions with unannotated args in this file.
python - <<'PY'
import ast
from pathlib import Path

p = Path("slangpy/tests/slangpy_tests/test_load_image.py")
tree = ast.parse(p.read_text(), filename=str(p))

for node in ast.walk(tree):
    if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
        missing = [a.arg for a in node.args.args if a.arg not in {"self", "cls"} and a.annotation is None]
        if missing:
            print(f"Line {node.lineno}: {node.name} -> missing annotations for {missing}")
PY

Repository: shader-slang/slangpy

Length of output: 961


Add missing argument type annotations in this test module.

Several functions accept untyped arguments (fixtures and test parameters), violating the repo-wide Python typing requirement.

Example patch pattern (apply consistently)
-@pytest.fixture(scope="module")
-def image_dir():
+@pytest.fixture(scope="module")
+def image_dir() -> str:
     d = os.path.join(tempfile.gettempdir(), "slangpy_test_images")
     os.makedirs(d, exist_ok=True)
     return d

 `@pytest.fixture`(scope="module")
-def rgb_image(image_dir):
+def rgb_image(image_dir: str) -> tuple[str, np.ndarray]:
     data = np.array(
         [[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]], [[0.7, 0.8, 0.9], [1.0, 0.0, 0.5]]], dtype=np.float32
     )
     path = os.path.join(image_dir, "test_rgb.exr")
     _save_image(path, data, spy.Bitmap.PixelFormat.rgb)
     return path, data

-def test_load_rgb(device, rgb_image):
+def test_load_rgb(device: spy.Device, rgb_image: tuple[str, np.ndarray]) -> None:

All fixtures and test functions (lines 27–145) require parameter type annotations.

🧰 Tools
🪛 Ruff (0.15.9)

[warning] 107-107: Unpacked variable expected is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)

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

In `@slangpy/tests/slangpy_tests/test_load_image.py` around lines 27 - 145, Update
missing type annotations: annotate the fixtures and test function parameters and
returns — change image_dir() -> str; rgb_image(), rgba_image(), grey_image(),
rg_image() -> Tuple[str, np.ndarray]; and annotate all test functions'
parameters (e.g. test_load_rgb(device, rgb_image) to test_load_rgb(device:
spy.Device, rgb_image: Tuple[str, np.ndarray])) and similarly for
test_load_rgba, test_load_greyscale_single_channel, test_load_two_channel,
test_load_flip_y, test_load_scale_offset, test_load_greyscale_flag,
test_load_linearize; add necessary imports from typing (Tuple) and use the
spy.Device type for the device parameter if available. Ensure signatures exactly
match symbols: image_dir, rgb_image, rgba_image, grey_image, rg_image, and all
test_* functions.

Comment thread slangpy/tests/slangpy_tests/test_torchintegration.py Outdated
Comment on lines +999 to +1011
with pytest.raises(Exception, match="shape"):
func(bad)


@pytest.mark.parametrize("device_type", DEVICE_TYPES)
def test_torch_cpu_tensor_rejected(device_type: DeviceType):
"""Non-CUDA torch tensors are rejected by write_shader_cursor_pre_dispatch."""
device = helpers.get_torch_device(device_type)
func = helpers.create_function_from_module(device, "identity", IDENTITY_SRC)

cpu_tensor = torch.tensor([1.0, 2.0], dtype=torch.float32)
with pytest.raises(Exception, match=r"[Cc][Uu][Dd][Aa]"):
func(cpu_tensor)

Copy link
Copy Markdown

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:

#!/bin/bash
# Locate broad exception assertions in this test file.
rg -n 'pytest\.raises\(\s*Exception' slangpy/tests/slangpy_tests/test_torchintegration.py

Repository: shader-slang/slangpy

Length of output: 184


🏁 Script executed:

# Read the test functions around the flagged lines for context
cat -n slangpy/tests/slangpy_tests/test_torchintegration.py | sed -n '980,1020p'

Repository: shader-slang/slangpy

Length of output: 1512


🏁 Script executed:

# Search for other pytest.raises patterns in this file to see what exception types are used elsewhere
rg -n 'pytest\.raises\(' slangpy/tests/slangpy_tests/test_torchintegration.py | head -20

Repository: shader-slang/slangpy

Length of output: 662


🏁 Script executed:

# Check for SlangPyError usage and imports in the test file
rg -n 'SlangPyError|from.*import|import' slangpy/tests/slangpy_tests/test_torchintegration.py | head -30

Repository: shader-slang/slangpy

Length of output: 1123


🏁 Script executed:

# Find where validate_tensor_shape is defined and what exception it raises
rg -n 'def validate_tensor_shape' --type py

Repository: shader-slang/slangpy

Length of output: 46


🏁 Script executed:

# Find where write_shader_cursor_pre_dispatch is defined and what exception it raises
rg -n 'def write_shader_cursor_pre_dispatch' --type py

Repository: shader-slang/slangpy

Length of output: 46


🏁 Script executed:

# Search for the exception raise statements in torch-related code
rg -n 'raise.*shape|raise.*[Cc][Uu][Dd][Aa]' slangpy/ --type py | head -20

Repository: shader-slang/slangpy

Length of output: 2373


🏁 Script executed:

# Search for shape validation errors related to torch tensors
rg -n 'does not match.*shape|shape.*does not match' --type py -B2 -A2

Repository: shader-slang/slangpy

Length of output: 3521


🏁 Script executed:

# Look for CUDA validation in torch tensor handling
rg -n 'CUDA|cuda.*device|device.*cuda' slangpy/torchintegration/ --type py -B2 -A2 | head -40

Repository: shader-slang/slangpy

Length of output: 3286


🏁 Script executed:

# Check what func() in the tests actually calls - search for create_function_from_module
rg -n 'def create_function_from_module' --type py -A10

Repository: shader-slang/slangpy

Length of output: 689


🏁 Script executed:

# Find where write_shader_cursor_pre_dispatch is implemented (might be in C++ binding)
rg -n 'write_shader_cursor_pre_dispatch' slangpy/torchintegration/ --type py -B5 -A5

Repository: shader-slang/slangpy

Length of output: 989


🏁 Script executed:

# Check what exception is raised when func() is called with a CPU tensor
rg -n 'device_type.*1.*if.*cuda|is_cuda.*raise|Tensor must be on CUDA' slangpy/ --type py -B3 -A3

Repository: shader-slang/slangpy

Length of output: 1894


🏁 Script executed:

# Verify what tensor.py:304 actually raises (we saw ValueError earlier)
cat -n slangpy/types/tensor.py | sed -n '300,310p'

Repository: shader-slang/slangpy

Length of output: 665


Use specific exception types instead of broad Exception in negative-path assertions.

Lines 999 and 1010 use pytest.raises(Exception, ...), which masks the actual exception types being raised and weakens error-contract validation. The underlying code raises concrete exceptions:

  • Line 999: slangpy/types/tensor.py raises ValueError for shape mismatches
  • Line 1010: slangpy/torchintegration/bridge_fallback.py raises RuntimeError for non-CUDA tensors

Update assertions to match the actual exception types: ValueError on line 999 and RuntimeError on line 1010. This aligns with the pattern used elsewhere in the file (line 170 correctly asserts ValueError).

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

In `@slangpy/tests/slangpy_tests/test_torchintegration.py` around lines 999 -
1011, Replace the broad Exception assertions with the concrete exception types
raised by the code: change the pytest.raises(Exception, match="shape") around
the call to func(bad) to pytest.raises(ValueError, match="shape") (this covers
the shape-check in slangpy/types/tensor.py), and change the
pytest.raises(Exception, match=r"[Cc][Uu][Dd][Aa]") around func(cpu_tensor) in
test_torch_cpu_tensor_rejected to pytest.raises(RuntimeError,
match=r"[Cc][Uu][Dd][Aa]") (this aligns with the RuntimeError raised by the
torch fallback in slangpy/torchintegration/bridge_fallback.py); keep the same
match strings and the same `func`/`cpu_tensor` call sites created via
helpers.create_function_from_module.

@jhelferty-nv jhelferty-nv self-assigned this Apr 10, 2026
Made-with: Cursor

# Conflicts:
#	src/slangpy_ext/utils/slangpytorchtensor.cpp
@jhelferty-nv

Copy link
Copy Markdown
Contributor Author

Discussed with Chris this morning; let's back out all of the driver changes, including the stuff from #934 which probably needs a different approach. We'll disable any tests that don't make sense given known issues.

Back out C++ interop buffer changes (slangpytorchtensor.cpp, slangpy.cpp,
slangpy.h) per discussion - these need a different approach in shader-slang#934.

Re-restrict test_diffpair_get_shape_grad_only to CUDA only since the
interop race condition (shader-slang#929) is no longer fixed in this branch.

Also fixes:
- test_torch_dtypes.py: guard torch import for macOS
- Non-ASCII em dashes in test docstrings

Made-with: Cursor
…ng#937)

The contiguity fix for load_buffer_data_from_image is now in PR shader-slang#937.
Mark test_load_flip_y as xfail until that fix lands.

Made-with: Cursor
…shader-slang#582)

Coverage gains were entirely in builtin/ndbuffer.py and types/buffer.py,
both of which are being replaced by Tensor.

Made-with: Cursor
These three grad validation tests crash with a nanobind uninitialized
instance error during the dispatch pipeline. Skip instead of xfail to
avoid potential state contamination from partially constructed C++ objects.

Made-with: Cursor
Even the CUDA-only + fallback bridge mode variant triggers the interop
buffer lifetime race. Convert from @requires_cuda to @pytest.mark.skip
to prevent worker crashes and CUDA context poisoning.

Issue shader-slang#929 updated with re-enablement instructions.

Made-with: Cursor
Section 1 - Fix vacuous/weak tests:
- test_shape: add element_count assertion, fix negative indexing, test copy independence
- test_torchintegration: add grad/output assertions to null_grad tests
- test_value_torch_pipeline: verify matrix values, packed arg repr

Section 2 - Add issue references to all skipped/xfailed tests:
- shader-slang#938: TensorMarshall nanobind lifecycle (grad validation crashes)
- shader-slang#939: DiffTensor/PrimalTensor sample not implemented
- shader-slang#940: Slang compiler bugs (generic vector crash, polynomial derivatives)

Section 3 - Pytest best practices:
- Remove allow_module_level=True from inside test functions (test_buffer_views)
- Fix mutable default arguments in load_test_module (test_raw_dispatch)
- Replace bare return with pytest.skip for 3D texture arrays (test_textures)
- Move global PYTHON_TYPES mutation to fixture with teardown (test_return_types)

Made-with: Cursor
- test_torchintegration: remove "Should this work??" backward call on
  non-differentiable RWTensor output (shader-slang#574). Other tests already cover
  backward passes with proper differentiable outputs.
- test_textures: remove dead transpose code commented out in PR shader-slang#48
  when dimension ordering was fixed upstream.
- test_tensor: add assert to test_existential_type_bug compile-only
  regression test for clarity.

Made-with: Cursor
The C++ Shape class has element_count but it's not bound via nanobind.
Verify element count indirectly through contiguous strides instead.

Made-with: Cursor
The C++ Shape::element_count() method exists but was never registered
via nanobind. Skip the test with an explanation rather than removing it,
so it can be re-enabled once the binding is added.

Made-with: Cursor
jhelferty-nv added a commit that referenced this pull request May 5, 2026
…age (#937)

np.flipud returns a view with negative strides, which copy_from_numpy
rejects. Ensure the returned array is always C-contiguous.

Relates to #928

Made-with: Cursor

Co-authored-by: Jay Kwak <82421531+jkwak-work@users.noreply.github.com>
@jhelferty-nv jhelferty-nv enabled auto-merge (squash) May 26, 2026 14:41
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.

Improve coverage of torch integration (python side) Improve coverage of torch integration (C++ side)

2 participants