.. _pyfdb-dev-setup:
Development Setup
##################
This guide walks through setting up a local build of the full FDB stack so you
can iterate on ``PyFDB`` against a locally compiled ``libfdb5``.
It covers the C++ bundle build and the Python editable install that lets you
edit Python source files and have changes take effect immediately — no
reinstall required.
Prerequisites
~~~~~~~~~~~~~
The following must be available on your system before you start:
* **CMake** ≥ 3.18
* **ecbuild** ≥ 3.8
* **pybind11** ≥ 3.0.1
* **Ninja** (recommended; ``make`` also works, drop ``-G Ninja`` below)
* **uv** — `Astral's fast Python package installer `__
* A C++17-capable compiler (GCC ≥ 9, Clang ≥ 10, Apple Clang ≥ 12)
Setting up the Bundle
~~~~~~~~~~~~~~~~~~~~~
An ecbuild *bundle* is a thin ``CMakeLists.txt`` that clones and builds a set of
ECMWF projects together in a single CMake configure step.
Create a ``stack`` directory and place the following file in it:
.. code-block:: sh
mkdir stack && cd stack
.. code-block:: cmake
:caption: stack/CMakeLists.txt
cmake_minimum_required( VERSION 3.18 FATAL_ERROR )
find_package( ecbuild 3.8 REQUIRED HINTS ${CMAKE_CURRENT_SOURCE_DIR} $ENV{HOME}/.local/ecbuild)
project( ecmwf_stack_bundle VERSION 0.0.1 LANGUAGES CXX )
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
ecbuild_bundle_initialize()
ecbuild_bundle( PROJECT eckit GIT "https://github.com/ecmwf/eckit" BRANCH develop UPDATE )
ecbuild_bundle( PROJECT eccodes GIT "https://github.com/ecmwf/eccodes" BRANCH develop UPDATE )
ecbuild_bundle( PROJECT metkit GIT "https://github.com/ecmwf/metkit" BRANCH develop UPDATE )
ecbuild_bundle( PROJECT fdb GIT "https://github.com/ecmwf/fdb" BRANCH develop UPDATE )
ecbuild_bundle_finalize()
.. tip::
To work against a local checkout of any dependency instead of having ecbuild
clone it, replace the ``GIT`` / ``BRANCH`` arguments with
``SOURCE ``. This is the typical workflow when you are
modifying ``fdb`` or one of its dependencies alongside ``PyFDB``.
Python Environment
~~~~~~~~~~~~~~~~~~
Create and activate a virtual environment in the ``stack`` root **before**
running CMake. CMake's Python finder and pybind11 will both pick up the active
interpreter, so the extension is always linked against the venv's Python:
.. code-block:: sh
cd stack # the directory that holds CMakeLists.txt
uv venv --python 3.11 # or any supported version
source .venv/bin/activate
Install the Python build toolchain and ``PyFDB``'s runtime dependencies:
.. code-block:: sh
uv pip install build setuptools wheel pybind11 \
findlibs PyYAML pytest eccodes GitPython
.. note::
The ``build`` package is needed because the CMake target ``pyfdb-wheel``
invokes ``python -m build`` to produce the wheel from the staging area.
It must be present in the active venv before running ``cmake`` configuration.
Configure and Build
~~~~~~~~~~~~~~~~~~~
Create a ``build`` subdirectory, configure with CMake, then compile with Ninja:
.. code-block:: sh
mkdir build && cd build
cmake .. \
-G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_INSTALL_PREFIX=../install \
-DENABLE_PYTHON_FDB_INTERFACE=ON \
-DENABLE_MEMFS=ON \
-DENABLE_FDB_DOCUMENTATION=OFF
.. tip::
``-DENABLE_PYTHON_FDB_INTERFACE=ON`` is the flag that activates the
pybind11 extension build and creates the Python staging area at
``build/pyfdb-python-package-staging/``.
Build the entire stack:
.. code-block:: sh
ninja
A successful build populates ``build/pyfdb-python-package-staging/`` with the
compiled extension (``pyfdb_bindings/pyfdb_bindings.cpython-*.so``) and a
symlink to the Python source tree, making the directory a self-contained,
installable Python project.
Environment Variables
~~~~~~~~~~~~~~~~~~~~~
Set the following variables before importing ``PyFDB`` so that ``findlibs`` can
locate the locally built shared libraries instead of searching the system or
any installed Python packages:
.. code-block:: sh
# Tell findlibs where libfdb5.so lives (build/lib/)
export FDB5_HOME=/build
# Tell findlibs where libeccodes.so lives
# If eccodes was built as part of the bundle this is the same directory
export ECCODES_HOME=/build
# Disable conda / pip package search so the local build is always used
export FINDLIBS_DISABLE_PACKAGE=yes
.. note::
If you link against a system eccodes (e.g. installed via a package manager),
omit ``ECCODES_HOME`` and let ``findlibs`` discover it via the default search
path.
``FINDLIBS_DISABLE_PACKAGE=yes`` prevents ``findlibs`` from loading a
library out of an installed conda or pip package that may shadow the locally
built version. The ``--print-home-deps`` CLI flag (see :doc:`installation`)
is useful for verifying which paths are resolved after setting these
variables.
Editable Install
~~~~~~~~~~~~~~~~
With the build complete and the environment variables exported, install
``PyFDB`` from the staging area in **editable** mode. Editable mode places a
link from the active venv back into the staging area, so any change you make to
the Python source in the repository is immediately visible without reinstalling:
.. code-block:: sh
uv pip install -e /build/pyfdb-python-package-staging
Verify that the library is found correctly:
.. code-block:: sh
python -m pyfdb --print-home
Running Tests
~~~~~~~~~~~~~
Switch to the ``PyFDB`` test folder and run ``pytest``:
.. code-block:: sh
cd /fdb/tests/pyfdb
pytest
.. note::
Depending on your system's ``ulimit`` settings you may encounter
``OSError: [Errno 24] Too many open files`` caused by unreleased file
handles during the test run. Re-run with ``--lf`` to execute only the
last failed tests:
.. code-block:: sh
pytest --lf