Skip to content

Commit 44bb070

Browse files
committed
Refine and document project-index linting
Signed-off-by: Andreas Fredhøi <andreas.fredhoi@fresio.no>
1 parent 20035da commit 44bb070

4 files changed

Lines changed: 27 additions & 29 deletions

File tree

docs/reference/cli.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,9 +650,12 @@ Usage: sqlmesh lint [OPTIONS]
650650
651651
Options:
652652
--model TEXT A model to lint. Multiple models can be linted. If no models are specified, every model will be linted.
653+
--use-project-index Use the persistent project index. With --model, only the selected models and their upstream dependencies
654+
are loaded, resolved, and validated, so errors in unrelated models are not reported. Without --model,
655+
every model is still loaded and linted.
653656
--local Lint using only locally loaded project files without loading state. In multi-repository setups, or when
654657
linting only a subset of projects, this may cause additional linting errors because SQLMesh will not resolve
655658
references or schemas from models that exist only in remote state.
656659
--help Show this message and exit.
657660
658-
```
661+
```

sqlmesh/core/context.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3419,32 +3419,30 @@ def lint_models(
34193419
raise_on_error: bool = True,
34203420
use_project_index: bool = False,
34213421
) -> t.List[AnnotatedRuleViolation]:
3422-
input_models = list(models) if models is not None else []
3422+
models = list(models) if models is not None else []
34233423

34243424
if not self._loaded:
3425-
if input_models and use_project_index:
3426-
target_fqns = {
3425+
target_fqns = (
3426+
{
34273427
normalize_model_name(
34283428
model,
34293429
default_catalog=self.default_catalog,
34303430
dialect=self.default_dialect,
34313431
)
34323432
if isinstance(model, str)
34333433
else model.fqn
3434-
for model in input_models
3434+
for model in models
34353435
}
3436-
self.load(
3437-
model_fqns=target_fqns,
3438-
use_project_index=True,
3439-
)
3440-
else:
3441-
self.load(use_project_index=use_project_index)
3436+
if models and use_project_index
3437+
else None
3438+
)
3439+
self.load(model_fqns=target_fqns, use_project_index=use_project_index)
34423440

34433441
found_error = False
34443442

34453443
model_list = (
3446-
list(self.get_model(model, raise_if_missing=True) for model in input_models)
3447-
if input_models
3444+
list(self.get_model(model, raise_if_missing=True) for model in models)
3445+
if models
34483446
else self.models.values()
34493447
)
34503448
all_violations = []

sqlmesh/core/loader.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -651,9 +651,9 @@ def _selected_model_paths(
651651

652652
model_to_path: t.Dict[str, Path] = {}
653653
dependencies: t.Dict[str, t.Set[str]] = {}
654-
for relative_path, file_info in indexed_files.items():
654+
for relative_path, file_models in indexed_files.items():
655655
path = self.config_path / relative_path
656-
for fqn, depends_on in file_info.get("models", {}).items():
656+
for fqn, depends_on in file_models.items():
657657
model_to_path[fqn] = path
658658
dependencies[fqn] = set(depends_on)
659659

@@ -673,17 +673,14 @@ def _write_model_index(self, models: UniqueKeyDict[str, Model]) -> None:
673673
if not model_paths:
674674
return
675675

676-
files: t.Dict[str, t.Dict[str, t.Any]] = {}
677-
for path in model_paths:
678-
files[str(path.relative_to(self.config_path))] = {
679-
"models": {},
680-
}
681-
676+
# Maps each model file to the models it defines and their dependencies.
677+
files: t.Dict[str, t.Dict[str, t.List[str]]] = {
678+
str(path.relative_to(self.config_path)): {} for path in model_paths
679+
}
682680
for model in models.values():
683-
if model._path not in model_paths:
684-
continue
685-
relative_path = str(t.cast(Path, model._path).relative_to(self.config_path))
686-
files[relative_path]["models"][model.fqn] = sorted(model.depends_on)
681+
if model._path in model_paths:
682+
relative_path = str(t.cast(Path, model._path).relative_to(self.config_path))
683+
files[relative_path][model.fqn] = sorted(model.depends_on)
687684

688685
self._model_index_path.parent.mkdir(parents=True, exist_ok=True)
689686
temporary_path = self._model_index_path.with_suffix(".tmp")

tests/core/test_context.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,13 +1403,13 @@ def get_sushi_fingerprints(context: Context):
14031403
def test_physical_schema_mapping(tmp_path: pathlib.Path) -> None:
14041404
create_temp_file(
14051405
tmp_path,
1406-
pathlib.Path("models", "a.sql"),
1406+
pathlib.Path(pathlib.Path("models"), "a.sql"),
14071407
"MODEL(name foo_staging.model_a); SELECT 1;",
14081408
)
14091409

14101410
create_temp_file(
14111411
tmp_path,
1412-
pathlib.Path("models", "b.sql"),
1412+
pathlib.Path(pathlib.Path("models"), "b.sql"),
14131413
"MODEL(name testone.model_b); SELECT 1;",
14141414
)
14151415

@@ -2951,7 +2951,7 @@ def create_context() -> Context:
29512951

29522952
create_temp_file(
29532953
tmp_path,
2954-
pathlib.Path(pathlib.Path("models"), "a.sql"),
2954+
pathlib.Path("models", "a.sql"),
29552955
"MODEL(name a); SELECT 1 AS col FROM raw.unregistered_source;",
29562956
)
29572957
create_temp_file(tmp_path, pathlib.Path("models", "b.sql"), "MODEL(name b); SELECT col FROM a;")
@@ -2990,7 +2990,7 @@ def create_context() -> Context:
29902990
# dependency outside this set, Context.load safely retries with a full load.
29912991
create_temp_file(
29922992
tmp_path,
2993-
pathlib.Path(pathlib.Path("models"), "b.sql"),
2993+
pathlib.Path("models", "b.sql"),
29942994
"MODEL(name b); SELECT col + 1 AS col FROM a;",
29952995
)
29962996
ctx = create_context()

0 commit comments

Comments
 (0)