Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 6 additions & 16 deletions docs/source/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,26 +201,16 @@ We can throw exceptions in C++ that will then be dealt with in Python. Two patte
Python Extensions
=================

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

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

.. literalinclude:: tutorial/libpy_tutorial/scalar_functions.cc
:lines: 47-53
:lines: 45-53

Second, we create a `PyModuleDef <https://docs.python.org/3/c-api/module.html#c.PyModuleDef>`_ module.

.. literalinclude:: tutorial/libpy_tutorial/scalar_functions.cc
:lines: 55-65

Then we intialize the module (`see also <https://docs.python.org/3/extending/extending.html#the-module-s-method-table-and-initialization-function>`_):

.. literalinclude:: tutorial/libpy_tutorial/scalar_functions.cc
:lines: 67-74

.. note:: The initialization function must be named ``PyInit_name()``, where name is the name of the module.

Finally, we must tell ``setup.py`` to build our module using the ``LibpyExtension`` helper:
Next, we must tell ``setup.py`` to build our module using the ``LibpyExtension`` helper:

.. literalinclude:: tutorial/setup.py
:lines: 18-28,37,50-54,71-72

Finally, we must ensure that we ``import libpy`` in Python before importing the extension module.
37 changes: 9 additions & 28 deletions docs/source/tutorial/libpy_tutorial/arrays.cc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <string>
#include <vector>

#include <libpy/abi.h>
#include <libpy/autofunction.h>
#include <libpy/automodule.h>
#include <libpy/ndarray_view.h>
#include <libpy/numpy_utils.h>

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

namespace {
PyMethodDef methods[] = {
py::autofunction<simple_sum>("simple_sum"),
py::autofunction<simple_sum_iterator>("simple_sum_iterator"),
py::autofunction<is_prime>("is_prime"),
py::end_method_list,
};

PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"libpy_tutorial.arrays",
nullptr,
-1,
methods,
nullptr,
nullptr,
nullptr,
nullptr,
};

PyMODINIT_FUNC PyInit_arrays() {
if (py::abi::ensure_compatible_libpy_abi()) {
return nullptr;
}
import_array();
return PyModule_Create(&module);
LIBPY_AUTOMODULE(libpy_tutorial,
arrays,
({py::autofunction<simple_sum>("simple_sum"),
py::autofunction<simple_sum_iterator>("simple_sum_iterator"),
py::autofunction<is_prime>("is_prime")}))
(py::borrowed_ref<>) {
return false;
}
} // namespace

} // namespace libpy_tutorial
65 changes: 18 additions & 47 deletions docs/source/tutorial/libpy_tutorial/classes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#include <string>
#include <vector>

#include <libpy/abi.h>
#include <libpy/autoclass.h>
#include <libpy/automodule.h>
#include <libpy/exception.h>

namespace libpy_tutorial {
Expand Down Expand Up @@ -67,52 +67,23 @@ struct LIBPY_NO_EXPORT to_object<libpy_tutorial::vec3d>
} // namespace py::dispatch

namespace libpy_tutorial {
namespace {
PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"libpy_tutorial.classes",
nullptr,
-1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
};

PyMODINIT_FUNC PyInit_classes() {
if (py::abi::ensure_compatible_libpy_abi()) {
return nullptr;
}
import_array();
auto m = py::owned_ref(PyModule_Create(&module));
if (!m) {
return nullptr;
}
try {
auto type =
py::autoclass<vec3d>("Vec3d")
.doc("An efficient 3-vector.") // add a class docstring
.new_<double, double, double>() //__new__ takes parameters
// bind the named methods to Python
.def<&vec3d::x>("x")
.def<&vec3d::y>("y")
.def<&vec3d::z>("z")
.def<&vec3d::magnitude>("magnitude")
.str() // set `operator<<(std::ostream&, vec3d) to `str(x)` in Python
.repr<repr>() // set `repr` to be the result of `repr(x)` in Python
.arithmetic<vec3d>() // bind the arithmetic operators to their Python
// equivalents
.type();
if (PyObject_SetAttrString(m.get(), "Vec3d", static_cast<PyObject*>(type))) {
return nullptr;
}
}
catch (const std::exception& e) {
return py::raise_from_cxx_exception(e);
}

return std::move(m).escape();
LIBPY_AUTOMODULE(libpy_tutorial, classes, ({}))
(py::borrowed_ref<> m) {
py::owned_ref t =
py::autoclass<vec3d>(PyModule_GetName(m) + ".Vec3d")
.doc("An efficient 3-vector.") // add a class docstring
.new_<double, double, double>() //__new__ takes parameters
// bind the named methods to Python
.def<&vec3d::x>("x")
.def<&vec3d::y>("y")
.def<&vec3d::z>("z")
.def<&vec3d::magnitude>("magnitude")
.str() // set `operator<<(std::ostream&, vec3d) to `str(x)` in Python
.repr<repr>() // set `repr` to be the result of `repr(x)` in Python
.arithmetic<vec3d>() // bind the arithmetic operators to their Python
// equivalents
.type();
return PyObject_SetAttrString(m.get(), "Vec3d", static_cast<PyObject*>(t));
}
} // namespace
} // namespace libpy_tutorial
35 changes: 8 additions & 27 deletions docs/source/tutorial/libpy_tutorial/exceptions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#include <string>
#include <vector>

