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 86c4042  feat: add `ffi.GetInvalidObject` global function for MISSING 
singleton (#447)
86c4042 is described below

commit 86c4042d66bf432a3c4a217be1eeab3568329b5b
Author: Junru Shao <[email protected]>
AuthorDate: Sun Feb 15 04:15:57 2026 -0800

    feat: add `ffi.GetInvalidObject` global function for MISSING singleton 
(#447)
    
    C++ changes:
    - Register `ffi.GetInvalidObject` global function in container.cc
    - Remove `ffi.MapGetMissingObject` in favor of the new name
    
    Python changes:
    - Initialize `MISSING` in core.pyx via `_get_global_func` after
    `Function` is registered
    - Re-export `MISSING` in container.py from core
    - Add `MISSING: Object` to core.pyi type stub
    - Update _ffi_api.py stubs and __all__
    
    Tests:
    - Add `test_missing_object` verifying singleton identity across imports
    and Map.get() integration
---
 python/tvm_ffi/_ffi_api.py     |  6 +++---
 python/tvm_ffi/container.py    |  2 +-
 python/tvm_ffi/core.pyi        |  1 +
 python/tvm_ffi/cython/core.pyx |  3 +++
 src/ffi/container.cc           | 17 +++++++++--------
 tests/python/test_container.py | 15 +++++++++++++++
 6 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/python/tvm_ffi/_ffi_api.py b/python/tvm_ffi/_ffi_api.py
index a76188d..92606c9 100644
--- a/python/tvm_ffi/_ffi_api.py
+++ b/python/tvm_ffi/_ffi_api.py
@@ -24,7 +24,7 @@ from .registry import init_ffi_api as _FFI_INIT_FUNC
 from typing import TYPE_CHECKING
 if TYPE_CHECKING:
     from collections.abc import Mapping, Sequence
-    from tvm_ffi import Module, Object
+    from tvm_ffi import Module
     from tvm_ffi.access_path import AccessPath
     from typing import Any, Callable
 # isort: on
@@ -42,6 +42,7 @@ if TYPE_CHECKING:
     def Bytes(_0: bytes, /) -> bytes: ...
     def FromJSONGraph(_0: Any, /) -> Any: ...
     def FromJSONGraphString(_0: str, /) -> Any: ...
+    def GetInvalidObject() -> Any: ...
     def FunctionListGlobalNamesFunctor() -> Callable[..., Any]: ...
     def FunctionRemoveGlobal(_0: str, /) -> bool: ...
     def GetFirstStructuralMismatch(_0: Any, _1: Any, _2: bool, _3: bool, /) -> 
tuple[AccessPath, AccessPath] | None: ...
@@ -66,7 +67,6 @@ if TYPE_CHECKING:
     def MapForwardIterFunctor(_0: Mapping[Any, Any], /) -> Callable[..., Any]: 
...
     def MapGetItem(_0: Mapping[Any, Any], _1: Any, /) -> Any: ...
     def MapGetItemOrMissing(_0: Mapping[Any, Any], _1: Any, /) -> Any: ...
-    def MapGetMissingObject() -> Object: ...
     def MapSize(_0: Mapping[Any, Any], /) -> int: ...
     def ModuleClearImports(_0: Module, /) -> None: ...
     def ModuleGetFunction(_0: Module, _1: str, _2: bool, /) -> Callable[..., 
Any] | None: ...
@@ -105,6 +105,7 @@ __all__ = [
     "FunctionRemoveGlobal",
     "GetFirstStructuralMismatch",
     "GetGlobalFuncMetadata",
+    "GetInvalidObject",
     "GetRegisteredTypeKeys",
     "List",
     "ListAppend",
@@ -125,7 +126,6 @@ __all__ = [
     "MapForwardIterFunctor",
     "MapGetItem",
     "MapGetItemOrMissing",
-    "MapGetMissingObject",
     "MapSize",
     "ModuleClearImports",
     "ModuleGetFunction",
diff --git a/python/tvm_ffi/container.py b/python/tvm_ffi/container.py
index 1b55fc8..2a358d2 100644
--- a/python/tvm_ffi/container.py
+++ b/python/tvm_ffi/container.py
@@ -79,7 +79,7 @@ K = TypeVar("K")
 V = TypeVar("V")
 _DefaultT = TypeVar("_DefaultT")
 
-MISSING = _ffi_api.MapGetMissingObject()
+from .core import MISSING
 
 
 def getitem_helper(
diff --git a/python/tvm_ffi/core.pyi b/python/tvm_ffi/core.pyi
index 2cee79c..3ad1fff 100644
--- a/python/tvm_ffi/core.pyi
+++ b/python/tvm_ffi/core.pyi
@@ -24,6 +24,7 @@ from enum import IntEnum
 from typing import Any, Callable
 
 # Public module-level variables referenced by Python code
+MISSING: Object
 ERROR_NAME_TO_TYPE: dict[str, type]
 ERROR_TYPE_TO_NAME: dict[type, str]
 
diff --git a/python/tvm_ffi/cython/core.pyx b/python/tvm_ffi/cython/core.pyx
index 6ffdc7e..b80beda 100644
--- a/python/tvm_ffi/cython/core.pyx
+++ b/python/tvm_ffi/cython/core.pyx
@@ -38,3 +38,6 @@ include "./tensor.pxi"
 _register_object_by_index(kTVMFFITensor, Tensor)
 include "./function.pxi"
 _register_object_by_index(kTVMFFIFunction, Function)
+
+# Global invalid/missing object singleton
+MISSING = _get_global_func("ffi.GetInvalidObject", False)()
diff --git a/src/ffi/container.cc b/src/ffi/container.cc
index f3171a8..6ebc7c0 100644
--- a/src/ffi/container.cc
+++ b/src/ffi/container.cc
@@ -153,14 +153,15 @@ TVM_FFI_STATIC_INIT_BLOCK() {
            [](const ffi::MapObj* n) -> ffi::Function {
              return ffi::Function::FromTyped(MapForwardIterFunctor(n->begin(), 
n->end()));
            })
-      .def("ffi.MapGetMissingObject", GetMissingObject)
-      .def("ffi.MapGetItemOrMissing", [](const ffi::MapObj* n, const Any& k) 
-> Any {
-        try {
-          return n->at(k);
-        } catch (const tvm::ffi::Error& e) {
-          return GetMissingObject();
-        }
-      });
+      .def("ffi.MapGetItemOrMissing",
+           [](const ffi::MapObj* n, const Any& k) -> Any {
+             try {
+               return n->at(k);
+             } catch (const tvm::ffi::Error& e) {
+               return GetMissingObject();
+             }
+           })
+      .def("ffi.GetInvalidObject", []() -> ObjectRef { return 
GetMissingObject(); });
 }
 }  // namespace ffi
 }  // namespace tvm
diff --git a/tests/python/test_container.py b/tests/python/test_container.py
index ec289cd..bb6384e 100644
--- a/tests/python/test_container.py
+++ b/tests/python/test_container.py
@@ -23,6 +23,8 @@ from typing import Any
 import pytest
 import tvm_ffi
 from tvm_ffi import testing
+from tvm_ffi.container import MISSING as CONTAINER_MISSING
+from tvm_ffi.core import MISSING
 
 if sys.version_info >= (3, 9):
     # PEP 585 generics
@@ -499,3 +501,16 @@ def test_seq_cross_conv_incompatible_array_to_list() -> 
None:
     arr = tvm_ffi.Array(["not", "ints"])
     with pytest.raises(TypeError):
         testing.schema_id_list_int(arr)  # type: ignore[arg-type]
+
+
+def test_missing_object() -> None:
+    """Test that MISSING is a valid singleton and works with Map.get()."""
+    # MISSING should be an Object instance
+    assert isinstance(MISSING, tvm_ffi.Object)
+    # MISSING should be the same object across imports
+    assert MISSING.same_as(CONTAINER_MISSING)
+    # Map.get() should use MISSING internally
+    m = tvm_ffi.Map({"a": 1})
+    assert m.get("a") == 1
+    assert m.get("b") is None
+    assert m.get("b", 42) == 42

Reply via email to