This is an automated email from the ASF dual-hosted git repository.

junrushao 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 721d8781 fix(python): remove broken 
__instancecheck__/__subclasscheck__ from _ObjectSlotsMeta (#498)
721d8781 is described below

commit 721d87816152e4a1cdc5c7906b116d46007699f8
Author: Junru Shao <[email protected]>
AuthorDate: Fri Mar 6 16:29:13 2026 -0800

    fix(python): remove broken __instancecheck__/__subclasscheck__ from 
_ObjectSlotsMeta (#498)
    
    ## Summary
    
    - Remove `__instancecheck__` and `__subclasscheck__` from
    `_ObjectSlotsMeta` in `python/tvm_ffi/cython/object.pxi`. These methods
    unconditionally returned `True` for any `CObject` instance/subclass
    regardless of which class was being checked, causing incorrect behavior
    (e.g., `isinstance(Map(...), Array)` returning `True`).
    - The methods are unnecessary because all objects returned from C++ are
    always constructed as proper `Object` subclasses, so standard Python
    MRO-based `isinstance`/`issubclass` checks work correctly.
    - Update `test_dataclass_init.py` to assert correct behavior (parent is
    not an instance of child).
    - Add 22 new regression tests in `TestIsinstanceIssubclass` covering
    containers, inheritance, and cross-hierarchy `isinstance`/`issubclass`
    checks.
    
    ## Test plan
    
    - [x] All 1517 existing Python tests pass
    - [x] 22 new tests verify correct isinstance/issubclass behavior for
    containers (Array, List, Map, Dict), inheritance hierarchies, and
    cross-hierarchy checks
    - [x] Pre-commit hooks pass (ruff, cython-lint, ty, etc.)
---
 python/tvm_ffi/cython/object.pxi    |  13 ----
 tests/python/test_dataclass_init.py |  11 +--
 tests/python/test_object.py         | 139 ++++++++++++++++++++++++++++++++++++
 3 files changed, 142 insertions(+), 21 deletions(-)

diff --git a/python/tvm_ffi/cython/object.pxi b/python/tvm_ffi/cython/object.pxi
index 6eb22539..b02c95e0 100644
--- a/python/tvm_ffi/cython/object.pxi
+++ b/python/tvm_ffi/cython/object.pxi
@@ -175,19 +175,6 @@ class _ObjectSlotsMeta(ABCMeta):
     def __init__(cls, name: str, bases: tuple[type, ...], ns: dict[str, Any], 
**kwargs: Any):
         super().__init__(name, bases, ns, **kwargs)
 
-    def __instancecheck__(cls, instance: Any) -> bool:
-        if isinstance(instance, CObject):
-            return True
-        return super().__instancecheck__(instance)
-
-    def __subclasscheck__(cls, subclass: type) -> bool:
-        try:
-            if issubclass(subclass, CObject):
-                return True
-        except TypeError:
-            pass
-        return super().__subclasscheck__(subclass)
-
 
 class Object(CObject, metaclass=_ObjectSlotsMeta):
     """Base class of all TVM FFI objects.
diff --git a/tests/python/test_dataclass_init.py 
b/tests/python/test_dataclass_init.py
index 918838cf..bc349746 100644
--- a/tests/python/test_dataclass_init.py
+++ b/tests/python/test_dataclass_init.py
@@ -740,15 +740,10 @@ class TestAutoInitTypeChecks:
         assert isinstance(obj, _TestCxxAutoInitParent)
         assert isinstance(obj, core.Object)
 
-    def test_parent_isinstance_child_due_to_metaclass(self) -> None:
-        """Due to _ObjectSlotsMeta, any CObject passes isinstance for any FFI 
class.
-
-        This is a pre-existing design choice in the TVM FFI type system, not a 
bug
-        introduced by the auto-init feature.
-        """
+    def test_parent_not_isinstance_child(self) -> None:
+        """A parent object should not pass isinstance check for a child 
class."""
         obj = _TestCxxAutoInitParent(1)
-        # _ObjectSlotsMeta.__instancecheck__ returns True for any CObject
-        assert isinstance(obj, _TestCxxAutoInitChild)
+        assert not isinstance(obj, _TestCxxAutoInitChild)
 
 
 class TestAutoInitInstanceIsolation:
diff --git a/tests/python/test_object.py b/tests/python/test_object.py
index 74ced83b..edb6fec2 100644
--- a/tests/python/test_object.py
+++ b/tests/python/test_object.py
@@ -326,3 +326,142 @@ def test_get_registered_type_keys() -> None:
         assert ty.startswith("ffi.") or ty.startswith("testing."), (
             f"Expected type key `{ty}` to start with `ffi.` or `testing.`"
         )
+
+
+# ---------------------------------------------------------------------------
+# isinstance / issubclass correctness for the Object type hierarchy
+# ---------------------------------------------------------------------------
+
+
+class TestIsinstanceIssubclass:
+    """Verify that isinstance/issubclass respect the actual type hierarchy.
+
+    Regression tests for a bug where _ObjectSlotsMeta.__instancecheck__
+    and __subclasscheck__ returned True for *any* CObject against *any*
+    Object subclass, making e.g. isinstance(Map(...), Array) == True.
+    """
+
+    # -- containers: sibling types must not match each other ----------------
+
+    def test_map_not_isinstance_array(self) -> None:
+        """Map instance should not pass isinstance check for Array."""
+        m = tvm_ffi.Map({"a": 1})
+        assert not isinstance(m, tvm_ffi.Array)
+
+    def test_array_not_isinstance_map(self) -> None:
+        """Array instance should not pass isinstance check for Map."""
+        a = tvm_ffi.Array([1, 2])
+        assert not isinstance(a, tvm_ffi.Map)
+
+    def test_list_not_isinstance_dict(self) -> None:
+        """List instance should not pass isinstance check for Dict."""
+        lst = tvm_ffi.List([1, 2])
+        assert not isinstance(lst, tvm_ffi.Dict)
+
+    def test_dict_not_isinstance_list(self) -> None:
+        """Dict instance should not pass isinstance check for List."""
+        d = tvm_ffi.Dict({"k": 1})
+        assert not isinstance(d, tvm_ffi.List)
+
+    def test_array_not_isinstance_list(self) -> None:
+        """Array instance should not pass isinstance check for List."""
+        a = tvm_ffi.Array([1])
+        assert not isinstance(a, tvm_ffi.List)
+
+    def test_map_not_isinstance_dict(self) -> None:
+        """Map instance should not pass isinstance check for Dict."""
+        m = tvm_ffi.Map({"a": 1})
+        assert not isinstance(m, tvm_ffi.Dict)
+
+    # -- containers: positive isinstance ------------------------------------
+
+    def test_array_isinstance_object(self) -> None:
+        """Array instance should pass isinstance check for Object."""
+        a = tvm_ffi.Array([1])
+        assert isinstance(a, tvm_ffi.Object)
+
+    def test_map_isinstance_object(self) -> None:
+        """Map instance should pass isinstance check for Object."""
+        m = tvm_ffi.Map({"a": 1})
+        assert isinstance(m, tvm_ffi.Object)
+
+    def test_list_isinstance_object(self) -> None:
+        """List instance should pass isinstance check for Object."""
+        lst = tvm_ffi.List([1])
+        assert isinstance(lst, tvm_ffi.Object)
+
+    def test_dict_isinstance_object(self) -> None:
+        """Dict instance should pass isinstance check for Object."""
+        d = tvm_ffi.Dict({"k": 1})
+        assert isinstance(d, tvm_ffi.Object)
+
+    # -- registered user types: parent / child ------------------------------
+
+    def test_derived_isinstance_base(self) -> None:
+        """Derived object should pass isinstance check for its base class."""
+        obj = tvm_ffi.testing.TestObjectDerived(
+            v_map={"a": 1},
+            v_array=[1],
+        )
+        assert isinstance(obj, tvm_ffi.testing.TestObjectBase)
+        assert isinstance(obj, tvm_ffi.Object)
+
+    def test_base_not_isinstance_derived(self) -> None:
+        """Base object should not pass isinstance check for a derived class."""
+        obj = tvm_ffi.testing.TestObjectBase()
+        assert not isinstance(obj, tvm_ffi.testing.TestObjectDerived)
+
+    def test_derived_isinstance_own_class(self) -> None:
+        """Derived object should pass isinstance check for its own class."""
+        obj = tvm_ffi.testing.TestObjectDerived(
+            v_map={"a": 1},
+            v_array=[1],
+        )
+        assert isinstance(obj, tvm_ffi.testing.TestObjectDerived)
+
+    # -- cross-hierarchy: user object vs container --------------------------
+
+    def test_user_object_not_isinstance_array(self) -> None:
+        """User-defined object should not pass isinstance check for Array."""
+        obj = tvm_ffi.testing.TestObjectBase()
+        assert not isinstance(obj, tvm_ffi.Array)
+
+    def test_array_not_isinstance_user_object(self) -> None:
+        """Array should not pass isinstance check for a user-defined type."""
+        a = tvm_ffi.Array([1])
+        assert not isinstance(a, tvm_ffi.testing.TestObjectBase)
+
+    # -- issubclass mirrors isinstance --------------------------------------
+
+    def test_issubclass_derived_base(self) -> None:
+        """Derived class should be a subclass of its base."""
+        assert issubclass(tvm_ffi.testing.TestObjectDerived, 
tvm_ffi.testing.TestObjectBase)
+
+    def test_issubclass_base_not_derived(self) -> None:
+        """Base class should not be a subclass of its derived class."""
+        assert not issubclass(tvm_ffi.testing.TestObjectBase, 
tvm_ffi.testing.TestObjectDerived)
+
+    def test_issubclass_array_not_map(self) -> None:
+        """Array should not be a subclass of Map."""
+        assert not issubclass(tvm_ffi.Array, tvm_ffi.Map)
+
+    def test_issubclass_map_not_array(self) -> None:
+        """Map should not be a subclass of Array."""
+        assert not issubclass(tvm_ffi.Map, tvm_ffi.Array)
+
+    def test_issubclass_all_containers_are_object(self) -> None:
+        """All container types should be subclasses of Object."""
+        for cls in (tvm_ffi.Array, tvm_ffi.List, tvm_ffi.Map, tvm_ffi.Dict):
+            assert issubclass(cls, tvm_ffi.Object)
+
+    def test_issubclass_user_type_is_object(self) -> None:
+        """User-defined types should be subclasses of Object."""
+        assert issubclass(tvm_ffi.testing.TestObjectBase, tvm_ffi.Object)
+        assert issubclass(tvm_ffi.testing.TestObjectDerived, tvm_ffi.Object)
+
+    def test_issubclass_sibling_containers(self) -> None:
+        """Sibling container types should not be subclasses of each other."""
+        assert not issubclass(tvm_ffi.List, tvm_ffi.Array)
+        assert not issubclass(tvm_ffi.Dict, tvm_ffi.Map)
+        assert not issubclass(tvm_ffi.Array, tvm_ffi.List)
+        assert not issubclass(tvm_ffi.Map, tvm_ffi.Dict)

Reply via email to