Skip to content

Commit

Permalink
added nb::del mirroring the Python del statement
Browse files Browse the repository at this point in the history
  • Loading branch information
wjakob committed Oct 8, 2023
1 parent d4cbf99 commit 4dd7459
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 0 deletions.
22 changes: 22 additions & 0 deletions docs/api_core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,24 @@ Attribute access
Equivalent to ``del h.key`` and ``delattr(h, key)`` in Python.
Raises :cpp:class:`python_error` if the operation fails.

.. cpp:function:: template <typename T> void del(detail::accessor<T> &)

Remove an element from a sequence or mapping. The C++ statement

.. code-block:: cpp
nb::del(o[key]);
is equivalent to ``del o[key]`` in Python.

When the element cannot be removed, the function will raise
:cpp:class:`python_error` wrapping either a Python ``IndexError`` (for
sequence types) or a ``KeyError`` (for mapping types).

.. cpp:function:: template <typename T> void del(detail::accessor<T> &&)

Rvalue equivalent of the above expression.

Size queries
------------

Expand Down Expand Up @@ -672,6 +690,8 @@ Wrapper classes
read and write list elements (the bindings for this operator are provided by
the parent class and not listed here).

Use the :cpp:func:`nb::del <del>` function to remove elements.

.. cpp:function:: list()

Create an empty list
Expand Down Expand Up @@ -718,6 +738,8 @@ Wrapper classes
elements (the bindings for this operator are provided by the parent class
and not listed here).

Use the :cpp:func:`nb::del <del>` function to remove elements.

.. cpp:function:: dict()

Create an empty dictionary
Expand Down
10 changes: 10 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ Version 1.7.0 (TBA)
* Added :cpp:func:`nb::globals() <globals>`. (PR `#311
<https://github.com/wjakob/nanobind/pull/311>`__).

* Added :cpp:func:`nb::del() <del>` that takes an arbitrary accessor object.
With this new function, the C++ statement

.. code-block:: cpp
nb::del(o[key]);
is equivalent to ``del o[key]`` in Python.


Version 1.6.2 (Oct 3, 2023)
-------------------

Expand Down
21 changes: 21 additions & 0 deletions include/nanobind/nb_accessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ NAMESPACE_BEGIN(NB_NAMESPACE)
NAMESPACE_BEGIN(detail)

template <typename Impl> class accessor : public api<accessor<Impl>> {
template <typename T> friend void nanobind::del(accessor<T> &);
template <typename T> friend void nanobind::del(accessor<T> &&);
public:
static constexpr auto Name = const_name("object");

Expand All @@ -35,6 +37,9 @@ template <typename Impl> class accessor : public api<accessor<Impl>> {
NB_INLINE handle base() const { return m_base; }
NB_INLINE object key() const { return steal(Impl::key(m_key)); }

private:
NB_INLINE void del () { Impl::del(m_base, m_key); }

private:
PyObject *m_base;
mutable PyObject *m_cache{nullptr};
Expand Down Expand Up @@ -87,6 +92,10 @@ struct str_item {
NB_INLINE static void set(PyObject *obj, const char *key, PyObject *v) {
setitem(obj, key, v);
}

NB_INLINE static void del(PyObject *obj, const char *key) {
delitem(obj, key);
}
};

struct obj_item {
Expand All @@ -100,6 +109,10 @@ struct obj_item {
NB_INLINE static void set(PyObject *obj, handle key, PyObject *v) {
setitem(obj, key.ptr(), v);
}

NB_INLINE static void del(PyObject *obj, handle key) {
delitem(obj, key.ptr());
}
};

struct num_item {
Expand All @@ -113,6 +126,10 @@ struct num_item {
NB_INLINE static void set(PyObject *obj, Py_ssize_t index, PyObject *v) {
setitem(obj, index, v);
}

NB_INLINE static void del(PyObject *obj, Py_ssize_t index) {
delitem(obj, index);
}
};

struct num_item_list {
Expand All @@ -134,6 +151,10 @@ struct num_item_list {
Py_DECREF(old);
#endif
}

NB_INLINE static void del(PyObject *obj, Py_ssize_t index) {
delitem(obj, index);
}
};

struct num_item_tuple {
Expand Down
5 changes: 5 additions & 0 deletions include/nanobind/nb_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ NB_CORE void setitem(PyObject *obj, Py_ssize_t, PyObject *value);
NB_CORE void setitem(PyObject *obj, const char *key, PyObject *value);
NB_CORE void setitem(PyObject *obj, PyObject *key, PyObject *value);

/// Set an item or raise an exception
NB_CORE void delitem(PyObject *obj, Py_ssize_t);
NB_CORE void delitem(PyObject *obj, const char *key);
NB_CORE void delitem(PyObject *obj, PyObject *key);

// ========================================================================

/// Determine the length of a Python object
Expand Down
3 changes: 3 additions & 0 deletions include/nanobind/nb_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -816,4 +816,7 @@ inline detail::fast_iterator list::end() const {
}
#endif

template <typename T> void del(detail::accessor<T> &a) { a.del(); }
template <typename T> void del(detail::accessor<T> &&a) { a.del(); }

NAMESPACE_END(NB_NAMESPACE)
30 changes: 30 additions & 0 deletions src/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,36 @@ void setitem(PyObject *obj, PyObject *key, PyObject *value) {
raise_python_error();
}

void delitem(PyObject *obj, Py_ssize_t key_) {
PyObject *key = PyLong_FromSsize_t(key_);
if (!key)
raise_python_error();

int rv = PyObject_DelItem(obj, key);
Py_DECREF(key);

if (rv)
raise_python_error();
}

void delitem(PyObject *obj, const char *key_) {
PyObject *key = PyUnicode_FromString(key_);
if (!key)
raise_python_error();

int rv = PyObject_DelItem(obj, key);
Py_DECREF(key);

if (rv)
raise_python_error();
}

void delitem(PyObject *obj, PyObject *key) {
int rv = PyObject_DelItem(obj, key);
if (rv)
raise_python_error();
}

// ========================================================================

PyObject *str_from_obj(PyObject *o) {
Expand Down
3 changes: 3 additions & 0 deletions tests/test_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,7 @@ NB_MODULE(test_functions_ext, m) {
});

m.def("test_set_contains", [](nb::set s, nb::handle h) { return s.contains(h); });

m.def("test_del_list", [](nb::list l) { nb::del(l[2]); });
m.def("test_del_dict", [](nb::dict l) { nb::del(l["a"]); });
}
16 changes: 16 additions & 0 deletions tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,19 @@ def test38_set():
assert t.test_set_contains(x, '123')
assert not t.test_set_contains(x, '1234')
assert not t.test_set_contains(x, 1234)


def test39_del():
l = [0,1,2,3,4]
t.test_del_list(l)
assert l == [0, 1, 3, 4]

l = {'a' : 0, 'b' : 1}
t.test_del_dict(l)
assert l == {'b' : 1}

with pytest.raises(IndexError):
t.test_del_list([])

with pytest.raises(KeyError):
t.test_del_dict({})

0 comments on commit 4dd7459

Please sign in to comment.