Skip to content

Commit 87d69ca

Browse files
gerrymanoimJoe Jevnik
andauthored
DOC: Use automodule in tutorial (#161)
* DOC: document the need to import libpy before an extension Co-authored-by: Joe Jevnik <[email protected]>
1 parent 6848820 commit 87d69ca

File tree

7 files changed

+61
-177
lines changed

7 files changed

+61
-177
lines changed

docs/source/tutorial.rst

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -201,26 +201,16 @@ We can throw exceptions in C++ that will then be dealt with in Python. Two patte
201201
Python Extensions
202202
=================
203203

204-
In order to create and use a Python Extension we must do four basic things:
204+
In order to create and use Python Extensions we must do two things:
205205

206-
First, we use :cpp:func:`py::autofunction` to create an array of `PyMethoddef <https://docs.python.org/3/c-api/structures.html#c.PyMethodDef>`_.
206+
First, we use the ``LIBPY_AUTOMODULE`` macro to create and initialize the module:
207207

208208
.. literalinclude:: tutorial/libpy_tutorial/scalar_functions.cc
209-
:lines: 47-53
209+
:lines: 45-53
210210

211-
Second, we create a `PyModuleDef <https://docs.python.org/3/c-api/module.html#c.PyModuleDef>`_ module.
212-
213-
.. literalinclude:: tutorial/libpy_tutorial/scalar_functions.cc
214-
:lines: 55-65
215-
216-
Then we intialize the module (`see also <https://docs.python.org/3/extending/extending.html#the-module-s-method-table-and-initialization-function>`_):
217-
218-
.. literalinclude:: tutorial/libpy_tutorial/scalar_functions.cc
219-
:lines: 67-74
220-
221-
.. note:: The initialization function must be named ``PyInit_name()``, where name is the name of the module.
222-
223-
Finally, we must tell ``setup.py`` to build our module using the ``LibpyExtension`` helper:
211+
Next, we must tell ``setup.py`` to build our module using the ``LibpyExtension`` helper:
224212

225213
.. literalinclude:: tutorial/setup.py
226214
:lines: 18-28,37,50-54,71-72
215+
216+
Finally, we must ensure that we ``import libpy`` in Python before importing the extension module.

docs/source/tutorial/libpy_tutorial/arrays.cc

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#include <string>
22
#include <vector>
33

4-
#include <libpy/abi.h>
54
#include <libpy/autofunction.h>
5+
#include <libpy/automodule.h>
66
#include <libpy/ndarray_view.h>
77
#include <libpy/numpy_utils.h>
88

@@ -42,32 +42,13 @@ py::owned_ref<> is_prime(py::array_view<const std::int64_t> values) {
4242
return py::move_to_numpy_array(std::move(out));
4343
}
4444

45-
namespace {
46-
PyMethodDef methods[] = {
47-
py::autofunction<simple_sum>("simple_sum"),
48-
py::autofunction<simple_sum_iterator>("simple_sum_iterator"),
49-
py::autofunction<is_prime>("is_prime"),
50-
py::end_method_list,
51-
};
52-
53-
PyModuleDef module = {
54-
PyModuleDef_HEAD_INIT,
55-
"libpy_tutorial.arrays",
56-
nullptr,
57-
-1,
58-
methods,
59-
nullptr,
60-
nullptr,
61-
nullptr,
62-
nullptr,
63-
};
64-
65-
PyMODINIT_FUNC PyInit_arrays() {
66-
if (py::abi::ensure_compatible_libpy_abi()) {
67-
return nullptr;
68-
}
69-
import_array();
70-
return PyModule_Create(&module);
45+
LIBPY_AUTOMODULE(libpy_tutorial,
46+
arrays,
47+
({py::autofunction<simple_sum>("simple_sum"),
48+
py::autofunction<simple_sum_iterator>("simple_sum_iterator"),
49+
py::autofunction<is_prime>("is_prime")}))
50+
(py::borrowed_ref<>) {
51+
return false;
7152
}
72-
} // namespace
53+
7354
} // namespace libpy_tutorial

docs/source/tutorial/libpy_tutorial/classes.cc

Lines changed: 18 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
#include <string>
33
#include <vector>
44

5-
#include <libpy/abi.h>
65
#include <libpy/autoclass.h>
6+
#include <libpy/automodule.h>
77
#include <libpy/exception.h>
88

99
namespace libpy_tutorial {
@@ -67,52 +67,23 @@ struct LIBPY_NO_EXPORT to_object<libpy_tutorial::vec3d>
6767
} // namespace py::dispatch
6868

6969
namespace libpy_tutorial {
70-
namespace {
71-
PyModuleDef module = {
72-
PyModuleDef_HEAD_INIT,
73-
"libpy_tutorial.classes",
74-
nullptr,
75-
-1,
76-
nullptr,
77-
nullptr,
78-
nullptr,
79-
nullptr,
80-
nullptr,
81-
};
82-
83-
PyMODINIT_FUNC PyInit_classes() {
84-
if (py::abi::ensure_compatible_libpy_abi()) {
85-
return nullptr;
86-
}
87-
import_array();
88-
auto m = py::owned_ref(PyModule_Create(&module));
89-
if (!m) {
90-
return nullptr;
91-
}
92-
try {
93-
auto type =
94-
py::autoclass<vec3d>("Vec3d")
95-
.doc("An efficient 3-vector.") // add a class docstring
96-
.new_<double, double, double>() //__new__ takes parameters
97-
// bind the named methods to Python
98-
.def<&vec3d::x>("x")
99-
.def<&vec3d::y>("y")
100-
.def<&vec3d::z>("z")
101-
.def<&vec3d::magnitude>("magnitude")
102-
.str() // set `operator<<(std::ostream&, vec3d) to `str(x)` in Python
103-
.repr<repr>() // set `repr` to be the result of `repr(x)` in Python
104-
.arithmetic<vec3d>() // bind the arithmetic operators to their Python
105-
// equivalents
106-
.type();
107-
if (PyObject_SetAttrString(m.get(), "Vec3d", static_cast<PyObject*>(type))) {
108-
return nullptr;
109-
}
110-
}
111-
catch (const std::exception& e) {
112-
return py::raise_from_cxx_exception(e);
113-
}
11470

115-
return std::move(m).escape();
71+
LIBPY_AUTOMODULE(libpy_tutorial, classes, ({}))
72+
(py::borrowed_ref<> m) {
73+
py::owned_ref t =
74+
py::autoclass<vec3d>(PyModule_GetName(m) + ".Vec3d")
75+
.doc("An efficient 3-vector.") // add a class docstring
76+
.new_<double, double, double>() //__new__ takes parameters
77+
// bind the named methods to Python
78+
.def<&vec3d::x>("x")
79+
.def<&vec3d::y>("y")
80+
.def<&vec3d::z>("z")
81+
.def<&vec3d::magnitude>("magnitude")
82+
.str() // set `operator<<(std::ostream&, vec3d) to `str(x)` in Python
83+
.repr<repr>() // set `repr` to be the result of `repr(x)` in Python
84+
.arithmetic<vec3d>() // bind the arithmetic operators to their Python
85+
// equivalents
86+
.type();
87+
return PyObject_SetAttrString(m.get(), "Vec3d", static_cast<PyObject*>(t));
11688
}
117-
} // namespace
11889
} // namespace libpy_tutorial

docs/source/tutorial/libpy_tutorial/exceptions.cc

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
#include <string>
33
#include <vector>
44

5-
#include <libpy/abi.h>
65
#include <libpy/autofunction.h>
6+
#include <libpy/automodule.h>
77
#include <libpy/exception.h>
88

99
namespace libpy_tutorial {
@@ -16,31 +16,12 @@ void raise_from_cxx() {
1616
throw std::invalid_argument("Supposedly a bad argument was used");
1717
}
1818

19-
namespace {
20-
PyMethodDef methods[] = {
21-
py::autofunction<throw_value_error>("throw_value_error"),
22-
py::autofunction<raise_from_cxx>("raise_from_cxx"),
23-
py::end_method_list,
24-
};
25-
26-
PyModuleDef module = {
27-
PyModuleDef_HEAD_INIT,
28-
"libpy_tutorial.exceptions",
29-
nullptr,
30-
-1,
31-
methods,
32-
nullptr,
33-
nullptr,
34-
nullptr,
35-
nullptr,
36-
};
37-
38-
PyMODINIT_FUNC PyInit_exceptions() {
39-
if (py::abi::ensure_compatible_libpy_abi()) {
40-
return nullptr;
41-
}
42-
import_array();
43-
return PyModule_Create(&module);
19+
LIBPY_AUTOMODULE(libpy_tutorial,
20+
exceptions,
21+
({py::autofunction<throw_value_error>("throw_value_error"),
22+
py::autofunction<raise_from_cxx>("raise_from_cxx")}))
23+
(py::borrowed_ref<>) {
24+
return false;
4425
}
45-
} // namespace
26+
4627
} // namespace libpy_tutorial

docs/source/tutorial/libpy_tutorial/ndarrays.cc

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include <iostream>
22

3-
#include <libpy/abi.h>
43
#include <libpy/autofunction.h>
4+
#include <libpy/automodule.h>
55
#include <libpy/ndarray_view.h>
66
#include <libpy/numpy_utils.h>
77

@@ -58,30 +58,10 @@ py::owned_ref<> apply_kernel(py::ndarray_view<const std::uint8_t, 3> pixels,
5858
pixels.strides());
5959
}
6060

61-
namespace {
62-
PyMethodDef methods[] = {
63-
py::autofunction<apply_kernel>("apply_kernel"),
64-
py::end_method_list,
65-
};
66-
67-
PyModuleDef module = {
68-
PyModuleDef_HEAD_INIT,
69-
"libpy_tutorial.ndarrays",
70-
nullptr,
71-
-1,
72-
methods,
73-
nullptr,
74-
nullptr,
75-
nullptr,
76-
nullptr,
77-
};
78-
79-
PyMODINIT_FUNC PyInit_ndarrays() {
80-
if (py::abi::ensure_compatible_libpy_abi()) {
81-
return nullptr;
82-
}
83-
import_array();
84-
return PyModule_Create(&module);
61+
LIBPY_AUTOMODULE(libpy_tutorial,
62+
ndarrays,
63+
({py::autofunction<apply_kernel>("apply_kernel")}))
64+
(py::borrowed_ref<>) {
65+
return false;
8566
}
86-
} // namespace
8767
} // namespace libpy_tutorial
Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#include <cmath>
22
#include <random>
33

4-
#include <libpy/abi.h>
54
#include <libpy/autofunction.h>
5+
#include <libpy/automodule.h>
66
#include <libpy/build_tuple.h>
77
#include <libpy/char_sequence.h>
88

@@ -35,41 +35,21 @@ std::string optional_arg(py::arg::optional<std::string> opt_arg) {
3535
return opt_arg.get().value_or("default value");
3636
}
3737

38-
py::owned_ref<> keyword_args(
39-
py::arg::kwd<decltype("kw_arg_kwd"_cs), int> kw_arg_kwd,
40-
py::arg::opt_kwd<decltype("opt_kw_arg_kwd"_cs), int>
41-
opt_kw_arg_kwd) {
38+
py::owned_ref<>
39+
keyword_args(py::arg::kwd<decltype("kw_arg_kwd"_cs), int> kw_arg_kwd,
40+
py::arg::opt_kwd<decltype("opt_kw_arg_kwd"_cs), int> opt_kw_arg_kwd) {
4241

4342
return py::build_tuple(kw_arg_kwd.get(), opt_kw_arg_kwd.get());
4443
}
4544

46-
namespace {
47-
PyMethodDef methods[] = {
48-
py::autofunction<bool_scalar>("bool_scalar"),
49-
py::autofunction<monte_carlo_pi>("monte_carlo_pi"),
50-
py::autofunction<optional_arg>("optional_arg"),
51-
py::autofunction<keyword_args>("keyword_args"),
52-
py::end_method_list,
53-
};
54-
55-
PyModuleDef module = {
56-
PyModuleDef_HEAD_INIT,
57-
"libpy_tutorial.scalar_functions",
58-
nullptr,
59-
-1,
60-
methods,
61-
nullptr,
62-
nullptr,
63-
nullptr,
64-
nullptr,
65-
};
66-
67-
PyMODINIT_FUNC PyInit_scalar_functions() {
68-
if (py::abi::ensure_compatible_libpy_abi()) {
69-
return nullptr;
70-
}
71-
import_array();
72-
return PyModule_Create(&module);
45+
LIBPY_AUTOMODULE(libpy_tutorial,
46+
scalar_functions,
47+
({py::autofunction<bool_scalar>("bool_scalar"),
48+
py::autofunction<monte_carlo_pi>("monte_carlo_pi"),
49+
py::autofunction<optional_arg>("optional_arg"),
50+
py::autofunction<keyword_args>("keyword_args")}))
51+
(py::borrowed_ref<>) {
52+
return false;
7353
}
74-
} // namespace
54+
7555
} // namespace libpy_tutorial

docs/source/tutorial/setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def extension(*args, **kwargs):
4545
package_data={
4646
"": ["*.png"],
4747
},
48+
include_package_data=True,
4849
install_requires=install_requires,
4950
license="Apache 2.0",
5051
url="https://github.com/quantopian/libpy",

0 commit comments

Comments
 (0)