Edit on GitHub

sqlmesh.utils.errors

  1from __future__ import annotations
  2
  3import typing as t
  4from enum import auto
  5from pathlib import Path
  6
  7from sqlglot import exp
  8from sqlglot.helper import AutoName
  9
 10if t.TYPE_CHECKING:
 11    from requests.models import Response
 12
 13    from sqlmesh.core.model import Model
 14    from sqlmesh.core.schema_diff import TableAlterOperation
 15
 16
 17class ErrorLevel(AutoName):
 18    IGNORE = auto()
 19    WARN = auto()
 20    RAISE = auto()
 21
 22
 23class SQLMeshError(Exception):
 24    pass
 25
 26
 27class ConfigError(SQLMeshError):
 28    location: t.Optional[Path] = None
 29
 30    def __init__(self, message: str | Exception, location: t.Optional[Path] = None) -> None:
 31        super().__init__(message)
 32        if location:
 33            self.location = Path(location) if isinstance(location, str) else location
 34
 35
 36class BaseMissingReferenceError(ConfigError):
 37    def __init__(self, ref: str) -> None:
 38        self.ref = ref
 39
 40
 41class MissingModelError(BaseMissingReferenceError):
 42    """Raised when a model that is referenced is missing."""
 43
 44
 45class MissingSourceError(BaseMissingReferenceError):
 46    """Raised when a source that is referenced is missing."""
 47
 48
 49class MissingDependencyError(SQLMeshError):
 50    """Local environment is missing a required dependency for the given operation"""
 51
 52
 53class MacroEvalError(SQLMeshError):
 54    pass
 55
 56
 57class PlanError(SQLMeshError):
 58    pass
 59
 60
 61class NoChangesPlanError(PlanError):
 62    pass
 63
 64
 65class UncategorizedPlanError(PlanError):
 66    pass
 67
 68
 69class ConflictingPlanError(PlanError):
 70    pass
 71
 72
 73class MissingContextException(Exception):
 74    pass
 75
 76
 77class SnapshotVersionError(SQLMeshError):
 78    pass
 79
 80
 81class MagicError(SQLMeshError):
 82    pass
 83
 84
 85class AuditConfigError(ConfigError):
 86    pass
 87
 88
 89class StateMigrationError(SQLMeshError):
 90    pass
 91
 92
 93class AuditError(SQLMeshError):
 94    def __init__(
 95        self,
 96        audit_name: str,
 97        audit_args: t.Dict[t.Any, t.Any],
 98        count: int,
 99        query: exp.Query,
100        model: t.Optional[Model] = None,
101        # the dialect of the engine adapter that evaluated the audit query
102        adapter_dialect: t.Optional[str] = None,
103    ) -> None:
104        self.audit_name = audit_name
105        self.audit_args = audit_args
106        self.model = model
107        self.count = count
108        self.query = query
109        self.adapter_dialect = adapter_dialect
110
111        super().__init__(
112            f"'{self.audit_name}' audit error: {self.count} {'row' if self.count == 1 else 'rows'} failed"
113        )
114
115    @property
116    def model_name(self) -> t.Optional[str]:
117        return self.model.name if self.model else None
118
119    def sql(self, dialect: t.Optional[str] = None, **opts: t.Any) -> str:
120        """
121        Returns the rendered audit query that failed.
122
123        Args:
124            dialect: the dialect of the output SQL string, by default,
125                     this will use the dialect of the engine adapter that ran the query.
126            opts: other `sqlglot.generator.Generator` options.
127
128        Returns:
129            The SQL string.
130        """
131        return self.query.sql(dialect=dialect or self.adapter_dialect, **opts)
132
133
134class NodeAuditsErrors(SQLMeshError):
135    def __init__(self, errors: t.List[AuditError]) -> None:
136        self.errors = errors
137
138        super().__init__(f"Audits failed: {', '.join([e.audit_name for e in errors])}")
139
140
141class TestError(SQLMeshError):
142    __test__ = False  # prevent pytest trying to collect this as a test class
143    pass
144
145
146class DestructiveChangeError(SQLMeshError):
147    pass
148
149
150class AdditiveChangeError(SQLMeshError):
151    pass
152
153
154class MigrationNotSupportedError(SQLMeshError):
155    pass
156
157
158class NotificationTargetError(SQLMeshError):
159    pass
160
161
162class ApiError(SQLMeshError):
163    def __init__(self, message: str, code: int) -> None:
164        super().__init__(message)
165        self.code = code
166
167
168class ApiClientError(ApiError):
169    pass
170
171
172class ApiServerError(ApiError):
173    pass
174
175
176class NotFoundError(ApiClientError):
177    def __init__(self, message: str) -> None:
178        super().__init__(message, 404)
179
180
181class CICDBotError(SQLMeshError):
182    pass
183
184
185class ParsetimeAdapterCallError(SQLMeshError):
186    pass
187
188
189class EngineAdapterError(SQLMeshError):
190    pass
191
192
193class UnsupportedCatalogOperationError(EngineAdapterError):
194    pass
195
196
197class CircuitBreakerError(SQLMeshError):
198    def __init__(self) -> None:
199        super().__init__("Circuit breaker triggered.")
200
201
202class PythonModelEvalError(SQLMeshError):
203    pass
204
205
206class MissingDefaultCatalogError(SQLMeshError):
207    pass
208
209
210class LinterError(SQLMeshError):
211    pass
212
213
214class SignalEvalError(SQLMeshError):
215    """Errors when evaluating a signal that is because of a user mistake and not a SQLMesh bug."""
216
217    pass
218
219
220def raise_config_error(
221    msg: str,
222    location: t.Optional[Path] = None,
223    error_type: t.Type[ConfigError] = ConfigError,
224) -> None:
225    if location:
226        raise error_type(f"{msg} at '{location}'", location)
227    raise error_type(msg, location=location)
228
229
230def raise_for_status(response: Response) -> None:
231    if response.status_code == 404:
232        raise NotFoundError(response.text)
233    if 400 <= response.status_code < 500:
234        raise ApiClientError(response.text, response.status_code)
235    if 500 <= response.status_code < 600:
236        raise ApiServerError(response.text, response.status_code)
237
238
239def _format_schema_change_msg(
240    snapshot_name: str,
241    is_destructive: bool,
242    alter_operations: t.List[TableAlterOperation],
243    dialect: str,
244    error: bool = True,
245) -> str:
246    """
247    Common function to format schema change messages.
248
249    Args:
250        snapshot_name: Name of the model/snapshot
251        is_destructive: if change is destructive else it would be additive
252        alter_operations: List of table alter operations
253        dialect: SQL dialect for formatting
254        error: Whether this is an error or warning
255    """
256    from sqlmesh.core.schema_diff import get_dropped_column_names, get_additive_column_names
257
258    change_type = "destructive" if is_destructive else "additive"
259    setting_name = "on_destructive_change" if is_destructive else "on_additive_change"
260    action_verb = "drops" if is_destructive else "adds"
261    cli_flag = "--allow-destructive-model" if is_destructive else "--allow-additive-model"
262
263    column_names = (
264        get_dropped_column_names(alter_operations)
265        if is_destructive
266        else get_additive_column_names(alter_operations)
267    )
268    column_str = "', '".join(column_names)
269    column_msg = (
270        f" that {action_verb} column{'s' if column_names and len(column_names) > 1 else ''} '{column_str}'"
271        if column_str
272        else ""
273    )
274
275    # Format ALTER expressions
276    alter_expr_msg = "\n\nSchema changes:\n  " + "\n  ".join(
277        [alter.expression.sql(dialect) for alter in alter_operations]
278    )
279
280    # Main warning message
281    warning_msg = (
282        f"Plan requires {change_type} change to forward-only model '{snapshot_name}'s schema"
283    )
284
285    if error:
286        permissive_values = "`warn`, `allow`, or `ignore`"
287        cli_part = f" or include the model in the plan's `{cli_flag}` option"
288        err_msg = f"\n\nTo allow the {change_type} change, set the model's `{setting_name}` setting to {permissive_values}{cli_part}.\n"
289    else:
290        err_msg = ""
291
292    return f"\n{warning_msg}{column_msg}.{alter_expr_msg}{err_msg}"
293
294
295def format_destructive_change_msg(
296    snapshot_name: str,
297    alter_expressions: t.List[TableAlterOperation],
298    dialect: str,
299    error: bool = True,
300) -> str:
301    return _format_schema_change_msg(
302        snapshot_name=snapshot_name,
303        is_destructive=True,
304        alter_operations=alter_expressions,
305        dialect=dialect,
306        error=error,
307    )
308
309
310def format_additive_change_msg(
311    snapshot_name: str,
312    alter_operations: t.List[TableAlterOperation],
313    dialect: str,
314    error: bool = True,
315) -> str:
316    return _format_schema_change_msg(
317        snapshot_name=snapshot_name,
318        is_destructive=False,
319        alter_operations=alter_operations,
320        dialect=dialect,
321        error=error,
322    )
class ErrorLevel(sqlglot.helper.AutoName):
18class ErrorLevel(AutoName):
19    IGNORE = auto()
20    WARN = auto()
21    RAISE = auto()

