Skip to content

Commit c249fc1

Browse files
author
Jouke Witteveen
committed
Add namespace_ type and tests
1 parent 721834b commit c249fc1

File tree

4 files changed

+91
-6
lines changed

4 files changed

+91
-6
lines changed

docs/advanced/pycpp/object.rst

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,60 @@ Available wrappers
99
All major Python types are available as thin C++ wrapper classes. These
1010
can also be used as function parameters -- see :ref:`python_objects_as_args`.
1111

12-
Available types include :class:`handle`, :class:`object`, :class:`bool_`,
13-
:class:`int_`, :class:`float_`, :class:`str`, :class:`bytes`, :class:`tuple`,
14-
:class:`list`, :class:`dict`, :class:`slice`, :class:`none`, :class:`capsule`,
15-
:class:`iterable`, :class:`iterator`, :class:`function`, :class:`buffer`,
16-
:class:`array`, and :class:`array_t`.
12+
Available types include :class:`handle`, :class:`object`, :class:`namespace_`,
13+
:class:`bool_`, :class:`int_`, :class:`float_`, :class:`str`, :class:`bytes`,
14+
:class:`tuple`, :class:`list`, :class:`dict`, :class:`slice`, :class:`none`,
15+
:class:`capsule`, :class:`iterable`, :class:`iterator`, :class:`function`,
16+
:class:`buffer`, :class:`array`, and :class:`array_t`.
1717

1818
.. warning::
1919

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+
A tuple of python objects can be instantiated using :func:`py::make_tuple`:
29+
30+
.. code-block:: cpp
31+
32+
py::tuple tup = py::make_tuple(42, py::none(), "spam");
33+
34+
Each element is converted to a supported Python type.
35+
36+
Dictionaries can be initialized in the :class:`dict` constructor:
37+
38+
.. code-block:: cpp
39+
40+
using namespace pybind11::literals; // to bring in the `_a` literal
41+
py::dict d("spam"_a=py::none(), "eggs"_a=42);
42+
43+
A `simple namespace`_ can be instantiated in the :class:`namespace_`
44+
constructor:
45+
46+
.. code-block:: cpp
47+
48+
using namespace pybind11::literals; // to bring in the `_a` literal
49+
py::namespace_ ns("spam"_a=py::none(), "eggs"_a=42);
50+
51+
The keys in the arguments to the :class:`namespace_` constructor become the
52+
attribute names in the resulting namespace. Attributes on a namespace can be
53+
modified with the :func:`py::delattr`, :func:`py::getattr`, and
54+
:func:`py::setattr` functions. Namespaces can be useful as stand-ins for class
55+
instances.
56+
57+
.. note::
58+
59+
The ``namespace_`` type is not available in Python 2.
60+
61+
.. versionchanged:: 2.7
62+
``namespace_`` added.
63+
64+
.. _simple namespace: https://docs.python.org/3/library/types.html#types.SimpleNamespace
65+
2366
.. _casting_back_and_forth:
2467

2568
Casting back and forth
@@ -30,7 +73,7 @@ types to Python, which can be done using :func:`py::cast`:
3073

3174
.. code-block:: cpp
3275
33-
MyClass *cls = ..;
76+
MyClass *cls = ...;
3477
py::object obj = py::cast(cls);
3578
3679
The reverse direction uses the following syntax:

include/pybind11/pytypes.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,10 @@ inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o)
763763
#define PYBIND11_STR_CHECK_FUN PyUnicode_Check
764764
#endif
765765

766+
#if PY_MAJOR_VERSION >= 3
767+
inline bool PyNamespace_Check(PyObject *o) { return isinstance(o, (PyObject *) &_PyNamespace_Type); }
768+
#endif
769+
766770
inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; }
767771

768772
class kwargs_proxy : public handle {
@@ -1326,6 +1330,18 @@ class dict : public object {
13261330
}
13271331
};
13281332

1333+
#if PY_MAJOR_VERSION >= 3
1334+
class namespace_ : public object {
1335+
public:
1336+
PYBIND11_OBJECT(namespace_, object, detail::PyNamespace_Check)
1337+
template <typename... Args,
1338+
typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>>
1339+
explicit namespace_(Args&&... args_) : object(_PyNamespace_New(dict(std::forward<Args>(args_)...).ptr()), stolen_t{}) {
1340+
if (!m_ptr) pybind11_fail("Could not allocate namespace object!");
1341+
}
1342+
};
1343+
#endif
1344+
13291345
class sequence : public object {
13301346
public:
13311347
PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check)

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+
py::namespace_ ns("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)