Skip to content

Commit

Permalink
Add an API isinstance(inst, cls) that mirrors the Python isinstance. (#…
Browse files Browse the repository at this point in the history
…805)

The current `isinstance<T>(inst)` tests if a Python object is an
instance of a bound C++ class. This new overload tests if a Python
object is an instance of a Python class. I have needed this helper in
several nanobind-using codebases, and the same API exists in pybind11.
  • Loading branch information
hawkinsp authored Dec 5, 2024
1 parent db7b87a commit ddc7ad5
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/api_core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,10 @@ Type queries
to convert the object to C++. It may be more efficient to just perform the
conversion using :cpp:func:`cast` and catch potential raised exceptions.

.. cpp:function:: isinstance(handle inst, handle cls)

Checks if the Python object `inst` is an instance of the Python type `cls`.

.. cpp:function:: template <typename T> handle type() noexcept

Returns the Python type object associated with the C++ type `T`. When the
Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ Version TBD (unreleased)
Previously, after ``a += b``, ``a`` would be replaced with a copy.
(PR `#803 <https://github.com/wjakob/nanobind/pull/803>`__)

- Added an overload to :cpp:func:`isinstance` which tests if a Python object
is an instance of a Python class. This is in addition to the existing
overload, which tests if a Python object is an instance of a bound C++ class.

- Added support for overriding static properties, such as those defined using
``def_prop_ro_static``, in subclasses. Previously this would fail with an
error.
Expand Down
7 changes: 7 additions & 0 deletions include/nanobind/nb_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ inline Py_hash_t hash(handle h) {
return rv;
}

inline bool isinstance(handle inst, handle cls) {
int ret = PyObject_IsInstance(inst.ptr(), cls.ptr());
if (ret == -1)
nanobind::raise_python_error();
return ret;
}

inline bool is_alive() noexcept {
return detail::is_alive();
}
Expand Down
3 changes: 3 additions & 0 deletions tests/test_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,9 @@ NB_MODULE(test_functions_ext, m) {
});

m.def("hash_it", [](nb::handle h) { return nb::hash(h); });
m.def("isinstance_", [](nb::handle inst, nb::handle cls) {
return nb::isinstance(inst, cls);
});

// Test bytearray type
m.def("test_bytearray_new", []() { return nb::bytearray(); });
Expand Down
6 changes: 6 additions & 0 deletions tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,3 +725,9 @@ def case(arg1, arg2, expect_ret): # type: (str, str, str | None) -> str
case("swapfrom", "xxx", "<unfinished>")
with pytest.raises(RuntimeError, match="offset too large"):
case("swapfrom", "10", "<unfinished>")

def test51_isinstance():
assert t.isinstance_(3, int)
assert not t.isinstance_(3, bool)
with pytest.raises(TypeError):
t.isinstance_(3, 7)

0 comments on commit ddc7ad5

Please sign in to comment.