datavault4sqlmesh is a Python library that wraps datavault4sqlglot with SQLMesh-native model
factories, eliminating the boilerplate in every SQLMesh model file.
Instead of writing a full @model decorator, a typed execute function, and manually
maintaining the columns={} schema dict in each model, users call a single factory function
(e.g. hub_model, satellite_model) that handles all of this automatically.
Key Objectives:
- Reduce boilerplate: One factory call replaces 30–40 lines of repetitive model setup.
- Auto-derive schema: Column types are inferred from DV metadata so users never manually
maintain
columns={}dicts. - SQLMesh parity: Every SQLMesh model kind (
INCREMENTAL_UNMANAGED,FULL,VIEW) is handled correctly for the corresponding DV entity type. - Extend, not replace: datavault4sqlglot generators remain the SQL engine; this package only adds the SQLMesh integration layer.
Package layout:
datavault4sqlmesh/
├── datavault4sqlmesh/
│ ├── __init__.py # top-level re-exports
│ ├── config.py # load_dv_config() convenience wrapper
│ ├── schema/
│ │ ├── __init__.py
│ │ └── inference.py # infer_*_columns() functions
│ └── models/
│ ├── __init__.py
│ ├── _utils.py # internal helpers (parse_model_name)
│ ├── hub.py # hub_model()
│ ├── link.py # link_model()
│ ├── satellite.py # satellite_model()
│ ├── satellite_v1.py # satellite_v1_model()
│ └── stage.py # stage_model()
└── tests/
├── conftest.py
├── test_schema_inference.py
├── test_hub.py
├── test_link.py
├── test_satellite.py
├── test_satellite_v1.py
└── test_stage.py
Data flow:
- User calls a factory function (e.g.
hub_model(name, hashkey, sources, ...)) - Factory infers
columnsdict from DV metadata viaschema/inference.py - Factory applies the SQLMesh
@model(...)decorator with correct kind/columns/grain - Factory instantiates the corresponding
datavault4sqlglotgenerator in theexecuteclosure and calls.generate_sql()at runtime - The decorated
executefunction is returned and discovered by SQLMesh at import time
Follow the exact same standards as datavault4sqlglot/CLAUDE.md:
- Python 3.9+, full type annotations,
from __future__ import annotations - Black + ruff + mypy compatible
- Absolute imports only
- PascalCase for classes, snake_case for functions/variables,
_prefixfor private helpers - Short, clear docstrings on all public functions and classes
loggingmodule, notprint()- Fail early with specific exceptions
- Never import SQLMesh at module top-level in model factory files.
Import inside the factory function (
from sqlmesh import model as sqlmesh_model) so thatdatavault4sqlmesh.schemaand other non-SQLMesh sub-packages can be imported without SQLMesh being installed. - Column inference is pure and stateless. The
infer_*_columnsfunctions must have no side effects and must not call external services or read files. - Factories must not execute SQL. They build and return decorated functions; actual SQL
generation happens inside
execute()at SQLMesh runtime.
| DV Entity | SQLMesh Kind | Notes |
|---|---|---|
| Stage | FULL |
Complete refresh every run |
| Hub | INCREMENTAL_UNMANAGED |
HWM-based incremental load |
| Link | INCREMENTAL_UNMANAGED |
HWM-based incremental load |
| Satellite v0 | INCREMENTAL_UNMANAGED |
HWM-based incremental load |
| Satellite v1 | FULL |
Python model constraint — cannot use VIEW; override via kind |
Why
FULLfor sat v1? SQLMesh Python models cannot useVIEWkind directly. Users may override withkind={"name": "VIEW"}only if they control the model type.
All inferred columns default to:
- Hash key / hash diff columns →
VARCHAR - Business keys / payload / foreign keys →
VARCHAR ldts_aliascolumn →TIMESTAMPrsrc_aliascolumn →VARCHARledts_aliascolumn (sat v1 only) →TIMESTAMPis_currentcolumn (sat v1 only) →BOOLEAN
Users override individual column types via the column_overrides parameter.
Always use the project venv:
# Unit tests (no SQLMesh/Postgres needed)
.venv/sqlglot/Scripts/pytest tests/ -v --ignore=tests/integration
# Integration tests (requires Postgres at localhost:5432, credentials dev/dev/dev)
.venv/sqlglot/Scripts/pytest tests/integration/ -v -m integrationIntegration tests are marked @pytest.mark.integration and skipped unless explicitly selected.
Each model factory supports two usage patterns:
Auto-generate mode — pass source table/schema, no execute body needed:
hub_model(
name="dv.customer_h",
hashkey="hk_customer_h",
business_keys=["customer_id"],
source_schema="staging",
source_table="customer_stg",
)Decorator mode — omit source, wrap a custom execute:
@hub_model(name="dv.customer_h", hashkey="hk_customer_h", business_keys=["customer_id"])
def execute(evaluator, **kwargs):
return HubGenerator(...).generate_sql()satellite_model and stage_model are single-source only (no sources list). Hub and link support multi-source via sources: List[SourceModel].
When writing or refactoring code in this package:
- Does the factory correctly parse the dotted
nameinto table/schema/database? - Does the
executeclosure capture all required variables by reference — not by value? - Are SQLMesh imports inside the factory function body (not at module top-level)?
- Does column inference handle edge cases: empty sources, dict-form hash_diff, etc.?
- Are validation errors raised with descriptive messages (fail early)?
- Do tests cover the column inference independently of SQLMesh installation?