From a3b0971f8194c04083e2f0a2043515fbf1fd0d95 Mon Sep 17 00:00:00 2001 From: Holger Kohr Date: Sun, 6 Sep 2020 11:42:00 +0200 Subject: [PATCH 1/5] Add py::object casting example to embedding docs --- docs/advanced/embedding.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/advanced/embedding.rst b/docs/advanced/embedding.rst index 98a5c52190..d41edfc0ac 100644 --- a/docs/advanced/embedding.rst +++ b/docs/advanced/embedding.rst @@ -105,6 +105,25 @@ The two approaches can also be combined: std::cout << message; } +Typically, a call to a function in a `py::module` returns a generic `py::object`, +which can often be autmatically cast to a specialized pybind11 type: + +.. code-block:: cpp + + py::module os = py::module::import("os"); + py::module path = py::module::import("os.path"); // like 'import os.path as path' + py::module np = py::module::import("numpy"); // like 'import numpy as np' + + py::str curdir_abs = path.attr("abspath")(path.attr("curdir")); + py::print(py::str("Current directory: ") + curdir_abs); + py::dict environ = os.attr("environ"); + py::print(environ["HOME"]); + py::array_t arr = np.attr("ones")(3, "dtype"_a="float32"); + py::print(py::repr(arr + py::int_(1))); + +(Note that this example requires ``#include `` and +``using namespace pybind11::literals;`` to work.) + Importing modules ================= From 8982c1ad054fa09626513c9381ea53d0382960cd Mon Sep 17 00:00:00 2001 From: Holger Kohr Date: Sun, 6 Sep 2020 17:26:39 +0200 Subject: [PATCH 2/5] Move implicit cast example to object.rst --- docs/advanced/embedding.rst | 19 ------------------ docs/advanced/pycpp/object.rst | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/docs/advanced/embedding.rst b/docs/advanced/embedding.rst index d41edfc0ac..98a5c52190 100644 --- a/docs/advanced/embedding.rst +++ b/docs/advanced/embedding.rst @@ -105,25 +105,6 @@ The two approaches can also be combined: std::cout << message; } -Typically, a call to a function in a `py::module` returns a generic `py::object`, -which can often be autmatically cast to a specialized pybind11 type: - -.. code-block:: cpp - - py::module os = py::module::import("os"); - py::module path = py::module::import("os.path"); // like 'import os.path as path' - py::module np = py::module::import("numpy"); // like 'import numpy as np' - - py::str curdir_abs = path.attr("abspath")(path.attr("curdir")); - py::print(py::str("Current directory: ") + curdir_abs); - py::dict environ = os.attr("environ"); - py::print(environ["HOME"]); - py::array_t arr = np.attr("ones")(3, "dtype"_a="float32"); - py::print(py::repr(arr + py::int_(1))); - -(Note that this example requires ``#include `` and -``using namespace pybind11::literals;`` to work.) - Importing modules ================= diff --git a/docs/advanced/pycpp/object.rst b/docs/advanced/pycpp/object.rst index c6c3b1b75b..54f871ebbe 100644 --- a/docs/advanced/pycpp/object.rst +++ b/docs/advanced/pycpp/object.rst @@ -20,6 +20,8 @@ Available types include :class:`handle`, :class:`object`, :class:`bool_`, Be sure to review the :ref:`pytypes_gotchas` before using this heavily in your C++ API. +.. _casting_back_and_forth: + Casting back and forth ====================== @@ -62,6 +64,40 @@ This example obtains a reference to the Python ``Decimal`` class. py::object scipy = py::module::import("scipy"); return scipy.attr("__version__"); +.. _implicit_casting: + +Implicit (automatic) casting +============================ + +Instead of using a generic :class:`object` as return type, it is possible to +specialize to a subtype, e.g., to use the lookup syntax of :class:`dict`. + +.. note:: + Casting to the wrong type will currently lead to downstream errors, e.g., + when casting a Python dict to :class:`list` and using the ``append`` method. + In the future, such a bad cast will likely result in a :class:`cast_error`, + see `PR #2349 `_. + +.. code-block:: cpp + #include + using namespace pybind11::literals; + + py::module os = py::module::import("os"); + py::module path = py::module::import("os.path"); // like 'import os.path as path' + py::module np = py::module::import("numpy"); // like 'import numpy as np' + + py::str curdir_abs = path.attr("abspath")(path.attr("curdir")); + py::print(py::str("Current directory: ") + curdir_abs); + py::dict environ = os.attr("environ"); + py::print(environ["HOME"]); + py::array_t arr = np.attr("ones")(3, "dtype"_a="float32"); + py::print(py::repr(arr + py::int_(1))); + +This constructor syntax is available for subclasses of :class:`object`; there +is no need to call ``obj.cast()`` explicitly as for custom classes, see +:ref:`_casting_back_and_forth`. + + .. _calling_python_functions: Calling Python functions From 250ed51d010419530aa207cae4504e4d334b5e42 Mon Sep 17 00:00:00 2001 From: Holger Kohr Date: Mon, 7 Sep 2020 22:20:33 +0200 Subject: [PATCH 3/5] Move to bottom and improve implicit casting text --- docs/advanced/pycpp/object.rst | 74 +++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/docs/advanced/pycpp/object.rst b/docs/advanced/pycpp/object.rst index 54f871ebbe..0ca561210d 100644 --- a/docs/advanced/pycpp/object.rst +++ b/docs/advanced/pycpp/object.rst @@ -64,39 +64,6 @@ This example obtains a reference to the Python ``Decimal`` class. py::object scipy = py::module::import("scipy"); return scipy.attr("__version__"); -.. _implicit_casting: - -Implicit (automatic) casting -============================ - -Instead of using a generic :class:`object` as return type, it is possible to -specialize to a subtype, e.g., to use the lookup syntax of :class:`dict`. - -.. note:: - Casting to the wrong type will currently lead to downstream errors, e.g., - when casting a Python dict to :class:`list` and using the ``append`` method. - In the future, such a bad cast will likely result in a :class:`cast_error`, - see `PR #2349 `_. - -.. code-block:: cpp - #include - using namespace pybind11::literals; - - py::module os = py::module::import("os"); - py::module path = py::module::import("os.path"); // like 'import os.path as path' - py::module np = py::module::import("numpy"); // like 'import numpy as np' - - py::str curdir_abs = path.attr("abspath")(path.attr("curdir")); - py::print(py::str("Current directory: ") + curdir_abs); - py::dict environ = os.attr("environ"); - py::print(environ["HOME"]); - py::array_t arr = np.attr("ones")(3, "dtype"_a="float32"); - py::print(py::repr(arr + py::int_(1))); - -This constructor syntax is available for subclasses of :class:`object`; there -is no need to call ``obj.cast()`` explicitly as for custom classes, see -:ref:`_casting_back_and_forth`. - .. _calling_python_functions: @@ -212,6 +179,47 @@ Generalized unpacking according to PEP448_ is also supported: .. _PEP448: https://www.python.org/dev/peps/pep-0448/ +.. _implicit_casting: + +Implicit casting +================ + +When using this C++ interface for Python types or calling Python functions, +and objects of type :class:`object` are returned, it is possible to +specialize to a subtype like :class:`dict`. The same holds for the proxy objects +returned by ``operator[]`` or ``obj.attr()``. +Casting to subtypes improves code readability and allows values to be used in +C++ functions that require a specific subtype rather than a generic :class:`object`. + +.. code-block:: cpp + + #include + using namespace pybind11::literals; + + py::module os = py::module::import("os"); + py::module path = py::module::import("os.path"); // like 'import os.path as path' + py::module np = py::module::import("numpy"); // like 'import numpy as np' + + py::str curdir_abs = path.attr("abspath")(path.attr("curdir")); + py::print(py::str("Current directory: ") + curdir_abs); + py::dict environ = os.attr("environ"); + py::print(environ["HOME"]); + py::array_t arr = np.attr("ones")(3, "dtype"_a="float32"); + py::print(py::repr(arr + py::int_(1))); + +These implicit conversions are available for subclasses of :class:`object`; there +is no need to call ``obj.cast()`` explicitly as for custom classes, see +:ref:`_casting_back_and_forth`. + +.. note:: + If a trivial conversion via move constructor is not possible, both implicit and + explicit casting (calling ``obj.cast()``) will attempt a "rich" conversion. + For instance, ``py::list env = os.attr("environ");`` will succeed and is + equivalent to the Python code ``env = list(os.environ)`` that produces a + list of the dict keys. + +.. TODO: Adapt text once PR #2349 has landed + Handling exceptions =================== From b9076789028c830e209058bfa6d5384fe2f7d996 Mon Sep 17 00:00:00 2001 From: Holger Kohr Date: Mon, 7 Sep 2020 22:27:37 +0200 Subject: [PATCH 4/5] Fix xref --- docs/advanced/pycpp/object.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/pycpp/object.rst b/docs/advanced/pycpp/object.rst index 0ca561210d..01fad5d241 100644 --- a/docs/advanced/pycpp/object.rst +++ b/docs/advanced/pycpp/object.rst @@ -209,7 +209,7 @@ C++ functions that require a specific subtype rather than a generic :class:`obje These implicit conversions are available for subclasses of :class:`object`; there is no need to call ``obj.cast()`` explicitly as for custom classes, see -:ref:`_casting_back_and_forth`. +:ref:`casting_back_and_forth`. .. note:: If a trivial conversion via move constructor is not possible, both implicit and From b4c0df380fb337ecd2c842474ba1adb4fb4e5668 Mon Sep 17 00:00:00 2001 From: Holger Kohr Date: Tue, 8 Sep 2020 20:05:52 +0200 Subject: [PATCH 5/5] Improve wording as per @bstaletic's suggestion --- docs/advanced/pycpp/object.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/advanced/pycpp/object.rst b/docs/advanced/pycpp/object.rst index 01fad5d241..70e493acd9 100644 --- a/docs/advanced/pycpp/object.rst +++ b/docs/advanced/pycpp/object.rst @@ -184,11 +184,11 @@ Generalized unpacking according to PEP448_ is also supported: Implicit casting ================ -When using this C++ interface for Python types or calling Python functions, -and objects of type :class:`object` are returned, it is possible to -specialize to a subtype like :class:`dict`. The same holds for the proxy objects +When using the C++ interface for Python types, or calling Python functions, +objects of type :class:`object` are returned. It is possible to invoke implicit +conversions to subclasses like :class:`dict`. The same holds for the proxy objects returned by ``operator[]`` or ``obj.attr()``. -Casting to subtypes improves code readability and allows values to be used in +Casting to subtypes improves code readability and allows values to be passed to C++ functions that require a specific subtype rather than a generic :class:`object`. .. code-block:: cpp