Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7dbaa82
refactor: make xorq an optional dependency (extra)
deepyaman Jun 13, 2026
052fc02
fix: tighten any post-optionalization housekeeping
deepyaman Jun 13, 2026
8870032
fix: align test-no-xorq CI job with the main test job
deepyaman Jun 13, 2026
ed4afd5
fix: replace xo.window with ibis.window in test_chart.py
deepyaman Jun 13, 2026
ee7c958
refactor: restructure dev extra, drop self-referential bundle
deepyaman Jun 13, 2026
d725f1c
fix: update CI install to skip xorq and examples extras
deepyaman Jun 13, 2026
4a1b162
fix: skip xorq-dependent tests when xorq is not installed
deepyaman Jun 13, 2026
2bf6c5c
fix: replace xorq.vendor.ibis with plain ibis
deepyaman Jun 13, 2026
719011b
fix: update dependency group tests for new dev structure
deepyaman Jun 13, 2026
4b867c4
fix: raise on undefined env vars in plain-ibis profiles
deepyaman Jun 13, 2026
0071d30
fix: no-xorq parity for join rebinding and dimension-only shortcut
deepyaman Jun 13, 2026
fd2d1a5
test: use xibis shim idiom for flavor-sensitive tests and demo
deepyaman Jun 13, 2026
d3ffe15
test: standardize xorq-only skips on importorskip; clarify pushdown x…
deepyaman Jun 13, 2026
e443034
ci: parametrize test job over xorq extras; split build into its own job
deepyaman Jun 13, 2026
6fc5ea6
chore: minimize lockfile churn; document test-core extra
deepyaman Jun 14, 2026
9cf04f3
refactor: drop duplicated walk_nodes fallback; trim rebinding comments
deepyaman Jun 14, 2026
606e035
ci: self-skip integration dir without xorq via collect_ignore_glob
deepyaman Jun 14, 2026
f56e7b4
docs: hyphenate xorq-vendored ibis; clarify walk_nodes stub rationale
deepyaman Jun 14, 2026
38e3741
chore: bump xorq minimum to >=0.3.31; relock
hussainsultan Jun 25, 2026
e4cbff5
fix: ibis + xorq walker
hussainsultan Jun 26, 2026
dd21210
fix
hussainsultan Jun 26, 2026
4482213
test: prefer public ibis window in demo and chart regression (#279)
hachej Jun 29, 2026
91fd2f5
fix: restore xorq flavor compatibility after public ibis cleanup (#282)
hachej Jun 30, 2026
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
56 changes: 50 additions & 6 deletions .github/workflows/ci-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,47 @@ concurrency:
cancel-in-progress: ${{ github.event_name != 'release' }}

jobs:
# Run tests, examples, and build docs
# Run the test suite with and without the xorq extra. The no-xorq leg
# verifies xorq is truly optional (its tests skip, not error).
test:
name: Test & Build
name: Test (${{ matrix.name }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: full
sync: uv sync --all-extras
- name: no-xorq
sync: uv sync --all-extras --no-extra xorq --no-extra examples
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Install dependencies (${{ matrix.name }})
run: ${{ matrix.sync }}

- name: Verify import works
run: uv run python -c "import boring_semantic_layer; print('import OK')"

- name: Run tests
run: uv run pytest

- name: Run no-xorq-compatible examples
if: matrix.name == 'no-xorq'
run: make examples-core

# Build examples, docs, and skills (require the full env incl. Node).
build:
name: Build (examples + docs + skills)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -47,8 +85,14 @@ jobs:
uv sync --all-extras
cd docs/web && npm ci

- name: Run all checks (tests + examples + docs build)
run: make check
- name: Run examples
run: make examples

- name: Build docs
run: make docs-build

- name: Check skills are up to date
run: make skills-check

- name: Upload docs artifact
if: github.event_name == 'release'
Expand All @@ -60,7 +104,7 @@ jobs:
deploy-docs:
name: Deploy Docs
if: github.event_name == 'release'
needs: test
needs: [test, build]
runs-on: ubuntu-latest
environment:
name: github-pages
Expand All @@ -74,7 +118,7 @@ jobs:
release-pypi:
name: Release to PyPI
if: github.event_name == 'release'
needs: test
needs: [test, build]
runs-on: ubuntu-latest
environment:
name: release
Expand Down
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ help:
@echo " make test IBIS_VERSION=all - Run tests with all ibis versions (9.5.0, 10.8.0, 11.0.0)"
@echo " make test IBIS_VERSION=10.8.0 - Run tests with specific ibis version"
@echo " make examples - Run all example scripts"
@echo " make examples-core - Run no-xorq-compatible examples"
@echo " make examples IBIS_VERSION=all - Run examples with all ibis versions"
@echo " make examples IBIS_VERSION=10.8.0 - Run examples with specific ibis version"
@echo " make docs-build - Build documentation"
Expand Down Expand Up @@ -105,6 +106,19 @@ else
@echo "✓ All examples passed!"
endif

examples-core:
@echo "Running no-xorq-compatible examples..."
@for file in \
scripts/demo_bsl_v2.py \
examples/quickstart.py \
examples/window_functions.py \
examples/bucketing_with_other.py \
examples/sessionized_data.py; do \
echo "Running $$file..."; \
uv run --with . "$$file" || exit 1; \
done
@echo "✓ No-xorq-compatible examples passed!"

# Build docs
docs-build:
@echo "Building documentation..."
Expand Down
12 changes: 8 additions & 4 deletions examples/bucketing_with_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@

from pathlib import Path

import xorq.api as xo
import ibis
from ibis import _

# CI runs this example with and without xorq. xibis matches BSL's active ibis
# flavor in both modes; users who are not using xorq can simply use `import ibis`.
from boring_semantic_layer._xorq import ibis as xibis

from boring_semantic_layer import from_yaml


Expand All @@ -28,13 +32,13 @@ def main():
nest={"data": lambda t: t.group_by(["code", "elevation"])},
)
.mutate(
rank=lambda t: xo.row_number().over(
xo.window(order_by=xo.desc(t.avg_elevation)),
rank=lambda t: xibis.row_number().over(
xibis.window(order_by=xibis.desc(t.avg_elevation)),
),
)
.mutate(
is_other=lambda t: t.rank > 4,
state_grouped=lambda t: xo.ifelse(t.rank > 4, "OTHER", t.state),
state_grouped=lambda t: xibis.ifelse(t.rank > 4, "OTHER", t.state),
)
.group_by("state_grouped")
.aggregate(
Expand Down
7 changes: 5 additions & 2 deletions examples/quickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@

import ibis
import pandas as pd
import xorq.api as xo

# CI runs this example with and without xorq. xibis matches BSL's active ibis
# flavor in both modes; users who are not using xorq can simply use `import ibis`.
from boring_semantic_layer._xorq import ibis as xibis

from boring_semantic_layer import to_semantic_table

Expand Down Expand Up @@ -112,7 +115,7 @@ def main():
.with_measures(sum_val=lambda t: t.value.sum())
)

rolling_window = xo.window(order_by="date", rows=(1, 1))
rolling_window = xibis.window(order_by="date", rows=(1, 1))
expr4 = (
ts_st.group_by("date")
.aggregate("sum_val")
Expand Down
9 changes: 6 additions & 3 deletions examples/sessionized_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@

import ibis
import pandas as pd
import xorq.api as xo

# CI runs this example with and without xorq. xibis matches BSL's active ibis
# flavor in both modes; users who are not using xorq can simply use `import ibis`.
from boring_semantic_layer._xorq import ibis as xibis

from boring_semantic_layer import from_yaml, to_untagged

Expand All @@ -32,7 +35,7 @@ def main():

# Filter for carrier WN on 2002-03-03 and add flight_date column
filtered_flights = flights.filter(
lambda t: (t.carrier == "WN") & (t.dep_time.date() == xo.date(2002, 3, 3)),
lambda t: (t.carrier == "WN") & (t.dep_time.date() == xibis.date(2002, 3, 3)),
).mutate(flight_date=lambda t: t.dep_time.date())

# Create sessions with nested flight legs
Expand All @@ -53,7 +56,7 @@ def main():
]),
},
)
.mutate(session_id=xo.row_number().over(xo.window()))
.mutate(session_id=xibis.row_number().over(xibis.window()))
.order_by("session_id")
)

