diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca9d392..b27a58a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,14 +60,8 @@ jobs: env: SKIP: no-commit-to-branch - # test notebooks in parallel across 3 jobs with GNU parallel - name: Test notebooks - run: | - cd ./notebooks - mkdir -p data images - uv run python -c "import osmnx; print(osmnx.__version__)" - uv run jupyter nbconvert --to python *.ipynb - parallel -j3 --line-buffer --tag 'uv run ipython {}' ::: *.py + run: cd notebooks && bash run_notebooks.sh # allow notebooks test runs to fail, but still save partial cache. this # saves after ordinary failures but not if job is canceled or times out. diff --git a/notebooks/run_notebooks.sh b/notebooks/run_notebooks.sh new file mode 100755 index 0000000..813800d --- /dev/null +++ b/notebooks/run_notebooks.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -euo pipefail + +export MPLBACKEND="Agg" +CACHE_DIR="./cache" +CACHE_READY_FILE="${CACHE_DIR}/.cache_ready" + +# test notebooks serially until the cache is ready, then in parallel +WORKERS=1 +if [[ -f "$CACHE_READY_FILE" ]]; then + WORKERS=4 +fi + +# convert the notebooks to .py files to execute them +uv run jupyter nbconvert --to python *.ipynb +uv run python -c "import osmnx; print(osmnx.__version__)" +mkdir -p data images "$CACHE_DIR" + +echo "Running notebooks with ${WORKERS} process(es)." +if (( WORKERS == 1 )); then + for filename in *.py; do + uv run ipython "$filename" + done +else + printf '%s\n' *.py | xargs -n1 -P4 uv run ipython +fi + +# mark the cache ready after each fully successful run +touch "$CACHE_READY_FILE" diff --git a/pyproject.toml b/pyproject.toml index 193bf12..4044b5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,17 +10,17 @@ requires-python = ">=3.11" version = "2.1.0" # match pinned version above [project.optional-dependencies] -all = ["folium", "jupyterlab", "mapclassify", "igraph"] +examples = ["folium", "jupyterlab", "mapclassify", "igraph"] [dependency-groups] -dev = ["nbdime", "prek"] +dev = ["ipython", "nbconvert", "nbdime", "prek"] [tool.ruff] cache-dir = "~/.cache/prek/ruff" line-length = 100 [tool.ruff.lint] -extend-select = ["A", "B", "E", "F", "I", "UP", "W"] +extend-select = ["A", "B", "E", "F", "I", "RUF200", "UP", "W"] [tool.ruff.lint.pycodestyle] max-line-length = 110 # line length + 10% since it isn't a hard upper bound diff --git a/uv.lock b/uv.lock index 3464d75..cf2c450 100644 --- a/uv.lock +++ b/uv.lock @@ -376,7 +376,7 @@ name = "click-plugins" version = "1.1.1.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click" }, + { name = "click", marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c3/a4/34847b59150da33690a36da3681d6bbc2ec14ee9a846bc30a6746e5984e4/click_plugins-1.1.1.2.tar.gz", hash = "sha256:d7af3984a99d243c131aa1a828331e7630f4a88a9741fd05c927b204bcf92261", size = 8343, upload-time = "2025-06-25T00:47:37.555Z" } wheels = [ @@ -1671,7 +1671,7 @@ dependencies = [ ] [package.optional-dependencies] -all = [ +examples = [ { name = "folium" }, { name = "igraph" }, { name = "jupyterlab" }, @@ -1680,22 +1680,26 @@ all = [ [package.dev-dependencies] dev = [ + { name = "ipython" }, + { name = "nbconvert" }, { name = "nbdime" }, { name = "prek" }, ] [package.metadata] requires-dist = [ - { name = "folium", marker = "extra == 'all'" }, - { name = "igraph", marker = "extra == 'all'" }, - { name = "jupyterlab", marker = "extra == 'all'" }, - { name = "mapclassify", marker = "extra == 'all'" }, + { name = "folium", marker = "extra == 'examples'" }, + { name = "igraph", marker = "extra == 'examples'" }, + { name = "jupyterlab", marker = "extra == 'examples'" }, + { name = "mapclassify", marker = "extra == 'examples'" }, { name = "osmnx", extras = ["all"], specifier = "==2.1.0" }, ] -provides-extras = ["all"] +provides-extras = ["examples"] [package.metadata.requires-dev] dev = [ + { name = "ipython" }, + { name = "nbconvert" }, { name = "nbdime" }, { name = "prek" }, ] @@ -1802,7 +1806,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess" }, + { name = "ptyprocess", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -2276,14 +2280,14 @@ resolution-markers = [ "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "affine" }, - { name = "attrs" }, - { name = "certifi" }, - { name = "click" }, - { name = "click-plugins" }, - { name = "cligj" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, - { name = "pyparsing" }, + { name = "affine", marker = "python_full_version < '3.12'" }, + { name = "attrs", marker = "python_full_version < '3.12'" }, + { name = "certifi", marker = "python_full_version < '3.12'" }, + { name = "click", marker = "python_full_version < '3.12'" }, + { name = "click-plugins", marker = "python_full_version < '3.12'" }, + { name = "cligj", marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "pyparsing", marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ec/fa/fce8dc9f09e5bc6520b6fc1b4ecfa510af9ca06eb42ad7bdff9c9b8989d0/rasterio-1.4.4.tar.gz", hash = "sha256:c95424e2c7f009b8f7df1095d645c52895cd332c0c2e1b4c2e073ea28b930320", size = 445004, upload-time = "2025-12-12T18:01:08.971Z" } wheels = [ @@ -2338,13 +2342,13 @@ resolution-markers = [ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "affine" }, - { name = "attrs" }, - { name = "certifi" }, - { name = "click" }, - { name = "cligj" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" } }, - { name = "pyparsing" }, + { name = "affine", marker = "python_full_version >= '3.12'" }, + { name = "attrs", marker = "python_full_version >= '3.12'" }, + { name = "certifi", marker = "python_full_version >= '3.12'" }, + { name = "click", marker = "python_full_version >= '3.12'" }, + { name = "cligj", marker = "python_full_version >= '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pyparsing", marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f6/88/edb4b66b6cb2c13f123af5a3896bf70c0cbe73ab3cd4243cb4eb0212a0f6/rasterio-1.5.0.tar.gz", hash = "sha256:1e0ea56b02eea4989b36edf8e58a5a3ef40e1b7edcb04def2603accd5ab3ee7b", size = 452184, upload-time = "2026-01-05T16:06:47.169Z" } wheels = [ @@ -2635,7 +2639,7 @@ resolution-markers = [ "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } wheels = [ @@ -2714,7 +2718,7 @@ resolution-markers = [ "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" } wheels = [