feat(python): add geolibre-wasm Python wrapper#10
Conversation
Scaffold a Python package (python/, distribution geolibre-wasm, import geolibre_wasm) that runs the same WASI tool runner in-process via wasmtime, mirroring the JS tools API: list_tools, list_manifests, run_tool returning a ToolResult(exit_code, stdout, files). Inputs go in as bytes under /work and new files (including nested trees like raster_to_tiles output) come back as bytes. The runtime geolibre-cli.wasm is resolved from an explicit path, the GEOLIBRE_WASM env var, or a cached download of the matching GitHub release asset. Named geolibre-wasm to match the npm package and avoid clashing with the separate geolibre application package.
|
Warning Review limit reached
More reviews will be available in 34 minutes and 44 seconds. Learn how PR review limits work. To continue reviewing without waiting, enable usage-based billing in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughA new Python package ChangesPython geolibre-wasm WASI wrapper
Sequence Diagram(s)sequenceDiagram
participant Caller as Python Caller
participant run_tool as run_tool()
participant runtime_path as runtime_path()
participant download_runtime as download_runtime()
participant _exec as _exec()
participant wasmtime as wasmtime (WASI)
Caller->>run_tool: run_tool(tool, args, input_files)
run_tool->>_exec: _exec([tool, *args], input_files, wasm_path)
_exec->>runtime_path: resolve effective wasm path
alt GEOLIBRE_WASM set or arg provided
runtime_path-->>_exec: explicit path
else cache miss
runtime_path->>download_runtime: fetch versioned release asset
download_runtime-->>runtime_path: cached .wasm path
runtime_path-->>_exec: cached path
end
_exec->>wasmtime: write inputs to tmpdir /work, configure WASI, call _start
wasmtime-->>_exec: ExitTrap(code) or normal exit
_exec->>_exec: read capture file, collect output files
_exec-->>run_tool: ToolResult(exit_code, stdout, files)
run_tool-->>Caller: ToolResult
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CI: after building the WASM, install the python package and run pytest against the freshly built geolibre-cli.wasm (via GEOLIBRE_WASM), so the wrapper is covered without a network download. Release: sync the Python package version (and the runtime version it downloads) to the tag, build a wheel + sdist, publish to PyPI via Trusted Publishing (OIDC, no token), and attach the wheel/sdist to the GitHub release. Requires a PyPI trusted publisher for "geolibre-wasm" (configure as a pending publisher before the first release).
|
Wired the Python package into CI and the release pipeline (commit 38fdc83):
One-time manual setup required before the first PyPI release: configure a PyPI Trusted Publisher (a "pending publisher" since the project is new) for project |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@python/pyproject.toml`:
- Line 20: The wasmtime version constraint in the dependencies field is too
loose, allowing versions spanning 25+ major versions which risks breaking
changes. Tighten the constraint in the dependencies line by either specifying a
minimum compatible version with an upper bound like wasmtime>=20,<50 or
specifying the latest tested version with wasmtime>=45.0.0. Choose the
constraint based on which versions have been confirmed compatible with your
codebase.
In `@python/src/geolibre_wasm/__init__.py`:
- Around line 30-38: The __all__ list in the module exports is not sorted
alphabetically, which reduces maintainability. Sort all the items in the __all__
list (ToolResult, RUNTIME_VERSION, list_tools, list_manifests, run_tool,
runtime_path, download_runtime) in alphabetical order to improve code
organization and make it easier to identify duplicates or missing exports.
In `@python/src/geolibre_wasm/_core.py`:
- Around line 128-131: In the loop that iterates over inputs.items() where
dest.write_bytes() is called, remove the unnecessary bytes() conversion wrapper
since the data variable is already typed as bytes according to the inputs
parameter type hint Mapping[str, bytes]. Change dest.write_bytes(bytes(data)) to
dest.write_bytes(data) to simplify the code and improve clarity.
- Around line 200-220: The parameter name `input` in the `run_tool` function
shadows the Python builtin `input()` function, which is flagged by static
analysis. Rename the `input` parameter to `inputs` in the function signature to
match the parameter name expected by the `_exec()` call, and update the function
call from `inputs=input` to `inputs=inputs`.
- Around line 69-84: The download_runtime function needs to add a timeout to
prevent indefinite hangs and integrity verification to prevent silent
corruption. Replace the urllib.request.urlretrieve() call with
urllib.request.urlopen() with an explicit timeout parameter (e.g., 30 seconds),
then read and write the file content manually. After writing the file, add basic
integrity verification by reading the downloaded file and checking for the WASM
magic bytes signature (\0asm) at the start of the file before replacing the
temporary file with the target file. If verification fails, raise an exception
with a clear error message describing the integrity check failure.
- Line 19: The import statement on line 19 is using the legacy type hint syntax
from Python 3.8 and earlier. Modernize this by updating the import to remove
Dict and List from the typing import, and add Mapping and Sequence to be
imported from collections.abc instead. Then update all type annotations
throughout the file by replacing List[...] with list[...], Dict[...] with
dict[...], and any usages of Mapping and Sequence to reference the
collections.abc imports. This modernizes the code to use Python 3.9+ syntax
which is more concise and the recommended approach.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 993fa8c8-2338-46d0-883a-3b23406c20d8
📒 Files selected for processing (7)
.gitignoreREADME.mdpython/README.mdpython/pyproject.tomlpython/src/geolibre_wasm/__init__.pypython/src/geolibre_wasm/_core.pypython/tests/test_smoke.py
Add a PyPI version badge and `pip install geolibre-wasm` / `npm install geolibre-wasm` to the README, and note the package is usable from both JavaScript and Python. Drop the now-inaccurate "no Python" from the intro (there is a Python binding) in favor of "no GDAL".
- download_runtime: use urlopen with a timeout and reject non-WASM payloads (check the "\0asm" magic) so a hung or error-page download fails loudly instead of producing a cryptic module-load error. - Tighten the wasmtime dependency to >=20,<50 to avoid surprise major bumps. - Modernize type hints: built-in dict/list and collections.abc Mapping/Sequence (the module already uses `from __future__ import annotations`). - Drop the redundant bytes() conversion when writing input files. - Sort __all__ alphabetically.
Both Python examples now spell out that paths in `args` refer to the tool's /work sandbox (not the host disk), that `input` files land at /work/<name>, and that `res.files` keys are relative to /work, with an `assert res.exit_code == 0` to surface tool errors. Prevents the common mistake of passing host paths like /content/dem.tif (e.g. on Colab) into args. Verified the snippet runs end to end.
Summary
python/(distributiongeolibre-wasm, importgeolibre_wasm) that runs the same WASI tool runner in-process viawasmtime, mirroring the JSgeolibre-wasm/toolsAPI.list_tools(),list_manifests(),run_tool(tool, args=, input=)returningToolResult(exit_code, stdout, files). Inputs are passed asbytesunder/work; new files (including nested trees likeraster_to_tilesoutput) come back asbytes.geolibre-cli.wasmis resolved from an explicitwasm_path, theGEOLIBRE_WASMenv var, or a cached download of the matching GitHub release asset (verified the v0.4.0 asset URL returns 200).geolibre-wasmto match the npm package and avoid clashing with the separategeolibreapplication package.Test plan
pytest python/testsgreen (list_tools, provenance, GeoParquet write->read roundtrip)slope,reproject_raster,render_raster_png(valid PNG), andraster_to_tiles(nestedtiles/z/x/y.pngkeys returned)python -m build --wheelproducesgeolibre_wasm-0.4.0-py3-none-any.whlSummary by CodeRabbit
New Features
geolibre-wasm) now available, enabling users to run GeoLibre geospatial tools via WebAssembly with no native dependencies or GDAL installation required.Documentation