An enumeration.

IGNORE = <ErrorLevel.IGNORE: 'IGNORE'>
WARN = <ErrorLevel.WARN: 'WARN'>
RAISE = <ErrorLevel.RAISE: 'RAISE'>
Inherited Members
enum.Enum
name
value
class SQLMeshError(builtins.Exception):
24class SQLMeshError(Exception):
25    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class ConfigError(SQLMeshError):
28class ConfigError(SQLMeshError):
29    location: t.Optional[Path] = None
30
31    def __init__(self, message: str | Exception, location: t.Optional[Path] = None) -> None:
32        super().__init__(message)
33        if location:
34            self.location = Path(location) if isinstance(location, str) else location

Common base class for all non-exit exceptions.

ConfigError(message: str | Exception, location: Optional[pathlib.Path] = None)
31    def __init__(self, message: str | Exception, location: t.Optional[Path] = None) -> None:
32        super().__init__(message)
33        if location:
34            self.location = Path(location) if isinstance(location, str) else location
location: Optional[pathlib.Path] = None
Inherited Members
builtins.BaseException
with_traceback
args
class BaseMissingReferenceError(ConfigError):
37class BaseMissingReferenceError(ConfigError):
38    def __init__(self, ref: str) -> None:
39        self.ref = ref

Common base class for all non-exit exceptions.

