Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions .github/workflows/pypi-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: Publish wren-core-py to PyPI

permissions:
contents: read
id-token: write # Required for trusted publishing (OIDC)

on:
workflow_dispatch:
inputs:
target:
description: "Publish target"
required: true
default: "testpypi"
type: choice
options:
- testpypi
- pypi

jobs:
build-wheels:
name: Build wheel — ${{ matrix.os }} (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# Linux x86_64
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
# Linux aarch64
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
# macOS x86_64
- os: macos-15-intel
target: x86_64-apple-darwin
# macOS arm64
- os: macos-15
target: aarch64-apple-darwin
# Windows x86_64
- os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Build wheel
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist
manylinux: auto
working-directory: wren-core-py
- name: Upload wheel
uses: actions/upload-artifact@v4
with:
name: wheel-${{ matrix.target }}
path: wren-core-py/dist/*.whl

build-sdist:
name: Build sdist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
working-directory: wren-core-py
- name: Upload sdist
uses: actions/upload-artifact@v4
with:
name: sdist
path: wren-core-py/dist/*.tar.gz

publish:
name: Publish to ${{ github.event.inputs.target || 'testpypi' }}
needs: [build-wheels, build-sdist]
runs-on: ubuntu-latest
environment:
name: ${{ github.event.inputs.target || 'testpypi' }}
url: ${{ github.event.inputs.target == 'pypi' && 'https://pypi.org/project/wren-core-py/' || 'https://test.pypi.org/project/wren-core-py/' }}
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true

- name: List artifacts
run: ls -lhR dist/

- name: Publish to ${{ github.event.inputs.target }}
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: ${{ github.event.inputs.target == 'testpypi' && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }}
packages-dir: dist/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ __pycache__/
venv/
**/.env*
**/*.so
wren-core-py/dict/
3 changes: 3 additions & 0 deletions wren-core-py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
name = "wren-core-py"
version = "0.1.0"
edition = "2021"
description = "Python bindings for Wren Engine semantic layer (wren-core)"
license = "Apache-2.0"
repository = "https://github.com/Canner/wren-engine"
include = ["/src", "pyproject.toml", "Cargo.toml", "Cargo.lock"]

[lib]
Expand Down
63 changes: 53 additions & 10 deletions wren-core-py/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
# Wren Core Python binding
# Wren Core Python Binding

This is a python binding for [wren-core](../wren-core). It uses [PyO3](https://github.com/PyO3/pyo3) to build the required wheel for [ibis-server](../ibis-server/).
Python bindings for [wren-core](../wren-core), the Rust semantic engine behind [Wren Engine](https://github.com/Canner/wren-engine). Built with [PyO3](https://github.com/PyO3/pyo3) and [Maturin](https://github.com/PyO3/maturin).

Wren Engine translates SQL queries through a semantic layer (MDL - Modeling Definition Language) and executes them against 22+ data sources (PostgreSQL, BigQuery, Snowflake, etc.).

## Installation

```bash
pip install wren-core-py
```

Requires Python >= 3.11.

## Quick Start

```python
from wren_core import SessionContext, to_manifest

# Load an MDL manifest from a base64-encoded JSON string
base64_mdl_json = "<your-base64-encoded-mdl-json>"
manifest = to_manifest(base64_mdl_json)

# Create a session context for query planning
ctx = SessionContext(manifest, remote_functions=[])

# Transform a SQL query through the semantic layer
planned_sql = ctx.transform_sql("SELECT * FROM my_model")
```

## Developer Guide

Expand All @@ -11,14 +37,31 @@ This is a python binding for [wren-core](../wren-core). It uses [PyO3](https://g
- Install [poetry](https://github.com/python-poetry/poetry)
- Install [casey/just](https://github.com/casey/just)

### Test and build
After install `casey/just`, you can use the following command to build or test:
- Execute `just install` to create Python venv and install dependencies.
- **Important**: Before testing Python, you need to build the Rust package by running `just develop`.
- Use `just test-rs` to test Rust only, and `just test-py` to test Python only.
- Use `just test` to test Rust and Python.
- Execute `just build` to build the Python package. You can find the wheel in the `target/wheels/` directory.
### Test and Build

After installing `casey/just`, you can use the following commands:

- `just install` — Create Python venv and install dependencies.
- `just develop` — Build the Rust package for local development (**required before running Python tests**).
- `just test-rs` — Run Rust tests only.
- `just test-py` — Run Python tests only.
- `just test` — Run both Rust and Python tests.
- `just build` — Build the Python wheel. Output goes to `target/wheels/`.

### Coding Style

Format via `just format`
Format via `just format`.

### Publishing

See `scripts/publish.sh` for local publishing to PyPI/TestPyPI:

```bash
./scripts/publish.sh --build # Build wheel only
./scripts/publish.sh --test # Build + publish to TestPyPI
./scripts/publish.sh # Build + publish to PyPI
```

## License

Apache-2.0
2 changes: 1 addition & 1 deletion wren-core-py/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 24 additions & 3 deletions wren-core-py/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
[project]
name = "wren-core-py"
classifiers = ["Programming Language :: Python :: 3.11"]
version = "0.1.0"
description = "Python bindings for Wren Engine semantic layer (wren-core)"
readme = "README.md"
requires-python = ">=3.11"
license = { text = "Apache-2.0" }
authors = [{ name = "Wren AI", email = "contact@getwren.ai" }]
keywords = ["sql", "semantic-layer", "data-modeling", "analytics", "wren", "wrenai", "datafusion"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Rust",
"Topic :: Database",
"Topic :: Software Development :: Libraries :: Python Modules",
]

[project.urls]
Homepage = "https://www.getwren.ai/"
Repository = "https://github.com/Canner/wren-engine"
Issues = "https://github.com/Canner/wren-engine/issues"

[tool.poetry]
name = "wren-core-py"
version = "0.1.0"
description = ""
authors = ["Canner <dev@cannerdata.com>"]
description = "Python bindings for Wren Engine semantic layer (wren-core)"
authors = ["Wren AI <contact@getwren.ai>"]

[tool.poetry.dependencies]
python = ">=3.11"
Expand Down
124 changes: 124 additions & 0 deletions wren-core-py/scripts/publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/usr/bin/env bash
#
# Build and publish wren-core-py to PyPI or TestPyPI.
#
# Usage:
# ./scripts/publish.sh # build + publish to PyPI
# ./scripts/publish.sh --test # build + publish to TestPyPI
# ./scripts/publish.sh --build # build only (no publish)
#
# Prerequisites:
# - Rust toolchain (rustup, cargo)
# - maturin (pip install maturin)
# - twine (pip install twine)
#
# Environment variables (optional):
# MATURIN_ARGS — extra args passed to maturin build (e.g. "--target x86_64-unknown-linux-gnu")
#
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
DIST_DIR="$PROJECT_DIR/dist"

MODE="publish" # publish | test | build
REPOSITORY="pypi"

while [[ $# -gt 0 ]]; do
case "$1" in
--test)
MODE="test"
REPOSITORY="testpypi"
shift
;;
--build)
MODE="build"
shift
;;
-h|--help)
echo "Usage: $0 [--test | --build | -h]"
echo ""
echo " (no flag) Build and publish to PyPI"
echo " --test Build and publish to TestPyPI"
echo " --build Build only, no publish"
echo " -h, --help Show this help"
exit 0
;;
*)
echo "Unknown option: $1" >&2
exit 1
;;
esac
done

cd "$PROJECT_DIR"

# --- Check prerequisites ---
for cmd in cargo maturin; do
if ! command -v "$cmd" &>/dev/null; then
echo "Error: '$cmd' is not installed." >&2
exit 1
fi
done

if [[ "$MODE" != "build" ]]; then
if ! command -v twine &>/dev/null; then
echo "Error: 'twine' is not installed. Run: pip install twine" >&2
exit 1
fi
fi

# --- Read version from Cargo.toml ---
VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
echo "==> Building wren-core-py v${VERSION}"

# --- Clean previous dist ---
rm -rf "$DIST_DIR"
mkdir -p "$DIST_DIR"

# --- Build wheel ---
echo "==> Running maturin build --release"
maturin build --release --out "$DIST_DIR" ${MATURIN_ARGS:-}

# --- Build sdist ---
echo "==> Running maturin sdist"
maturin sdist --out "$DIST_DIR"

# --- List artifacts ---
echo ""
echo "==> Built artifacts:"
ls -lh "$DIST_DIR"

# --- Validate ---
echo ""
echo "==> Validating with twine check"
if command -v twine &>/dev/null; then
twine check "$DIST_DIR"/*
elif [[ "$MODE" == "build" ]]; then
echo "Warning: twine not installed; skipping validation in build-only mode"
else
echo "Error: 'twine' is not installed. Run: pip install twine" >&2
exit 1
fi

if [[ "$MODE" == "build" ]]; then
echo ""
echo "==> Build complete. Artifacts in: $DIST_DIR"
exit 0
fi

# --- Publish ---
echo ""
if [[ "$REPOSITORY" == "testpypi" ]]; then
echo "==> Publishing to TestPyPI"
echo " After upload, install with:"
echo " pip install --index-url https://test.pypi.org/simple/ wren-core-py"
else
echo "==> Publishing to PyPI"
fi
echo ""

twine upload --repository "$REPOSITORY" "$DIST_DIR"/*

echo ""
echo "==> Done! Published wren-core-py v${VERSION} to ${REPOSITORY}"
Loading