Skip to content
This repository was archived by the owner on May 7, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 8 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
3 changes: 2 additions & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"Bash(gh run:*)",
"Bash(gh api:*)",
"Bash(ls:*)",
"Bash(wc:*)"
"Bash(wc:*)",
"Bash(uv sync:*)"
]
}
}
108 changes: 108 additions & 0 deletions .github/workflows/wren-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: Wren SDK CI
permissions:
contents: read
pull-requests: write

on:
pull_request:
paths:
- 'wren/**'
- 'wren-core/**'
- 'wren-core-py/**'
- 'wren-core-base/**'

concurrency:
group: ${{ github.workflow }}-${{ github.event.number }}
cancel-in-progress: true

jobs:
lint:
name: lint
runs-on: ubuntu-latest
defaults:
run:
working-directory: wren
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Lint
run: |
uvx ruff format --check src/
uvx ruff check src/

test-unit:
name: unit tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: wren
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
./wren-core-py/target/
key: ${{ runner.os }}-cargo-wren-${{ hashFiles('**/Cargo.lock') }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Build wren-core-py wheel
working-directory: wren-core-py
run: |
pipx install poetry
poetry install --no-root
poetry run maturin build
- name: Install dependencies
run: |
uv lock --find-links ../wren-core-py/target/wheels/
uv sync --extra dev --find-links ../wren-core-py/target/wheels/
- name: Run unit tests
run: uv run pytest tests/unit/ -v -m unit

test-postgres:
name: postgres tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: wren
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
./wren-core-py/target/
key: ${{ runner.os }}-cargo-wren-${{ hashFiles('**/Cargo.lock') }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Build wren-core-py wheel
working-directory: wren-core-py
run: |
pipx install poetry
poetry install --no-root
poetry run maturin build
- name: Install dependencies
run: |
uv lock --find-links ../wren-core-py/target/wheels/
uv sync --extra postgres --extra dev --find-links ../wren-core-py/target/wheels/
- name: Run postgres tests
run: uv run pytest tests/connectors/test_postgres.py -v -m postgres
1 change: 1 addition & 0 deletions ibis-server/docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ To start the server:
```
WREN_ENGINE_ENDPOINT=http://localhost:8080
```
Install the dependencies for testing by `just install --with dev`. Then you can run the tests by `just test`. \
More information about the environment variables can be found in the [Environment Variables](#Environment-Variables) section.
- Run specific data source test using [pytest marker](https://docs.pytest.org/en/stable/example/markers.html). There are some markers for different data sources. See the list
in [pyproject.toml](../pyproject.toml).
Expand Down
2 changes: 2 additions & 0 deletions ibis-server/poetry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[virtualenvs]
in-project = true
59 changes: 59 additions & 0 deletions wren/.claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# CLAUDE.md — wren package

Standalone Python SDK and CLI for Wren Engine. Wraps `wren-core-py` (PyO3 bindings) + Ibis connectors into a single installable package.

## Package Structure

```
wren/
src/wren/
engine.py — WrenEngine facade (transpile / query / dry_run / dry_plan)
cli.py — Typer CLI: wren query|dry-run|transpile|validate
mdl/ — wren-core-py session context + manifest extraction helpers
connector/ — Per-datasource Ibis connectors (factory.py + one file per source)
model/
data_source.py — DataSource enum + per-source ConnectionInfo factories
error.py — WrenError, ErrorCode, ErrorPhase
tests/
```

## Build & Development

```bash
cd wren
just install # build wren-core-py wheel + uv sync
just install-all # with all optional extras
just install-extra <extra> # e.g. just install-extra postgres
just test # pytest tests/
just lint # ruff format --check + ruff check
just format # ruff auto-fix
just build # uv build (produces wheel)
```

Uses `uv` (not Poetry). `pyproject.toml` uses `hatchling` as build backend.

## Key Design Points

- **WrenEngine** is the main entry point. It accepts a base64-encoded MDL JSON string, a `DataSource`, and a connection dict.
- **Query flow**: `_plan()` → wren-core `SessionContext.transform_sql()` → `_transpile()` via sqlglot → connector `.query()`.
- **Manifest extraction**: `_plan()` tries to extract a minimal sub-manifest scoped to the query's referenced tables before calling wren-core — this reduces planning overhead. Falls back to the full manifest on error.
- **`get_session_context` is `@cache`-decorated** — same `(manifest_str, function_path, properties, data_source)` tuple reuses the same SessionContext. Avoid mutating session state.
- **Write dialect mapping**: `canner` → `trino`; file sources (`local_file`, `s3_file`, `minio_file`, `gcs_file`) → `duckdb`. All others use `data_source.name` directly.
- **WrenEngine is a context manager** (`__enter__` / `__exit__` call `close()`).

## Connectors

`connector/factory.py` dispatches on `DataSource` to return the right connector. Each connector wraps an Ibis backend and exposes `.query(sql, limit)` and `.dry_run(sql)`. Base class in `connector/base.py`; Ibis-backed connectors share `connector/ibis.py`.

## Optional Extras

Install per data-source extras: `postgres`, `mysql`, `bigquery`, `snowflake`, `clickhouse`, `trino`, `mssql`, `databricks`, `redshift`, `spark`, `athena`, `oracle`, `all`, `dev`.

On macOS, `mysql` extra needs:
```bash
PKG_CONFIG_PATH="$(brew --prefix mysql-client)/lib/pkgconfig" just install-extra mysql
```

## Dependency on wren-core-py

`wren-core-py` wheel is built locally from `../wren-core-py/` and installed via `--find-links`. Run `just build-core` (or `just install`) to rebuild after Rust changes.
42 changes: 42 additions & 0 deletions wren/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# wren

Wren Engine CLI and Python SDK — semantic SQL layer for 20+ data sources.

## Installation

```bash
pip install wren
```

## Usage

```python
from wren import WrenEngine, DataSource
```

See the [Wren Engine documentation](https://getwren.ai) for details.

## Running tests

Install dev dependencies first:

```bash
just install-dev
```

| Command | What it runs | Docker needed |
|---------|-------------|---------------|
| `just test-unit` | Unit tests (transpile, dry-plan, context manager) | No |
| `just test-duckdb` | DuckDB connector tests — generates TPCH data via `dbgen` | No |
| `just test-postgres` | PostgreSQL connector tests — spins up a container | Yes |
| `just test` | All tests | Yes |

Run a specific connector via marker:

```bash
just test-connector postgres
```

To add tests for a new connector, subclass `WrenQueryTestSuite` in
`tests/connectors/test_<name>.py` and provide a class-scoped `engine` fixture.
All base tests are inherited automatically.
70 changes: 70 additions & 0 deletions wren/justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
default:
@just --list

build-core:
cd ../wren-core-py && just install && just build

core-wheel-dir := "../wren-core-py/target/wheels/"

install-core:
just build-core

install:
just build-core
uv sync --find-links {{ core-wheel-dir }}

install-dev:
just build-core
uv sync --extra dev --find-links {{ core-wheel-dir }}

install-all:
just build-core
uv sync --all-extras --find-links {{ core-wheel-dir }}

install-extra extra *flags:
#!/usr/bin/env bash
set -euo pipefail
if [ "{{ extra }}" = "mysql" ]; then
mysql_prefix=$(brew --prefix mysql-client 2>/dev/null || true)
if [ -n "$mysql_prefix" ]; then
export PKG_CONFIG_PATH="$mysql_prefix/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
fi
fi
dev_flag=""
for flag in {{ flags }}; do
if [ "$flag" = "--dev" ]; then
dev_flag="--extra dev"
fi
done
uv sync --extra {{ extra }} $dev_flag --find-links {{ core-wheel-dir }}

dev:
uv run wren

test:
uv run pytest tests/ -v

test-unit:
uv run pytest tests/unit/ -v -m unit

test-duckdb:
uv run pytest tests/connectors/test_duckdb.py -v -m duckdb

test-postgres:
uv run pytest tests/connectors/test_postgres.py -v -m postgres

test-connector marker:
uv run pytest tests/connectors/ -v -m {{ marker }}

lint:
uv run ruff format --check src/
uv run ruff check src/

format:
uv run ruff format src/
uv run ruff check --fix src/

build:
uv build

alias fmt := format
74 changes: 74 additions & 0 deletions wren/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "wren"
dynamic = ["version"]
description = "Wren Engine CLI and Python SDK — semantic SQL layer for 20+ data sources"
readme = "README.md"
requires-python = ">=3.11"
license = { text = "Apache-2.0" }
keywords = ["wren", "sql", "semantic", "mdl", "datafusion"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"wren-core-py>=0.1",
"ibis-framework>=10",
"duckdb>=1.0",
"sqlglot>=27",
"typer>=0.12",
"pydantic>=2",
"pyarrow>=14",
"pyarrow-hotfix>=0.6",
"loguru>=0.7",
"opendal>=0.45",
"pandas>=2",
"boto3>=1.26",
]

[project.optional-dependencies]
postgres = ["psycopg>=3", "ibis-framework[postgres]"]
mysql = ["mysqlclient>=2.2", "ibis-framework[mysql]"]
bigquery = ["ibis-framework[bigquery]", "google-auth"]
snowflake = ["ibis-framework[snowflake]"]
clickhouse = ["ibis-framework[clickhouse]"]
trino = ["ibis-framework[trino]", "trino>=0.321"]
mssql = ["ibis-framework[mssql]"]
databricks = ["databricks-sql-connector", "databricks-sdk"]
redshift = ["redshift_connector"]
spark = ["pyspark>=3.5"]
athena = ["ibis-framework[athena]"]
oracle = ["ibis-framework[oracle]", "oracledb"]
all = ["wren[postgres,mysql,bigquery,snowflake,clickhouse,trino,mssql,databricks,redshift,athena,oracle]"]
dev = ["pytest>=8", "ruff>=0.4", "orjson>=3", "testcontainers[postgres]>=4"]

[project.scripts]
wren = "wren.cli:app"

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

[tool.hatch.version]
path = "src/wren/__init__.py"

[tool.hatch.build.targets.wheel]
packages = ["src/wren"]

[tool.ruff]
line-length = 88
target-version = "py311"

[tool.ruff.lint]
select = ["E", "F", "I", "PLC"]
ignore = [
"E501", # line-too-long, enforced by ruff format
]
9 changes: 9 additions & 0 deletions wren/src/wren/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Wren — semantic SQL layer for 20+ data sources."""

__version__ = "0.0.1"

from wren.engine import WrenEngine
from wren.model.data_source import DataSource
from wren.model.error import WrenError

__all__ = ["WrenEngine", "DataSource", "WrenError", "__version__"]
Loading
Loading