This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new dad4c402 test(python): add regression test for register_object
__init__ wiring (#493)
dad4c402 is described below
commit dad4c402c1dbd8321a53f8f7501f227cbb067117
Author: Junru Shao <[email protected]>
AuthorDate: Sun Mar 1 19:03:55 2026 -0800
test(python): add regression test for register_object __init__ wiring (#493)
## Summary
Add a regression test for the `register_object` `__init__` wiring that
prevents a NULL-handle segfault.
## Root Cause
`CObject` (Cython extension type) has a custom `tp_new`, so CPython's
`object.__init__` silently ignores extra positional arguments. Before
`_install_init` was wired into `register_object` (#491), calling
`TestIntPair(1, 2)` fell through to `object.__init__`, producing
`chandle = NULL`. Any subsequent field access (e.g. `pair.a`)
dereferences `chandle + field_offset` → **segfault**.
## Test Coverage
The new `test_register_object_wires_init` verifies:
1. Construction via the wired `__init__` yields a live (non-NULL) handle
with correct field values.
2. `__new__` alone leaves `chandle` at 0, confirming `__init__` is the
only path that creates the C++ object.
The test does not exercise the actual segfault (which would crash the
runner); the NULL-handle assertion is the safe proxy.
## Test Plan
- [x] `pytest -xvs
tests/python/test_object.py::test_register_object_wires_init` — passed
---
tests/python/test_object.py | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/tests/python/test_object.py b/tests/python/test_object.py
index afed61bf..74ced83b 100644
--- a/tests/python/test_object.py
+++ b/tests/python/test_object.py
@@ -153,6 +153,26 @@ def test_object_init() -> None:
assert obj.v_i64 == 7 # ty: ignore[unresolved-attribute]
+def test_register_object_wires_init() -> None:
+ """Regression: register_object must wire __init__ from __ffi_init__.
+
+ Without _install_init in register_object, calling TestIntPair(1, 2)
+ falls through to object.__init__ which silently ignores args on
+ Cython extension types (CObject has custom tp_new). The result is
+ chandle = NULL. Any subsequent field access dereferences
+ chandle + offset → segfault.
+ """
+ # Construction must produce a live handle, not NULL.
+ pair = tvm_ffi.testing.TestIntPair(3, 7)
+ assert pair.__chandle__() != 0
+ assert pair.a == 3
+ assert pair.b == 7
+
+ # __new__ alone must NOT produce a usable object (chandle stays NULL).
+ bare = tvm_ffi.testing.TestIntPair.__new__(tvm_ffi.testing.TestIntPair)
+ assert bare.__chandle__() == 0
+
+
def test_object_protocol() -> None:
class CompactObject:
def __init__(self, backend_obj: Any) -> None: