Development with VSCode

This page collects the developer workflow for building LuPNT, wiring up the Python bindings, and debugging the C++/Python code from Visual Studio Code. Day-to-day builds are driven by Pixi, which provisions the compiler toolchain, Python, and all C++ dependencies from conda-forge — see the Introducing LuPNT and the project README.md for the one-time setup (pixi install, pixi run build, pixi run build-py).

Environment

LuPNT uses Pixi for reproducible environments; you do not need to create a conda or mamba environment by hand. The Pixi manifest pixi.toml defines both the dependencies and the task shortcuts (pixi run build, build-py, test, build-docs, …).

Activate the environment in a shell — this exports LUPNT_DATA_PATH, LUPNT_OUTPUT_PATH, PECSIMPY_BASE_PATH, and PYTHONPATH (pointed at python/) automatically:

pixi shell

or run a single command without entering a shell:

pixi run <command>

To point VS Code’s Python and CMake extensions at the Pixi interpreter, select the interpreter under .pixi/envs/default/bin/python (Command Palette → Python: Select Interpreter).

Building the Python bindings

The Pixi task build-py runs the CMake pylupnt-dev target, which builds LuPNT and the pybind11 bindings, copies the generated _pylupnt.*.so into python/pylupnt/, and generates type stubs (.pyi) so IDEs like VS Code can autocomplete the compiled package:

pixi run build-py          # Release bindings
pixi run build-py-reldbg   # RelWithDebInfo bindings
pixi run build-py-debug    # Debug bindings

Inside pixi shell the package is already importable (PYTHONPATH includes python/). Verify with:

python -c "import pylupnt as pnt; print(pnt.R_MOON)"

Outside of Pixi activation (for example a Jupyter/VS Code kernel launched directly), register the LuPNT kernel once so import pylupnt and the runtime data paths resolve:

pixi run install-kernel     # bakes PYTHONPATH / LUPNT_DATA_PATH into the "LuPNT (pixi)" kernel

Debugging

You can debug Python and C++ together by installing the Python C++ Debugger extension; its website has examples for Windows and gdb.

For Apple silicon (and generally on macOS), install the CodeLLDB extension for C++ debugging and create .vscode/launch.json with the following configurations. Change the path to eigenlldb.py (shipped at the repo root) to enable pretty-printing of Eigen / LuPNT matrix types.

{
    "configurations": [
        {
            "name": "* C++ Attach",
            "type": "lldb",
            "request": "attach",
            "pid": "",
            "initCommands": [
                # ***** CHANGE THIS *****
                "command script import \"YOUR-PATH-TO-LUPNT/LuPNT/eigenlldb.py\"",
                # ***** CHANGE THIS *****
            ],
        },
        {
            "name": "* Python",
            "type": "debugpy",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "console": "integratedTerminal"
        },
        {
            "name": "* Python/C++ Debugger",
            "type": "pythoncpp",
            "request": "launch",
            "pythonLaunchName": "* Python Debugger: Current File",
            "cppAttachName": "* Attach",
        },
    ],
}

The Python/C++ Debugger starts a normal Python debug session and passes the process PID to the C++ debugger so you can step across the pybind11 boundary.

To debug a pure C++ target with the CMake extension, edit .vscode/settings.json:

{
    "cmake.debugConfig": {
        "name": "* C++ Launch",
        "type": "lldb",
        "request": "launch",
        "initCommands": [
            // ***** CHANGE THIS *****
            "command script import \"YOUR-PATH-TO-LUPNT/LuPNT/eigenlldb.py\"",
            // ***** CHANGE THIS *****
        ],
    },
}

Pretty printing

To use eigenlldb.py for pretty-printing with CodeLLDB, install NumPy into LLDB’s Python. Open the Command Palette (Cmd/Ctrl + Shift + P), select LLDB: Command Prompt, and run pip install numpy.

In the debug console, use p <variable> to print a value or ? <variable> to inspect its raw contents. For example, given

Vec3 r(4338.99, -4338.99, -0.0757297);
Body moon = Body::Moon();

the console shows

p r
(lupnt::Vec3) (3,1) (static,static)
[[ 4338.99     ]
 [-4338.99     ]
 [   -0.0757297]]

? moon
{id:MOON, name:"MOON", GM:4902.8001180000001, R:1737.4, ...}
    id = MOON
    name = "MOON"
    GM = 4902.8001180000001
    R = 1737.4000000000001
    fixed_frame = MOON_PA
    inertial_frame = MOON_CI
    use_gravity_field = true

Pre-commit hooks

LuPNT uses pre-commit to enforce formatting (clang-format / cmake-format). The hooks run automatically on git commit; run them manually with:

pixi run pre-commit run --all-files

A failing formatting check rejects the commit; git commit --no-verify bypasses the hook if you need to commit work in progress.