Architecture¶
Overview¶
InterpolatePy contains three layers:
flowchart TD
User[User code] --> Public[interpolatepy package exports]
Public --> Router[_api.py]
Router -->|HAS_CPP is false| Python[Python implementations]
Router -->|HAS_CPP is true| Adapters[_adapters]
Adapters --> Extension[interpolatecpp_py extension]
Extension --> Library[interpolatecpp C++20 library]
Application code should normally depend only on the package exports. The implementation modules remain importable for development and debugging, but a direct implementation import bypasses backend selection.
Backend detection¶
interpolatepy/_backend.py runs once during package import:
- if
INTERPOLATEPY_NO_CPPis nonempty, native loading is skipped; - otherwise it imports
.interpolatecpp_pyrelative to the package; HAS_CPPbecomesTrueonly if that import succeeds;- an extension
ImportErrorleaves the Python fallback active.
Any nonempty value disables the extension, including the string "0". Set the
variable before Python starts:
An unavailable optional extension is intentionally silent. Call
get_cpp_module() only in backend internals; it raises when the extension was
not loaded.
Public import routing¶
interpolatepy/__init__.py exposes version and backend information, imports the
backend-routed algorithms from _api.py, and exports the always-Python
Quaternion, plotting helper, configuration classes, and runtime-checkable
protocols.
_api.py has two explicit branches. This makes the resolved class stable for
the life of the process and avoids conditional checks in every evaluation.
The top-level export list in interpolatepy.__all__ is the compatibility
boundary. New public APIs must be wired through:
- the Python implementation;
- the C++ binding and adapter when a native equivalent exists;
- both branches of
_api.py; interpolatepy/__init__.pyand its__all__list;- tests and the API reference.
Adapter layer¶
The pybind11 classes are fast but do not always present Python-native input and
output behavior. Files under interpolatepy/_adapters/ handle differences such
as:
- accepting lists and NumPy arrays consistently;
- vectorizing scalar C++ evaluators over NumPy arrays;
- converting native quaternions back to the Python
Quaternionclass; - preserving Python parameter data classes;
- adding plotting and batch path helpers;
- providing aliases for differently named native properties.
Use the package root for the backend-neutral workflows shown in the quick
start. Some implementation-specific helpers are Python-only. In particular,
the current native logarithmic-quaternion bindings do not expose
generate_trajectory(), get_physical_kinematics(), or acceleration boundary
arguments, and some B-spline refinement/diagnostic helpers exist only in the
Python implementation. Use INTERPOLATEPY_NO_CPP=1 if an application depends
on those helpers.
Source layout¶
interpolatepy/
__init__.py public namespace
_backend.py extension detection
_api.py backend routing
_adapters/ Python-facing native wrappers
*.py Python algorithms and utilities
cpp/
include/interpolatecpp/ public C++ headers
src/ C++ implementations
bindings/ pybind11 module
tests/ Catch2 tests
examples/ C++ example programs
tests/ Python tests
examples/ Python example programs
docs/ MkDocs sources
C++ targets¶
The CMake project requires C++20 and creates the interpolatecpp library. The
optional interpolatecpp_py module links that library and is copied beside the
Python package modules for local use.
| CMake option | Default |
|---|---|
INTERPOLATECPP_BUILD_TESTS |
ON |
INTERPOLATECPP_BUILD_BINDINGS |
OFF |
INTERPOLATECPP_BUILD_EXAMPLES |
OFF |
CMake FetchContent pins Eigen 3.4.0, Catch2 3.7.1, and pybind11 2.13.6 for the targets that need them. See Installation for build commands.
Testing the two implementations¶
The main Python suite exercises the selected public backend and also imports some implementation modules directly for focused unit coverage. To guarantee a fallback-only run:
After copying a built extension into interpolatepy/, start a new process and
run the same suite without the variable. C++ implementation tests are discovered
by CTest from cpp/tests/.
Backend parity tests should compare observable results and documented errors, not private coefficients or implementation-specific object identity.