-
Notifications
You must be signed in to change notification settings - Fork 414
Support @property getters for warp.struct #1155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughCollects Python Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Kernel as "Kernel (source)"
participant Codegen as "Codegen / emit_Attribute"
participant StructType as "StructType / Struct"
participant PropFunc as "Generated Property Function"
participant Runtime as "Runtime / Native Function"
Kernel->>Codegen: emit attribute read for `s.neg_value`
Note right of Codegen: detect aggregate is Struct\nlookup `neg_value` in StructType.properties
Codegen->>PropFunc: emit add_call, bind self to struct instance
PropFunc->>Runtime: invoke native property getter
Runtime-->>PropFunc: return value
PropFunc-->>Codegen: provide value expression
Codegen-->>Kernel: inline call result into kernel code
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
| for name, item in property_members: | ||
| # We currently support only getters | ||
| if item.fset is not None: | ||
| raise TypeError("Struct properties with setters are not supported") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, only getters are supported. Setters should be doable too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
warp/tests/test_struct.py (1)
912-921: Add backward pass test for property differentiation.The test validates forward pass execution but doesn't verify that properties work correctly in the backward pass. Since properties are converted to Warp functions, they should be differentiable. Consider adding a gradient test to ensure
d(neg_value)/d(value) = -1:Suggested additional test
def test_struct_property_grad(test, device): s = StructWithProperty() s.value = wp.array([42.0], dtype=float, device=device, requires_grad=True) out = wp.zeros(1, dtype=float, device=device, requires_grad=True) tape = wp.Tape() with tape: @wp.kernel def grad_test_kernel(s: StructWithProperty, out: wp.array(dtype=float)): out[0] = s.neg_value wp.launch(grad_test_kernel, dim=1, inputs=[s, out], device=device) tape.backward(loss=out) # d(neg_value)/d(value) should be -1 assert_np_equal(s.value.grad.numpy(), np.array([-1.0]))Note: This assumes the property can access array fields. If that's not supported yet, use a simpler struct with scalar fields, or defer this test if array field access in properties is out of scope.
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
warp/_src/codegen.pywarp/tests/test_struct.py
🧰 Additional context used
🧬 Code graph analysis (2)
warp/_src/codegen.py (1)
warp/_src/context.py (3)
context(3470-3485)Function(132-528)func(862-905)
warp/tests/test_struct.py (1)
warp/_src/context.py (4)
struct(1320-1342)kernel(1174-1316)launch(6594-6635)launch(6638-6877)
🪛 Ruff (0.14.10)
warp/_src/codegen.py
526-526: Avoid specifying long messages outside the exception class
(TRY003)
528-528: Avoid specifying long messages outside the exception class
(TRY003)
warp/tests/test_struct.py
912-912: Unused function argument: test
(ARG001)
920-920: assert_np_equal may be undefined, or defined from star imports
(F405)
923-923: add_function_test may be undefined, or defined from star imports
(F405)
🔇 Additional comments (8)
warp/tests/test_struct.py (3)
898-905: LGTM! Clean property definition.The struct definition is straightforward and serves as a good test case for the property feature. The property correctly uses the
@propertydecorator and returns a derived value from the struct field.
907-910: LGTM! Property access test.The kernel correctly demonstrates property access via dot notation (
s.neg_value), which is the key feature being tested.
923-923: LGTM! Standard test registration.The test registration follows the established pattern in this file. The static analysis warning about
add_function_testbeing undefined is a false positive due to the star import fromwarp.tests.unittest_utilson line 24.warp/_src/codegen.py (5)
463-463: LGTM! Properties dictionary added.The initialization of
self.propertiesfollows the same pattern asself.varsand includes a helpful type hint.
486-490: LGTM! Property metadata collection.The early collection of property members with deferred Function creation is a sound design. The comment clearly explains why Function creation is postponed until after
native_nameis computed.
512-515: Property names added to hash.Including property names in the struct hash ensures module rebuilds when property names change. The property implementations will be hashed separately via the Function class mechanism, so implementation changes should also trigger rebuilds.
521-555: Property Function creation logic is sound.The implementation correctly:
- Validates that only getters are supported (no setters/deleters)
- Adds type annotations for the
selfargument to enable overload resolution- Creates unique Function objects with predictable C++ names (
{struct_native_name}_{property_name})- Handles edge cases like missing
__annotations__attributeThe static analysis warnings about long error messages (lines 526, 528) are minor style suggestions that don't affect functionality.
2309-2313: LGTM! Property access code generation.The property access is cleanly integrated into the existing attribute resolution logic. Calling the property Function via
add_callensures that both forward and adjoint code are generated correctly, enabling automatic differentiation through properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Overview
Greptile Summary
Added support for @property getters in warp.struct classes, allowing computed properties to be accessed within Warp kernels by converting property getters into Warp functions at codegen time.
Key changes:
- Added
propertiesdict toStructclass to store property-to-Function mappings - Property collection using
inspect.getmembers()during struct initialization - Property names included in struct hash for identity tracking
- Property getters converted to Warp Functions with struct instance as first argument
- Property access handled in
Adjoint.emit_Attribute()before regular field access - Added validation to reject properties with setters or deleters
- Test coverage for basic property getter functionality
Minor issues:
- Misleading comment about hash stability at line 512
- Missing validation for edge case where property getter has no arguments
Confidence Score: 4/5
- This PR is safe to merge with minor documentation improvements recommended
- The implementation is solid and well-structured, with proper validation for setters/deleters and correct integration into the attribute resolution pipeline. Test coverage demonstrates basic functionality. Minor issues include a misleading comment and a missing edge case validation, neither of which would cause runtime errors in normal usage.
- No files require special attention - the implementation is straightforward and well-integrated
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| warp/_src/codegen.py | 4/5 | Adds @property support for warp.struct by collecting properties during struct initialization, converting them to Warp Functions, and handling property access in attribute resolution |
| warp/tests/test_struct.py | 5/5 | Adds basic test for property getter functionality with a simple struct that has a neg_value property returning the negated value |
Sequence Diagram
sequenceDiagram
participant User
participant Decorator as @wp.struct
participant Struct as Struct.__init__
participant InspectModule as inspect.getmembers
participant FunctionClass as Function
participant Adjoint as Adjoint.emit_Attribute
User->>Decorator: Define struct with @property
Decorator->>Struct: Create Struct instance
Struct->>Struct: Collect field annotations
Struct->>InspectModule: getmembers(cls)
InspectModule-->>Struct: Return all members including properties
Struct->>Struct: Filter for property instances
Struct->>Struct: Compute hash (includes property names)
Struct->>Struct: Set native_name
loop For each property
Struct->>Struct: Validate no setter/deleter
Struct->>Struct: Add self annotation to getter
Struct->>FunctionClass: Create Function from getter
FunctionClass-->>Struct: Return Function instance
Struct->>Struct: Set native_func name
Struct->>Struct: Store in properties dict
end
Note over User,Adjoint: Later: During kernel compilation
User->>Adjoint: Access struct.property in kernel
Adjoint->>Adjoint: Check if attr is property
alt Is property
Adjoint->>Adjoint: add_call(prop, (aggregate,), {}, {})
Adjoint-->>User: Return property value
else Is regular field
Adjoint->>Adjoint: Access field normally
Adjoint-->>User: Return field value
end
Additional Comments (2)
|
antoinebrl
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to augment the documentation to illustrate this new feature?
Greptile OverviewGreptile SummaryThis PR adds support for Key Implementation Details:
Issues Found:
Architecture Considerations: Confidence Score: 3/5
Important Files ChangedFile Analysis
Sequence DiagramsequenceDiagram
participant User as User Code
participant Struct as @wp.struct Decorator
participant Codegen as Struct.__init__
participant Kernel as @wp.kernel
participant Adjoint as emit_Attribute
User->>Struct: Define struct with @property
Struct->>Codegen: Create Struct instance
Codegen->>Codegen: Collect fields from annotations
Codegen->>Codegen: inspect.getmembers(cls)
Codegen->>Codegen: Filter for property objects
Note over Codegen: Validate property (no setter/deleter)
Codegen->>Codegen: get_full_arg_spec(getter)
Codegen->>Codegen: Add self annotation to getter
Codegen->>Codegen: Create Function from getter
Codegen->>Codegen: Store in self.properties[name]
User->>Kernel: Launch kernel with struct
Kernel->>Adjoint: Access s.property_name
Adjoint->>Adjoint: Check if attr in aggregate_type.properties
Adjoint->>Adjoint: add_call(prop, (aggregate,), {}, {})
Note over Adjoint: Property getter invoked as function<br/>with struct instance as argument
Adjoint-->>Kernel: Return property value
Kernel-->>User: Kernel execution complete
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional Comments (3)
-
warp/_src/codegen.py, line 532-539 (link)logic: Mutating
getter.__annotations__directly modifies the original function's annotations, which could cause issues if the same property getter is used in multiple structs or accessed outside the struct context. Should create a copy of annotations before modifying. -
warp/_src/codegen.py, line 536-539 (link)logic: Property getters without a
selfparameter will silently fail to get proper type annotations, leading to confusing errors during Function creation. Should validate that property getters have at least one parameter. -
warp/tests/test_struct.py, line 899-924 (link)style: Test coverage is minimal - only validates basic property getter. Missing tests for:
- Multiple properties in same struct
- Properties with complex types (structs, arrays)
- Error cases (setter/deleter rejection)
- Property calling other properties
- Properties with no return type annotation
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
2 files reviewed, 3 comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
warp/_src/codegen.py (1)
521-555: Property Function creation is well-structured.The implementation correctly:
- Validates that only getters are supported (no setters/deleters)
- Injects the struct type as the
selfparameter annotation- Creates a unique native function name based on struct and property names
One consideration: modifying
getter.__annotations__directly at line 539 mutates the original function's annotations. If the same getter function is reused elsewhere, this could cause unexpected behavior. Consider working with a copy:♻️ Optional: Avoid mutating original function annotations
# Find the name of the first argument (conventionally 'self') argspec = inspect.getfullargspec(getter) if len(argspec.args) > 0: self_arg = argspec.args[0] - getter.__annotations__[self_arg] = self + # Use a copy to avoid mutating the original function's annotations + if not hasattr(getter, "__annotations__"): + getter.__annotations__ = {} + else: + getter.__annotations__ = getter.__annotations__.copy() + getter.__annotations__[self_arg] = self
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
warp/_src/codegen.pywarp/tests/test_struct.py
🧰 Additional context used
🧬 Code graph analysis (1)
warp/tests/test_struct.py (1)
warp/_src/context.py (4)
struct(1320-1342)kernel(1174-1316)launch(6594-6635)launch(6638-6877)
🪛 Ruff (0.14.10)
warp/_src/codegen.py
526-526: Avoid specifying long messages outside the exception class
(TRY003)
528-528: Avoid specifying long messages outside the exception class
(TRY003)
warp/tests/test_struct.py
913-913: Unused function argument: test
(ARG001)
921-921: assert_np_equal may be undefined, or defined from star imports
(F405)
924-924: Unused function argument: device
(ARG001)
940-940: add_function_test may be undefined, or defined from star imports
(F405)
941-941: add_function_test may be undefined, or defined from star imports
(F405)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Greptile Review
🔇 Additional comments (8)
warp/tests/test_struct.py (4)
899-910: Well-structured test struct and kernel for property access.The struct definition with the
@propertydecorator and the corresponding kernel that readss.neg_valuecorrectly exercise the new property getter functionality.
913-921: Test correctly validates property getter in kernel.The test instantiates the struct, sets a value, launches the kernel that reads the property, and validates the output. This provides good coverage for the basic property getter flow.
924-937: Good addition of setter rejection test.This test validates that attempting to define a property with a setter raises an appropriate
TypeErrorwith a clear message. This addresses the previous review feedback about testing setter rejection.
940-941: Test registrations look correct.Both new tests are properly registered with
add_function_testand include thedevicesparameter for cross-device testing.warp/_src/codegen.py (4)
463-463: Correct initialization of properties dictionary.The
propertiesdict is properly initialized as empty and will be populated afternative_nameis computed, following the deferred initialization pattern described in the AI summary.
486-490: Property collection correctly defers Function creation.Collecting property metadata early (before hash computation) while deferring Function creation until after
native_nameis set is the right approach. This ensures property names are included in the hash while avoiding issues with forward references.
512-515: Property names correctly included in struct hash.Hashing property names ensures the struct identity changes if properties are renamed. Note that changes to property getter implementation won't affect the hash, which is consistent with how struct field changes are handled.
2309-2312: Property dispatch in emit_Attribute correctly handles property access.The implementation properly checks if the accessed attribute is a property on the struct and dispatches to the corresponding Function via
add_call. This integrates cleanly with the existing attribute access logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional Comments (1)
-
warp/tests/test_struct.py, line 925 (link)syntax: error message regex doesn't match actual error in
codegen.py:526
2 files reviewed, 1 comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Overview
Greptile Summary
Adds @property getter support for warp.struct, converting properties into warp functions at codegen. Properties are collected via inspect.getmembers(), validated (rejecting setters/deleters), and stored as Function objects. During attribute access, properties are dispatched through add_call() instead of direct field access.
Key Changes:
- Added
Struct.propertiesdict to track property Functions - Property names hashed into struct identity (
native_name) - Property access handled in
emit_Attribute()via function call dispatch - Validation rejects setters and deleters at struct definition time
Issues Found:
- Test error message regex doesn't match implementation ("do not support" vs "with setters are not supported")
- Uses
inspect.getfullargspec()instead ofget_full_arg_spec()helper, risking stringized annotation issues - Only property names hashed, not return types (inconsistent with field hashing)
- No test coverage for property assignment attempts inside kernels
Confidence Score: 3/5
- This PR has implementation issues that need fixing before merge
- Score reflects one critical test failure (regex mismatch will cause test to fail), plus several code quality and consistency issues. The core feature implementation is sound, but the test bug must be fixed.
- Pay close attention to the test regex fix in
warp/tests/test_struct.pyand consider the annotation handling improvement inwarp/_src/codegen.py
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| warp/_src/codegen.py | 3/5 | Added property support for warp structs; found issues with error message mismatch, potential annotation handling bug, and missing assignment validation |
| warp/tests/test_struct.py | 3/5 | Added basic property tests; error message regex doesn't match actual error message in implementation |
Sequence Diagram
sequenceDiagram
participant User
participant Decorator as @wp.struct
participant Struct as Struct.__init__
participant inspect
participant Function
participant Kernel as @wp.kernel
participant Adjoint as emit_Attribute
User->>Decorator: Define struct with @property
Decorator->>Struct: Create Struct instance
Struct->>inspect: getmembers(cls)
inspect-->>Struct: property_members list
Struct->>Struct: Hash fields and property names
Struct->>Struct: Set native_name
loop For each property
Struct->>inspect: getfullargspec(getter)
Struct->>Struct: Add self annotation
Struct->>Function: Create Function(getter)
Function-->>Struct: Property function
Struct->>Struct: Store in self.properties
end
User->>Kernel: Access struct.property
Kernel->>Adjoint: emit_Attribute(node)
Adjoint->>Adjoint: Check if attr in properties
Adjoint->>Adjoint: add_call(prop_func, args)
Adjoint-->>Kernel: Return property value
Additional Comments (4)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
warp/tests/test_struct.py (2)
925-939: LGTM! Test correctly validates setter rejection.The test properly verifies that defining a property with a setter raises a
TypeErrorwith an appropriate message. This addresses the past review feedback requesting this validation.Optional: Consider removing unused
deviceparameter.The
deviceparameter on line 925 is not used in this test since it only validates struct definition, not kernel execution. While keeping it maintains signature consistency with other test functions, removing it would eliminate the static analysis warning.♻️ Optional refactor to remove unused parameter
-def test_struct_property_with_setter(test, device): +def test_struct_property_with_setter(test, _device): """Tests that structs with properties (setters) are not supported."""or update the test registration to pass
devices=None:-add_function_test(TestStruct, "test_struct_property_with_setter", test_struct_property_with_setter, devices=devices) +add_function_test(TestStruct, "test_struct_property_with_setter", test_struct_property_with_setter, devices=None)
899-943: Optional: Consider expanding test coverage for edge cases.While the current tests adequately cover the basic property functionality, you might consider adding tests for:
- Multiple properties on a single struct
- Properties that reference multiple fields
- Properties on nested structs
- Property with a deleter (to verify it's handled correctly or rejected)
These are nice-to-have improvements rather than blockers.
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
warp/tests/test_struct.py
🧰 Additional context used
🧬 Code graph analysis (1)
warp/tests/test_struct.py (1)
warp/_src/context.py (1)
struct(1320-1342)
🪛 Ruff (0.14.10)
warp/tests/test_struct.py
913-913: Unused function argument: test
(ARG001)
922-922: assert_np_equal may be undefined, or defined from star imports
(F405)
925-925: Unused function argument: device
(ARG001)
942-942: add_function_test may be undefined, or defined from star imports
(F405)
943-943: add_function_test may be undefined, or defined from star imports
(F405)
🔇 Additional comments (4)
warp/tests/test_struct.py (4)
899-906: LGTM! Clean test struct with read-only property.The
StructWithPropertydefinition is well-structured with a simple property getter that returns the negation of thevaluefield. This provides a clear test case for the property feature.
908-910: LGTM! Kernel correctly demonstrates property access.The kernel properly accesses the struct property using dot-notation (
s.neg_value), which aligns with the PR objectives for enabling property access inside kernels.
913-922: LGTM! Test correctly verifies property getter functionality.The test properly validates that property getters work end-to-end: it sets a value, launches a kernel that reads the property, and asserts the correct negated result.
942-943: LGTM! Test registrations are correct.Both tests are properly registered following the standard pattern used throughout the file.
Greptile found no issues!From now on, if a review finishes and we haven't found any issues, we will not post anything, but you can confirm that we reviewed your changes in the status check section. This feature can be toggled off in your Code Review Settings by deselecting "Create a status check for each PR". |
997063a to
6168508
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
warp/_src/codegen.py (1)
532-539: Consider copying annotations before mutation.The code modifies
getter.__annotations__directly, which mutates the original function's annotations. While property getters on Warp structs are unlikely to be reused elsewhere, defensively copying the annotations dict before modification would be safer.♻️ Suggested fix
# We need to add 'self' as the first argument, with the type of the struct itself. # This allows overload resolution to match the struct instance to the 'self' argument. - if not hasattr(getter, "__annotations__"): - getter.__annotations__ = {} + # Copy annotations to avoid mutating the original function + annotations = dict(getattr(getter, "__annotations__", {})) # Find the name of the first argument (conventionally 'self') argspec = get_full_arg_spec(getter) if len(argspec.args) > 0: self_arg = argspec.args[0] - getter.__annotations__[self_arg] = self + annotations[self_arg] = self + getter.__annotations__ = annotations
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
warp/_src/codegen.pywarp/tests/test_struct.py
🧰 Additional context used
🧬 Code graph analysis (1)
warp/tests/test_struct.py (2)
warp/_src/context.py (4)
struct(1320-1342)kernel(1174-1316)launch(6594-6635)launch(6638-6877)warp/tests/unittest_utils.py (1)
add_function_test(282-301)
🪛 Ruff (0.14.10)
warp/tests/test_struct.py
913-913: Unused function argument: test
(ARG001)
922-922: assert_np_equal may be undefined, or defined from star imports
(F405)
925-925: Unused function argument: device
(ARG001)
942-942: add_function_test may be undefined, or defined from star imports
(F405)
943-943: add_function_test may be undefined, or defined from star imports
(F405)
warp/_src/codegen.py
526-526: Avoid specifying long messages outside the exception class
(TRY003)
528-528: Avoid specifying long messages outside the exception class
(TRY003)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Greptile Review
🔇 Additional comments (8)
warp/tests/test_struct.py (3)
899-910: LGTM!The struct definition with a read-only property and the corresponding kernel that accesses
s.neg_valueare correctly implemented for testing the new property feature.
913-922: LGTM!The test correctly validates that struct properties (getters) work as expected by verifying the computed negative value.
925-943: LGTM!The test correctly validates that struct properties with setters raise a
TypeError, addressing the previous review feedback. The unuseddeviceparameter is consistent with similar test patterns in this file (e.g.,test_struct_inheritance_error).warp/_src/codegen.py (5)
463-463: LGTM!Proper initialization of the properties dictionary to store generated Warp Functions for struct property getters.
486-490: LGTM!Using
inspect.getmembersto collect properties is appropriate. Deferring Function creation until afternative_nameis computed is the correct approach since the Function's native name depends on it.
512-515: LGTM!Including property names in the hash ensures that adding, removing, or renaming properties invalidates the module hash, triggering recompilation as expected.
545-555: LGTM!The Function creation correctly:
- Uses the property getter as the source function for type inference.
- Sets a unique key
{struct.key}.{property_name}.- Uses an empty namespace to generate a free function.
- Assigns a predictable native function name
{native_name}_{property_name}.
2309-2312: LGTM!The attribute dispatch logic correctly intercepts property access on struct types and routes to the generated property Function via
add_call, seamlessly integrating with existing codegen infrastructure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 files reviewed, 2 comments
| if len(argspec.args) > 0: | ||
| self_arg = argspec.args[0] | ||
| getter.__annotations__[self_arg] = self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: If property getter has no arguments, self annotation won't be added but Function will still be created, potentially causing issues during code generation
| if len(argspec.args) > 0: | |
| self_arg = argspec.args[0] | |
| getter.__annotations__[self_arg] = self | |
| if len(argspec.args) == 0: | |
| raise TypeError(f"Property getter '{name}' must have at least one argument (self)") | |
| self_arg = argspec.args[0] | |
| getter.__annotations__[self_arg] = self |
| for name, item in inspect.getmembers(self.cls): | ||
| if isinstance(item, property): | ||
| property_members.append((name, item)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: No validation prevents properties from having the same name as struct fields, which could cause ambiguity since properties are checked before vars in attribute access (line 2309)
| for name, item in inspect.getmembers(self.cls): | |
| if isinstance(item, property): | |
| property_members.append((name, item)) | |
| for name, item in inspect.getmembers(self.cls): | |
| if isinstance(item, property): | |
| if name in self.vars: | |
| raise TypeError(f"Property '{name}' conflicts with struct field of the same name") | |
| property_members.append((name, item)) |
Signed-off-by: Alexandre Ghelfi <[email protected]>
6168508 to
6c226e1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
warp/tests/test_struct.py (1)
913-923: Minor: Unusedtestparameter.The test logic is correct and properly verifies that property getters work in kernels. However, the
testparameter is unused in this function (though it may be required by theadd_function_testframework signature).If the framework allows it, consider removing the parameter:
-def test_struct_property(test, device): +def test_struct_property(device):Otherwise, you can prefix it with an underscore to indicate it's intentionally unused:
_test.warp/_src/codegen.py (1)
521-556: Property Function creation is correct with minor style suggestion.The implementation properly:
- Defers Function creation until after
native_nameis available (needed for unique native_func naming)- Correctly rejects setters and deleters with appropriate error messages
- Annotates the getter's first argument with the Struct type for proper overload resolution
- Creates Functions with unique
native_funcnames based on the struct's native_nameThe error messages on lines 526 and 528 could be extracted to constants to follow best practices, but this is a minor style issue.
♻️ Optional: Extract error messages to constants
To follow TRY003 guidelines, consider extracting the error messages:
+# At module level +_STRUCT_PROPERTY_SETTER_ERROR = "Struct properties with setters are not supported" +_STRUCT_PROPERTY_DELETER_ERROR = "Struct properties with deleters are not supported" + # In Struct.__init__: if item.fset is not None: - raise TypeError("Struct properties with setters are not supported") + raise TypeError(_STRUCT_PROPERTY_SETTER_ERROR) if item.fdel is not None: - raise TypeError("Struct properties with deleters are not supported") + raise TypeError(_STRUCT_PROPERTY_DELETER_ERROR)
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
warp/_src/codegen.pywarp/tests/test_struct.py
🧰 Additional context used
🧬 Code graph analysis (2)
warp/tests/test_struct.py (1)
warp/_src/context.py (4)
struct(1320-1342)kernel(1174-1316)launch(6594-6635)launch(6638-6877)
warp/_src/codegen.py (1)
warp/_src/context.py (3)
context(3470-3485)Function(132-528)func(862-905)
🪛 Ruff (0.14.10)
warp/tests/test_struct.py
913-913: Unused function argument: test
(ARG001)
922-922: assert_np_equal may be undefined, or defined from star imports
(F405)
925-925: Unused function argument: device
(ARG001)
942-942: add_function_test may be undefined, or defined from star imports
(F405)
943-943: add_function_test may be undefined, or defined from star imports
(F405)
warp/_src/codegen.py
526-526: Avoid specifying long messages outside the exception class
(TRY003)
528-528: Avoid specifying long messages outside the exception class
(TRY003)
🔇 Additional comments (6)
warp/tests/test_struct.py (3)
899-906: LGTM! Clean test struct definition.The
StructWithPropertyprovides a clear test case for read-only property getters on structs.
908-911: LGTM! Correct kernel implementation.The kernel properly accesses the struct property and demonstrates the expected usage pattern.
925-943: Test logic is correct.The test properly verifies that struct properties with setters are rejected with the appropriate error message. The unused
deviceparameter (line 925) is likely required by theadd_function_testframework signature, since the test is registered withdevices=devices(line 943).warp/_src/codegen.py (3)
463-463: LGTM! Property storage correctly initialized.The
propertiesdictionary is properly initialized to store the mapping from property names to their corresponding Warp Functions.
486-516: LGTM! Property collection and hashing correctly implemented.The implementation properly:
- Collects property metadata early using
inspect.getmembers- Includes property names in the struct hash to ensure identity stability
- Computes the hash before
native_name, which depends on itThis ensures that adding, removing, or renaming properties will result in a different struct hash, which is the correct behavior.
2309-2313: LGTM! Property attribute dispatch correctly implemented.The code properly routes struct property reads to the corresponding Warp Functions:
- Checks that the aggregate is a Struct and the attribute is in its properties
- Uses
add_callto invoke the property Function with the struct instance as the first argument- This integrates seamlessly with the existing attribute resolution flow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4 files reviewed, 4 comments
| argspec = get_full_arg_spec(getter) | ||
| if len(argspec.args) > 0: | ||
| self_arg = argspec.args[0] | ||
| getter.__annotations__[self_arg] = self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing validation for property getters without arguments. If a property getter has no arguments (e.g., @property\ndef prop() -> float: return 1.0), the code silently skips adding the self annotation, which will cause issues during function resolution when the property is accessed.
Consider adding validation:
| argspec = get_full_arg_spec(getter) | |
| if len(argspec.args) > 0: | |
| self_arg = argspec.args[0] | |
| getter.__annotations__[self_arg] = self | |
| # Find the name of the first argument (conventionally 'self') | |
| argspec = get_full_arg_spec(getter) | |
| if len(argspec.args) == 0: | |
| raise TypeError(f"Struct property '{name}' must have at least one argument (self)") | |
| if len(argspec.args) > 0: | |
| self_arg = argspec.args[0] | |
| getter.__annotations__[self_arg] = self |
| # Collect properties, but postpone Function creation until after native_name is set | ||
| property_members = [] | ||
| for name, item in inspect.getmembers(self.cls): | ||
| if isinstance(item, property): | ||
| property_members.append((name, item)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No validation prevents property names from colliding with field names. Since property access is checked before field access (line 2309-2312), a property with the same name as a field will shadow the field, making the field inaccessible. This could lead to confusing behavior.
Consider adding a validation check:
| # Collect properties, but postpone Function creation until after native_name is set | |
| property_members = [] | |
| for name, item in inspect.getmembers(self.cls): | |
| if isinstance(item, property): | |
| property_members.append((name, item)) | |
| # Collect properties, but postpone Function creation until after native_name is set | |
| property_members = [] | |
| for name, item in inspect.getmembers(self.cls): | |
| if isinstance(item, property): | |
| if name in self.vars: | |
| raise TypeError(f"Property '{name}' conflicts with field name in struct '{self.key}'") | |
| property_members.append((name, item)) |
| if not hasattr(getter, "__annotations__"): | ||
| getter.__annotations__ = {} | ||
|
|
||
| # Find the name of the first argument (conventionally 'self') | ||
| argspec = get_full_arg_spec(getter) | ||
| if len(argspec.args) > 0: | ||
| self_arg = argspec.args[0] | ||
| getter.__annotations__[self_arg] = self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Directly mutating the original getter function's __annotations__ dictionary is problematic. If the struct class is instantiated multiple times or the getter function is used elsewhere, this mutation will affect all instances and could cause unexpected behavior or conflicts.
Consider copying the annotations dictionary instead:
| if not hasattr(getter, "__annotations__"): | |
| getter.__annotations__ = {} | |
| # Find the name of the first argument (conventionally 'self') | |
| argspec = get_full_arg_spec(getter) | |
| if len(argspec.args) > 0: | |
| self_arg = argspec.args[0] | |
| getter.__annotations__[self_arg] = self | |
| # We need to add 'self' as the first argument, with the type of the struct itself. | |
| # This allows overload resolution to match the struct instance to the 'self' argument. | |
| # Copy annotations to avoid mutating the original function | |
| annotations = dict(getattr(getter, "__annotations__", {})) | |
| # Find the name of the first argument (conventionally 'self') | |
| argspec = get_full_arg_spec(getter) | |
| if len(argspec.args) > 0: | |
| self_arg = argspec.args[0] | |
| annotations[self_arg] = self | |
| getter.__annotations__ = annotations |
| add_function_test(TestStruct, "test_struct_property", test_struct_property, devices=devices) | ||
| add_function_test(TestStruct, "test_struct_property_with_setter", test_struct_property_with_setter, devices=devices) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test coverage could be improved. Consider adding tests for:
- Property with deleter (should raise TypeError)
- Property name that conflicts with a field name (currently silently shadows the field)
- Property getter with no arguments (currently fails with unclear error)
- Attempting to assign to a property inside a kernel (e.g.,
s.neg_value = 10) - Property without return type annotation
- Multiple properties on the same struct
- Property that accesses another property
These edge cases would help ensure robust error handling and prevent confusing runtime errors.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Description
Add support for
@propertyinwarp.struct.Allows user to define property getters in the warp.struct. The properties are converted into warp functions at the codegen step.
Closes #1152
Before your PR is "Ready for review"
__init__.pyi,docs/api_reference/,docs/language_reference/)pre-commit run -aSummary by CodeRabbit
New Features
Bug Fixes / Behavior
Tests
✏️ Tip: You can customize this high-level summary in your review settings.