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 , forward declare the bind_* functions, and 83 : then have a PYBIND11_MODULE function. For example, 84 : 85 : \code{.cpp} 86 : #include 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