SpECTRE Documentation Coverage Report
Current view: top level - __w/spectre/spectre/docs/DevGuide - PythonBindings.md Hit Total Coverage
Commit: f1ddee3e40d81480e49140855d2b0e66fafaa908 Lines: 0 1 0.0 %
Date: 2020-12-02 17:35:08
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : \cond NEVER
       2             : Distributed under the MIT License.
       3             : See LICENSE.txt for details.
       4             : \endcond
       5             : # Writing Python Bindings {#spectre_writing_python_bindings}
       6             : 
       7             : ## CMake and Directory Layout
       8             : 
       9             : To allow users to analyze output from simulations and take advantage of
      10             : SpECTRE's data structures and functions in python, bindings must sometimes be
      11             : written. SpECTRE uses [pybind11](https://pybind11.readthedocs.io/)
      12             : to aid with generating the bindings. The C++ code for the bindings should
      13             : generally go in a `Python` subdirectory. For example, the bindings for the
      14             : DataStructures library would go in `src/DataStructures/Python/`. SpECTRE
      15             : provides the `spectre_python_add_module` CMake function to make adding a new
      16             : python module, be it with or without bindings, easy.  The python bindings are
      17             : built only if `-D BUILD_PYTHON_BINDINGS=ON` is passed when invoking cmake.
      18             : You can specify the Python version, interpreter and libraries used for compiling
      19             : and testing the bindings by setting the `-D PYTHON_EXECUTABLE` to an absolute
      20             : path such as `/usr/bin/python3`.
      21             : 
      22             : The function `spectre_python_add_module` takes as its first argument the module,
      23             : in our case `DataStructures`. Optionally, a list of `SOURCES` can be passed to
      24             : the CMake function. If you specify `SOURCES`, you must also specify a
      25             : `LIBRARY_NAME`. A good `LIBRARY_NAME` is the name of the C++ library for which
      26             : bindings are being built prefixed with `Py`, e.g. `PyDataStructures`. If the
      27             : Python module will only consist of Python files, then the `SOURCES` option
      28             : should not be specified. Python files that should be part
      29             : of the module can be passed with the keyword `PYTHON_FILES`, e.g.
      30             : `PYTHON_FILES Hello.py HelloWorld.py`. Finally, the `MODULE_PATH` named argument
      31             : can be passed with a string that is the path to where the module should be. For
      32             : example, `MODULE_PATH "submodule0/submodule1/"` would mean the module is
      33             : accessed from python using `import spectre.submodule0.submodule1.MODULE_NAME`.
      34             : 
      35             : Here is a complete example of how to call the `spectre_python_add_module`
      36             : function:
      37             : 
      38             : \code
      39             : spectre_python_add_module(
      40             :   Extra
      41             :   LIBRARY_NAME "PyExtraDataStructures"
      42             :   MODULE_PATH "DataStructures/"
      43             :   SOURCES Bindings.cpp MyCoolDataStructure.cpp
      44             :   PYTHON_FILES CoolPythonDataStructure.py
      45             :   )
      46             : \endcode
      47             : 
      48             : The library that is added has the name `PyExtraDataStructures`. Make sure to
      49             : call `spectre_python_link_libraries` for every Python module that compiles
      50             : `SOURCES`. For example,
      51             : 
      52             : \code
      53             : spectre_python_link_libraries(
      54             :   PyExtraDataStructures
      55             :   PUBLIC ExtraDataStructures
      56             :   )
      57             : \endcode
      58             : 
      59             : You may also call `spectre_python_add_dependencies` for Python modules that
      60             : have `SOURCES`, e.g.
      61             : 
      62             : \code
      63             : spectre_python_add_dependencies(
      64             :   PyExtraDataStructures
      65             :   PyDataStructures
      66             :   )
      67             : \endcode
      68             : 
      69             : Note that these functions will skip adding or configure any C++ libraries if
      70             : the `BUILD_PYTHON_BINDINGS` flag is `OFF`.
      71             : 
      72             : ## Writing Bindings
      73             : 
      74             : Once a python module has been added you can write the actual bindings. You
      75             : should structure your bindings directory to reflect the structure of the library
      76             : you're writing bindings for. For example, say we want bindings for `DataVector`
      77             : and `Matrix` then we should have one source file for each class's bindings
      78             : inside `src/DataStructures/Python`. The functions that generate the bindings
      79             : should be in the `py_bindings` namespace and have a reasonable name such as
      80             : `bind_datavector`. There should be a file named `Bindings.cpp` which calls all
      81             : the `bind_*` functions. The `Bindings.cpp` file is quite simple and should
      82             : `include <pybind11/pybind11.h>`, forward declare the `bind_*` functions, and
      83             : then have a `PYBIND11_MODULE` function. For example,
      84             : 
      85             : \code{.cpp}
      86             : #include <pybind11/pybind11.h>
      87             : 
      88             : namespace py = pybind11;
      89             : 
      90             : namespace py_bindings {
      91             : void bind_datavector(py::module& m);
      92             : }  // namespace py_bindings
      93             : 
      94             : PYBIND11_MODULE(_PyDataStructures, m) {
      95             :   py_bindings::bind_datavector(m);
      96             : }
      97             : \endcode
      98             : 
      99             : Note that the library name is passed to `PYBIND11_MODULE` and is prefixed
     100             : with an underscore. The underscore is important and the library name must be the
     101             : same that is passed as `LIBRARY_NAME` to `spectre_python_add_module` (see
     102             : above).
     103             : 
     104             : The `DataVector` bindings serve as an example with code comments on how to write
     105             : bindings for a class. There is also extensive documentation available directly
     106             : from [pybind11](https://pybind11.readthedocs.io/). SpECTRE currently aims to
     107             : support both Python 2.7 and Python 3 and as such all bindings must support both.
     108             : 
     109             : \note Exceptions should be allowed to propagate through the bindings so that
     110             : error handling via exceptions is possible from python rather than having the
     111             : python interpreter being killed with a call to `abort`.
     112             : 
     113             : ## Testing Python Bindings and Code
     114             : 
     115             : All the python bindings must be tested. SpECTRE uses the
     116             : [unittest](https://docs.python.org/3/library/unittest.html) framework
     117             : provided as part of python. To register a test file with CMake use the
     118             : SpECTRE-provided function `spectre_add_python_test` passing as the first
     119             : argument the test name (e.g. `"Unit.DataStructures.Python.DataVector"`), the
     120             : file as the second argument (e.g. `Test_DataVector.py`), and a semicolon
     121             : separated list of labels as the last (e.g. `"unit;datastructures;python"`).
     122             : All the test cases should be in a single class so that the python unit testing
     123             : framework will run all test functions on a single invocation to avoid startup
     124             : cost.
     125             : 
     126             : Below is an example of registering a python test file for bindings:
     127             : 
     128             : \snippet tests/Unit/DataStructures/CMakeLists.txt example_add_pybindings_test
     129             : 
     130             : Python code that does not use bindings must also be tested. You can register the
     131             : test file using the `spectre_add_python_test` CMake function with the same
     132             : signature as shown above.
     133             : 
     134             : Please note that the tests must be formatted according to the `.style.yapf` file
     135             : in the root of the repository.
     136             : 
     137             : ## Using The Bindings
     138             : 
     139             : See \ref spectre_using_python "Using SpECTRE's Python"
     140             : 
     141             : ## Notes:
     142             : 
     143             : - All python libraries are dynamic/shared libraries.
     144             : - Exceptions should be allowed to propagate through the bindings so that
     145             :   error handling via exceptions is possible from python rather than having the
     146             :   python interpreter being killed with a call to `abort`.
     147             : - All function arguments in Python bindings should be named using `py::arg`.
     148             :   See the Python bindings in `IO/H5/` for examples. Using the named arguments in
     149             :   Python code is optional, but preferred when it makes code more readable.
     150             :   In particular, use the argument names in the tests for the Python bindings so
     151             :   they are being tested as well.

Generated by: LCOV version 1.14