Skip to content

Commit e89fefd

Browse files
authored
Chore: Improve error messages return to a user when there are models with the same name (#4425)
1 parent 4b3e2bb commit e89fefd

3 files changed

Lines changed: 26 additions & 9 deletions

File tree

sqlmesh/core/loader.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ def _load_external_models(
223223
if external_models_path.exists() and external_models_path.is_dir():
224224
paths_to_load.extend(self._glob_paths(external_models_path, extension=".yaml"))
225225

226-
def _load() -> t.List[Model]:
226+
def _load(path: Path) -> t.List[Model]:
227227
try:
228228
with open(path, "r", encoding="utf-8") as file:
229229
return [
@@ -241,21 +241,30 @@ def _load() -> t.List[Model]:
241241
for row in YAML().load(file.read())
242242
]
243243
except Exception as ex:
244-
raise ConfigError(f"Failed to load model definition at '{path}'.\n{ex}")
244+
self._raise_failed_to_load_model_error(path, str(ex))
245+
raise
245246

246247
for path in paths_to_load:
247248
self._track_file(path)
248249

249-
external_models = cache.get_or_load_models(path, _load)
250+
external_models = cache.get_or_load_models(path, lambda: _load(path))
250251
# external models with no explicit gateway defined form the base set
251252
for model in external_models:
252253
if model.gateway is None:
254+
if model.fqn in models:
255+
self._raise_failed_to_load_model_error(
256+
path, f"Duplicate external model name: '{model.name}'."
257+
)
253258
models[model.fqn] = model
254259

255260
# however, if there is a gateway defined, gateway-specific models take precedence
256261
if gateway:
257262
for model in external_models:
258263
if model.gateway == gateway:
264+
if model.fqn in models and models[model.fqn].gateway == gateway:
265+
self._raise_failed_to_load_model_error(
266+
path, f"Duplicate external model name: '{model.name}'."
267+
)
259268
models.update({model.fqn: model})
260269

261270
return models
@@ -363,6 +372,9 @@ def _get_variables(self, gateway_name: t.Optional[str] = None) -> t.Dict[str, t.
363372

364373
return self._variables_by_gateway[gateway_name]
365374

375+
def _raise_failed_to_load_model_error(self, path: Path, message: str) -> None:
376+
raise ConfigError(f"Failed to load model definition at '{path}'.\n{message}")
377+
366378

367379
class SqlMeshLoader(Loader):
368380
"""Loads macros and models for a context using the SQLMesh file formats"""
@@ -484,9 +496,14 @@ def _load() -> t.List[Model]:
484496
default_catalog_per_gateway=self.context.default_catalog_per_gateway,
485497
)
486498
except Exception as ex:
487-
raise ConfigError(f"Failed to load model definition at '{path}'.\n{ex}")
499+
self._raise_failed_to_load_model_error(path, str(ex))
500+
raise
488501

489502
for model in cache.get_or_load_models(path, _load):
503+
if model.fqn in models:
504+
self._raise_failed_to_load_model_error(
505+
path, f"Duplicate SQL model name: '{model.name}'."
506+
)
490507
if model.enabled:
491508
models[model.fqn] = model
492509

@@ -544,7 +561,7 @@ def _load_python_models(
544561
if model.enabled:
545562
models[model.fqn] = model
546563
except Exception as ex:
547-
raise ConfigError(f"Failed to load model definition at '{path}'.\n{ex}")
564+
self._raise_failed_to_load_model_error(path, str(ex))
548565

549566
finally:
550567
model_registry._dialect = None

sqlmesh/utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def __call__(
136136
except ValueError:
137137
# No need to raise due to duplicate key if the functions are identical
138138
if func.__code__.co_code != registry[func_name].func.__code__.co_code:
139-
raise
139+
raise ValueError(f"Duplicate name: '{func_name}'.")
140140

141141
@wraps(func)
142142
def wrapper(*args: t.Any, **kwargs: t.Any) -> DECORATOR_RETURN_TYPE:

tests/core/test_loader.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ def duplicate_model_path(fpath):
109109
duplicate_fpath.write_text(model["contents"])
110110

111111
with pytest.raises(
112-
ValueError,
113-
match=r'Duplicate key \'"memory"."test_schema"."test_model"\' found in UniqueKeyDict<models>. Call dict.update\(\.\.\.\) if this is intentional.',
112+
ConfigError,
113+
match=r".*Duplicate .* model name: 'test_schema.test_model'",
114114
):
115115
Context(paths=tmp_path, config=config)
116116

@@ -159,7 +159,7 @@ def execute(
159159

160160
with pytest.raises(
161161
ConfigError,
162-
match=r"Failed to load model definition at '.*'.\nDuplicate key 'test_schema.test_model' found in UniqueKeyDict<python_models>.",
162+
match=r"Failed to load model definition at '.*'.\nDuplicate name: 'test_schema.test_model'.",
163163
):
164164
Context(paths=tmp_path, config=config)
165165

0 commit comments

Comments
 (0)