#include <libpy/abi.h>
#include <libpy/autofunction.h>
#include <libpy/automodule.h>
#include <libpy/exception.h>

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

namespace {
PyMethodDef methods[] = {
py::autofunction<throw_value_error>("throw_value_error"),
py::autofunction<raise_from_cxx>("raise_from_cxx"),
py::end_method_list,
};

PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"libpy_tutorial.exceptions",
nullptr,
-1,
methods,
nullptr,
nullptr,
nullptr,
nullptr,
};

PyMODINIT_FUNC PyInit_exceptions() {
if (py::abi::ensure_compatible_libpy_abi()) {
return nullptr;
}
import_array();
return PyModule_Create(&module);
LIBPY_AUTOMODULE(libpy_tutorial,
exceptions,
({py::autofunction<throw_value_error>("throw_value_error"),
py::autofunction<raise_from_cxx>("raise_from_cxx")}))
(py::borrowed_ref<>) {
return false;
}
} // namespace

} // namespace libpy_tutorial
32 changes: 6 additions & 26 deletions docs/source/tutorial/libpy_tutorial/ndarrays.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <iostream>

#include <libpy/abi.h>
#include <libpy/autofunction.h>
#include <libpy/automodule.h>
#include <libpy/ndarray_view.h>
#include <libpy/numpy_utils.h>

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

namespace {
PyMethodDef methods[] = {
py::autofunction<apply_kernel>("apply_kernel"),
py::end_method_list,
};

PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"libpy_tutorial.ndarrays",
nullptr,
-1,
methods,
nullptr,
nullptr,
nullptr,
nullptr,
};

PyMODINIT_FUNC PyInit_ndarrays() {
if (py::abi::ensure_compatible_libpy_abi()) {
return nullptr;
}
import_array();
return PyModule_Create(&module);
LIBPY_AUTOMODULE(libpy_tutorial,
ndarrays,
({py::autofunction<apply_kernel>("apply_kernel")}))
(py::borrowed_ref<>) {
return false;
}
} // namespace
} // namespace libpy_tutorial
46 changes: 13 additions & 33 deletions docs/source/tutorial/libpy_tutorial/scalar_functions.cc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <cmath>
#include <random>

#include <libpy/abi.h>
#include <libpy/autofunction.h>
#include <libpy/automodule.h>
#include <libpy/build_tuple.h>
#include <libpy/char_sequence.h>

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

py::owned_ref<> keyword_args(
py::arg::kwd<decltype("kw_arg_kwd"_cs), int> kw_arg_kwd,
py::arg::opt_kwd<decltype("opt_kw_arg_kwd"_cs), int>
opt_kw_arg_kwd) {
py::owned_ref<>
keyword_args(py::arg::kwd<decltype("kw_arg_kwd"_cs), int> kw_arg_kwd,
py::arg::opt_kwd<decltype("opt_kw_arg_kwd"_cs), int> opt_kw_arg_kwd) {

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

namespace {
PyMethodDef methods[] = {
py::autofunction<bool_scalar>("bool_scalar"),
py::autofunction<monte_carlo_pi>("monte_carlo_pi"),
py::autofunction<optional_arg>("optional_arg"),
py::autofunction<keyword_args>("keyword_args"),
py::end_method_list,
};

PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"libpy_tutorial.scalar_functions",
nullptr,
-1,
methods,
nullptr,
nullptr,
nullptr,
nullptr,
};

PyMODINIT_FUNC PyInit_scalar_functions() {
if (py::abi::ensure_compatible_libpy_abi()) {
return nullptr;
}
import_array();
return PyModule_Create(&module);
LIBPY_AUTOMODULE(libpy_tutorial,
scalar_functions,
({py::autofunction<bool_scalar>("bool_scalar"),
py::autofunction<monte_carlo_pi>("monte_carlo_pi"),
py::autofunction<optional_arg>("optional_arg"),
py::autofunction<keyword_args>("keyword_args")}))
(py::borrowed_ref<>) {
return false;
}
} // namespace

} // namespace libpy_tutorial
2 changes: 2 additions & 0 deletions docs/source/tutorial/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def extension(*args, **kwargs):
**kwargs
)


install_requires = [
'setuptools',
'libpy',
Expand All @@ -44,6 +45,7 @@ def extension(*args, **kwargs):
package_data={
"": ["*.png"],
},
include_package_data=True,
install_requires=install_requires,
license="Apache 2.0",
url="https://github.com/quantopian/libpy",
Expand Down