summaryrefslogtreecommitdiff
path: root/ext/pybind11/docs/advanced/pycpp
diff options
context:
space:
mode:
Diffstat (limited to 'ext/pybind11/docs/advanced/pycpp')
-rw-r--r--ext/pybind11/docs/advanced/pycpp/numpy.rst79
-rw-r--r--ext/pybind11/docs/advanced/pycpp/object.rst76
-rw-r--r--ext/pybind11/docs/advanced/pycpp/utilities.rst103
3 files changed, 202 insertions, 56 deletions
diff --git a/ext/pybind11/docs/advanced/pycpp/numpy.rst b/ext/pybind11/docs/advanced/pycpp/numpy.rst
index 6bcc46719..98b0c25b9 100644
--- a/ext/pybind11/docs/advanced/pycpp/numpy.rst
+++ b/ext/pybind11/docs/advanced/pycpp/numpy.rst
@@ -57,11 +57,11 @@ specification.
struct buffer_info {
void *ptr;
- size_t itemsize;
+ ssize_t itemsize;
std::string format;
- int ndim;
- std::vector<size_t> shape;
- std::vector<size_t> strides;
+ ssize_t ndim;
+ std::vector<ssize_t> shape;
+ std::vector<ssize_t> strides;
};
To create a C++ function that can take a Python buffer object as an argument,
@@ -95,11 +95,11 @@ buffer objects (e.g. a NumPy matrix).
throw std::runtime_error("Incompatible buffer dimension!");
auto strides = Strides(
- info.strides[rowMajor ? 0 : 1] / sizeof(Scalar),
- info.strides[rowMajor ? 1 : 0] / sizeof(Scalar));
+ info.strides[rowMajor ? 0 : 1] / (py::ssize_t)sizeof(Scalar),
+ info.strides[rowMajor ? 1 : 0] / (py::ssize_t)sizeof(Scalar));
auto map = Eigen::Map<Matrix, 0, Strides>(
- static_cat<Scalar *>(info.ptr), info.shape[0], info.shape[1], strides);
+ static_cast<Scalar *>(info.ptr), info.shape[0], info.shape[1], strides);
new (&m) Matrix(map);
});
@@ -111,18 +111,14 @@ as follows:
.def_buffer([](Matrix &m) -> py::buffer_info {
return py::buffer_info(
- m.data(), /* Pointer to buffer */
- sizeof(Scalar), /* Size of one scalar */
- /* Python struct-style format descriptor */
- py::format_descriptor<Scalar>::format(),
- /* Number of dimensions */
- 2,
- /* Buffer dimensions */
- { (size_t) m.rows(),
- (size_t) m.cols() },
- /* Strides (in bytes) for each index */
+ m.data(), /* Pointer to buffer */
+ sizeof(Scalar), /* Size of one scalar */
+ py::format_descriptor<Scalar>::format(), /* Python struct-style format descriptor */
+ 2, /* Number of dimensions */
+ { m.rows(), m.cols() }, /* Buffer dimensions */
{ sizeof(Scalar) * (rowMajor ? m.cols() : 1),
sizeof(Scalar) * (rowMajor ? 1 : m.rows()) }
+ /* Strides (in bytes) for each index */
);
})
@@ -194,7 +190,7 @@ expects the type followed by field names:
};
// ...
- PYBIND11_PLUGIN(test) {
+ PYBIND11_MODULE(test, m) {
// ...
PYBIND11_NUMPY_DTYPE(A, x, y);
@@ -202,6 +198,13 @@ expects the type followed by field names:
/* now both A and B can be used as template arguments to py::array_t */
}
+The structure should consist of fundamental arithmetic types, ``std::complex``,
+previously registered substructures, and arrays of any of the above. Both C++
+arrays and ``std::array`` are supported. While there is a static assertion to
+prevent many types of unsupported structures, it is still the user's
+responsibility to use only "plain" structures that can be safely manipulated as
+raw memory without violating invariants.
+
Vectorizing functions
=====================
@@ -236,27 +239,13 @@ by the compiler. The result is returned as a NumPy array of type
The scalar argument ``z`` is transparently replicated 4 times. The input
arrays ``x`` and ``y`` are automatically converted into the right types (they
are of type ``numpy.dtype.int64`` but need to be ``numpy.dtype.int32`` and
-``numpy.dtype.float32``, respectively)
-
-Sometimes we might want to explicitly exclude an argument from the vectorization
-because it makes little sense to wrap it in a NumPy array. For instance,
-suppose the function signature was
-
-.. code-block:: cpp
+``numpy.dtype.float32``, respectively).
- double my_func(int x, float y, my_custom_type *z);
+.. note::
-This can be done with a stateful Lambda closure:
-
-.. code-block:: cpp
-
- // Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the vectorization)
- m.def("vectorized_func",
- [](py::array_t<int> x, py::array_t<float> y, my_custom_type *z) {
- auto stateful_closure = [z](int x, float y) { return my_func(x, y, z); };
- return py::vectorize(stateful_closure)(x, y);
- }
- );
+ Only arithmetic, complex, and POD types passed by value or by ``const &``
+ reference are vectorized; all other arguments are passed through as-is.
+ Functions taking rvalue reference arguments cannot be vectorized.
In cases where the computation is too complicated to be reduced to
``vectorize``, it will be necessary to create and access the buffer contents
@@ -295,10 +284,8 @@ simply using ``vectorize``).
return result;
}
- PYBIND11_PLUGIN(test) {
- py::module m("test");
+ PYBIND11_MODULE(test, m) {
m.def("add_arrays", &add_arrays, "Add two NumPy arrays");
- return m.ptr();
}
.. seealso::
@@ -322,17 +309,17 @@ where ``N`` gives the required dimensionality of the array:
m.def("sum_3d", [](py::array_t<double> x) {
auto r = x.unchecked<3>(); // x must have ndim = 3; can be non-writeable
double sum = 0;
- for (size_t i = 0; i < r.shape(0); i++)
- for (size_t j = 0; j < r.shape(1); j++)
- for (size_t k = 0; k < r.shape(2); k++)
+ for (ssize_t i = 0; i < r.shape(0); i++)
+ for (ssize_t j = 0; j < r.shape(1); j++)
+ for (ssize_t k = 0; k < r.shape(2); k++)
sum += r(i, j, k);
return sum;
});
m.def("increment_3d", [](py::array_t<double> x) {
auto r = x.mutable_unchecked<3>(); // Will throw if ndim != 3 or flags.writeable is false
- for (size_t i = 0; i < r.shape(0); i++)
- for (size_t j = 0; j < r.shape(1); j++)
- for (size_t k = 0; k < r.shape(2); k++)
+ for (ssize_t i = 0; i < r.shape(0); i++)
+ for (ssize_t j = 0; j < r.shape(1); j++)
+ for (ssize_t k = 0; k < r.shape(2); k++)
r(i, j, k) += 1.0;
}, py::arg().noconvert());
diff --git a/ext/pybind11/docs/advanced/pycpp/object.rst b/ext/pybind11/docs/advanced/pycpp/object.rst
index ae58876de..117131edc 100644
--- a/ext/pybind11/docs/advanced/pycpp/object.rst
+++ b/ext/pybind11/docs/advanced/pycpp/object.rst
@@ -33,12 +33,50 @@ The reverse direction uses the following syntax:
When conversion fails, both directions throw the exception :class:`cast_error`.
+.. _python_libs:
+
+Accessing Python libraries from C++
+===================================
+
+It is also possible to import objects defined in the Python standard
+library or available in the current Python environment (``sys.path``) and work
+with these in C++.
+
+This example obtains a reference to the Python ``Decimal`` class.
+
+.. code-block:: cpp
+
+ // Equivalent to "from decimal import Decimal"
+ py::object Decimal = py::module::import("decimal").attr("Decimal");
+
+.. code-block:: cpp
+
+ // Try to import scipy
+ py::object scipy = py::module::import("scipy");
+ return scipy.attr("__version__");
+
.. _calling_python_functions:
Calling Python functions
========================
-It is also possible to call python functions via ``operator()``.
+It is also possible to call Python classes, functions and methods
+via ``operator()``.
+
+.. code-block:: cpp
+
+ // Construct a Python object of class Decimal
+ py::object pi = Decimal("3.14159");
+
+.. code-block:: cpp
+
+ // Use Python to make our directories
+ py::object os = py::module::import("os");
+ py::object makedirs = os.attr("makedirs");
+ makedirs("/tmp/path/to/somewhere");
+
+One can convert the result obtained from Python to a pure C++ version
+if a ``py::class_`` or type conversion is defined.
.. code-block:: cpp
@@ -46,6 +84,37 @@ It is also possible to call python functions via ``operator()``.
py::object result_py = f(1234, "hello", some_instance);
MyClass &result = result_py.cast<MyClass>();
+.. _calling_python_methods:
+
+Calling Python methods
+========================
+
+To call an object's method, one can again use ``.attr`` to obtain access to the
+Python method.
+
+.. code-block:: cpp
+
+ // Calculate e^π in decimal
+ py::object exp_pi = pi.attr("exp")();
+ py::print(py::str(exp_pi));
+
+In the example above ``pi.attr("exp")`` is a *bound method*: it will always call
+the method for that same instance of the class. Alternately one can create an
+*unbound method* via the Python class (instead of instance) and pass the ``self``
+object explicitly, followed by other arguments.
+
+.. code-block:: cpp
+
+ py::object decimal_exp = Decimal.attr("exp");
+
+ // Compute the e^n for n=0..4
+ for (int n = 0; n < 5; n++) {
+ py::print(decimal_exp(Decimal(n));
+ }
+
+Keyword arguments
+=================
+
Keyword arguments are also supported. In Python, there is the usual call syntax:
.. code-block:: python
@@ -62,6 +131,9 @@ In C++, the same call can be made using:
using namespace pybind11::literals; // to bring in the `_a` literal
f(1234, "say"_a="hello", "to"_a=some_instance); // keyword call in C++
+Unpacking arguments
+===================
+
Unpacking of ``*args`` and ``**kwargs`` is also possible and can be mixed with
other arguments:
@@ -90,7 +162,7 @@ Generalized unpacking according to PEP448_ is also supported:
.. seealso::
- The file :file:`tests/test_python_types.cpp` contains a complete
+ The file :file:`tests/test_pytypes.cpp` contains a complete
example that demonstrates passing native Python types in more detail. The
file :file:`tests/test_callbacks.cpp` presents a few examples of calling
Python functions from C++, including keywords arguments and unpacking.
diff --git a/ext/pybind11/docs/advanced/pycpp/utilities.rst b/ext/pybind11/docs/advanced/pycpp/utilities.rst
index ba0dbef88..369e7c94d 100644
--- a/ext/pybind11/docs/advanced/pycpp/utilities.rst
+++ b/ext/pybind11/docs/advanced/pycpp/utilities.rst
@@ -21,19 +21,81 @@ expected in Python:
auto args = py::make_tuple("unpacked", true);
py::print("->", *args, "end"_a="<-"); // -> unpacked True <-
+.. _ostream_redirect:
+
+Capturing standard output from ostream
+======================================
+
+Often, a library will use the streams ``std::cout`` and ``std::cerr`` to print,
+but this does not play well with Python's standard ``sys.stdout`` and ``sys.stderr``
+redirection. Replacing a library's printing with `py::print <print>` may not
+be feasible. This can be fixed using a guard around the library function that
+redirects output to the corresponding Python streams:
+
+.. code-block:: cpp
+
+ #include <pybind11/iostream.h>
+
+ ...
+
+ // Add a scoped redirect for your noisy code
+ m.def("noisy_func", []() {
+ py::scoped_ostream_redirect stream(
+ std::cout, // std::ostream&
+ py::module::import("sys").attr("stdout") // Python output
+ );
+ call_noisy_func();
+ });
+
+This method respects flushes on the output streams and will flush if needed
+when the scoped guard is destroyed. This allows the output to be redirected in
+real time, such as to a Jupyter notebook. The two arguments, the C++ stream and
+the Python output, are optional, and default to standard output if not given. An
+extra type, `py::scoped_estream_redirect <scoped_estream_redirect>`, is identical
+except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with
+`py::call_guard`, which allows multiple items, but uses the default constructor:
+
+.. code-block:: py
+
+ // Alternative: Call single function using call guard
+ m.def("noisy_func", &call_noisy_function,
+ py::call_guard<py::scoped_ostream_redirect,
+ py::scoped_estream_redirect>());
+
+The redirection can also be done in Python with the addition of a context
+manager, using the `py::add_ostream_redirect() <add_ostream_redirect>` function:
+
+.. code-block:: cpp
+
+ py::add_ostream_redirect(m, "ostream_redirect");
+
+The name in Python defaults to ``ostream_redirect`` if no name is passed. This
+creates the following context manager in Python:
+
+.. code-block:: python
+
+ with ostream_redirect(stdout=True, stderr=True):
+ noisy_function()
+
+It defaults to redirecting both streams, though you can use the keyword
+arguments to disable one of the streams if needed.
+
+.. note::
+
+ The above methods will not redirect C-level output to file descriptors, such
+ as ``fprintf``. For those cases, you'll need to redirect the file
+ descriptors either directly in C or with Python's ``os.dup2`` function
+ in an operating-system dependent way.
+
+.. _eval:
+
Evaluating Python expressions from strings and files
====================================================
-pybind11 provides the :func:`eval` and :func:`eval_file` functions to evaluate
+pybind11 provides the `eval`, `exec` and `eval_file` functions to evaluate
Python expressions and statements. The following example illustrates how they
can be used.
-Both functions accept a template parameter that describes how the argument
-should be interpreted. Possible choices include ``eval_expr`` (isolated
-expression), ``eval_single_statement`` (a single statement, return value is
-always ``none``), and ``eval_statements`` (sequence of statements, return value
-is always ``none``).
-
.. code-block:: cpp
// At beginning of file
@@ -48,10 +110,35 @@ is always ``none``).
int result = py::eval("my_variable + 10", scope).cast<int>();
// Evaluate a sequence of statements
- py::eval<py::eval_statements>(
+ py::exec(
"print('Hello')\n"
"print('world!');",
scope);
// Evaluate the statements in an separate Python file on disk
py::eval_file("script.py", scope);
+
+C++11 raw string literals are also supported and quite handy for this purpose.
+The only requirement is that the first statement must be on a new line following
+the raw string delimiter ``R"(``, ensuring all lines have common leading indent:
+
+.. code-block:: cpp
+
+ py::exec(R"(
+ x = get_answer()
+ if x == 42:
+ print('Hello World!')
+ else:
+ print('Bye!')
+ )", scope
+ );
+
+.. note::
+
+ `eval` and `eval_file` accept a template parameter that describes how the
+ string/file should be interpreted. Possible choices include ``eval_expr``
+ (isolated expression), ``eval_single_statement`` (a single statement, return
+ value is always ``none``), and ``eval_statements`` (sequence of statements,
+ return value is always ``none``). `eval` defaults to ``eval_expr``,
+ `eval_file` defaults to ``eval_statements`` and `exec` is just a shortcut
+ for ``eval<eval_statements>``.