Skip to content

Commit 02e38e9

Browse files
Jouke Witteveenjoukewitteveen
Jouke Witteveen
authored andcommitted
Add make_simple_namespace function and tests
1 parent 4f29b8a commit 02e38e9

File tree

4 files changed

+78
-1
lines changed

4 files changed

+78
-1
lines changed

docs/advanced/pycpp/object.rst

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,47 @@ 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
44+
:func:`py::make_simple_namespace`:
45+
46+
.. code-block:: cpp
47+
48+
using namespace pybind11::literals; // to bring in the `_a` literal
49+
py::object ns = py::make_simple_namespace("spam"_a=py::none(), "eggs"_a=42);
50+
51+
Attributes on a namespace can be modified with the :func:`py::delattr`,
52+
:func:`py::getattr`, and :func:`py::setattr` functions. Simple namespaces can
53+
be useful as lightweight stand-ins for class instances.
54+
55+
.. note::
56+
57+
``make_simple_namespace`` is not available in Python 2.
58+
59+
.. versionchanged:: 2.8
60+
``make_simple_namespace`` added.
61+
62+
.. _simple namespace: https://docs.python.org/3/library/types.html#types.SimpleNamespace
63+
2364
.. _casting_back_and_forth:
2465

2566
Casting back and forth
@@ -30,7 +71,7 @@ types to Python, which can be done using :func:`py::cast`:
3071

3172
.. code-block:: cpp
3273
33-
MyClass *cls = ..;
74+
MyClass *cls = ...;
3475
py::object obj = py::cast(cls);
3576
3677
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
@@ -1018,6 +1018,16 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
10181018
return result;
10191019
}
10201020

1021+
#if PY_VERSION_HEX >= 0x03030000
1022+
template <typename... Args,
1023+
typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>>
1024+
object make_simple_namespace(Args&&... args_) {
1025+
PyObject *ns = _PyNamespace_New(dict(std::forward<Args>(args_)...).ptr());
1026+
if (!ns) throw error_already_set();
1027+
return reinterpret_steal<object>(ns);
1028+
}
1029+
#endif
1030+
10211031
/// \ingroup annotations
10221032
/// Annotation for arguments
10231033
struct arg {

tests/test_pytypes.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ TEST_SUBMODULE(pytypes, m) {
7070
m.def("dict_contains",
7171
[](const py::dict &dict, const char *val) { return dict.contains(val); });
7272

73+
// test_tuple
74+
m.def("get_tuple", []() { return py::make_tuple(42, py::none(), "spam"); });
75+
76+
#if PY_VERSION_HEX >= 0x03030000
77+
// test_simple_namespace
78+
m.def("get_simple_namespace", []() {
79+
auto ns = py::make_simple_namespace("attr"_a=42, "x"_a="foo", "wrong"_a=1);
80+
py::delattr(ns, "wrong");
81+
py::setattr(ns, "right", py::int_(2));
82+
return ns;
83+
});
84+
#endif
85+
7386
// test_str
7487
m.def("str_from_string", []() { return py::str(std::string("baz")); });
7588
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
@@ -99,6 +99,19 @@ def test_dict(capture, doc):
9999
assert m.dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3}
100100

101101

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

0 commit comments

Comments
 (0)