The GitHub Actions job "CI" on tvm-ffi.git/pyobject has failed. Run started by GitHub user cyx-6 (triggered by cyx-6).
Head commit for run: ab793e69f447c47cab7ba536362463f86e533796 / Yaxing Cai <[email protected]> [FEAT][Python] Tie Python wrapper lifetime to underlying C++ FFI object Make `a.x is a.x` and `id(a.x)` stable in Python by attaching every Python wrapper to its underlying C++ object via a 16-byte `PyCustomAllocHeader` prepended to every Object allocation. Reported by Junru Shao as a top source of agent / OAI-monorepo migration failures. Implements the state-(a)<->(b) portion of Tianqi Chen's "PyObjectTying" doc (2026-05-01). Design: - Generic custom-allocator hook in core libtvm_ffi.so (no Python knowledge): `TVMFFICustomAllocHeader { delete_space }`, `TVMFFICustomAllocator { allocate }`, plus `TVMFFIGetCustomAllocator` / `TVMFFISetCustomAllocator` / `TVMFFISetDefaultCustomAllocator`. libtvm_ffi installs a builtin default at registry init, so every `make_object<T>` carries at least a 8-byte base header. The Python Cython module overrides the global default at module load with `TVMFFIPyAllocate`, which prepends the 16-byte `PyCustomAllocHeader { py_object; base }`. - Single deleter per Handler: `Handler<T>::Deleter_` always invokes `GetCustomAllocHeader(tptr)->delete_space(tptr)`. No flag bit, no branching at deletion time. The deleter is uniform; dispatch is in the function pointer chosen at allocation time. - State-machine reduced to (a)<->(b): `py_object == NULL` (no canonical wrapper) <-> `py_object == wrapper` (canonical wrapper alive). `_install_chandle_binding` and `_detach_chandle_binding` flip a single field. `make_ret_object`'s cache-hit fast path type-checks the cached wrapper and Py_INCREFs it; stale entries (post-move chandle, type re-registration) clear the field and fall through to a fresh wrap. - Frontend-allocation detection by `delete_space` pointer comparison (`TVMFFIPyIsCanonical`): the Python frontend recognizes its own allocations by checking `base.delete_space == &TVMFFIPyDeleteSpace`, avoiding a flag bit on TVMFFIObject. Pre-Python-init chandles (statically-initialized global functions in libtvm_ffi.so) carry only the base header; the Python side detects this and skips the binding install. - State (c) (preserve wrapper memory across a Python finalize) is intentionally out of scope. The Cython side has no `tp_finalize` resurrection, no `cache_mem`, no cross-language `PyObject_GC_Del`. Wrapper memory is owned by Python's tp_free; the C++ block is owned by the chandle's deleter. `a.x is a.x` holds while the wrapper is held alive (the user-reported case); `id()` is not preserved across a `del + gc + re-fetch` cycle. `PyClassDeleter` in extra/dataclass.cc and the `__ffi_new__` / `__ffi_shallow_copy__` paths are routed through the same registry, so Python-defined types share the layout and lifetime semantics. `TVMFFIPyArgSetterObjectRValueRef_` clears the source's binding eagerly before the C++ side nulls its `chandle`; otherwise a downstream cache lookup would see a stale back-pointer to a still-alive wrapper. Tests: full Python suite passes (2317 passed, 19 skipped, 2 xfailed). New `tests/python/test_pyobject_tying.py` covers state-(b) identity, last-ref clean-free, RValueRef move, pickle round-trip, type-mismatch fallthrough, mutable-field replacement, and PyNativeObject exemption. Report URL: https://github.com/apache/tvm-ffi/actions/runs/26214873682 With regards, GitHub Actions via GitBox --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
