add documention workflow
This commit is contained in:
179
docs/python/developer_guide.md
Normal file
179
docs/python/developer_guide.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# Developer Guide – Python Bindings
|
||||
|
||||
This guide is aimed at contributors who want to extend or modify the Python bindings for `uLib`.
|
||||
|
||||
---
|
||||
|
||||
## Repository Layout
|
||||
|
||||
```
|
||||
ulib/
|
||||
├── src/
|
||||
│ └── Python/
|
||||
│ ├── module.cpp # pybind11 module entry point
|
||||
│ ├── core_bindings.cpp # uLib::Core bindings
|
||||
│ ├── math_bindings.cpp # uLib::Math bindings
|
||||
│ ├── math_filters_bindings.cpp# VoxImageFilter bindings
|
||||
│ ├── CMakeLists.txt # builds uLib_python shared lib
|
||||
│ ├── testing/ # Python unit tests
|
||||
│ │ ├── pybind_test.py
|
||||
│ │ ├── core_pybind_test.py
|
||||
│ │ ├── math_pybind_test.py
|
||||
│ │ └── math_filters_test.py
|
||||
│ └── uLib/ # Python package (uLib_python.so lands here)
|
||||
│ └── __init__.py
|
||||
├── build_python.py # poetry build hook (calls CMake)
|
||||
├── pyproject.toml # poetry metadata
|
||||
└── condaenv.yml # conda/micromamba environment
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding a New Binding
|
||||
|
||||
All bindings live in the four source files listed above. The module entry point `module.cpp` calls `init_core()`, `init_math()`, and `init_math_filters()` in order.
|
||||
|
||||
### 1. Pick (or create) the right binding file
|
||||
|
||||
| C++ header location | Binding file |
|
||||
|---|---|
|
||||
| `src/Core/` | `core_bindings.cpp` |
|
||||
| `src/Math/` (geometry, grids, VoxImage) | `math_bindings.cpp` |
|
||||
| `src/Math/VoxImageFilter*.hpp` | `math_filters_bindings.cpp` |
|
||||
|
||||
### 2. Add the `#include` directive
|
||||
|
||||
```cpp
|
||||
// math_bindings.cpp
|
||||
#include "Math/MyNewClass.h"
|
||||
```
|
||||
|
||||
### 3. Write the pybind11 binding inside the appropriate `init_*` function
|
||||
|
||||
```cpp
|
||||
void init_math(py::module_ &m) {
|
||||
// ... existing bindings ...
|
||||
|
||||
py::class_<MyNewClass>(m, "MyNewClass")
|
||||
.def(py::init<>())
|
||||
.def("MyMethod", &MyNewClass::MyMethod)
|
||||
.def("AnotherMethod", &MyNewClass::AnotherMethod,
|
||||
py::arg("x"), py::arg("y") = 0.0f);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Rebuild only the Python target
|
||||
|
||||
```bash
|
||||
cmake --build build --target uLib_python -j$(nproc)
|
||||
```
|
||||
|
||||
### 5. Write a Python test
|
||||
|
||||
Add a new test class to the relevant test file (or create a new one under `src/Python/testing/`):
|
||||
|
||||
```python
|
||||
# src/Python/testing/math_pybind_test.py
|
||||
class TestMyNewClass(unittest.TestCase):
|
||||
def test_basic(self):
|
||||
obj = uLib.Math.MyNewClass()
|
||||
result = obj.MyMethod()
|
||||
self.assertAlmostEqual(result, expected_value)
|
||||
```
|
||||
|
||||
Register the test in `src/Python/CMakeLists.txt` if you add a new file:
|
||||
|
||||
```cmake
|
||||
add_test(NAME pybind_my_new
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/testing/my_new_test.py)
|
||||
set_tests_properties(pybind_my_new PROPERTIES
|
||||
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>:${PROJECT_SOURCE_DIR}/src/Python")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build System Details
|
||||
|
||||
### CMakeLists.txt (`src/Python/`)
|
||||
|
||||
`pybind11_add_module` compiles the shared library `uLib_python` and links it against the C++ static/shared libraries `uLibCore` and `uLibMath`. The install target copies the `.so` into the standard library directory.
|
||||
|
||||
```cmake
|
||||
pybind11_add_module(uLib_python
|
||||
module.cpp core_bindings.cpp math_bindings.cpp math_filters_bindings.cpp)
|
||||
|
||||
target_link_libraries(uLib_python PRIVATE uLibCore uLibMath)
|
||||
```
|
||||
|
||||
### poetry / build_python.py
|
||||
|
||||
`pyproject.toml` declares `build_python.py` as the custom build hook. When `poetry install` or `poetry build` is invoked it:
|
||||
|
||||
1. Calls `cmake <root> -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=<pkg_dir> ...` in `build_python/`.
|
||||
2. Builds only the `uLib_python` target.
|
||||
3. The resulting `.so` is placed inside `src/Python/uLib/` so it is picked up by Poetry as a package data file.
|
||||
|
||||
The `USE_CUDA` environment variable gates CUDA support at build time:
|
||||
|
||||
```bash
|
||||
USE_CUDA=ON poetry install # with CUDA
|
||||
USE_CUDA=OFF poetry install # CPU only (default)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Running All Tests
|
||||
|
||||
```bash
|
||||
# From the repository root, with PYTHONPATH set:
|
||||
export PYTHONPATH="$(pwd)/build/src/Python:$(pwd)/src/Python"
|
||||
|
||||
python -m pytest src/Python/testing/ -v
|
||||
```
|
||||
|
||||
Or through CMake's test runner (after building the full project):
|
||||
|
||||
```bash
|
||||
cd build
|
||||
ctest --output-on-failure -R pybind
|
||||
```
|
||||
|
||||
Expected output (all passing):
|
||||
|
||||
```
|
||||
Start 1: pybind_general
|
||||
1/4 Test #1: pybind_general ............. Passed
|
||||
Start 2: pybind_core
|
||||
2/4 Test #2: pybind_core ................ Passed
|
||||
Start 3: pybind_math
|
||||
3/4 Test #3: pybind_math ................ Passed
|
||||
Start 4: pybind_math_filters
|
||||
4/4 Test #4: pybind_math_filters ........ Passed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory Management Notes
|
||||
|
||||
`uLib::Vector<T>` has explicit GPU memory management. When wrapping methods that return references to internal data, use `py::return_value_policy::reference_internal` to avoid dangling references:
|
||||
|
||||
```cpp
|
||||
.def("Data", &VoxImage<Voxel>::Data,
|
||||
py::return_value_policy::reference_internal)
|
||||
```
|
||||
|
||||
For objects held by `std::unique_ptr` without Python-side deletion, use `py::nodelete`:
|
||||
|
||||
```cpp
|
||||
py::class_<Abstract::VoxImageFilter,
|
||||
std::unique_ptr<Abstract::VoxImageFilter, py::nodelete>>(m, "AbstractVoxImageFilter")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Useful References
|
||||
|
||||
- [pybind11 documentation](https://pybind11.readthedocs.io)
|
||||
- [pybind11 – STL containers](https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html)
|
||||
- [pybind11 – Eigen integration](https://pybind11.readthedocs.io/en/stable/advanced/cast/eigen.html)
|
||||
- [CMake – pybind11 integration](https://pybind11.readthedocs.io/en/stable/compiling.html)
|
||||
Reference in New Issue
Block a user