Skip to content

Commit 82c9e66

Browse files
committed
Add py::arg::noconvert documentation
1 parent d63633f commit 82c9e66

File tree

4 files changed

+73
-2
lines changed

4 files changed

+73
-2
lines changed

docs/advanced/classes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,8 @@ crucial that instances are deallocated on the C++ side to avoid memory leaks.
388388
py::class_<MyClass, std::unique_ptr<MyClass, py::nodelete>>(m, "MyClass")
389389
.def(py::init<>())
390390
391+
.. _implicit_conversions:
392+
391393
Implicit conversions
392394
====================
393395

docs/advanced/functions.rst

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,57 @@ like so:
318318
319319
py::class_<MyClass>("MyClass")
320320
.def("myFunction", py::arg("arg") = (SomeType *) nullptr);
321+
322+
Non-converting arguments
323+
========================
324+
325+
Certain argument types may support conversion from one type to another. Some
326+
examples of conversions are:
327+
328+
* :ref:`implicit_conversions` declared using ``py::implicitly_convertible<A,B>()``
329+
* Calling a method accepting a double with an integer argument
330+
* Calling a ``std::complex<float>`` argument with a non-complex python type
331+
(for example, with a float). (Requires the optional ``pybind11/complex.h``
332+
header).
333+
* Calling a function taking an Eigen matrix reference with a numpy array of the
334+
wrong type or of an incompatible data layout. (Requires the optional
335+
``pybind11/eigen.h`` header).
336+
337+
This behaviour is sometimes undesirable: the binding code may prefer to raise
338+
an error rather than convert the argument. This behaviour can be obtained
339+
through ``py::arg`` by calling the ``.noconvert()`` method of the ``py::arg``
340+
object, such as:
341+
342+
.. code-block:: cpp
343+
344+
m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
345+
m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));
346+
347+
Attempting the call the second function (the one without ``.noconvert()``) with
348+
an integer will succeed, but attempting to call the ``.noconvert()`` version
349+
will fail with a ``TypeError``:
350+
351+
.. code-block:: pycon
352+
353+
>>> floats_preferred(4)
354+
2.0
355+
>>> floats_only(4)
356+
Traceback (most recent call last):
357+
File "<stdin>", line 1, in <module>
358+
TypeError: floats_only(): incompatible function arguments. The following argument types are supported:
359+
1. (f: float) -> float
360+
361+
Invoked with: 4
362+
363+
You may, of course, combine this with the :var:`_a` shorthand notation (see
364+
:ref:`keyword_args`) and/or :ref:`default_args`. It is also permitted to omit
365+
the argument name by using the ``py::arg()`` constructor without an argument
366+
name, i.e. by specifying ``py::arg().noconvert()``.
367+
368+
.. note::
369+
370+
When specifying ``py::arg`` options it is necessary to provide the same
371+
number of options as the bound function has arguments. Thus if you want to
372+
enable no-convert behaviour for just one of several arguments, you will
373+
need to specify a ``py::arg()`` annotation for each argument with the
374+
no-convert argument modified to ``py::arg().noconvert()``.

tests/test_methods_and_attributes.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,8 @@ test_initializer methods_and_attributes([](py::module &m) {
236236
;
237237
m.def("arg_inspect_func", [](ArgInspector2 a, ArgInspector1 b) { return a.arg + "\n" + b.arg; },
238238
py::arg().noconvert(false), py::arg_v(nullptr, ArgInspector1()).noconvert(true));
239+
240+
m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));
241+
m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
242+
239243
});

tests/test_methods_and_attributes.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,8 @@ def test_cyclic_gc():
205205
assert cstats.alive() == 0
206206

207207

208-
def test_type_caster_arg_info(msg):
209-
from pybind11_tests import ArgInspector, arg_inspect_func
208+
def test_noconvert_args(msg):
209+
from pybind11_tests import ArgInspector, arg_inspect_func, floats_only, floats_preferred
210210

211211
a = ArgInspector()
212212
assert msg(a.f("hi")) == """
@@ -236,3 +236,14 @@ def test_type_caster_arg_info(msg):
236236
loading ArgInspector2 argument WITH conversion allowed. Argument value = A1
237237
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2
238238
"""
239+
240+
assert floats_preferred(4) == 2.0
241+
assert floats_only(4.0) == 2.0
242+
with pytest.raises(TypeError) as excinfo:
243+
floats_only(4)
244+
assert msg(excinfo.value) == """
245+
floats_only(): incompatible function arguments. The following argument types are supported:
246+
1. (f: float) -> float
247+
248+
Invoked with: 4
249+
"""

0 commit comments

Comments
 (0)