feat: Added typed method registration for py_class#567
feat: Added typed method registration for py_class#567junrushao merged 4 commits intoapache:mainfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the @method decorator for explicit FFI TypeMethod registration, enabling C++ and Rust to resolve Python methods by name. It includes validation for reserved names and method types. Feedback suggests improving the decorator's robustness for callables with restricted attribute access and adding a redundant check for classmethods during registration to ensure FFI compatibility.
| raise TypeError( | ||
| f"@tvm_ffi.method: expected a callable, got {type(fn).__name__}.", | ||
| ) | ||
| fn.__ffi_method__ = True |
There was a problem hiding this comment.
Setting attributes on arbitrary callables might fail for certain types (e.g., built-in functions or objects with __slots__ that don't include __dict__). While @method is primarily intended for user-defined functions in a class body, adding a check or a try-except block would make the decorator more robust against unexpected input types.
| is_static = isinstance(value, staticmethod) | ||
| func = value.__func__ if is_static else value |
There was a problem hiding this comment.
The logic for determining is_static and func assumes that any callable not wrapped in staticmethod is an instance method. However, if a user manually marks a classmethod (bypassing the decorator's check), is_static will be False and func will be the classmethod object itself. This might lead to unexpected behavior in the FFI packed-call convention which expects either a bound method or a plain function. Consider explicitly checking for and rejecting classmethod objects here as well, or ensuring the FFI layer handles them correctly.
| is_static = isinstance(value, staticmethod) | |
| func = value.__func__ if is_static else value | |
| is_static = isinstance(value, staticmethod) | |
| if not is_static and isinstance(value, classmethod): | |
| raise TypeError(f"@py_class({cls.__name__}): classmethod {name!r} is not supported for FFI registration.") | |
| func = value.__func__ if is_static else value |
There was a problem hiding this comment.
Addressed in latest commit
|
Please fix the CI, and update docstring to be decoupled with not-yet-available features |
Add type method registration on py_class
feat(dataclasses): add @method decorator for FFI TypeMethod registration
Closes the gap where
@py_class-decorated Python classes couldn'texpose user-defined methods to the FFI reflection table. Trait
references like
$method:NAMEin__ffi_ir_traits__now resolvecleanly against Python-defined methods, on parity with C++-defined
refl::ObjectDef<T>().def("name", ...).Architecture
tvm_ffi.methoddecorator stampsfn.__ffi_method__ = Trueon thedecorated function (unwraps
staticmethodto__func__so both@method @staticmethodand@staticmethod @methodwork)._collect_py_methodsinpy_class.pywidens beyond the existing_FFI_RECOGNIZED_METHODSallowlist: it now also picks up anycallable in
cls.__dict__carrying the__ffi_method__markervia
_is_method_marked. The allowlist still routes TypeAttrColumndunders (
__ffi_repr__,__ffi_ir_traits__, etc.) throughTVMFFITypeRegisterAttrunchanged.(
TVMFFITypeRegisterMethod) — same machinery the C++ side uses.The C++ printer's
FindMethod(pyast_trait_print.cc:130-148) walksinfo->methods[]and discovers these entries identically toC++-defined methods, including ancestor-walking for inheritance.
_validate_method_namerejects@methoddecoration on:__ffi_*reserved prefix (would double-register as TypeAttrColumn),__len__,__iter__, etc. — reservedfor Python semantics),
@classmethod(theclsfirst arg breaks the packed-callconvention; raises at decoration time).
@methodreturns the original function unchanged(no wrapping); IDE / type-checker sees the method's signature
unmodified.
Public Interfaces
tvm_ffi.method(re-exported fromtvm_ffi.dataclasses)Behavioral Changes
None for existing classes. Only NEW
@method-decorated callables landin
TVMFFITypeInfo.methods[].Tests
tests/python/test_typed_method.py(new, 13 tests): registrationshape, FFI Function callability, validation errors.
tests/python/test_ir_traits.py(5 new tests): end-to-end viapyast.to_python()against$method:refs inAssignTraits.text_printer_post,`