BaseMissingReferenceError(ref: str)
38    def __init__(self, ref: str) -> None:
39        self.ref = ref
ref
Inherited Members
ConfigError
location
builtins.BaseException
with_traceback
args
class MissingModelError(BaseMissingReferenceError):
42class MissingModelError(BaseMissingReferenceError):
43    """Raised when a model that is referenced is missing."""

Raised when a model that is referenced is missing.

Inherited Members
BaseMissingReferenceError
BaseMissingReferenceError
ref
ConfigError
location
builtins.BaseException
with_traceback
args
class MissingSourceError(BaseMissingReferenceError):
46class MissingSourceError(BaseMissingReferenceError):
47    """Raised when a source that is referenced is missing."""

Raised when a source that is referenced is missing.

Inherited Members
BaseMissingReferenceError
BaseMissingReferenceError
ref
ConfigError
location
builtins.BaseException
with_traceback
args
class MissingDependencyError(SQLMeshError):
50class MissingDependencyError(SQLMeshError):
51    """Local environment is missing a required dependency for the given operation"""

Local environment is missing a required dependency for the given operation

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class MacroEvalError(SQLMeshError):
54class MacroEvalError(SQLMeshError):
55    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class PlanError(SQLMeshError):
58class PlanError(SQLMeshError):
59    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class NoChangesPlanError(PlanError):
62class NoChangesPlanError(PlanError):
63    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class UncategorizedPlanError(PlanError):
66class UncategorizedPlanError(PlanError):
67    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class ConflictingPlanError(PlanError):
70class ConflictingPlanError(PlanError):
71    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class MissingContextException(builtins.Exception):
74class MissingContextException(Exception):
75    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class SnapshotVersionError(SQLMeshError):
78class SnapshotVersionError(SQLMeshError):
79    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class MagicError(SQLMeshError):
82class MagicError(SQLMeshError):
83    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class AuditConfigError(ConfigError):
86class AuditConfigError(ConfigError):
87    pass

Common base class for all non-exit exceptions.

