Universal SQL-first semantic layer in Python. Imports from Cube/LookML/dbt/Hex/Rill/Malloy, queries against DuckDB/Postgres/BigQuery/Snowflake/etc.
- Sidemantic usage is overwhelmingly CLI-first in practice (roughly 95% CLI usage).
- Default to
sidemanticCLI workflows (validate,info,query,migrator, etc.). - Treat Python API examples as secondary, advanced automation paths, not the default onboarding path.
Main Python package: sidemantic/
core/- SemanticLayer, Model, Dimension, Metric, SemanticGraphadapters/- Format parsers (Cube, LookML, MetricFlow, Hex, Rill, Malloy, etc.)db/- Database adapterssql/- SQL generation and query rewritingwidget/,server/,workbench/,mcp_server.py- Optional features with lazy imports
Separate experimental implementations (not the main codebase):
sidemantic-rs/- Rust rewrite (WIP)sidemantic-duckdb/- DuckDB extension wrapping sidemantic-rs
These don't share code with Python. CI runs them only when their dirs change.
Update BOTH when releasing:
pyproject.toml:version = "X.Y.Z"sidemantic/__init__.py:__version__ = "X.Y.Z"
ALWAYS run the EXACT same commands CI runs before committing:
# Run these in order:
uv run ruff check . --exclude docs/_extensions --exclude sidemantic-duckdb/extension-ci-tools --exclude sidemantic-duckdb/scripts --exclude sidemantic-duckdb/duckdb --exclude sidemantic/adapters/malloy_grammar --exclude sidemantic/adapters/holistics_grammar
uv run ruff format --check . --exclude docs/_extensions --exclude sidemantic-duckdb/extension-ci-tools --exclude sidemantic-duckdb/scripts --exclude sidemantic-duckdb/duckdb --exclude sidemantic/adapters/malloy_grammar --exclude sidemantic/adapters/holistics_grammar
uv run pytest -vIf any fail, fix them:
# Fix ruff issues
uv run ruff check --fix . --exclude docs/_extensions --exclude sidemantic-duckdb/extension-ci-tools --exclude sidemantic-duckdb/scripts --exclude sidemantic-duckdb/duckdb --exclude sidemantic/adapters/malloy_grammar --exclude sidemantic/adapters/holistics_grammar
uv run ruff format . --exclude docs/_extensions --exclude sidemantic-duckdb/extension-ci-tools --exclude sidemantic-duckdb/scripts --exclude sidemantic-duckdb/duckdb --exclude sidemantic/adapters/malloy_grammar --exclude sidemantic/adapters/holistics_grammarThis is NON-NEGOTIABLE. You MUST run these BEFORE every commit.
PRs: Do not include test commands in PR bodies unless explicitly requested.
Why this matters:
- CI runs these exact commands and will fail if they don't pass
- You keep pushing broken code because you don't run these locally
- Ruff must be in
[project.optional-dependencies] devfor CI - NOT in
[dependency-groups](that's uv-specific, CI uses optional-dependencies)
- Use
uvfor all Python package management - Ruff should be in dev dependencies (
[dependency-groups] dev) - DO NOT add dev tools to main dependencies unless explicitly requested
- Optional features use
[project.optional-dependencies]:workbench- textual, plotext, textual-plotext (for TUI, NOT Pyodide compatible)serve- mcp[cli], riffq, pyarrow (for PostgreSQL server, NOT Pyodide compatible)dev- pytest, ruff, pandas, numpy (for development)
CRITICAL: Pyodide Compatibility Rules
- Core dependencies (in main
dependencieslist) MUST work in Pyodide/WASM - Optional dependencies can use packages incompatible with Pyodide (textual, riffq, pyarrow, mcp)
- ALL imports of optional deps MUST be lazy (inside functions, NOT at module level)
- Test:
from sidemantic import Model, Dimension, Metricmust work without any optional deps
Import Structure:
sidemantic/__init__.py- Only imports core classes (Model, Dimension, etc.)sidemantic/cli.py- Imports typer (core dep) at top, workbench/server imports inside command functionssidemantic/workbench/__init__.py- Lazy imports textual insiderun_workbench()functionsidemantic/server/- Never imported unlesssidemantic servecommand is run
Pyodide typing-extensions issue:
- Pyodide has typing-extensions==4.11.0
- Some deps (pydantic>=2.10, inflect>=7.2) require typing-extensions>=4.12+
- Dashboard handles this by installing pydantic<2.10 with deps=False
- inflect<7.2 constraint in core deps marked with
# PYODIDE:comment
Version constraints with "PYODIDE:" comments are REQUIRED:
- Heavy deps (textual, riffq, mcp) are optional to avoid Pyodide conflicts
- Pyodide CI builds local wheel and installs it with
deps=False - This ensures code changes are tested in Pyodide before publish
- If adding new core deps, check they work in Pyodide or make them optional
If Pyodide CI fails:
- Check if a dep version changed that requires newer typing-extensions
- Either downgrade that dep OR add workaround in dashboard.qmd install
- CI installs local wheel to test current code, not PyPI version
Run tests before committing significant changes:
uv run pytest -vgh workflow run publish.yml- Breaking ruff - Always ensure ruff is installed in dev dependencies
- Pyodide compatibility - Keep heavy deps (textual, pygments) optional
- Not linting - Format and lint EVERY TIME before commit