Expand Down
17 changes: 10 additions & 7 deletions examples/window_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
"""Window Functions - Rolling Averages, Rankings, Running Totals, and t.all()."""

import ibis
import xorq.api as xo
from ibis import _

# CI runs this example with and without xorq. xibis matches BSL's active ibis
# flavor in both modes; users who are not using xorq can simply use `import ibis`.
from boring_semantic_layer._xorq import ibis as xibis

from boring_semantic_layer import to_semantic_table

BASE_URL = "https://pub-a45a6a332b4646f2a6f44775695c64df.r2.dev"
Expand Down Expand Up @@ -32,13 +35,13 @@ def main():
result = (
daily_stats.mutate(
rolling_avg=lambda t: t.flight_count.mean().over(
xo.window(order_by=t.flight_date, preceding=6, following=0),
xibis.window(order_by=t.flight_date, preceding=6, following=0),
),
rank=lambda t: xo.dense_rank().over(
xo.window(order_by=xo.desc(t.flight_count)),
rank=lambda t: xibis.dense_rank().over(
xibis.window(order_by=xibis.desc(t.flight_count)),
),
running_total=lambda t: t.flight_count.sum().over(
xo.window(order_by=t.flight_date),
xibis.window(order_by=t.flight_date),
),
)
.order_by("flight_date")
Expand All @@ -65,8 +68,8 @@ def main():

result = (
carrier_stats.mutate(
total_flights=lambda t: t.flight_count.sum().over(xo.window()),
percent_manual=lambda t: (t.flight_count / t.flight_count.sum().over(xo.window()))
total_flights=lambda t: t.flight_count.sum().over(xibis.window()),
percent_manual=lambda t: (t.flight_count / t.flight_count.sum().over(xibis.window()))
* 100,
)
.order_by(_.percent_manual.desc())
Expand Down
17 changes: 14 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ dependencies = [
"pyyaml>=6.0",
"returns>=0.26.0",
"toolz>=1.0.0",
"xorq>=0.3.25",
]
urls = { Homepage = "https://github.com/boringdata/boring-semantic-layer/tree/main" }
license = "MIT"
Expand All @@ -26,7 +25,8 @@ bsl = "boring_semantic_layer.agents.cli:main"
bsl = "boring_semantic_layer.serialization.tag_handler:bsl_tag_handler"

[project.optional-dependencies]
examples = ["xorq[duckdb]>=0.3.4", "xorq", "duckdb<1.4"]
xorq = ["xorq>=0.3.31"]
examples = ["xorq[duckdb]>=0.3.31", "duckdb<1.4"]

# Visualization backends
viz-altair = ["altair>=5.0.0", "vl-convert-python>=1.0.0"]
Expand All @@ -51,11 +51,22 @@ server = [
"uvicorn[standard]>=0.30.0",
]

# Baseline test environment for the no-xorq CI leg: pulled in via
# `uv sync --all-extras` so the suite has a duckdb backend (and pyarrow)
# without depending on xorq or the examples extra.
test-core = [
"ibis-framework[duckdb]>=11.0.0",
"pytest",
"pandas>=2.3.0",
"pytest-asyncio",
]

dev = [
"boring-semantic-layer[agent,mcp,server,examples,viz-altair,viz-plotly,viz-plotext]",
# LLM provider clients needed to test agent backends (not user-facing extras)
"openai>=1.0.0",
"langchain-openai>=0.3.0",
"langchain-anthropic>=0.3.0",
# Developer tooling
"ruff>=0.6.7",
"pre-commit>=4.2.0",
"pytest",
Expand Down
Loading
Loading