Inherited Members
ConfigError
ConfigError
location
builtins.BaseException
with_traceback
args
class StateMigrationError(SQLMeshError):
90class StateMigrationError(SQLMeshError):
91    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class AuditError(SQLMeshError):
 94class AuditError(SQLMeshError):
 95    def __init__(
 96        self,
 97        audit_name: str,
 98        audit_args: t.Dict[t.Any, t.Any],
 99        count: int,
100        query: exp.Query,
101        model: t.Optional[Model] = None,
102        # the dialect of the engine adapter that evaluated the audit query
103        adapter_dialect: t.Optional[str] = None,
104    ) -> None:
105        self.audit_name = audit_name
106        self.audit_args = audit_args
107        self.model = model
108        self.count = count
109        self.query = query
110        self.adapter_dialect = adapter_dialect
111
112        super().__init__(
113            f"'{self.audit_name}' audit error: {self.count} {'row' if self.count == 1 else 'rows'} failed"
114        )
115
116    @property
117    def model_name(self) -> t.Optional[str]:
118        return self.model.name if self.model else None
119
120    def sql(self, dialect: t.Optional[str] = None, **opts: t.Any) -> str:
121        """
122        Returns the rendered audit query that failed.
123
124        Args:
125            dialect: the dialect of the output SQL string, by default,
126                     this will use the dialect of the engine adapter that ran the query.
127            opts: other `sqlglot.generator.Generator` options.
128
129        Returns:
130            The SQL string.
131        """
132        return self.query.sql(dialect=dialect or self.adapter_dialect, **opts)

Common base class for all non-exit exceptions.

AuditError( audit_name: str, audit_args: Dict[Any, Any], count: int, query: sqlglot.expressions.query.Query, model: Union[sqlmesh.core.model.definition.SqlModel, sqlmesh.core.model.definition.SeedModel, sqlmesh.core.model.definition.PythonModel, sqlmesh.core.model.definition.ExternalModel, NoneType] = None, adapter_dialect: Optional[str] = None)
 95    def __init__(
 96        self,
 97        audit_name: str,
 98        audit_args: t.Dict[t.Any, t.Any],
 99        count: int,
100        query: exp.Query,
101        model: t.Optional[Model] = None,
102        # the dialect of the engine adapter that evaluated the audit query
103        adapter_dialect: t.Optional[str] = None,
104    ) -> None:
105        self.audit_name = audit_name
106        self.audit_args = audit_args
107        self.model = model
108        self.count = count
109        self.query = query
110        self.adapter_dialect = adapter_dialect
111
112        super().__init__(
113            f"'{self.audit_name}' audit error: {self.count} {'row' if self.count == 1 else 'rows'} failed"
114        )
audit_name
audit_args
model
count
query
adapter_dialect
model_name: Optional[str]
116    @property
117    def model_name(self) -> t.Optional[str]:
118        return self.model.name if self.model else None
def sql(self, dialect: Optional[str] = None, **opts: Any) -> str:
120    def sql(self, dialect: t.Optional[str] = None, **opts: t.Any) -> str:
121        """
122        Returns the rendered audit query that failed.
123
124        Args:
125            dialect: the dialect of the output SQL string, by default,
126                     this will use the dialect of the engine adapter that ran the query.
127            opts: other `sqlglot.generator.Generator` options.
128
129        Returns:
130            The SQL string.
131        """
132        return self.query.sql(dialect=dialect or self.adapter_dialect, **opts)

Returns the rendered audit query that failed.

Arguments:
  • dialect: the dialect of the output SQL string, by default, this will use the dialect of the engine adapter that ran the query.
  • opts: other sqlglot.generator.Generator options.
Returns:

The SQL string.

Inherited Members
builtins.BaseException
with_traceback
args
class NodeAuditsErrors(SQLMeshError):
135class NodeAuditsErrors(SQLMeshError):
136    def __init__(self, errors: t.List[AuditError]) -> None:
137        self.errors = errors
138
139        super().__init__(f"Audits failed: {', '.join([e.audit_name for e in errors])}")

Common base class for all non-exit exceptions.

NodeAuditsErrors(errors: List[AuditError])
136    def __init__(self, errors: t.List[AuditError]) -> None:
137        self.errors = errors
138
139        super().__init__(f"Audits failed: {', '.join([e.audit_name for e in errors])}")
errors
Inherited Members
builtins.BaseException
with_traceback
args
class TestError(SQLMeshError):
142class TestError(SQLMeshError):
143    __test__ = False  # prevent pytest trying to collect this as a test class
144    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class DestructiveChangeError(SQLMeshError):
147class DestructiveChangeError(SQLMeshError):
148    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class AdditiveChangeError(SQLMeshError):
151class AdditiveChangeError(SQLMeshError):
152    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class MigrationNotSupportedError(SQLMeshError):
155class MigrationNotSupportedError(SQLMeshError):
156    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class NotificationTargetError(SQLMeshError):
159class NotificationTargetError(SQLMeshError):
160    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class ApiError(SQLMeshError):
163class ApiError(SQLMeshError):
164    def __init__(self, message: str, code: int) -> None:
165        super().__init__(message)
166        self.code = code

