Skip to content

Commit e9dc8d7

Browse files
author
Jouke Witteveen
committed
Add make_namespace function and tests
1 parent 721834b commit e9dc8d7

File tree

4 files changed

+77
-1
lines changed

4 files changed

+77
-1
lines changed

docs/advanced/pycpp/object.rst

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,46 @@ Available types include :class:`handle`, :class:`object`, :class:`bool_`,
2020
Be sure to review the :ref:`pytypes_gotchas` before using this heavily in
2121
your C++ API.
2222

23+
.. _instantiating_compound_types:
24+
25+
Instantiating compound Python types from C++
26+
============================================
27+
28+
Dictionaries can be initialized in the :class:`dict` constructor:
29+
30+
.. code-block:: cpp
31+
32+
using namespace pybind11::literals; // to bring in the `_a` literal
33+
py::dict d("spam"_a=py::none(), "eggs"_a=42);
34+
35+
A tuple of python objects can be instantiated using :func:`py::make_tuple`:
36+
37+
.. code-block:: cpp
38+
39+
py::tuple tup = py::make_tuple(42, py::none(), "spam");
40+
41+
Each element is converted to a supported Python type.
42+
43+
A `simple namespace`_ can be instantiated using :func:`py::make_namespace`:
44+
45+
.. code-block:: cpp
46+
47+
using namespace pybind11::literals; // to bring in the `_a` literal
48+
py::object ns = py::make_namespace("spam"_a=py::none(), "eggs"_a=42);
49+
50+
Attributes on a namespace can be modified with the :func:`py::delattr`,
51+
:func:`py::getattr`, and :func:`py::setattr` functions. Namespaces can be useful
52+
as stand-ins for class instances.
53+
54+
.. note::
55+
56+
``make_namespace`` is not available in Python 2.
57+
58+
.. versionchanged:: 2.7
59+
``make_namespace`` added.
60+
61+
.. _simple namespace: https://docs.python.org/3/library/types.html#types.SimpleNamespace
62+
2363
.. _casting_back_and_forth:
2464

2565
Casting back and forth
@@ -30,7 +70,7 @@ types to Python, which can be done using :func:`py::cast`:
3070

3171
.. code-block:: cpp
3272
33-
MyClass *cls = ..;
73+
MyClass *cls = ...;
3474
py::object obj = py::cast(cls);
3575
3676
The reverse direction uses the following syntax:

include/pybind11/cast.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,16 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
18901890
return result;
18911891
}
18921892

1893+
#if PY_MAJOR_VERSION >= 3
1894+
template <typename... Args,
1895+
typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>>
1896+
object make_namespace(Args&&... args_) {
1897+
PyObject *ns = _PyNamespace_New(dict(std::forward<Args>(args_)...).ptr());
1898+
if (!ns) throw error_already_set();
1899+
return reinterpret_steal<object>(ns);
1900+
}
1901+
#endif
1902+
18931903
/// \ingroup annotations
18941904
/// Annotation for arguments
18951905
struct arg {

tests/test_pytypes.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,19 @@ TEST_SUBMODULE(pytypes, m) {
7575
return dict.contains(val);
7676
});
7777

78+
// test_tuple
79+
m.def("get_tuple", []() { return py::make_tuple(42, py::none(), "spam"); });
80+
81+
#if PY_MAJOR_VERSION >= 3
82+
// test_namespace
83+
m.def("get_namespace", []() {
84+
auto ns = py::make_namespace("attr"_a=42, "x"_a="foo", "wrong"_a=1);
85+
py::delattr(ns, "wrong");
86+
py::setattr(ns, "right", py::int_(2));
87+
return ns;
88+
});
89+
#endif
90+
7891
// test_str
7992
m.def("str_from_string", []() { return py::str(std::string("baz")); });
8093
m.def("str_from_bytes", []() { return py::str(py::bytes("boo", 3)); });

tests/test_pytypes.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,19 @@ def test_dict(capture, doc):
9898
assert m.dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3}
9999

100100

101+
def test_tuple():
102+
assert m.get_tuple() == (42, None, "spam")
103+
104+
105+
@pytest.mark.skipif("env.PY2")
106+
def test_namespace():
107+
ns = m.get_namespace()
108+
assert ns.attr == 42
109+
assert ns.x == "foo"
110+
assert ns.right == 2
111+
assert not hasattr(ns, "wrong")
112+
113+
101114
def test_str(doc):
102115
assert m.str_from_string().encode().decode() == "baz"
103116
assert m.str_from_bytes().encode().decode() == "boo"

0 commit comments

Comments
 (0)