Skip to content

Python bindings built on top of C-API #57

Python bindings built on top of C-API

Python bindings built on top of C-API #57

Workflow file for this run

name: Python CI
on:
workflow_dispatch:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
actions: read
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
python-ci-capi:
runs-on: ubuntu-latest
steps:
- name: Checkout ladybug
uses: actions/checkout@v4
with:
repository: LadybugDB/ladybug
fetch-depth: 1
path: ladybug
- name: Update submodules
working-directory: ladybug
run: git submodule update --init --recursive dataset
- name: Checkout ladybug-python into ladybug/tools/python_api
uses: actions/checkout@v4
with:
fetch-depth: 1
path: ladybug/tools/python_api
- name: Setup ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: python-${{ runner.os }}-${{ runner.arch }}-${{ github.ref }}
max-size: 2G
create-symlink: true
restore-keys: |
python-${{ runner.os }}-${{ runner.arch }}-refs/heads/main
python-${{ runner.os }}-${{ runner.arch }}-
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
working-directory: ladybug/tools/python_api
run: |
uv venv .venv
uv pip install -e .[dev]
- name: Resolve compatible lbug artifact run
working-directory: ladybug
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
SHA="$(git rev-parse HEAD)"
API_URL="https://api.github.com/repos/LadybugDB/ladybug/actions/workflows/build-and-deploy.yml/runs"
AUTH_HEADER="Authorization: Bearer $GITHUB_TOKEN"
ACCEPT_HEADER="Accept: application/vnd.github+json"
VERSION_HEADER="X-GitHub-Api-Version: 2022-11-28"
RUN_ID="$(
curl -fsSL \
-H "$AUTH_HEADER" \
-H "$ACCEPT_HEADER" \
-H "$VERSION_HEADER" \
"$API_URL?head_sha=$SHA&status=success&per_page=1" \
| python -c 'import json,sys; data=json.load(sys.stdin); runs=data.get("workflow_runs") or []; print(runs[0]["id"] if runs else "")'
)"
if [ -z "$RUN_ID" ]; then
RUN_ID="$(
curl -fsSL \
-H "$AUTH_HEADER" \
-H "$ACCEPT_HEADER" \
-H "$VERSION_HEADER" \
"$API_URL?branch=main&status=success&per_page=1" \
| python -c 'import json,sys; data=json.load(sys.stdin); runs=data.get("workflow_runs") or []; print(runs[0]["id"] if runs else "")'
)"
fi
if [ -z "$RUN_ID" ]; then
echo "Could not find a successful LadybugDB/ladybug build-and-deploy run." >&2
exit 1
fi
echo "Using Ladybug build-and-deploy RUN_ID=$RUN_ID for SHA=$SHA"
echo "LBUG_BUILD_RUN_ID=$RUN_ID" >> "$GITHUB_ENV"
- name: Download shared lbug library
working-directory: ladybug/tools/python_api
env:
GH_TOKEN: ${{ github.token }}
run: |
gh --version
LBUG_PRECOMPILED_RUN_ID="$LBUG_BUILD_RUN_ID" LBUG_LIB_KIND=shared bash scripts/download_lbug.sh .cache/lbug-capi.env
cat .cache/lbug-capi.env >> "$GITHUB_ENV"
- name: Check formatting (black)
working-directory: ladybug/tools/python_api
run: |
uv pip install black
.venv/bin/black --check src_py test
- name: Run ruff check
working-directory: ladybug/tools/python_api
run: |
.venv/bin/ruff check src_py test
- name: Debug dataset resolution
working-directory: ladybug/tools/python_api
run: |
pwd
ls -la
echo "--- test_helper candidates ---"
find . .. ../.. -name 'test_helper.py' -print
echo "--- dataset candidates ---"
find . .. ../.. -maxdepth 3 -type d -name dataset -print
echo "--- python debug ---"
.venv/bin/python - <<'PY'
import importlib.util
import pathlib
import sys
print("cwd =", pathlib.Path.cwd())
print("sys.path[:6] =", sys.path[:6])
print("find_spec(test_helper) =", importlib.util.find_spec("test_helper"))
try:
import test_helper
print("test_helper.__file__ =", getattr(test_helper, "__file__", None))
print("LBUG_ROOT =", getattr(test_helper, "LBUG_ROOT", None))
print("LBUG_ROOT_PATH =", getattr(test_helper, "LBUG_ROOT_PATH", None))
print("DATASET_ROOT =", getattr(test_helper, "DATASET_ROOT", None))
except Exception as exc:
print("import test_helper failed:", repr(exc))
for rel in (
pathlib.Path("dataset"),
pathlib.Path("../dataset"),
pathlib.Path("../../dataset"),
pathlib.Path("test/test_helper.py"),
pathlib.Path("test_helper.py"),
pathlib.Path("../dataset/tinysnb/schema.cypher"),
pathlib.Path("../../dataset/tinysnb/schema.cypher"),
):
print(f"{rel} exists={rel.exists()} resolved={rel.resolve()}")
PY
- name: Run pytest (C API backend, non-torch)
working-directory: ladybug/tools/python_api
env:
LBUG_PYTHON_BACKEND: capi
run: |
.venv/bin/python -m pytest -vv ./test --ignore=./test/test_torch_geometric.py
- name: Run pytest (C API backend, torch-geometric)
working-directory: ladybug/tools/python_api
env:
LBUG_PYTHON_BACKEND: capi
run: |
.venv/bin/python -c 'import sys; import torch; import pytest; raise SystemExit(pytest.main(["-vv", "./test/test_torch_geometric.py"]))'
python-ci-pybind:
runs-on: ubuntu-latest
steps:
- name: Checkout ladybug
uses: actions/checkout@v4
with:
repository: LadybugDB/ladybug
fetch-depth: 1
path: ladybug
- name: Update submodules
working-directory: ladybug
run: git submodule update --init --recursive dataset
- name: Checkout ladybug-python into ladybug/tools/python_api
uses: actions/checkout@v4
with:
fetch-depth: 1
path: ladybug/tools/python_api
- name: Setup ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: python-${{ runner.os }}-${{ runner.arch }}-${{ github.ref }}
max-size: 2G
create-symlink: true
restore-keys: |
python-${{ runner.os }}-${{ runner.arch }}-refs/heads/main
python-${{ runner.os }}-${{ runner.arch }}-
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
working-directory: ladybug/tools/python_api
run: |
uv venv .venv
uv pip install -e .[dev]
- name: Check formatting (black)
working-directory: ladybug/tools/python_api
run: |
uv pip install black
.venv/bin/black --check src_py test
- name: Run ruff check
working-directory: ladybug/tools/python_api
run: |
.venv/bin/ruff check src_py test
- name: Build native module
working-directory: ladybug
env:
GEN: Ninja
CMAKE_C_COMPILER_LAUNCHER: ccache
CMAKE_CXX_COMPILER_LAUNCHER: ccache
run: |
make python
cp tools/python_api/src_py/*.py tools/python_api/build/ladybug/
- name: Debug dataset resolution
working-directory: ladybug/tools/python_api
run: |
pwd
ls -la
echo "--- test_helper candidates ---"
find . .. ../.. -name 'test_helper.py' -print
echo "--- dataset candidates ---"
find . .. ../.. -maxdepth 3 -type d -name dataset -print
echo "--- python debug ---"
.venv/bin/python - <<'PY'
import importlib.util
import pathlib
import sys
print("cwd =", pathlib.Path.cwd())
print("sys.path[:6] =", sys.path[:6])
print("find_spec(test_helper) =", importlib.util.find_spec("test_helper"))
try:
import test_helper
print("test_helper.__file__ =", getattr(test_helper, "__file__", None))
print("LBUG_ROOT =", getattr(test_helper, "LBUG_ROOT", None))
print("LBUG_ROOT_PATH =", getattr(test_helper, "LBUG_ROOT_PATH", None))
print("DATASET_ROOT =", getattr(test_helper, "DATASET_ROOT", None))
except Exception as exc:
print("import test_helper failed:", repr(exc))
for rel in (
pathlib.Path("dataset"),
pathlib.Path("../dataset"),
pathlib.Path("../../dataset"),
pathlib.Path("test/test_helper.py"),
pathlib.Path("test_helper.py"),
pathlib.Path("../dataset/tinysnb/schema.cypher"),
pathlib.Path("../../dataset/tinysnb/schema.cypher"),
):
print(f"{rel} exists={rel.exists()} resolved={rel.resolve()}")
PY
- name: Run pytest (pybind backend, non-torch)
working-directory: ladybug/tools/python_api
env:
LBUG_PYTHON_BACKEND: pybind
run: |
export PYTHONPATH=./build
.venv/bin/python -m pytest -vv ./test --ignore=./test/test_torch_geometric.py
- name: Run pytest (pybind backend, torch-geometric)
working-directory: ladybug/tools/python_api
env:
LBUG_PYTHON_BACKEND: pybind
run: |
export PYTHONPATH=./build
.venv/bin/python -c 'import sys; import torch; import pytest; raise SystemExit(pytest.main(["-vv", "./test/test_torch_geometric.py"]))'