Common base class for all non-exit exceptions.

ApiError(message: str, code: int)
164    def __init__(self, message: str, code: int) -> None:
165        super().__init__(message)
166        self.code = code
code
Inherited Members
builtins.BaseException
with_traceback
args
class ApiClientError(ApiError):
169class ApiClientError(ApiError):
170    pass

Common base class for all non-exit exceptions.

Inherited Members
ApiError
ApiError
code
builtins.BaseException
with_traceback
args
class ApiServerError(ApiError):
173class ApiServerError(ApiError):
174    pass

Common base class for all non-exit exceptions.

Inherited Members
ApiError
ApiError
code
builtins.BaseException
with_traceback
args
class NotFoundError(ApiClientError):
177class NotFoundError(ApiClientError):
178    def __init__(self, message: str) -> None:
179        super().__init__(message, 404)

Common base class for all non-exit exceptions.

NotFoundError(message: str)
178    def __init__(self, message: str) -> None:
179        super().__init__(message, 404)
Inherited Members
ApiError
code
builtins.BaseException
with_traceback
args
class CICDBotError(SQLMeshError):
182class CICDBotError(SQLMeshError):
183    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class ParsetimeAdapterCallError(SQLMeshError):
186class ParsetimeAdapterCallError(SQLMeshError):
187    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class EngineAdapterError(SQLMeshError):
190class EngineAdapterError(SQLMeshError):
191    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class UnsupportedCatalogOperationError(EngineAdapterError):
194class UnsupportedCatalogOperationError(EngineAdapterError):
195    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class CircuitBreakerError(SQLMeshError):
198class CircuitBreakerError(SQLMeshError):
199    def __init__(self) -> None:
200        super().__init__("Circuit breaker triggered.")

Common base class for all non-exit exceptions.

Inherited Members
builtins.BaseException
with_traceback
args
class PythonModelEvalError(SQLMeshError):
203class PythonModelEvalError(SQLMeshError):
204    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class MissingDefaultCatalogError(SQLMeshError):
207class MissingDefaultCatalogError(SQLMeshError):
208    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class LinterError(SQLMeshError):
211class LinterError(SQLMeshError):
212    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
class SignalEvalError(SQLMeshError):
215class SignalEvalError(SQLMeshError):
216    """Errors when evaluating a signal that is because of a user mistake and not a SQLMesh bug."""
217
218    pass

Errors when evaluating a signal that is because of a user mistake and not a SQLMesh bug.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
def raise_config_error( msg: str, location: Optional[pathlib.Path] = None, error_type: Type[ConfigError] = <class 'ConfigError'>) -> None:
221def raise_config_error(
222    msg: str,
223    location: t.Optional[Path] = None,
224    error_type: t.Type[ConfigError] = ConfigError,
225) -> None:
226    if location:
227        raise error_type(f"{msg} at '{location}'", location)
228    raise error_type(msg, location=location)
def raise_for_status(response: requests.models.Response) -> None:
231def raise_for_status(response: Response) -> None:
232    if response.status_code == 404:
233        raise NotFoundError(response.text)
234    if 400 <= response.status_code < 500:
235        raise ApiClientError(response.text, response.status_code)
236    if 500 <= response.status_code < 600:
237        raise ApiServerError(response.text, response.status_code)
def format_destructive_change_msg( snapshot_name: str, alter_expressions: List[sqlmesh.core.schema_diff.TableAlterOperation], dialect: str, error: bool = True) -> str:
296def format_destructive_change_msg(
297    snapshot_name: str,
298    alter_expressions: t.List[TableAlterOperation],
299    dialect: str,
300    error: bool = True,
301) -> str:
302    return _format_schema_change_msg(
303        snapshot_name=snapshot_name,
304        is_destructive=True,
305        alter_operations=alter_expressions,
306        dialect=dialect,
307        error=error,
308    )
def format_additive_change_msg( snapshot_name: str, alter_operations: List[sqlmesh.core.schema_diff.TableAlterOperation], dialect: str, error: bool = True) -> str:
311def format_additive_change_msg(
312    snapshot_name: str,
313    alter_operations: t.List[TableAlterOperation],
314    dialect: str,
315    error: bool = True,
316) -> str:
317    return _format_schema_change_msg(
318        snapshot_name=snapshot_name,
319        is_destructive=False,
320        alter_operations=alter_operations,
321        dialect=dialect,
322        error=error,
323    )