[FIX] Make ffi.Error picklable (#709)

## Motivation

`Error.py_error()` attaches the FFI error object to the Python exception
it raises:

```python
py_error.__tvm_ffi_error__ = self
```

`BaseException.__reduce__` includes the instance `__dict__`, so pickling
an FFI-originated exception pickles that `Error` through
`CObject.__getstate__` → `ToJSONGraph`. `ffi.Error` has no native
creator and no `__ffi_new__` type attr, so this fails:

```
TypeError: Type `ffi.Error` does not support ToJSONGraph
           (no native creator or __ffi_new__ type attr)
```

Harnesses that ship exceptions across a process boundary lose the
original error and report this instead. It was reported downstream
against a CUDA IMA (error 700), where the replacement message made it
look like tvm-ffi had suppressed a coredump — it had not; the coredump
happens before the error is raised, and the only real defect is that the
exception could not be pickled.

`copy.deepcopy` hits the same path.

## Changes

`ffi.Error` now pickles by value. An error is fully described by its
`(kind, message, backtrace)` strings, so `Error.__reduce__` reconstructs
it through `__init__` rather than the JSON graph. This mirrors `Device`
and `DataType`, which already carry hand-written `__reduce__`
implementations for the same reason.

`extra_context` is intentionally dropped — it may hold arbitrary native
payloads, and preserving it only when it happens to be serializable
would make pickling an error depend on where the error came from.

The attribute stays a live `Error` rather than degrading to `None`,
because `set_last_ffi_error` calls `update_backtrace` on
`__tvm_ffi_error__` when a restored exception propagates back through
the FFI; a `None` there would fail on an unrelated `hasattr` check that
is already in the code.

## Not in scope

An earlier revision of this PR also rewrote the message raised when
pickling types that genuinely cannot round-trip (`ffi.Function`,
`ffi.Module`, `ffi.Tensor`, `ffi.OpaquePyObject`). Per review, that is
dropped here and can be revisited separately — this PR is now only the
`Error` fix. Those types are unchanged and still surface the
serializer's own message.

## Testing

New tests in `tests/python/test_error.py`: exception and bare-`Error`
pickle round trips, `deepcopy`, NULL handle, the `extra_context` drop,
and re-propagating a restored exception through an FFI call (which
exercises `set_last_ffi_error`'s `update_backtrace` on the restored
object).

Full Python suite: 2421 passed, 52 skipped, 2 xfailed. `pre-commit`
clean on all changed files.
3 files changed
tree: 9b9c221a49d9f76d0f26fef4e1650875a074b9a5
  1. .agents/
  2. .claude/
  3. .github/
  4. 3rdparty/
  5. addons/
  6. cmake/
  7. docs/
  8. examples/
  9. include/
  10. licenses/
  11. python/
  12. rust/
  13. src/
  14. tests/
  15. .asf.yaml
  16. .clang-format
  17. .clang-tidy
  18. .cmake-format.json
  19. .gitignore
  20. .gitmodules
  21. .markdownlint-cli2.yaml
  22. .pre-commit-config.yaml
  23. .yamllint.yaml
  24. AGENTS.md
  25. CLAUDE.md
  26. CMakeLists.txt
  27. CONTRIBUTING.md
  28. KEYS
  29. LICENSE
  30. NOTICE
  31. pyproject.toml
  32. README.md
README.md

TVM FFI: Open ABI and FFI for Machine Learning Systems

📚 Documentation | 🚀 Quickstart

Apache TVM FFI is an open ABI and FFI for machine learning systems. It is a minimal, framework-agnostic, yet flexible open convention with the following systems in mind:

  • Kernel libraries - ship one wheel to support multiple frameworks, Python versions, and different languages. [FlashInfer]
  • Kernel DSLs - reusable open ABI for JIT and AOT kernel exposure frameworks and runtimes. [TileLang][cuteDSL]
  • Frameworks and runtimes - a uniform extension point for ABI-compliant libraries and DSLs. [PyTorch][JAX][PaddlePaddle][NumPy/CuPy]
  • ML infrastructure - out-of-box bindings and interop across languages. [Python][C++][Rust][XGrammar]
  • Coding agents - a unified mechanism for shipping generated code in production.

Features

  • Stable, minimal C ABI designed for kernels, DSLs, and runtime extensibility.
  • Zero-copy interop across PyTorch, JAX, and CuPy using DLPack protocol.
  • Compact value and call convention covering common data types for ultra low-overhead ML applications.
  • Multi-language support out of the box: Python, C++, and Rust (with a path towards more languages).

These enable broad interoperability across frameworks, libraries, DSLs, and agents; the ability to ship one wheel for multiple frameworks and Python versions (including free-threaded Python); and consistent infrastructure across environments.

Getting Started

Install TVM-FFI with pip, uv or from source:

pip install apache-tvm-ffi
pip install torch-c-dlpack-ext  # compatibility package for torch <= 2.9

Status and Release Versioning

C ABI stability is our top priority.

Status: RFC Main features are complete and ABI stable. We recognize potential needs for evolution to ensure it works best for the machine learning systems community, and would like to work together with the community for such evolution. We plan to stay in the RFC stage for three months from the v0.1.0 release.

Releases during the RFC stage will be 0.X.Y, where bumps in X indicate C ABI-breaking changes and Y indicates other changes. We anticipate the RFC stage will last for three months, then we will start following Semantic Versioning (major.minor.patch) going forward.

Documentation

Our documentation site includes:

Get Started

Guides

Concepts

Packaging

Developer Manual