Legacy CFFI interface#
Deprecated since version 1.19.0: The CFFI-based pymetkit package (under python/pymetkit) is superseded by the
pybind11-based package documented in API. It is retained for reference only and
is no longer built, tested, or published.
Warning
Do not use the legacy CFFI interface for new code. It wraps the metkit C API
(metkit_c.h) through cffi rather than binding the
C++ classes directly, and it is not wired into the build or release workflows. All new
development should target the Pythonic API.
Background#
The legacy interface exposed a MarsRequest class and a parse_mars_request function
backed by cffi (ffi.dlopen + ffi.cdef against a stripped copy of
metkit_c.h), together with a PatchedLib error-wrapping layer and the
MetKitException / CFFIModuleLoadFailed exceptions.
Installation#
Warning
The legacy package is not published on PyPI and has no build system of its own.
These steps are provided for reference only. All new code should use the
pybind11-based pymetkit documented in Installation.
The legacy CFFI interface requires metkit = 1.19.2. The steps below build that
version from source and wire up the Python package.
Build dependencies
Tool |
Link |
CMake |
|
ecbuild |
|
Ninja |
Runtime dependencies
Library |
Link |
eckit |
|
eccodes |
|
metkit |
|
libaec |
Python dependencies
Requirement |
Link |
Python 3.11 |
|
cffi |
|
findlibs |
1. Build metkit 1.19.2 from source
Create a bundle directory and switch to it:
mkdir stack && cd stack
Place the following CMakeLists.txt in it:
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)
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" TAG 1.19.2 UPDATE)
ecbuild_bundle_finalize()
Tip
If ecbuild, ninja or aec are not available on your PATH, load them via the
environment modules system before running cmake:
module load ecbuild ninja aec
Alternatively, adjust the HINTS path in the find_package call to point at
your ecbuild installation, and drop -G Ninja to fall back to make.
Create a build directory, configure and compile:
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=../install \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
.. -G Ninja
ninja
2. Set up a Python environment
Note
Python 3.11 is required. If it is not your system default, load it first:
module load python3/3.11
cd ../.. # back to the stack root
python3 -m venv .venv
source .venv/bin/activate
pip install cffi findlibs
cffi provides the C-extension glue; findlibs
(ecmwf/findlibs) locates libmetkit at runtime.
3. Install the legacy package
The metkit 1.19.2 tag ships a pyproject.toml at its root that packages the
legacy CFFI interface. Install it directly from the checked-out source:
cd stack/metkit
pip install .
cd ../..
Then let findlibs know where libmetkit was installed:
export METKIT_DIR=stack/install
4. Verify
python - <<'EOF'
from pymetkit import MarsRequest
req = MarsRequest("retrieve", class_="od", date="-1", param="130")
print(list(req.keys()))
EOF
Migration#
The new package keeps the same core concepts, so migration is largely mechanical:
from pymetkit import MarsRequest, parse_mars_request— unchanged import surface.A request is now built from a verb and a plain mapping rather than keyword arguments:
MarsRequest("retrieve", {"class": "od", "date": "-1"})replacesMarsRequest("retrieve", class_="od", date="-1").MarsRequest.expand,validate,merge,keys,__setitem__,__contains__and__eq__behaviours are preserved.num_valuesand__getitem__now raiseKeyErrorfor a missing parameter instead of returning0/[]as the legacy C-API layer did.MarsSelectionis now a type alias for a user-supplied mapping; value normalisation is handled internally rather than ad hoc.MetKitExceptionis still raised for MARS language errors and is importable aspymetkit.MetKitException.
Examples#
Constructing a request
The legacy interface accepted keyword arguments, using a trailing underscore to
escape Python reserved words such as class:
# Legacy CFFI
request = MarsRequest("retrieve", class_="od", date="-1", step=[0, 6, 12])
The new interface takes a plain mapping; no escaping is needed:
from pymetkit import MarsRequest
request = MarsRequest("retrieve", {"class": "od", "date": "-1", "step": [0, 6, 12]})
Missing parameters
The legacy C-API returned sentinel values for absent parameters. The new interface
raises KeyError instead. Guard with in where absence is expected:
# Legacy: silent sentinel values
n = request.num_values("step") # returned 0 if not set
v = request["step"] # returned [] if not set
# New: raises KeyError — guard explicitly
from pymetkit import MarsRequest
request = MarsRequest("retrieve", {"class": "od", "date": "-1"})
n = request.num_values("step") if "step" in request else 0
v = request["step"] if "step" in request else []