diff --git a/.changes/unreleased/Features-20231107-135635.yaml b/.changes/unreleased/Features-20231107-135635.yaml new file mode 100644 index 00000000000..711ba4ce102 --- /dev/null +++ b/.changes/unreleased/Features-20231107-135635.yaml @@ -0,0 +1,6 @@ +kind: Features +body: Remove legacy logger +time: 2023-11-07T13:56:35.186648-08:00 +custom: + Author: colin-rogers-dbt + Issue: "8027" diff --git a/.changes/unreleased/Under the Hood-20231107-135728.yaml b/.changes/unreleased/Under the Hood-20231107-135728.yaml new file mode 100644 index 00000000000..025c871519a --- /dev/null +++ b/.changes/unreleased/Under the Hood-20231107-135728.yaml @@ -0,0 +1,6 @@ +kind: Under the Hood +body: Remove use of dbt/core exceptions in dbt/adapter +time: 2023-11-07T13:57:28.683727-08:00 +custom: + Author: colin-rogers-dbt MichelleArk + Issue: "8920" diff --git a/core/dbt/adapters/base/column.py b/core/dbt/adapters/base/column.py index 50a687c2f5d..7d08780c4f4 100644 --- a/core/dbt/adapters/base/column.py +++ b/core/dbt/adapters/base/column.py @@ -2,7 +2,7 @@ import re from typing import Dict, ClassVar, Any, Optional -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError @dataclass diff --git a/core/dbt/adapters/base/connections.py b/core/dbt/adapters/base/connections.py index d720cbe0d88..2bce213d905 100644 --- a/core/dbt/adapters/base/connections.py +++ b/core/dbt/adapters/base/connections.py @@ -24,7 +24,8 @@ import agate -import dbt.exceptions +import dbt.adapters.exceptions +import dbt.common.exceptions.base from dbt.adapters.contracts.connection import ( Connection, Identifier, @@ -91,13 +92,15 @@ def get_thread_connection(self) -> Connection: key = self.get_thread_identifier() with self.lock: if key not in self.thread_connections: - raise dbt.exceptions.InvalidConnectionError(key, list(self.thread_connections)) + raise dbt.adapters.exceptions.InvalidConnectionError( + key, list(self.thread_connections) + ) return self.thread_connections[key] def set_thread_connection(self, conn: Connection) -> None: key = self.get_thread_identifier() if key in self.thread_connections: - raise dbt.exceptions.DbtInternalError( + raise dbt.common.exceptions.DbtInternalError( "In set_thread_connection, existing connection exists for {}" ) self.thread_connections[key] = conn @@ -137,7 +140,7 @@ def exception_handler(self, sql: str) -> ContextManager: :return: A context manager that handles exceptions raised by the underlying database. """ - raise dbt.exceptions.NotImplementedError( + raise dbt.common.exceptions.base.NotImplementedError( "`exception_handler` is not implemented for this adapter!" ) @@ -220,14 +223,14 @@ def retry_connection( :param int _attempts: Parameter used to keep track of the number of attempts in calling the connect function across recursive calls. Passed as an argument to retry_timeout if it is a Callable. This parameter should not be set by the initial caller. - :raises dbt.exceptions.FailedToConnectError: Upon exhausting all retry attempts without + :raises dbt.adapters.exceptions.FailedToConnectError: Upon exhausting all retry attempts without successfully acquiring a handle. :return: The given connection with its appropriate state and handle attributes set depending on whether we successfully acquired a handle or not. """ timeout = retry_timeout(_attempts) if callable(retry_timeout) else retry_timeout if timeout < 0: - raise dbt.exceptions.FailedToConnectError( + raise dbt.adapters.exceptions.FailedToConnectError( "retry_timeout cannot be negative or return a negative time." ) @@ -235,7 +238,7 @@ def retry_connection( # This guard is not perfect others may add to the recursion limit (e.g. built-ins). connection.handle = None connection.state = ConnectionState.FAIL - raise dbt.exceptions.FailedToConnectError("retry_limit cannot be negative") + raise dbt.adapters.exceptions.FailedToConnectError("retry_limit cannot be negative") try: connection.handle = connect() @@ -246,7 +249,7 @@ def retry_connection( if retry_limit <= 0: connection.handle = None connection.state = ConnectionState.FAIL - raise dbt.exceptions.FailedToConnectError(str(e)) + raise dbt.adapters.exceptions.FailedToConnectError(str(e)) logger.debug( f"Got a retryable error when attempting to open a {cls.TYPE} connection.\n" @@ -268,12 +271,12 @@ def retry_connection( except Exception as e: connection.handle = None connection.state = ConnectionState.FAIL - raise dbt.exceptions.FailedToConnectError(str(e)) + raise dbt.adapters.exceptions.FailedToConnectError(str(e)) @abc.abstractmethod def cancel_open(self) -> Optional[List[str]]: """Cancel all open connections on the adapter. (passable)""" - raise dbt.exceptions.NotImplementedError( + raise dbt.common.exceptions.base.NotImplementedError( "`cancel_open` is not implemented for this adapter!" ) @@ -288,7 +291,9 @@ def open(cls, connection: Connection) -> Connection: This should be thread-safe, or hold the lock if necessary. The given connection should not be in either in_use or available. """ - raise dbt.exceptions.NotImplementedError("`open` is not implemented for this adapter!") + raise dbt.common.exceptions.base.NotImplementedError( + "`open` is not implemented for this adapter!" + ) def release(self) -> None: with self.lock: @@ -320,12 +325,16 @@ def cleanup_all(self) -> None: @abc.abstractmethod def begin(self) -> None: """Begin a transaction. (passable)""" - raise dbt.exceptions.NotImplementedError("`begin` is not implemented for this adapter!") + raise dbt.common.exceptions.base.NotImplementedError( + "`begin` is not implemented for this adapter!" + ) @abc.abstractmethod def commit(self) -> None: """Commit a transaction. (passable)""" - raise dbt.exceptions.NotImplementedError("`commit` is not implemented for this adapter!") + raise dbt.common.exceptions.base.NotImplementedError( + "`commit` is not implemented for this adapter!" + ) @classmethod def _rollback_handle(cls, connection: Connection) -> None: @@ -361,7 +370,7 @@ def _close_handle(cls, connection: Connection) -> None: def _rollback(cls, connection: Connection) -> None: """Roll back the given connection.""" if connection.transaction_open is False: - raise dbt.exceptions.DbtInternalError( + raise dbt.common.exceptions.DbtInternalError( f"Tried to rollback transaction on connection " f'"{connection.name}", but it does not have one open!' ) @@ -412,7 +421,9 @@ def execute( :return: A tuple of the query status and results (empty if fetch=False). :rtype: Tuple[AdapterResponse, agate.Table] """ - raise dbt.exceptions.NotImplementedError("`execute` is not implemented for this adapter!") + raise dbt.common.exceptions.base.NotImplementedError( + "`execute` is not implemented for this adapter!" + ) def add_select_query(self, sql: str) -> Tuple[Connection, Any]: """ @@ -422,7 +433,7 @@ def add_select_query(self, sql: str) -> Tuple[Connection, Any]: See https://github.com/dbt-labs/dbt-core/issues/8396 for more information. """ - raise dbt.exceptions.NotImplementedError( + raise dbt.common.exceptions.base.NotImplementedError( "`add_select_query` is not implemented for this adapter!" ) @@ -430,6 +441,6 @@ def add_select_query(self, sql: str) -> Tuple[Connection, Any]: def data_type_code_to_name(cls, type_code: Union[int, str]) -> str: """Get the string representation of the data type from the type_code.""" # https://peps.python.org/pep-0249/#type-objects - raise dbt.exceptions.NotImplementedError( + raise dbt.common.exceptions.base.NotImplementedError( "`data_type_code_to_name` is not implemented for this adapter!" ) diff --git a/core/dbt/adapters/base/impl.py b/core/dbt/adapters/base/impl.py index e9befd599a0..9bc978dbd5c 100644 --- a/core/dbt/adapters/base/impl.py +++ b/core/dbt/adapters/base/impl.py @@ -28,22 +28,25 @@ import agate import pytz -from dbt.exceptions import ( +from dbt.adapters.exceptions import ( + SnapshotTargetIncompleteError, + SnapshotTargetNotSnapshotTableError, + NullRelationDropAttemptedError, + NullRelationCacheAttemptedError, + RelationReturnedMultipleResultsError, + UnexpectedNonTimestampError, + RenameToNoneAttemptedError, + QuoteConfigTypeError, +) + +from dbt.common.exceptions import ( + NotImplementedError, DbtInternalError, DbtRuntimeError, DbtValidationError, + UnexpectedNullError, MacroArgTypeError, MacroResultError, - NotImplementedError, - NullRelationCacheAttemptedError, - NullRelationDropAttemptedError, - QuoteConfigTypeError, - RelationReturnedMultipleResultsError, - RenameToNoneAttemptedError, - SnapshotTargetIncompleteError, - SnapshotTargetNotSnapshotTableError, - UnexpectedNonTimestampError, - UnexpectedNullError, ) from dbt.adapters.protocol import AdapterConfig diff --git a/core/dbt/adapters/base/query_headers.py b/core/dbt/adapters/base/query_headers.py index 9e3aa738b2a..1dc2b143dde 100644 --- a/core/dbt/adapters/base/query_headers.py +++ b/core/dbt/adapters/base/query_headers.py @@ -7,7 +7,7 @@ from dbt.adapters.contracts.connection import AdapterRequiredConfig, QueryComment from dbt.contracts.graph.nodes import ResultNode from dbt.contracts.graph.manifest import Manifest -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError class NodeWrapper: diff --git a/core/dbt/adapters/base/relation.py b/core/dbt/adapters/base/relation.py index abe403a178c..040d5e94442 100644 --- a/core/dbt/adapters/base/relation.py +++ b/core/dbt/adapters/base/relation.py @@ -11,16 +11,13 @@ Policy, Path, ) -from dbt.exceptions import ( - ApproximateMatchError, - DbtInternalError, - MultipleDatabasesNotAllowedError, -) +from dbt.common.exceptions import DbtInternalError +from dbt.adapters.exceptions import MultipleDatabasesNotAllowedError, ApproximateMatchError from dbt.node_types import NodeType from dbt.common.utils import filter_null_values, deep_merge from dbt.adapters.utils import classproperty -import dbt.exceptions +import dbt.common.exceptions Self = TypeVar("Self", bound="BaseRelation") @@ -101,7 +98,7 @@ def matches( if not search: # nothing was passed in - raise dbt.exceptions.DbtRuntimeError( + raise dbt.common.exceptions.DbtRuntimeError( "Tried to match relation, but no search path was passed!" ) @@ -387,7 +384,7 @@ class InformationSchema(BaseRelation): def __post_init__(self): if not isinstance(self.information_schema_view, (type(None), str)): - raise dbt.exceptions.CompilationError( + raise dbt.common.exceptions.CompilationError( "Got an invalid name: {}".format(self.information_schema_view) ) diff --git a/core/dbt/adapters/cache.py b/core/dbt/adapters/cache.py index 750a75cb7ad..69e5e4903d6 100644 --- a/core/dbt/adapters/cache.py +++ b/core/dbt/adapters/cache.py @@ -7,12 +7,12 @@ _make_ref_key_dict, _ReferenceKey, ) -from dbt.exceptions import ( - DependentLinkNotCachedError, +from dbt.common.exceptions.cache import ( NewNameAlreadyInCacheError, - NoneRelationFoundError, ReferencedLinkNotCachedError, + DependentLinkNotCachedError, TruncatedModelNameCausedCollisionError, + NoneRelationFoundError, ) from dbt.common.events.functions import fire_event, fire_event_if from dbt.common.events.types import CacheAction, CacheDumpGraph diff --git a/core/dbt/adapters/exceptions/__init__.py b/core/dbt/adapters/exceptions/__init__.py new file mode 100644 index 00000000000..9b36beb21b3 --- /dev/null +++ b/core/dbt/adapters/exceptions/__init__.py @@ -0,0 +1,4 @@ +from dbt.adapters.exceptions.compilation import * # noqa +from dbt.adapters.exceptions.alias import * # noqa +from dbt.adapters.exceptions.database import * # noqa +from dbt.adapters.exceptions.connection import * # noqa diff --git a/core/dbt/adapters/exceptions.py b/core/dbt/adapters/exceptions/alias.py similarity index 93% rename from core/dbt/adapters/exceptions.py rename to core/dbt/adapters/exceptions/alias.py index 3b4433f6798..68a677088d2 100644 --- a/core/dbt/adapters/exceptions.py +++ b/core/dbt/adapters/exceptions/alias.py @@ -1,6 +1,6 @@ from typing import Mapping, Any -from dbt.exceptions import DbtValidationError +from dbt.common.exceptions import DbtValidationError class AliasError(DbtValidationError): diff --git a/core/dbt/adapters/exceptions/compilation.py b/core/dbt/adapters/exceptions/compilation.py new file mode 100644 index 00000000000..c87e74b05e7 --- /dev/null +++ b/core/dbt/adapters/exceptions/compilation.py @@ -0,0 +1,255 @@ +from typing import List, Mapping, Any + +from dbt.common.exceptions import CompilationError, DbtDatabaseError +from dbt.common.ui import line_wrap_message + + +class MissingConfigError(CompilationError): + def __init__(self, unique_id: str, name: str): + self.unique_id = unique_id + self.name = name + msg = ( + f"Model '{self.unique_id}' does not define a required config parameter '{self.name}'." + ) + super().__init__(msg=msg) + + +class MultipleDatabasesNotAllowedError(CompilationError): + def __init__(self, databases): + self.databases = databases + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = str(self.databases) + return msg + + +class ApproximateMatchError(CompilationError): + def __init__(self, target, relation): + self.target = target + self.relation = relation + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = ( + "When searching for a relation, dbt found an approximate match. " + "Instead of guessing \nwhich relation to use, dbt will move on. " + f"Please delete {self.relation}, or rename it to be less ambiguous." + f"\nSearched for: {self.target}\nFound: {self.relation}" + ) + + return msg + + +class SnapshotTargetIncompleteError(CompilationError): + def __init__(self, extra: List, missing: List): + self.extra = extra + self.missing = missing + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = ( + 'Snapshot target has ("{}") but not ("{}") - is it an ' + "unmigrated previous version archive?".format( + '", "'.join(self.extra), '", "'.join(self.missing) + ) + ) + return msg + + +class DuplicateMacroInPackageError(CompilationError): + def __init__(self, macro, macro_mapping: Mapping): + self.macro = macro + self.macro_mapping = macro_mapping + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + other_path = self.macro_mapping[self.macro.unique_id].original_file_path + # subtract 2 for the "Compilation Error" indent + # note that the line wrap eats newlines, so if you want newlines, + # this is the result :( + msg = line_wrap_message( + f"""\ + dbt found two macros named "{self.macro.name}" in the project + "{self.macro.package_name}". + + + To fix this error, rename or remove one of the following + macros: + + - {self.macro.original_file_path} + + - {other_path} + """, + subtract=2, + ) + return msg + + +class DuplicateMaterializationNameError(CompilationError): + def __init__(self, macro, other_macro): + self.macro = macro + self.other_macro = other_macro + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + macro_name = self.macro.name + macro_package_name = self.macro.package_name + other_package_name = self.other_macro.macro.package_name + + msg = ( + f"Found two materializations with the name {macro_name} (packages " + f"{macro_package_name} and {other_package_name}). dbt cannot resolve " + "this ambiguity" + ) + return msg + + +class ColumnTypeMissingError(CompilationError): + def __init__(self, column_names: List): + self.column_names = column_names + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = ( + "Contracted models require data_type to be defined for each column. " + "Please ensure that the column name and data_type are defined within " + f"the YAML configuration for the {self.column_names} column(s)." + ) + return msg + + +class MacroNotFoundError(CompilationError): + def __init__(self, node, target_macro_id: str): + self.node = node + self.target_macro_id = target_macro_id + msg = f"'{self.node.unique_id}' references macro '{self.target_macro_id}' which is not defined!" + + super().__init__(msg=msg) + + +class MissingMaterializationError(CompilationError): + def __init__(self, materialization, adapter_type): + self.materialization = materialization + self.adapter_type = adapter_type + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + valid_types = "'default'" + + if self.adapter_type != "default": + valid_types = f"'default' and '{self.adapter_type}'" + + msg = f"No materialization '{self.materialization}' was found for adapter {self.adapter_type}! (searched types {valid_types})" + return msg + + +class SnapshotTargetNotSnapshotTableError(CompilationError): + def __init__(self, missing: List): + self.missing = missing + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = 'Snapshot target is not a snapshot table (missing "{}")'.format( + '", "'.join(self.missing) + ) + return msg + + +class NullRelationDropAttemptedError(CompilationError): + def __init__(self, name: str): + self.name = name + self.msg = f"Attempted to drop a null relation for {self.name}" + super().__init__(msg=self.msg) + + +class NullRelationCacheAttemptedError(CompilationError): + def __init__(self, name: str): + self.name = name + self.msg = f"Attempted to cache a null relation for {self.name}" + super().__init__(msg=self.msg) + + +class RelationTypeNullError(CompilationError): + def __init__(self, relation): + self.relation = relation + self.msg = f"Tried to drop relation {self.relation}, but its type is null." + super().__init__(msg=self.msg) + + +class MaterializationNotAvailableError(CompilationError): + def __init__(self, materialization, adapter_type: str): + self.materialization = materialization + self.adapter_type = adapter_type + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = f"Materialization '{self.materialization}' is not available for {self.adapter_type}!" + return msg + + +class RelationReturnedMultipleResultsError(CompilationError): + def __init__(self, kwargs: Mapping[str, Any], matches: List): + self.kwargs = kwargs + self.matches = matches + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = ( + "get_relation returned more than one relation with the given args. " + "Please specify a database or schema to narrow down the result set." + f"\n{self.kwargs}\n\n{self.matches}" + ) + return msg + + +class UnexpectedNonTimestampError(DbtDatabaseError): + def __init__(self, field_name: str, source, dt: Any): + self.field_name = field_name + self.source = source + self.type_name = type(dt).__name__ + msg = ( + f"Expected a timestamp value when querying field '{self.field_name}' of table " + f"{self.source} but received value of type '{self.type_name}' instead" + ) + super().__init__(msg) + + +class RenameToNoneAttemptedError(CompilationError): + def __init__(self, src_name: str, dst_name: str, name: str): + self.src_name = src_name + self.dst_name = dst_name + self.name = name + self.msg = f"Attempted to rename {self.src_name} to {self.dst_name} for {self.name}" + super().__init__(msg=self.msg) + + +class QuoteConfigTypeError(CompilationError): + def __init__(self, quote_config: Any): + self.quote_config = quote_config + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = ( + 'The seed configuration value of "quote_columns" has an ' + f"invalid type {type(self.quote_config)}" + ) + return msg + + +class RelationWrongTypeError(CompilationError): + def __init__(self, relation, expected_type, model=None): + self.relation = relation + self.expected_type = expected_type + self.model = model + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = ( + f"Trying to create {self.expected_type} {self.relation}, " + f"but it currently exists as a {self.relation.type}. Either " + f"drop {self.relation} manually, or run dbt with " + "`--full-refresh` and dbt will drop it for you." + ) + + return msg diff --git a/core/dbt/adapters/exceptions/connection.py b/core/dbt/adapters/exceptions/connection.py new file mode 100644 index 00000000000..aac55166407 --- /dev/null +++ b/core/dbt/adapters/exceptions/connection.py @@ -0,0 +1,16 @@ +from typing import List + +from dbt.common.exceptions import DbtRuntimeError, DbtDatabaseError + + +class InvalidConnectionError(DbtRuntimeError): + def __init__(self, thread_id, known: List) -> None: + self.thread_id = thread_id + self.known = known + super().__init__( + msg=f"connection never acquired for thread {self.thread_id}, have {self.known}" + ) + + +class FailedToConnectError(DbtDatabaseError): + pass diff --git a/core/dbt/adapters/exceptions/database.py b/core/dbt/adapters/exceptions/database.py new file mode 100644 index 00000000000..ff177289a03 --- /dev/null +++ b/core/dbt/adapters/exceptions/database.py @@ -0,0 +1,51 @@ +from typing import Any + +from dbt.common.exceptions import NotImplementedError, CompilationError + + +class UnexpectedDbReferenceError(NotImplementedError): + def __init__(self, adapter, database, expected): + self.adapter = adapter + self.database = database + self.expected = expected + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = f"Cross-db references not allowed in {self.adapter} ({self.database} vs {self.expected})" + return msg + + +class CrossDbReferenceProhibitedError(CompilationError): + def __init__(self, adapter, exc_msg: str): + self.adapter = adapter + self.exc_msg = exc_msg + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = f"Cross-db references not allowed in adapter {self.adapter}: Got {self.exc_msg}" + return msg + + +class IndexConfigNotDictError(CompilationError): + def __init__(self, raw_index: Any): + self.raw_index = raw_index + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = ( + f"Invalid index config:\n" + f" Got: {self.raw_index}\n" + f' Expected a dictionary with at minimum a "columns" key' + ) + return msg + + +class IndexConfigError(CompilationError): + def __init__(self, exc: TypeError): + self.exc = exc + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + validator_msg = self.validator_error_message(self.exc) + msg = f"Could not parse index config: {validator_msg}" + return msg diff --git a/core/dbt/adapters/factory.py b/core/dbt/adapters/factory.py index d24d46d143a..81f5f7b8653 100644 --- a/core/dbt/adapters/factory.py +++ b/core/dbt/adapters/factory.py @@ -10,7 +10,7 @@ from dbt.adapters.contracts.connection import AdapterRequiredConfig, Credentials from dbt.common.events.functions import fire_event from dbt.common.events.types import AdapterImportError, PluginLoadError, AdapterRegistered -from dbt.exceptions import DbtInternalError, DbtRuntimeError +from dbt.common.exceptions import DbtInternalError, DbtRuntimeError from dbt.include.global_project import PACKAGE_PATH as GLOBAL_PROJECT_PATH from dbt.include.global_project import PROJECT_NAME as GLOBAL_PROJECT_NAME from dbt.semver import VersionSpecifier diff --git a/core/dbt/adapters/relation_configs/config_validation.py b/core/dbt/adapters/relation_configs/config_validation.py index 17bf74bf3e7..ef7b18bb7bb 100644 --- a/core/dbt/adapters/relation_configs/config_validation.py +++ b/core/dbt/adapters/relation_configs/config_validation.py @@ -1,7 +1,7 @@ from dataclasses import dataclass from typing import Set, Optional -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError @dataclass(frozen=True, eq=True, unsafe_hash=True) diff --git a/core/dbt/adapters/sql/connections.py b/core/dbt/adapters/sql/connections.py index 5cd93d8e054..6a0c558e92c 100644 --- a/core/dbt/adapters/sql/connections.py +++ b/core/dbt/adapters/sql/connections.py @@ -5,7 +5,7 @@ import agate import dbt.common.clients.agate_helper -import dbt.exceptions +import dbt.common.exceptions from dbt.adapters.base import BaseConnectionManager from dbt.adapters.contracts.connection import Connection, ConnectionState, AdapterResponse from dbt.common.events.functions import fire_event @@ -27,7 +27,9 @@ class SQLConnectionManager(BaseConnectionManager): @abc.abstractmethod def cancel(self, connection: Connection): """Cancel the given connection.""" - raise dbt.exceptions.NotImplementedError("`cancel` is not implemented for this adapter!") + raise dbt.common.exceptions.base.NotImplementedError( + "`cancel` is not implemented for this adapter!" + ) def cancel_open(self) -> List[str]: names = [] @@ -93,7 +95,7 @@ def add_query( @abc.abstractmethod def get_response(cls, cursor: Any) -> AdapterResponse: """Get the status of the cursor.""" - raise dbt.exceptions.NotImplementedError( + raise dbt.common.exceptions.base.NotImplementedError( "`get_response` is not implemented for this adapter!" ) @@ -156,7 +158,7 @@ def add_select_query(self, sql: str) -> Tuple[Connection, Any]: def begin(self): connection = self.get_thread_connection() if connection.transaction_open is True: - raise dbt.exceptions.DbtInternalError( + raise dbt.common.exceptions.DbtInternalError( 'Tried to begin a new transaction on connection "{}", but ' "it already had one open!".format(connection.name) ) @@ -169,7 +171,7 @@ def begin(self): def commit(self): connection = self.get_thread_connection() if connection.transaction_open is False: - raise dbt.exceptions.DbtInternalError( + raise dbt.common.exceptions.DbtInternalError( 'Tried to commit transaction on connection "{}", but ' "it does not have one open!".format(connection.name) ) diff --git a/core/dbt/adapters/sql/impl.py b/core/dbt/adapters/sql/impl.py index e43e3f41d02..13de87619e3 100644 --- a/core/dbt/adapters/sql/impl.py +++ b/core/dbt/adapters/sql/impl.py @@ -2,7 +2,7 @@ from typing import Any, Optional, Tuple, Type, List from dbt.adapters.contracts.connection import Connection, AdapterResponse -from dbt.exceptions import RelationTypeNullError +from dbt.adapters.exceptions import RelationTypeNullError from dbt.adapters.base import BaseAdapter, available from dbt.adapters.cache import _make_ref_key_dict from dbt.adapters.sql import SQLConnectionManager diff --git a/core/dbt/cli/flags.py b/core/dbt/cli/flags.py index dd30cb6eca9..1a03de0de96 100644 --- a/core/dbt/cli/flags.py +++ b/core/dbt/cli/flags.py @@ -10,11 +10,13 @@ from dbt.cli.exceptions import DbtUsageException from dbt.cli.resolvers import default_log_path, default_project_dir from dbt.cli.types import Command as CliCommand +from dbt.common import ui +from dbt.common.events import functions from dbt.config.profile import read_user_config from dbt.contracts.project import UserConfig -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.deprecations import renamed_env_var -from dbt.helper_types import WarnErrorOptions +from dbt.common.helper_types import WarnErrorOptions if os.name != "nt": # https://bugs.python.org/issue41567 @@ -253,6 +255,8 @@ def _assign_params( for param in params: object.__setattr__(self, param.lower(), getattr(self, param)) + self.set_common_global_flags() + def __str__(self) -> str: return str(pf(self.__dict__)) @@ -293,6 +297,19 @@ def from_dict(cls, command: CliCommand, args_dict: Dict[str, Any]) -> "Flags": flags.fire_deprecations() return flags + def set_common_global_flags(self): + # Set globals for common.ui + if getattr(self, "PRINTER_WIDTH", None) is not None: + ui.PRINTER_WIDTH = getattr(self, "PRINTER_WIDTH") + if getattr(self, "USE_COLORS", None) is not None: + ui.USE_COLOR = getattr(self, "USE_COLORS") + + # Set globals for common.events.functions + # + functions.WARN_ERROR = getattr(self, "WARN_ERROR", False) + if getattr(self, "WARN_ERROR_OPTIONS", None) is not None: + functions.WARN_ERROR_OPTIONS = getattr(self, "WARN_ERROR_OPTIONS") + CommandParams = List[str] diff --git a/core/dbt/cli/option_types.py b/core/dbt/cli/option_types.py index fbb8ceb76c9..f56740161be 100644 --- a/core/dbt/cli/option_types.py +++ b/core/dbt/cli/option_types.py @@ -1,9 +1,10 @@ from click import ParamType, Choice from dbt.config.utils import parse_cli_yaml_string -from dbt.exceptions import ValidationError, DbtValidationError, OptionNotYamlDictError +from dbt.exceptions import ValidationError, OptionNotYamlDictError +from dbt.common.exceptions import DbtValidationError -from dbt.helper_types import WarnErrorOptions +from dbt.common.helper_types import WarnErrorOptions class YAML(ParamType): diff --git a/core/dbt/cli/requires.py b/core/dbt/cli/requires.py index 37de7b7307a..24a041c2c68 100644 --- a/core/dbt/cli/requires.py +++ b/core/dbt/cli/requires.py @@ -26,7 +26,8 @@ ) from dbt.common.events.helpers import get_json_string_utcnow from dbt.common.events.types import MainEncounteredError, MainStackTrace -from dbt.exceptions import Exception as DbtException, DbtProjectError, FailFastError +from dbt.common.exceptions import DbtBaseException as DbtException +from dbt.exceptions import DbtProjectError, FailFastError from dbt.parser.manifest import ManifestLoader, write_manifest from dbt.profiler import profiler from dbt.tracking import active_user, initialize_from_flags, track_run diff --git a/core/dbt/cli/types.py b/core/dbt/cli/types.py index 14028a69451..f43314c873f 100644 --- a/core/dbt/cli/types.py +++ b/core/dbt/cli/types.py @@ -1,7 +1,7 @@ from enum import Enum from typing import List -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError class Command(Enum): diff --git a/core/dbt/clients/jinja.py b/core/dbt/clients/jinja.py index 6942acb515a..fb372956970 100644 --- a/core/dbt/clients/jinja.py +++ b/core/dbt/clients/jinja.py @@ -27,18 +27,16 @@ from dbt.contracts.graph.nodes import GenericTestNode from dbt.exceptions import ( - CaughtMacroError, CaughtMacroErrorWithNodeError, CompilationError, DbtInternalError, MaterializationArgError, JinjaRenderingError, - MacroReturn, MaterializtionMacroNotUsedError, NoSupportedLanguagesFoundError, UndefinedCompilationError, - UndefinedMacroError, ) +from dbt.common.exceptions.macros import MacroReturn, UndefinedMacroError, CaughtMacroError from dbt.flags import get_flags from dbt.node_types import ModelLanguage diff --git a/core/dbt/clients/jinja_static.py b/core/dbt/clients/jinja_static.py index 8184c43622e..8094ee55703 100644 --- a/core/dbt/clients/jinja_static.py +++ b/core/dbt/clients/jinja_static.py @@ -1,6 +1,7 @@ import jinja2 from dbt.clients.jinja import get_environment -from dbt.exceptions import MacroNamespaceNotStringError, MacroNameNotStringError +from dbt.exceptions import MacroNamespaceNotStringError +from dbt.common.exceptions.macros import MacroNameNotStringError def statically_extract_macro_calls(string, ctx, db_wrapper=None): diff --git a/core/dbt/clients/system.py b/core/dbt/clients/system.py index a2d972cff99..5d74ab95637 100644 --- a/core/dbt/clients/system.py +++ b/core/dbt/clients/system.py @@ -13,7 +13,7 @@ from pathlib import Path from typing import Any, Callable, Dict, List, NoReturn, Optional, Tuple, Type, Union -import dbt.exceptions +import dbt.common.exceptions import requests from dbt.common.events.functions import fire_event from dbt.common.events.types import ( @@ -23,7 +23,7 @@ SystemStdErr, SystemReportReturnCode, ) -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.utils import _connection_exception_retry as connection_exception_retry from pathspec import PathSpec # type: ignore @@ -385,7 +385,7 @@ def _handle_posix_error(exc: OSError, cwd: str, cmd: List[str]) -> NoReturn: def _handle_windows_error(exc: OSError, cwd: str, cmd: List[str]) -> NoReturn: - cls: Type[dbt.exceptions.Exception] = dbt.exceptions.CommandError + cls: Type[dbt.common.exceptions.DbtBaseException] = dbt.exceptions.CommandError if exc.errno == errno.ENOENT: message = ( "Could not find command, ensure it is in the user's PATH " diff --git a/core/dbt/clients/yaml_helper.py b/core/dbt/clients/yaml_helper.py index d5a29b0309f..bfdf0ff189b 100644 --- a/core/dbt/clients/yaml_helper.py +++ b/core/dbt/clients/yaml_helper.py @@ -1,3 +1,4 @@ +import dbt.common.exceptions.base import dbt.exceptions from typing import Any, Dict, Optional import yaml @@ -60,4 +61,4 @@ def load_yaml_text(contents, path=None): else: error = str(e) - raise dbt.exceptions.DbtValidationError(error) + raise dbt.common.exceptions.base.DbtValidationError(error) diff --git a/core/dbt/common/clients/agate_helper.py b/core/dbt/common/clients/agate_helper.py index c10d723c4f8..d7ac0916cdb 100644 --- a/core/dbt/common/clients/agate_helper.py +++ b/core/dbt/common/clients/agate_helper.py @@ -4,10 +4,10 @@ import datetime import isodate import json -import dbt.utils from typing import Iterable, List, Dict, Union, Optional, Any -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError +from dbt.common.utils import ForgivingJSONEncoder BOM = BOM_UTF8.decode("utf-8") # '\ufeff' @@ -124,7 +124,7 @@ def table_from_data_flat(data, column_names: Iterable[str]) -> agate.Table: value = _row[col_name] if isinstance(value, (dict, list, tuple)): # Represent container types as json strings - value = json.dumps(value, cls=dbt.utils.JSONEncoder) + value = json.dumps(value, cls=ForgivingJSONEncoder) text_only_columns.add(col_name) elif isinstance(value, str): text_only_columns.add(col_name) diff --git a/core/dbt/common/events/format.py b/core/dbt/common/events/format.py index 4b173b18cca..90f7607dc52 100644 --- a/core/dbt/common/events/format.py +++ b/core/dbt/common/events/format.py @@ -1,8 +1,10 @@ -from dbt import ui -from dbt.node_types import NodeType +from dbt.common import ui + from typing import Optional, Union from datetime import datetime +from dbt.common.events.interfaces import LoggableDbtObject + def format_fancy_output_line( msg: str, @@ -35,16 +37,14 @@ def format_fancy_output_line( return output -def _pluralize(string: Union[str, NodeType]) -> str: - try: - convert = NodeType(string) - except ValueError: - return f"{string}s" +def _pluralize(string: Union[str, LoggableDbtObject]) -> str: + if isinstance(string, LoggableDbtObject): + return string.pluralize() else: - return convert.pluralize() + return f"{string}s" -def pluralize(count, string: Union[str, NodeType]) -> str: +def pluralize(count, string: Union[str, LoggableDbtObject]) -> str: pluralized: str = str(string) if count != 1: pluralized = _pluralize(string) diff --git a/core/dbt/common/events/functions.py b/core/dbt/common/events/functions.py index d6a70128d22..d8e2fd17f0a 100644 --- a/core/dbt/common/events/functions.py +++ b/core/dbt/common/events/functions.py @@ -1,30 +1,37 @@ -from dbt.constants import METADATA_ENV_PREFIX +from pathlib import Path + +from dbt.common.helper_types import WarnErrorOptions +from dbt.common.utils import ForgivingJSONEncoder from dbt.common.events.base_types import BaseEvent, EventLevel, EventMsg from dbt.common.events.eventmgr import EventManager, IEventManager -from dbt.common.events.logger import LoggerConfig, NoFilter, LineFormat -from dbt.exceptions import scrub_secrets, env_secrets +from dbt.common.events.logger import LoggerConfig, LineFormat +from dbt.common.exceptions import scrub_secrets, env_secrets from dbt.common.events.types import Note -from dbt.flags import get_flags, ENABLE_LEGACY_LOGGER -from dbt.logger import GLOBAL_LOGGER, make_log_dir_if_missing from functools import partial import json import os import sys -from typing import Callable, Dict, List, Optional, TextIO +from typing import Callable, Dict, List, Optional, TextIO, Union import uuid from google.protobuf.json_format import MessageToDict -import dbt.utils - LOG_VERSION = 3 metadata_vars: Optional[Dict[str, str]] = None - +_METADATA_ENV_PREFIX = "DBT_ENV_CUSTOM_ENV_" # These are the logging events issued by the "clean" command, # where we can't count on having a log directory. We've removed # the "class" flags on the events in types.py. If necessary we # could still use class or method flags, but we'd have to get # the type class from the msg and then get the information from the class. nofile_codes = ["Z012", "Z013", "Z014", "Z015"] +WARN_ERROR_OPTIONS = WarnErrorOptions(include=[], exclude=[]) +WARN_ERROR = False + + +def make_log_dir_if_missing(log_path: Union[Path, str]) -> None: + if isinstance(log_path, str): + log_path = Path(log_path) + log_path.mkdir(parents=True, exist_ok=True) def setup_event_logger(flags, callbacks: List[Callable[[EventMsg], None]] = []) -> None: @@ -32,50 +39,43 @@ def setup_event_logger(flags, callbacks: List[Callable[[EventMsg], None]] = []) make_log_dir_if_missing(flags.LOG_PATH) EVENT_MANAGER.callbacks = callbacks.copy() - if ENABLE_LEGACY_LOGGER: - EVENT_MANAGER.add_logger( - _get_logbook_log_config( - flags.DEBUG, flags.USE_COLORS, flags.LOG_CACHE_EVENTS, flags.QUIET - ) + if flags.LOG_LEVEL != "none": + line_format = _line_format_from_str(flags.LOG_FORMAT, LineFormat.PlainText) + log_level = ( + EventLevel.ERROR + if flags.QUIET + else EventLevel.DEBUG + if flags.DEBUG + else EventLevel(flags.LOG_LEVEL) ) - else: - if flags.LOG_LEVEL != "none": - line_format = _line_format_from_str(flags.LOG_FORMAT, LineFormat.PlainText) - log_level = ( - EventLevel.ERROR - if flags.QUIET - else EventLevel.DEBUG - if flags.DEBUG - else EventLevel(flags.LOG_LEVEL) - ) - console_config = _get_stdout_config( - line_format, - flags.USE_COLORS, - log_level, - flags.LOG_CACHE_EVENTS, - ) + console_config = _get_stdout_config( + line_format, + flags.USE_COLORS, + log_level, + flags.LOG_CACHE_EVENTS, + ) + EVENT_MANAGER.add_logger(console_config) + + if _CAPTURE_STREAM: + # Create second stdout logger to support test which want to know what's + # being sent to stdout. + console_config.output_stream = _CAPTURE_STREAM EVENT_MANAGER.add_logger(console_config) - if _CAPTURE_STREAM: - # Create second stdout logger to support test which want to know what's - # being sent to stdout. - console_config.output_stream = _CAPTURE_STREAM - EVENT_MANAGER.add_logger(console_config) - - if flags.LOG_LEVEL_FILE != "none": - # create and add the file logger to the event manager - log_file = os.path.join(flags.LOG_PATH, "dbt.log") - log_file_format = _line_format_from_str(flags.LOG_FORMAT_FILE, LineFormat.DebugText) - log_level_file = EventLevel.DEBUG if flags.DEBUG else EventLevel(flags.LOG_LEVEL_FILE) - EVENT_MANAGER.add_logger( - _get_logfile_config( - log_file, - flags.USE_COLORS_FILE, - log_file_format, - log_level_file, - flags.LOG_FILE_MAX_BYTES, - ) + if flags.LOG_LEVEL_FILE != "none": + # create and add the file logger to the event manager + log_file = os.path.join(flags.LOG_PATH, "dbt.log") + log_file_format = _line_format_from_str(flags.LOG_FORMAT_FILE, LineFormat.DebugText) + log_level_file = EventLevel.DEBUG if flags.DEBUG else EventLevel(flags.LOG_LEVEL_FILE) + EVENT_MANAGER.add_logger( + _get_logfile_config( + log_file, + flags.USE_COLORS_FILE, + log_file_format, + log_level_file, + flags.LOG_FILE_MAX_BYTES, ) + ) def _line_format_from_str(format_str: str, default: LineFormat) -> LineFormat: @@ -125,15 +125,15 @@ def _get_logfile_config( line_format: LineFormat, level: EventLevel, log_file_max_bytes: int, + log_cache_events: bool = False, ) -> LoggerConfig: - return LoggerConfig( name="file_log", line_format=line_format, use_colors=use_colors, level=level, # File log is *always* debug level scrubber=env_scrubber, - filter=partial(_logfile_filter, bool(get_flags().LOG_CACHE_EVENTS), line_format), + filter=partial(_logfile_filter, log_cache_events, line_format), invocation_id=EVENT_MANAGER.invocation_id, output_file_name=log_path, output_file_max_bytes=log_file_max_bytes, @@ -146,26 +146,6 @@ def _logfile_filter(log_cache_events: bool, line_format: LineFormat, msg: EventM ) -def _get_logbook_log_config( - debug: bool, use_colors: bool, log_cache_events: bool, quiet: bool -) -> LoggerConfig: - config = _get_stdout_config( - LineFormat.PlainText, - use_colors, - EventLevel.ERROR if quiet else EventLevel.DEBUG if debug else EventLevel.INFO, - log_cache_events, - ) - config.name = "logbook_log" - config.filter = ( - NoFilter - if log_cache_events - else lambda e: e.info.name not in ["CacheAction", "CacheDumpGraph"] - ) - config.logger = GLOBAL_LOGGER - config.output_stream = None - return config - - def env_scrubber(msg: str) -> str: return scrub_secrets(msg, env_secrets()) @@ -182,11 +162,7 @@ def cleanup_event_logger() -> None: # currently fire before logs can be configured by setup_event_logger(), we # create a default configuration with default settings and no file output. EVENT_MANAGER: IEventManager = EventManager() -EVENT_MANAGER.add_logger( - _get_logbook_log_config(False, True, False, False) # type: ignore - if ENABLE_LEGACY_LOGGER - else _get_stdout_config(LineFormat.PlainText, True, EventLevel.INFO, False) -) +EVENT_MANAGER.add_logger(_get_stdout_config(LineFormat.PlainText, True, EventLevel.INFO, False)) # This global, and the following two functions for capturing stdout logs are # an unpleasant hack we intend to remove as part of API-ification. The GitHub @@ -209,7 +185,7 @@ def stop_capture_stdout_logs() -> None: # the message may contain secrets which must be scrubbed at the usage site. def msg_to_json(msg: EventMsg) -> str: msg_dict = msg_to_dict(msg) - raw_log_line = json.dumps(msg_dict, sort_keys=True, cls=dbt.utils.ForgivingJSONEncoder) + raw_log_line = json.dumps(msg_dict, sort_keys=True, cls=ForgivingJSONEncoder) return raw_log_line @@ -235,11 +211,10 @@ def msg_to_dict(msg: EventMsg) -> dict: def warn_or_error(event, node=None) -> None: - flags = get_flags() - if flags.WARN_ERROR or flags.WARN_ERROR_OPTIONS.includes(type(event).__name__): + if WARN_ERROR or WARN_ERROR_OPTIONS.includes(type(event).__name__): # TODO: resolve this circular import when at top - from dbt.exceptions import EventCompilationError + from dbt.common.exceptions import EventCompilationError raise EventCompilationError(event.message(), node) else: @@ -274,9 +249,9 @@ def get_metadata_vars() -> Dict[str, str]: global metadata_vars if not metadata_vars: metadata_vars = { - k[len(METADATA_ENV_PREFIX) :]: v + k[len(_METADATA_ENV_PREFIX) :]: v for k, v in os.environ.items() - if k.startswith(METADATA_ENV_PREFIX) + if k.startswith(_METADATA_ENV_PREFIX) } return metadata_vars diff --git a/core/dbt/common/events/interfaces.py b/core/dbt/common/events/interfaces.py new file mode 100644 index 00000000000..13c7df9d8a9 --- /dev/null +++ b/core/dbt/common/events/interfaces.py @@ -0,0 +1,7 @@ +from typing import Protocol, runtime_checkable + + +@runtime_checkable +class LoggableDbtObject(Protocol): + def pluralize(self) -> str: + ... diff --git a/core/dbt/common/events/logger.py b/core/dbt/common/events/logger.py index fa15c78dcd0..679e711e322 100644 --- a/core/dbt/common/events/logger.py +++ b/core/dbt/common/events/logger.py @@ -9,9 +9,9 @@ from colorama import Style -import dbt.utils from dbt.common.events.base_types import EventLevel, EventMsg from dbt.common.events.format import timestamp_to_datetime_string +from dbt.common.utils import ForgivingJSONEncoder # A Filter is a function which takes a BaseEvent and returns True if the event # should be logged, False otherwise. @@ -175,6 +175,6 @@ def create_line(self, msg: EventMsg) -> str: from dbt.common.events.functions import msg_to_dict msg_dict = msg_to_dict(msg) - raw_log_line = json.dumps(msg_dict, sort_keys=True, cls=dbt.utils.ForgivingJSONEncoder) + raw_log_line = json.dumps(msg_dict, sort_keys=True, cls=ForgivingJSONEncoder) line = self.scrubber(raw_log_line) # type: ignore return line diff --git a/core/dbt/common/events/types.py b/core/dbt/common/events/types.py index e9db8b3c457..161c3a7ff60 100644 --- a/core/dbt/common/events/types.py +++ b/core/dbt/common/events/types.py @@ -14,8 +14,9 @@ pluralize, timestamp_to_datetime_string, ) -from dbt.node_types import NodeType -from dbt.ui import line_wrap_message, warning_tag, red, green, yellow + +# from dbt.node_types import NodeType +from dbt.common.ui import line_wrap_message, warning_tag, red, green, yellow # The classes in this file represent the data necessary to describe a @@ -1594,7 +1595,9 @@ def code(self) -> str: def message(self) -> str: if self.status == "error": info = "ERROR" - status = red(info) + status = red( + info, + ) elif self.status == "pass": info = "PASS" status = green(info) @@ -1867,7 +1870,8 @@ def code(self) -> str: return "Q034" def message(self) -> str: - if self.resource_type in NodeType.refable(): + # ToDo: move to core or figure out NodeType + if self.resource_type in ["model", "seed", "snapshot"]: msg = f"SKIP relation {self.schema}.{self.node_name}" else: msg = f"SKIP {self.resource_type} {self.node_name}" diff --git a/core/dbt/common/exceptions.py b/core/dbt/common/exceptions.py deleted file mode 100644 index fb36eb75aaf..00000000000 --- a/core/dbt/common/exceptions.py +++ /dev/null @@ -1,58 +0,0 @@ -from typing import List -import os - -from dbt.common.constants import SECRET_ENV_PREFIX - - -def env_secrets() -> List[str]: - return [v for k, v in os.environ.items() if k.startswith(SECRET_ENV_PREFIX) and v.strip()] - - -def scrub_secrets(msg: str, secrets: List[str]) -> str: - scrubbed = str(msg) - - for secret in secrets: - scrubbed = scrubbed.replace(secret, "*****") - - return scrubbed - - -class DbtInternalError(Exception): - def __init__(self, msg: str): - self.stack: List = [] - self.msg = scrub_secrets(msg, env_secrets()) - - @property - def type(self): - return "Internal" - - def process_stack(self): - lines = [] - stack = self.stack - first = True - - if len(stack) > 1: - lines.append("") - - for item in stack: - msg = "called by" - - if first: - msg = "in" - first = False - - lines.append(f"> {msg}") - - return lines - - def __str__(self): - if hasattr(self.msg, "split"): - split_msg = self.msg.split("\n") - else: - split_msg = str(self.msg).split("\n") - - lines = ["{}".format(self.type + " Error")] + split_msg - - lines += self.process_stack() - - return lines[0] + "\n" + "\n".join([" " + line for line in lines[1:]]) diff --git a/core/dbt/common/exceptions/__init__.py b/core/dbt/common/exceptions/__init__.py new file mode 100644 index 00000000000..208ba24dbf7 --- /dev/null +++ b/core/dbt/common/exceptions/__init__.py @@ -0,0 +1,3 @@ +from dbt.common.exceptions.base import * # noqa +from dbt.common.exceptions.events import * # noqa +from dbt.common.exceptions.macros import * # noqa diff --git a/core/dbt/common/exceptions/base.py b/core/dbt/common/exceptions/base.py new file mode 100644 index 00000000000..8c07b8512e6 --- /dev/null +++ b/core/dbt/common/exceptions/base.py @@ -0,0 +1,261 @@ +import builtins +from typing import List, Any, Optional +import os + +from dbt.common.constants import SECRET_ENV_PREFIX +from dbt.common.dataclass_schema import ValidationError + + +def env_secrets() -> List[str]: + return [v for k, v in os.environ.items() if k.startswith(SECRET_ENV_PREFIX) and v.strip()] + + +def scrub_secrets(msg: str, secrets: List[str]) -> str: + scrubbed = str(msg) + + for secret in secrets: + scrubbed = scrubbed.replace(secret, "*****") + + return scrubbed + + +class DbtBaseException(Exception): + CODE = -32000 + MESSAGE = "Server Error" + + def data(self): + # if overriding, make sure the result is json-serializable. + return { + "type": self.__class__.__name__, + "message": str(self), + } + + +class DbtInternalError(DbtBaseException): + def __init__(self, msg: str): + self.stack: List = [] + self.msg = scrub_secrets(msg, env_secrets()) + + @property + def type(self): + return "Internal" + + def process_stack(self): + lines = [] + stack = self.stack + first = True + + if len(stack) > 1: + lines.append("") + + for item in stack: + msg = "called by" + + if first: + msg = "in" + first = False + + lines.append(f"> {msg}") + + return lines + + def __str__(self): + if hasattr(self.msg, "split"): + split_msg = self.msg.split("\n") + else: + split_msg = str(self.msg).split("\n") + + lines = ["{}".format(self.type + " Error")] + split_msg + + lines += self.process_stack() + + return lines[0] + "\n" + "\n".join([" " + line for line in lines[1:]]) + + +class DbtRuntimeError(RuntimeError, DbtBaseException): + CODE = 10001 + MESSAGE = "Runtime error" + + def __init__(self, msg: str, node=None) -> None: + self.stack: List = [] + self.node = node + self.msg = scrub_secrets(msg, env_secrets()) + + def add_node(self, node=None): + if node is not None and node is not self.node: + if self.node is not None: + self.stack.append(self.node) + self.node = node + + @property + def type(self): + return "Runtime" + + def node_to_string(self, node: Any): + """ + Given a node-like object we attempt to create the best identifier we can + """ + result = "" + if hasattr(node, "resource_type"): + result += node.resource_type + if hasattr(node, "name"): + result += f" {node.name}" + if hasattr(node, "original_file_path"): + result += f" ({node.original_file_path})" + + return result.strip() if result != "" else "" + + def process_stack(self): + lines = [] + stack = self.stack + [self.node] + first = True + + if len(stack) > 1: + lines.append("") + + for item in stack: + msg = "called by" + + if first: + msg = "in" + first = False + + lines.append(f"> {msg} {self.node_to_string(item)}") + + return lines + + def validator_error_message(self, exc: builtins.Exception): + """Given a dbt.dataclass_schema.ValidationError (which is basically a + jsonschema.ValidationError), return the relevant parts as a string + """ + if not isinstance(exc, ValidationError): + return str(exc) + path = "[%s]" % "][".join(map(repr, exc.relative_path)) + return f"at path {path}: {exc.message}" + + def __str__(self, prefix: str = "! "): + node_string = "" + + if self.node is not None: + node_string = f" in {self.node_to_string(self.node)}" + + if hasattr(self.msg, "split"): + split_msg = self.msg.split("\n") + else: + split_msg = str(self.msg).split("\n") + + lines = ["{}{}".format(self.type + " Error", node_string)] + split_msg + + lines += self.process_stack() + + return lines[0] + "\n" + "\n".join([" " + line for line in lines[1:]]) + + def data(self): + result = DbtBaseException.data(self) + if self.node is None: + return result + + result.update( + { + "raw_code": self.node.raw_code, + # the node isn't always compiled, but if it is, include that! + "compiled_code": getattr(self.node, "compiled_code", None), + } + ) + return result + + +class CompilationError(DbtRuntimeError): + CODE = 10004 + MESSAGE = "Compilation Error" + + @property + def type(self): + return "Compilation" + + def _fix_dupe_msg(self, path_1: str, path_2: str, name: str, type_name: str) -> str: + if path_1 == path_2: + return ( + f"remove one of the {type_name} entries for {name} in this file:\n - {path_1!s}\n" + ) + else: + return ( + f"remove the {type_name} entry for {name} in one of these files:\n" + f" - {path_1!s}\n{path_2!s}" + ) + + +class RecursionError(DbtRuntimeError): + pass + + +class DbtConfigError(DbtRuntimeError): + CODE = 10007 + MESSAGE = "DBT Configuration Error" + + # ToDo: Can we remove project? + def __init__(self, msg: str, project=None, result_type="invalid_project", path=None) -> None: + self.project = project + super().__init__(msg) + self.result_type = result_type + self.path = path + + def __str__(self, prefix="! ") -> str: + msg = super().__str__(prefix) + if self.path is None: + return msg + else: + return f"{msg}\n\nError encountered in {self.path}" + + +class NotImplementedError(DbtBaseException): + def __init__(self, msg: str) -> None: + self.msg = msg + self.formatted_msg = f"ERROR: {self.msg}" + super().__init__(self.formatted_msg) + + +class SemverError(Exception): + def __init__(self, msg: Optional[str] = None) -> None: + self.msg = msg + if msg is not None: + super().__init__(msg) + else: + super().__init__() + + +class VersionsNotCompatibleError(SemverError): + pass + + +class DbtValidationError(DbtRuntimeError): + CODE = 10005 + MESSAGE = "Validation Error" + + +class DbtDatabaseError(DbtRuntimeError): + CODE = 10003 + MESSAGE = "Database Error" + + def process_stack(self): + lines = [] + + if hasattr(self.node, "build_path") and self.node.build_path: + lines.append(f"compiled Code at {self.node.build_path}") + + return lines + DbtRuntimeError.process_stack(self) + + @property + def type(self): + return "Database" + + +class UnexpectedNullError(DbtDatabaseError): + def __init__(self, field_name: str, source): + self.field_name = field_name + self.source = source + msg = ( + f"Expected a non-null value when querying field '{self.field_name}' of table " + f" {self.source} but received value 'null' instead" + ) + super().__init__(msg) diff --git a/core/dbt/common/exceptions/cache.py b/core/dbt/common/exceptions/cache.py new file mode 100644 index 00000000000..d557be07d6c --- /dev/null +++ b/core/dbt/common/exceptions/cache.py @@ -0,0 +1,68 @@ +import re +from typing import Dict + +from dbt.common.exceptions import DbtInternalError + + +class CacheInconsistencyError(DbtInternalError): + def __init__(self, msg: str): + self.msg = msg + formatted_msg = f"Cache inconsistency detected: {self.msg}" + super().__init__(msg=formatted_msg) + + +class NewNameAlreadyInCacheError(CacheInconsistencyError): + def __init__(self, old_key: str, new_key: str): + self.old_key = old_key + self.new_key = new_key + msg = ( + f'in rename of "{self.old_key}" -> "{self.new_key}", new name is in the cache already' + ) + super().__init__(msg) + + +class ReferencedLinkNotCachedError(CacheInconsistencyError): + def __init__(self, referenced_key: str): + self.referenced_key = referenced_key + msg = f"in add_link, referenced link key {self.referenced_key} not in cache!" + super().__init__(msg) + + +class DependentLinkNotCachedError(CacheInconsistencyError): + def __init__(self, dependent_key: str): + self.dependent_key = dependent_key + msg = f"in add_link, dependent link key {self.dependent_key} not in cache!" + super().__init__(msg) + + +class TruncatedModelNameCausedCollisionError(CacheInconsistencyError): + def __init__(self, new_key, relations: Dict): + self.new_key = new_key + self.relations = relations + super().__init__(self.get_message()) + + def get_message(self) -> str: + # Tell user when collision caused by model names truncated during + # materialization. + match = re.search("__dbt_backup|__dbt_tmp$", self.new_key.identifier) + if match: + truncated_model_name_prefix = self.new_key.identifier[: match.start()] + message_addendum = ( + "\n\nName collisions can occur when the length of two " + "models' names approach your database's builtin limit. " + "Try restructuring your project such that no two models " + f"share the prefix '{truncated_model_name_prefix}'. " + "Then, clean your warehouse of any removed models." + ) + else: + message_addendum = "" + + msg = f"in rename, new key {self.new_key} already in cache: {list(self.relations.keys())}{message_addendum}" + + return msg + + +class NoneRelationFoundError(CacheInconsistencyError): + def __init__(self): + msg = "in get_relations, a None relation was found in the cache!" + super().__init__(msg) diff --git a/core/dbt/common/exceptions/events.py b/core/dbt/common/exceptions/events.py new file mode 100644 index 00000000000..a7fd09c3534 --- /dev/null +++ b/core/dbt/common/exceptions/events.py @@ -0,0 +1,9 @@ +from dbt.common.exceptions import CompilationError, scrub_secrets, env_secrets + + +# event level exception +class EventCompilationError(CompilationError): + def __init__(self, msg: str, node) -> None: + self.msg = scrub_secrets(msg, env_secrets()) + self.node = node + super().__init__(msg=self.msg) diff --git a/core/dbt/common/exceptions/macros.py b/core/dbt/common/exceptions/macros.py new file mode 100644 index 00000000000..7fae116267f --- /dev/null +++ b/core/dbt/common/exceptions/macros.py @@ -0,0 +1,80 @@ +from typing import Any + +from dbt.common.exceptions import CompilationError, DbtBaseException + + +class MacroReturn(DbtBaseException): + """ + Hack of all hacks + This is not actually an exception. + It's how we return a value from a macro. + """ + + def __init__(self, value) -> None: + self.value = value + + +class UndefinedMacroError(CompilationError): + def __str__(self, prefix: str = "! ") -> str: + msg = super().__str__(prefix) + return ( + f"{msg}. This can happen when calling a macro that does " + "not exist. Check for typos and/or install package dependencies " + 'with "dbt deps".' + ) + + +class CaughtMacroError(CompilationError): + def __init__(self, exc) -> None: + self.exc = exc + super().__init__(msg=str(exc)) + + +class MacroNameNotStringError(CompilationError): + def __init__(self, kwarg_value) -> None: + self.kwarg_value = kwarg_value + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = ( + f"The macro_name parameter ({self.kwarg_value}) " + "to adapter.dispatch was not a string" + ) + return msg + + +class MacrosSourcesUnWriteableError(CompilationError): + def __init__(self, node) -> None: + self.node = node + msg = 'cannot "write" macros or sources' + super().__init__(msg=msg) + + +class MacroArgTypeError(CompilationError): + def __init__(self, method_name: str, arg_name: str, got_value: Any, expected_type) -> None: + self.method_name = method_name + self.arg_name = arg_name + self.got_value = got_value + self.expected_type = expected_type + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + got_type = type(self.got_value) + msg = ( + f"'adapter.{self.method_name}' expects argument " + f"'{self.arg_name}' to be of type '{self.expected_type}', instead got " + f"{self.got_value} ({got_type})" + ) + return msg + + +class MacroResultError(CompilationError): + def __init__(self, freshness_macro_name: str, table): + self.freshness_macro_name = freshness_macro_name + self.table = table + super().__init__(msg=self.get_message()) + + def get_message(self) -> str: + msg = f'Got an invalid result from "{self.freshness_macro_name}" macro: {[tuple(r) for r in self.table]}' + + return msg diff --git a/core/dbt/helper_types.py b/core/dbt/common/helper_types.py similarity index 100% rename from core/dbt/helper_types.py rename to core/dbt/common/helper_types.py diff --git a/core/dbt/ui.py b/core/dbt/common/ui.py similarity index 90% rename from core/dbt/ui.py rename to core/dbt/common/ui.py index ef9089c857a..d7665797eba 100644 --- a/core/dbt/ui.py +++ b/core/dbt/common/ui.py @@ -3,8 +3,6 @@ import colorama -from dbt.flags import get_flags - COLORS: Dict[str, str] = { "red": colorama.Fore.RED, "green": colorama.Fore.GREEN, @@ -19,18 +17,19 @@ COLOR_RESET_ALL = COLORS["reset_all"] +USE_COLOR = True +PRINTER_WIDTH = 80 + + def color(text: str, color_code: str) -> str: - if get_flags().USE_COLORS: + if USE_COLOR: return "{}{}{}".format(color_code, text, COLOR_RESET_ALL) else: return text def printer_width() -> int: - flags = get_flags() - if flags.PRINTER_WIDTH: - return flags.PRINTER_WIDTH - return 80 + return PRINTER_WIDTH def green(text: str) -> str: diff --git a/core/dbt/common/utils/__init__.py b/core/dbt/common/utils/__init__.py index 04bdad47f6b..43fb47d88dc 100644 --- a/core/dbt/common/utils/__init__.py +++ b/core/dbt/common/utils/__init__.py @@ -1,7 +1,4 @@ -from dbt.common.utils.encoding import ( - md5, - JSONEncoder, -) +from dbt.common.utils.encoding import md5, JSONEncoder, ForgivingJSONEncoder from dbt.common.utils.casting import ( cast_to_str, diff --git a/core/dbt/common/utils/dict.py b/core/dbt/common/utils/dict.py index fc51efc319c..f994fb3e552 100644 --- a/core/dbt/common/utils/dict.py +++ b/core/dbt/common/utils/dict.py @@ -2,7 +2,7 @@ import datetime from typing import Dict, Optional, TypeVar, Callable, Any, Tuple, Union, Type -from dbt.exceptions import DbtConfigError, RecursionError +from dbt.common.exceptions import DbtConfigError, RecursionError K_T = TypeVar("K_T") V_T = TypeVar("V_T") @@ -40,7 +40,7 @@ def _merge(a, b): # http://stackoverflow.com/questions/20656135/python-deep-merge-dictionary-data def deep_merge(*args): """ - >>> dbt.utils.deep_merge({'a': 1, 'b': 2, 'c': 3}, {'a': 2}, {'a': 3, 'b': 1}) # noqa + >>> dbt.common.utils.deep_merge({'a': 1, 'b': 2, 'c': 3}, {'a': 2}, {'a': 3, 'b': 1}) # noqa {'a': 3, 'b': 1, 'c': 3} """ if len(args) == 0: diff --git a/core/dbt/config/profile.py b/core/dbt/config/profile.py index d96b0d80063..47ab790638b 100644 --- a/core/dbt/config/profile.py +++ b/core/dbt/config/profile.py @@ -13,10 +13,10 @@ CompilationError, DbtProfileError, DbtProjectError, - DbtValidationError, DbtRuntimeError, ProfileConfigError, ) +from dbt.common.exceptions import DbtValidationError from dbt.common.events.types import MissingProfileTarget from dbt.common.events.functions import fire_event from dbt.utils import coerce_dict_str diff --git a/core/dbt/config/project.py b/core/dbt/config/project.py index 36c6329415a..8593c5c6b6e 100644 --- a/core/dbt/config/project.py +++ b/core/dbt/config/project.py @@ -26,13 +26,13 @@ from dbt.adapters.contracts.connection import QueryComment from dbt.exceptions import ( DbtProjectError, - SemverError, ProjectContractBrokenError, ProjectContractError, DbtRuntimeError, ) +from dbt.common.exceptions import SemverError from dbt.graph import SelectionSpec -from dbt.helper_types import NoValue +from dbt.common.helper_types import NoValue from dbt.semver import VersionSpecifier, versions_compatible from dbt.version import get_installed_version from dbt.utils import MultiDict, md5 diff --git a/core/dbt/config/renderer.py b/core/dbt/config/renderer.py index 59dfb9d4901..8fc72197a71 100644 --- a/core/dbt/config/renderer.py +++ b/core/dbt/config/renderer.py @@ -9,7 +9,8 @@ from dbt.context.secret import SecretContext, SECRET_PLACEHOLDER from dbt.context.base import BaseContext from dbt.adapters.contracts.connection import HasCredentials -from dbt.exceptions import DbtProjectError, CompilationError, RecursionError +from dbt.exceptions import DbtProjectError +from dbt.common.exceptions import CompilationError, RecursionError from dbt.common.utils import deep_map_render diff --git a/core/dbt/config/runtime.py b/core/dbt/config/runtime.py index 82200e33e49..2a66f2f31b5 100644 --- a/core/dbt/config/runtime.py +++ b/core/dbt/config/runtime.py @@ -32,7 +32,7 @@ DbtRuntimeError, UninstalledPackagesFoundError, ) -from dbt.helper_types import DictDefaultEmptyStr, FQNPath, PathSet +from dbt.common.helper_types import DictDefaultEmptyStr, FQNPath, PathSet from .profile import Profile from .project import Project from .renderer import DbtProjectYamlRenderer, ProfileRenderer diff --git a/core/dbt/config/selectors.py b/core/dbt/config/selectors.py index 63316c7006a..bb1cec0591c 100644 --- a/core/dbt/config/selectors.py +++ b/core/dbt/config/selectors.py @@ -12,7 +12,8 @@ resolve_path_from_base, ) from dbt.contracts.selection import SelectorFile -from dbt.exceptions import DbtSelectorsError, DbtRuntimeError +from dbt.exceptions import DbtSelectorsError +from dbt.common.exceptions import DbtRuntimeError from dbt.graph import parse_from_selectors_definition, SelectionSpec from dbt.graph.selector_spec import SelectionCriteria diff --git a/core/dbt/config/utils.py b/core/dbt/config/utils.py index 31f18ba0477..891b463dfbf 100644 --- a/core/dbt/config/utils.py +++ b/core/dbt/config/utils.py @@ -4,7 +4,8 @@ from dbt.clients import yaml_helper from dbt.common.events.functions import fire_event from dbt.common.events.types import InvalidOptionYAML -from dbt.exceptions import DbtValidationError, OptionNotYamlDictError +from dbt.exceptions import OptionNotYamlDictError +from dbt.common.exceptions import DbtValidationError def parse_cli_vars(var_string: str) -> Dict[str, Any]: diff --git a/core/dbt/constants.py b/core/dbt/constants.py index b97b944fccf..0532f0c073d 100644 --- a/core/dbt/constants.py +++ b/core/dbt/constants.py @@ -1,7 +1,6 @@ # TODO: remove SECRET_ENV_PREFIX and import from dbt.common SECRET_ENV_PREFIX = "DBT_ENV_SECRET_" DEFAULT_ENV_PLACEHOLDER = "DBT_DEFAULT_PLACEHOLDER" -METADATA_ENV_PREFIX = "DBT_ENV_CUSTOM_ENV_" MAXIMUM_SEED_SIZE = 1 * 1024 * 1024 MAXIMUM_SEED_SIZE_NAME = "1MB" diff --git a/core/dbt/context/base.py b/core/dbt/context/base.py index 58c675c00fc..8b2818cfd2b 100644 --- a/core/dbt/context/base.py +++ b/core/dbt/context/base.py @@ -16,11 +16,11 @@ from dbt.exceptions import ( SecretEnvVarLocationError, EnvVarMissingError, - MacroReturn, RequiredVarNotFoundError, SetStrictWrongTypeError, ZipStrictWrongTypeError, ) +from dbt.common.exceptions.macros import MacroReturn from dbt.common.events.functions import fire_event, get_invocation_id from dbt.common.events.types import JinjaLogInfo, JinjaLogDebug from dbt.common.events.contextvars import get_node_info diff --git a/core/dbt/context/context_config.py b/core/dbt/context/context_config.py index 479cab44c58..591cde8847b 100644 --- a/core/dbt/context/context_config.py +++ b/core/dbt/context/context_config.py @@ -5,7 +5,7 @@ from dbt.config import RuntimeConfig, Project, IsFQNResource from dbt.contracts.graph.model_config import BaseConfig, get_config_for, _listify -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.node_types import NodeType from dbt.utils import fqn_search diff --git a/core/dbt/context/exceptions_jinja.py b/core/dbt/context/exceptions_jinja.py index 0e113c305f4..87d7977982f 100644 --- a/core/dbt/context/exceptions_jinja.py +++ b/core/dbt/context/exceptions_jinja.py @@ -4,30 +4,30 @@ from dbt.common.events.functions import warn_or_error from dbt.common.events.types import JinjaLogWarning -from dbt.exceptions import ( - DbtRuntimeError, +from dbt.common.exceptions import DbtRuntimeError, NotImplementedError, DbtDatabaseError +from dbt.adapters.exceptions import ( MissingConfigError, + ColumnTypeMissingError, MissingMaterializationError, + RelationWrongTypeError, +) +from dbt.exceptions import ( MissingRelationError, AmbiguousAliasError, AmbiguousCatalogMatchError, - CacheInconsistencyError, DataclassNotDictError, CompilationError, - DbtDatabaseError, DependencyNotFoundError, DependencyError, DuplicatePatchPathError, DuplicateResourceNameError, PropertyYMLError, - NotImplementedError, - RelationWrongTypeError, ContractError, - ColumnTypeMissingError, FailFastError, scrub_secrets, env_secrets, ) +from dbt.common.exceptions.cache import CacheInconsistencyError def warn(msg, node=None): diff --git a/core/dbt/context/providers.py b/core/dbt/context/providers.py index cc51f692b0b..e9094d4518c 100644 --- a/core/dbt/context/providers.py +++ b/core/dbt/context/providers.py @@ -43,12 +43,13 @@ from dbt.contracts.graph.metrics import MetricReference, ResolvedMetricReference from dbt.contracts.graph.unparsed import NodeVersion from dbt.common.events.functions import get_metadata_vars +from dbt.common.exceptions import DbtInternalError, DbtRuntimeError, DbtValidationError +from dbt.adapters.exceptions import MissingConfigError from dbt.exceptions import ( CompilationError, ConflictingConfigKeysError, SecretEnvVarLocationError, EnvVarMissingError, - DbtInternalError, InlineModelConfigError, NumberSourceArgsError, PersistDocsValueTypeError, @@ -56,19 +57,16 @@ LoadAgateTableValueError, MacroDispatchArgError, MacroResultAlreadyLoadedError, - MacrosSourcesUnWriteableError, MetricArgsError, - MissingConfigError, OperationsCannotRefEphemeralNodesError, PackageNotInDepsError, ParsingError, RefBadContextError, RefArgsError, - DbtRuntimeError, TargetNotFoundError, - DbtValidationError, DbtReferenceError, ) +from dbt.common.exceptions.macros import MacrosSourcesUnWriteableError from dbt.config import IsFQNResource from dbt.node_types import NodeType, ModelLanguage diff --git a/core/dbt/contracts/files.py b/core/dbt/contracts/files.py index 267b5358416..3709a330657 100644 --- a/core/dbt/contracts/files.py +++ b/core/dbt/contracts/files.py @@ -160,6 +160,10 @@ def file_id(self): return None return f"{self.project_name}://{self.path.original_file_path}" + @property + def original_file_path(self): + return self.path.original_file_path + def _serialize(self): dct = self.to_dict() return dct diff --git a/core/dbt/contracts/graph/manifest.py b/core/dbt/contracts/graph/manifest.py index 22e1eb59482..d88f8cc387b 100644 --- a/core/dbt/contracts/graph/manifest.py +++ b/core/dbt/contracts/graph/manifest.py @@ -50,11 +50,10 @@ from dbt.exceptions import ( CompilationError, DuplicateResourceNameError, - DuplicateMacroInPackageError, - DuplicateMaterializationNameError, AmbiguousResourceNameRefError, ) -from dbt.helper_types import PathSet +from dbt.adapters.exceptions import DuplicateMacroInPackageError, DuplicateMaterializationNameError +from dbt.common.helper_types import PathSet from dbt.common.events.functions import fire_event from dbt.common.events.types import MergedFromState, UnpinnedRefNewVersionAvailable from dbt.common.events.contextvars import get_node_info diff --git a/core/dbt/contracts/graph/model_config.py b/core/dbt/contracts/graph/model_config.py index b4f91d08161..327a56d1a4f 100644 --- a/core/dbt/contracts/graph/model_config.py +++ b/core/dbt/contracts/graph/model_config.py @@ -12,7 +12,7 @@ from dbt.contracts.graph.unparsed import AdditionalPropertiesAllowed, Docs from dbt.contracts.graph.utils import validate_color from dbt.contracts.util import Replaceable, list_str -from dbt.exceptions import DbtInternalError, CompilationError +from dbt.common.exceptions import DbtInternalError, CompilationError from dbt import hooks from dbt.node_types import NodeType, AccessType from mashumaro.jsonschema.annotations import Pattern diff --git a/core/dbt/contracts/graph/unparsed.py b/core/dbt/contracts/graph/unparsed.py index 6a17a75861c..e525ccee493 100644 --- a/core/dbt/contracts/graph/unparsed.py +++ b/core/dbt/contracts/graph/unparsed.py @@ -15,8 +15,9 @@ ) # trigger the PathEncoder -import dbt.helper_types # noqa:F401 -from dbt.exceptions import CompilationError, ParsingError, DbtInternalError +import dbt.common.helper_types # noqa:F401 +from dbt.exceptions import ParsingError +from dbt.common.exceptions import DbtInternalError, CompilationError from dbt.common.dataclass_schema import ( dbtClassMixin, StrEnum, @@ -148,7 +149,7 @@ class UnparsedVersion(dbtClassMixin): constraints: List[Dict[str, Any]] = field(default_factory=list) docs: Docs = field(default_factory=Docs) tests: Optional[List[TestDef]] = None - columns: Sequence[Union[dbt.helper_types.IncludeExclude, UnparsedColumn]] = field( + columns: Sequence[Union[dbt.common.helper_types.IncludeExclude, UnparsedColumn]] = field( default_factory=list ) deprecation_date: Optional[datetime.datetime] = None @@ -160,7 +161,7 @@ def __lt__(self, other): return str(self.v) < str(other.v) @property - def include_exclude(self) -> dbt.helper_types.IncludeExclude: + def include_exclude(self) -> dbt.common.helper_types.IncludeExclude: return self._include_exclude @property @@ -173,10 +174,10 @@ def formatted_v(self) -> str: def __post_init__(self): has_include_exclude = False - self._include_exclude = dbt.helper_types.IncludeExclude(include="*") + self._include_exclude = dbt.common.helper_types.IncludeExclude(include="*") self._unparsed_columns = [] for column in self.columns: - if isinstance(column, dbt.helper_types.IncludeExclude): + if isinstance(column, dbt.common.helper_types.IncludeExclude): if not has_include_exclude: self._include_exclude = column has_include_exclude = True diff --git a/core/dbt/contracts/project.py b/core/dbt/contracts/project.py index 3e3c11f0e2a..dc5da36143b 100644 --- a/core/dbt/contracts/project.py +++ b/core/dbt/contracts/project.py @@ -1,6 +1,6 @@ from dbt.contracts.util import Replaceable, Mergeable, list_str, Identifier from dbt.adapters.contracts.connection import QueryComment, UserConfigContract -from dbt.helper_types import NoValue +from dbt.common.helper_types import NoValue from dbt.common.dataclass_schema import ( dbtClassMixin, ValidationError, diff --git a/core/dbt/contracts/relation.py b/core/dbt/contracts/relation.py index 9684f2a38d6..62a62d814ee 100644 --- a/core/dbt/contracts/relation.py +++ b/core/dbt/contracts/relation.py @@ -9,7 +9,8 @@ from dbt.common.dataclass_schema import dbtClassMixin, StrEnum from dbt.contracts.util import Replaceable -from dbt.exceptions import CompilationError, DataclassNotDictError +from dbt.common.exceptions import CompilationError +from dbt.exceptions import DataclassNotDictError from dbt.common.utils import deep_merge diff --git a/core/dbt/contracts/results.py b/core/dbt/contracts/results.py index 429181e306c..c093ea1bc74 100644 --- a/core/dbt/contracts/results.py +++ b/core/dbt/contracts/results.py @@ -9,7 +9,7 @@ Replaceable, schema_version, ) -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.common.events.functions import fire_event from dbt.common.events.types import TimingInfoCollected from dbt.common.events.contextvars import get_node_info diff --git a/core/dbt/deps/registry.py b/core/dbt/deps/registry.py index 351a9985206..47d15fc3ebc 100644 --- a/core/dbt/deps/registry.py +++ b/core/dbt/deps/registry.py @@ -13,8 +13,8 @@ DependencyError, PackageNotFoundError, PackageVersionNotFoundError, - VersionsNotCompatibleError, ) +from dbt.common.exceptions import VersionsNotCompatibleError class RegistryPackageMixin: diff --git a/core/dbt/exceptions.py b/core/dbt/exceptions.py index 9e841a0fad1..00bf6fdce6b 100644 --- a/core/dbt/exceptions.py +++ b/core/dbt/exceptions.py @@ -1,210 +1,21 @@ -import builtins import json import re import io import agate from typing import Any, Dict, List, Mapping, Optional, Union +from dbt.common.exceptions import ( + DbtRuntimeError, + CompilationError, + DbtInternalError, + DbtConfigError, + env_secrets, + scrub_secrets, + DbtValidationError, +) from dbt.node_types import NodeType, AccessType -from dbt.ui import line_wrap_message -import dbt.common.dataclass_schema from dbt.common.dataclass_schema import ValidationError -from dbt.common.exceptions import env_secrets, scrub_secrets - - -class MacroReturn(builtins.BaseException): - """ - Hack of all hacks - This is not actually an exception. - It's how we return a value from a macro. - """ - - def __init__(self, value) -> None: - self.value = value - - -class Exception(builtins.Exception): - CODE = -32000 - MESSAGE = "Server Error" - - def data(self): - # if overriding, make sure the result is json-serializable. - return { - "type": self.__class__.__name__, - "message": str(self), - } - - -class DbtInternalError(Exception): - def __init__(self, msg: str) -> None: - self.stack: List = [] - self.msg = scrub_secrets(msg, env_secrets()) - - @property - def type(self): - return "Internal" - - def process_stack(self): - lines = [] - stack = self.stack - first = True - - if len(stack) > 1: - lines.append("") - - for item in stack: - msg = "called by" - - if first: - msg = "in" - first = False - - lines.append(f"> {msg}") - - return lines - - def __str__(self): - if hasattr(self.msg, "split"): - split_msg = self.msg.split("\n") - else: - split_msg = str(self.msg).split("\n") - - lines = ["{}".format(self.type + " Error")] + split_msg - - lines += self.process_stack() - - return lines[0] + "\n" + "\n".join([" " + line for line in lines[1:]]) - - -class DbtRuntimeError(RuntimeError, Exception): - CODE = 10001 - MESSAGE = "Runtime error" - - def __init__(self, msg: str, node=None) -> None: - self.stack: List = [] - self.node = node - self.msg = scrub_secrets(msg, env_secrets()) - - def add_node(self, node=None): - if node is not None and node is not self.node: - if self.node is not None: - self.stack.append(self.node) - self.node = node - - @property - def type(self): - return "Runtime" - - def node_to_string(self, node): - if node is None: - return "" - if not hasattr(node, "name"): - # we probably failed to parse a block, so we can't know the name - return f"{node.resource_type} ({node.original_file_path})" - - if hasattr(node, "contents"): - # handle FileBlocks. They aren't really nodes but we want to render - # out the path we know at least. This indicates an error during - # block parsing. - return f"{node.path.original_file_path}" - return f"{node.resource_type} {node.name} ({node.original_file_path})" - - def process_stack(self): - lines = [] - stack = self.stack + [self.node] - first = True - - if len(stack) > 1: - lines.append("") - - for item in stack: - msg = "called by" - - if first: - msg = "in" - first = False - - lines.append(f"> {msg} {self.node_to_string(item)}") - - return lines - - def validator_error_message(self, exc: builtins.Exception): - """Given a dbt.dataclass_schema.ValidationError (which is basically a - jsonschema.ValidationError), return the relevant parts as a string - """ - if not isinstance(exc, dbt.common.dataclass_schema.ValidationError): - return str(exc) - path = "[%s]" % "][".join(map(repr, exc.relative_path)) - return f"at path {path}: {exc.message}" - - def __str__(self, prefix: str = "! "): - node_string = "" - - if self.node is not None: - node_string = f" in {self.node_to_string(self.node)}" - - if hasattr(self.msg, "split"): - split_msg = self.msg.split("\n") - else: - split_msg = str(self.msg).split("\n") - - lines = ["{}{}".format(self.type + " Error", node_string)] + split_msg - - lines += self.process_stack() - - return lines[0] + "\n" + "\n".join([" " + line for line in lines[1:]]) - - def data(self): - result = Exception.data(self) - if self.node is None: - return result - - result.update( - { - "raw_code": self.node.raw_code, - # the node isn't always compiled, but if it is, include that! - "compiled_code": getattr(self.node, "compiled_code", None), - } - ) - return result - - -class DbtDatabaseError(DbtRuntimeError): - CODE = 10003 - MESSAGE = "Database Error" - - def process_stack(self): - lines = [] - - if hasattr(self.node, "build_path") and self.node.build_path: - lines.append(f"compiled Code at {self.node.build_path}") - - return lines + DbtRuntimeError.process_stack(self) - - @property - def type(self): - return "Database" - - -class CompilationError(DbtRuntimeError): - CODE = 10004 - MESSAGE = "Compilation Error" - - @property - def type(self): - return "Compilation" - - def _fix_dupe_msg(self, path_1: str, path_2: str, name: str, type_name: str) -> str: - if path_1 == path_2: - return ( - f"remove one of the {type_name} entries for {name} in this file:\n - {path_1!s}\n" - ) - else: - return ( - f"remove the {type_name} entry for {name} in one of these files:\n" - f" - {path_1!s}\n{path_2!s}" - ) class ContractBreakingChangeError(DbtRuntimeError): @@ -234,15 +45,6 @@ def message(self): ) -class RecursionError(DbtRuntimeError): - pass - - -class DbtValidationError(DbtRuntimeError): - CODE = 10005 - MESSAGE = "Validation Error" - - class ParsingError(DbtRuntimeError): CODE = 10015 MESSAGE = "Parsing Error" @@ -303,16 +105,6 @@ class JinjaRenderingError(CompilationError): pass -class UndefinedMacroError(CompilationError): - def __str__(self, prefix: str = "! ") -> str: - msg = super().__str__(prefix) - return ( - f"{msg}. This can happen when calling a macro that does " - "not exist. Check for typos and/or install package dependencies " - 'with "dbt deps".' - ) - - class AliasError(DbtValidationError): pass @@ -322,24 +114,6 @@ class DependencyError(Exception): MESSAGE = "Dependency Error" -class DbtConfigError(DbtRuntimeError): - CODE = 10007 - MESSAGE = "DBT Configuration Error" - - def __init__(self, msg: str, project=None, result_type="invalid_project", path=None) -> None: - self.project = project - super().__init__(msg) - self.result_type = result_type - self.path = path - - def __str__(self, prefix="! ") -> str: - msg = super().__str__(prefix) - if self.path is None: - return msg - else: - return f"{msg}\n\nError encountered in {self.path}" - - class FailFastError(DbtRuntimeError): CODE = 10013 MESSAGE = "FailFast Error" @@ -365,30 +139,6 @@ class DbtProfileError(DbtConfigError): pass -class SemverError(Exception): - def __init__(self, msg: Optional[str] = None) -> None: - self.msg = msg - if msg is not None: - super().__init__(msg) - else: - super().__init__() - - -class VersionsNotCompatibleError(SemverError): - pass - - -class NotImplementedError(Exception): - def __init__(self, msg: str) -> None: - self.msg = msg - self.formatted_msg = f"ERROR: {self.msg}" - super().__init__(self.formatted_msg) - - -class FailedToConnectError(DbtDatabaseError): - pass - - class CommandError(DbtRuntimeError): def __init__(self, cwd: str, cmd: List[str], msg: str = "Error running command") -> None: cmd_scrubbed = list(scrub_secrets(cmd_txt, env_secrets()) for cmd_txt in cmd) @@ -436,15 +186,6 @@ def __str__(self): return f"{self.msg} running: {self.cmd}" -class InvalidConnectionError(DbtRuntimeError): - def __init__(self, thread_id, known: List) -> None: - self.thread_id = thread_id - self.known = known - super().__init__( - msg=f"connection never acquired for thread {self.thread_id}, have {self.known}" - ) - - class InvalidSelectorError(DbtRuntimeError): def __init__(self, name: str) -> None: self.name = name @@ -464,14 +205,6 @@ class ConnectionError(Exception): pass -# event level exception -class EventCompilationError(CompilationError): - def __init__(self, msg: str, node) -> None: - self.msg = scrub_secrets(msg, env_secrets()) - self.node = node - super().__init__(msg=self.msg) - - # compilation level exceptions class GraphDependencyNotFoundError(CompilationError): def __init__(self, node, dependency: str) -> None: @@ -516,25 +249,6 @@ def __init__(self, exc, node) -> None: super().__init__(msg=str(exc)) -class CaughtMacroError(CompilationError): - def __init__(self, exc) -> None: - self.exc = exc - super().__init__(msg=str(exc)) - - -class MacroNameNotStringError(CompilationError): - def __init__(self, kwarg_value) -> None: - self.kwarg_value = kwarg_value - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = ( - f"The macro_name parameter ({self.kwarg_value}) " - "to adapter.dispatch was not a string" - ) - return msg - - class MissingControlFlowStartTagError(CompilationError): def __init__(self, tag, expected_tag: str, tag_parser) -> None: self.tag = tag @@ -760,13 +474,6 @@ def __init__(self, resource_type, node) -> None: super().__init__(msg=msg) -class MacrosSourcesUnWriteableError(CompilationError): - def __init__(self, node) -> None: - self.node = node - msg = 'cannot "write" macros or sources' - super().__init__(msg=msg) - - class PackageNotInDepsError(CompilationError): def __init__(self, package_name: str, node) -> None: self.package_name = package_name @@ -857,24 +564,6 @@ def get_message(self) -> str: return msg -class MacroArgTypeError(CompilationError): - def __init__(self, method_name: str, arg_name: str, got_value: Any, expected_type) -> None: - self.method_name = method_name - self.arg_name = arg_name - self.got_value = got_value - self.expected_type = expected_type - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - got_type = type(self.got_value) - msg = ( - f"'adapter.{self.method_name}' expects argument " - f"'{self.arg_name}' to be of type '{self.expected_type}', instead got " - f"{self.got_value} ({got_type})" - ) - return msg - - class BooleanError(CompilationError): def __init__(self, return_value: Any, macro_name: str) -> None: self.return_value = return_value @@ -972,7 +661,7 @@ def __init__( def get_message(self) -> str: target_package_string = "" if self.target_doc_package is not None: - target_package_string = f"in package '{self. target_doc_package}' " + target_package_string = f"in package '{self.target_doc_package}' " msg = f"Documentation for '{self.node.unique_id}' depends on doc '{self.target_doc_name}' {target_package_string} which was not found" return msg @@ -1282,7 +971,6 @@ def __init__(self, test_name: Any) -> None: super().__init__(msg=self.get_message()) def get_message(self) -> str: - msg = f"test name must be a str, got {type(self.test_name)} (value {self.test_name})" return msg @@ -1293,7 +981,6 @@ def __init__(self, test_args: Any) -> None: super().__init__(msg=self.get_message()) def get_message(self) -> str: - msg = f"test arguments must be a dict, got {type(self.test_args)} (value {self.test_args})" return msg @@ -1304,7 +991,6 @@ def __init__(self, test): super().__init__(msg=self.get_message()) def get_message(self) -> str: - msg = ( "test definition dictionary must have exactly one key, got" f" {self.test} instead ({len(self.test)} keys)" @@ -1437,216 +1123,6 @@ def get_message(self) -> str: return msg -# Postgres Exceptions -class UnexpectedDbReferenceError(NotImplementedError): - def __init__(self, adapter, database, expected): - self.adapter = adapter - self.database = database - self.expected = expected - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = f"Cross-db references not allowed in {self.adapter} ({self.database} vs {self.expected})" - return msg - - -class CrossDbReferenceProhibitedError(CompilationError): - def __init__(self, adapter, exc_msg: str): - self.adapter = adapter - self.exc_msg = exc_msg - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = f"Cross-db references not allowed in adapter {self.adapter}: Got {self.exc_msg}" - return msg - - -class IndexConfigNotDictError(CompilationError): - def __init__(self, raw_index: Any): - self.raw_index = raw_index - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = ( - f"Invalid index config:\n" - f" Got: {self.raw_index}\n" - f' Expected a dictionary with at minimum a "columns" key' - ) - return msg - - -class IndexConfigError(CompilationError): - def __init__(self, exc: TypeError): - self.exc = exc - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - validator_msg = self.validator_error_message(self.exc) - msg = f"Could not parse index config: {validator_msg}" - return msg - - -# adapters exceptions -class MacroResultError(CompilationError): - def __init__(self, freshness_macro_name: str, table): - self.freshness_macro_name = freshness_macro_name - self.table = table - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = f'Got an invalid result from "{self.freshness_macro_name}" macro: {[tuple(r) for r in self.table]}' - - return msg - - -class SnapshotTargetNotSnapshotTableError(CompilationError): - def __init__(self, missing: List): - self.missing = missing - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = 'Snapshot target is not a snapshot table (missing "{}")'.format( - '", "'.join(self.missing) - ) - return msg - - -class SnapshotTargetIncompleteError(CompilationError): - def __init__(self, extra: List, missing: List): - self.extra = extra - self.missing = missing - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = ( - 'Snapshot target has ("{}") but not ("{}") - is it an ' - "unmigrated previous version archive?".format( - '", "'.join(self.extra), '", "'.join(self.missing) - ) - ) - return msg - - -class RenameToNoneAttemptedError(CompilationError): - def __init__(self, src_name: str, dst_name: str, name: str): - self.src_name = src_name - self.dst_name = dst_name - self.name = name - self.msg = f"Attempted to rename {self.src_name} to {self.dst_name} for {self.name}" - super().__init__(msg=self.msg) - - -class NullRelationDropAttemptedError(CompilationError): - def __init__(self, name: str): - self.name = name - self.msg = f"Attempted to drop a null relation for {self.name}" - super().__init__(msg=self.msg) - - -class NullRelationCacheAttemptedError(CompilationError): - def __init__(self, name: str): - self.name = name - self.msg = f"Attempted to cache a null relation for {self.name}" - super().__init__(msg=self.msg) - - -class QuoteConfigTypeError(CompilationError): - def __init__(self, quote_config: Any): - self.quote_config = quote_config - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = ( - 'The seed configuration value of "quote_columns" has an ' - f"invalid type {type(self.quote_config)}" - ) - return msg - - -class MultipleDatabasesNotAllowedError(CompilationError): - def __init__(self, databases): - self.databases = databases - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = str(self.databases) - return msg - - -class RelationTypeNullError(CompilationError): - def __init__(self, relation): - self.relation = relation - self.msg = f"Tried to drop relation {self.relation}, but its type is null." - super().__init__(msg=self.msg) - - -class MaterializationNotAvailableError(CompilationError): - def __init__(self, materialization, adapter_type: str): - self.materialization = materialization - self.adapter_type = adapter_type - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = f"Materialization '{self.materialization}' is not available for {self.adapter_type}!" - return msg - - -class RelationReturnedMultipleResultsError(CompilationError): - def __init__(self, kwargs: Mapping[str, Any], matches: List): - self.kwargs = kwargs - self.matches = matches - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = ( - "get_relation returned more than one relation with the given args. " - "Please specify a database or schema to narrow down the result set." - f"\n{self.kwargs}\n\n{self.matches}" - ) - return msg - - -class ApproximateMatchError(CompilationError): - def __init__(self, target, relation): - self.target = target - self.relation = relation - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - - msg = ( - "When searching for a relation, dbt found an approximate match. " - "Instead of guessing \nwhich relation to use, dbt will move on. " - f"Please delete {self.relation}, or rename it to be less ambiguous." - f"\nSearched for: {self.target}\nFound: {self.relation}" - ) - - return msg - - -class UnexpectedNullError(DbtDatabaseError): - def __init__(self, field_name: str, source): - self.field_name = field_name - self.source = source - msg = ( - f"Expected a non-null value when querying field '{self.field_name}' of table " - f" {self.source} but received value 'null' instead" - ) - super().__init__(msg) - - -class UnexpectedNonTimestampError(DbtDatabaseError): - def __init__(self, field_name: str, source, dt: Any): - self.field_name = field_name - self.source = source - self.type_name = type(dt).__name__ - msg = ( - f"Expected a timestamp value when querying field '{self.field_name}' of table " - f"{self.source} but received value of type '{self.type_name}' instead" - ) - super().__init__(msg) - - # deps exceptions class MultipleVersionGitDepsError(DependencyError): def __init__(self, git: str, requested): @@ -1830,67 +1306,7 @@ def get_message(self) -> str: return msg -class DuplicateMacroInPackageError(CompilationError): - def __init__(self, macro, macro_mapping: Mapping): - self.macro = macro - self.macro_mapping = macro_mapping - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - other_path = self.macro_mapping[self.macro.unique_id].original_file_path - # subtract 2 for the "Compilation Error" indent - # note that the line wrap eats newlines, so if you want newlines, - # this is the result :( - msg = line_wrap_message( - f"""\ - dbt found two macros named "{self.macro.name}" in the project - "{self.macro.package_name}". - - - To fix this error, rename or remove one of the following - macros: - - - {self.macro.original_file_path} - - - {other_path} - """, - subtract=2, - ) - return msg - - -class DuplicateMaterializationNameError(CompilationError): - def __init__(self, macro, other_macro): - self.macro = macro - self.other_macro = other_macro - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - macro_name = self.macro.name - macro_package_name = self.macro.package_name - other_package_name = self.other_macro.macro.package_name - - msg = ( - f"Found two materializations with the name {macro_name} (packages " - f"{macro_package_name} and {other_package_name}). dbt cannot resolve " - "this ambiguity" - ) - return msg - - # jinja exceptions -class ColumnTypeMissingError(CompilationError): - def __init__(self, column_names: List): - self.column_names = column_names - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = ( - "Contracted models require data_type to be defined for each column. " - "Please ensure that the column name and data_type are defined within " - f"the YAML configuration for the {self.column_names} column(s)." - ) - return msg class PatchTargetNotFoundError(CompilationError): @@ -1907,42 +1323,6 @@ def get_message(self) -> str: return msg -class MacroNotFoundError(CompilationError): - def __init__(self, node, target_macro_id: str): - self.node = node - self.target_macro_id = target_macro_id - msg = f"'{self.node.unique_id}' references macro '{self.target_macro_id}' which is not defined!" - - super().__init__(msg=msg) - - -class MissingConfigError(CompilationError): - def __init__(self, unique_id: str, name: str): - self.unique_id = unique_id - self.name = name - msg = ( - f"Model '{self.unique_id}' does not define a required config parameter '{self.name}'." - ) - super().__init__(msg=msg) - - -class MissingMaterializationError(CompilationError): - def __init__(self, materialization, adapter_type): - self.materialization = materialization - self.adapter_type = adapter_type - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - - valid_types = "'default'" - - if self.adapter_type != "default": - valid_types = f"'default' and '{self.adapter_type}'" - - msg = f"No materialization '{self.materialization}' was found for adapter {self.adapter_type}! (searched types {valid_types})" - return msg - - class MissingRelationError(CompilationError): def __init__(self, relation, model=None): self.relation = relation @@ -2013,70 +1393,6 @@ def get_message(self) -> str: return msg -class CacheInconsistencyError(DbtInternalError): - def __init__(self, msg: str): - self.msg = msg - formatted_msg = f"Cache inconsistency detected: {self.msg}" - super().__init__(msg=formatted_msg) - - -class NewNameAlreadyInCacheError(CacheInconsistencyError): - def __init__(self, old_key: str, new_key: str): - self.old_key = old_key - self.new_key = new_key - msg = ( - f'in rename of "{self.old_key}" -> "{self.new_key}", new name is in the cache already' - ) - super().__init__(msg) - - -class ReferencedLinkNotCachedError(CacheInconsistencyError): - def __init__(self, referenced_key: str): - self.referenced_key = referenced_key - msg = f"in add_link, referenced link key {self.referenced_key} not in cache!" - super().__init__(msg) - - -class DependentLinkNotCachedError(CacheInconsistencyError): - def __init__(self, dependent_key: str): - self.dependent_key = dependent_key - msg = f"in add_link, dependent link key {self.dependent_key} not in cache!" - super().__init__(msg) - - -class TruncatedModelNameCausedCollisionError(CacheInconsistencyError): - def __init__(self, new_key, relations: Dict): - self.new_key = new_key - self.relations = relations - super().__init__(self.get_message()) - - def get_message(self) -> str: - # Tell user when collision caused by model names truncated during - # materialization. - match = re.search("__dbt_backup|__dbt_tmp$", self.new_key.identifier) - if match: - truncated_model_name_prefix = self.new_key.identifier[: match.start()] - message_addendum = ( - "\n\nName collisions can occur when the length of two " - "models' names approach your database's builtin limit. " - "Try restructuring your project such that no two models " - f"share the prefix '{truncated_model_name_prefix}'. " - "Then, clean your warehouse of any removed models." - ) - else: - message_addendum = "" - - msg = f"in rename, new key {self.new_key} already in cache: {list(self.relations.keys())}{message_addendum}" - - return msg - - -class NoneRelationFoundError(CacheInconsistencyError): - def __init__(self): - msg = "in get_relations, a None relation was found in the cache!" - super().__init__(msg) - - # this is part of the context and also raised in dbt.contracts.relation.py class DataclassNotDictError(CompilationError): def __init__(self, obj: Any): @@ -2219,24 +1535,6 @@ def get_message(self) -> str: return msg -class RelationWrongTypeError(CompilationError): - def __init__(self, relation, expected_type, model=None): - self.relation = relation - self.expected_type = expected_type - self.model = model - super().__init__(msg=self.get_message()) - - def get_message(self) -> str: - msg = ( - f"Trying to create {self.expected_type} {self.relation}, " - f"but it currently exists as a {self.relation.type}. Either " - f"drop {self.relation} manually, or run dbt with " - "`--full-refresh` and dbt will drop it for you." - ) - - return msg - - class ContractError(CompilationError): def __init__(self, yaml_columns, sql_columns): self.yaml_columns = yaml_columns diff --git a/core/dbt/flags.py b/core/dbt/flags.py index 5d6d0fae680..05b2ca2c261 100644 --- a/core/dbt/flags.py +++ b/core/dbt/flags.py @@ -56,6 +56,7 @@ def set_from_args(args: Namespace, user_config): args_param_value = convert_config(arg_name, args_param_value) object.__setattr__(flags, arg_name.upper(), args_param_value) object.__setattr__(flags, arg_name.lower(), args_param_value) + flags.set_common_global_flags() GLOBAL_FLAGS = flags # type: ignore diff --git a/core/dbt/graph/cli.py b/core/dbt/graph/cli.py index 5f8620f6ee6..8919e6af7b0 100644 --- a/core/dbt/graph/cli.py +++ b/core/dbt/graph/cli.py @@ -8,7 +8,7 @@ from typing import Dict, List, Optional, Tuple, Any, Union from dbt.contracts.selection import SelectorDefinition, SelectorFile -from dbt.exceptions import DbtInternalError, DbtValidationError +from dbt.common.exceptions import DbtInternalError, DbtValidationError from .selector_spec import ( SelectionUnion, diff --git a/core/dbt/graph/graph.py b/core/dbt/graph/graph.py index 94747036fd4..994aeeb7fb3 100644 --- a/core/dbt/graph/graph.py +++ b/core/dbt/graph/graph.py @@ -3,7 +3,7 @@ import networkx as nx # type: ignore from functools import partial -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError UniqueId = NewType("UniqueId", str) diff --git a/core/dbt/graph/selector_methods.py b/core/dbt/graph/selector_methods.py index 3502b0d4829..90353ad9c91 100644 --- a/core/dbt/graph/selector_methods.py +++ b/core/dbt/graph/selector_methods.py @@ -23,7 +23,7 @@ ) from dbt.contracts.graph.unparsed import UnparsedVersion from dbt.contracts.state import PreviousState -from dbt.exceptions import ( +from dbt.common.exceptions import ( DbtInternalError, DbtRuntimeError, ) diff --git a/core/dbt/graph/selector_spec.py b/core/dbt/graph/selector_spec.py index 05e85e1eb4a..86a20a99295 100644 --- a/core/dbt/graph/selector_spec.py +++ b/core/dbt/graph/selector_spec.py @@ -7,7 +7,8 @@ from typing import Set, Iterator, List, Optional, Dict, Union, Any, Iterable, Tuple from .graph import UniqueId from .selector_methods import MethodName -from dbt.exceptions import DbtRuntimeError, InvalidSelectorError +from dbt.common.exceptions import DbtRuntimeError +from dbt.exceptions import InvalidSelectorError RAW_SELECTOR_PATTERN = re.compile( diff --git a/core/dbt/logger.py b/core/dbt/logger.py index 103a05f920b..e3bbb00e5d7 100644 --- a/core/dbt/logger.py +++ b/core/dbt/logger.py @@ -1,5 +1,5 @@ import dbt.flags -import dbt.ui +import dbt.common.ui import json import logging @@ -531,6 +531,6 @@ def timestamped_line(msg: str) -> str: def print_timestamped_line(msg: str, use_color: Optional[str] = None): if use_color is not None: - msg = dbt.ui.color(msg, use_color) + msg = dbt.common.ui.color(msg, use_color) GLOBAL_LOGGER.info(timestamped_line(msg)) diff --git a/core/dbt/parser/common.py b/core/dbt/parser/common.py index 0e10d9c6cf0..8947eea1364 100644 --- a/core/dbt/parser/common.py +++ b/core/dbt/parser/common.py @@ -17,7 +17,8 @@ from dbt.parser.search import FileBlock from typing import List, Dict, Any, TypeVar, Generic, Union, Optional from dataclasses import dataclass -from dbt.exceptions import DbtInternalError, ParsingError +from dbt.common.exceptions import DbtInternalError +from dbt.exceptions import ParsingError def trimmed(inp: str) -> str: diff --git a/core/dbt/parser/generic_test_builders.py b/core/dbt/parser/generic_test_builders.py index d6ff1ad7382..2e29bf568ff 100644 --- a/core/dbt/parser/generic_test_builders.py +++ b/core/dbt/parser/generic_test_builders.py @@ -27,8 +27,8 @@ TestTypeError, TestNameNotStringError, UnexpectedTestNamePatternError, - UndefinedMacroError, ) +from dbt.common.exceptions.macros import UndefinedMacroError from dbt.parser.common import Testable from dbt.utils import md5 diff --git a/core/dbt/parser/hooks.py b/core/dbt/parser/hooks.py index d96257a0e71..08c04be1555 100644 --- a/core/dbt/parser/hooks.py +++ b/core/dbt/parser/hooks.py @@ -4,7 +4,7 @@ from dbt.context.context_config import ContextConfig from dbt.contracts.files import FilePath from dbt.contracts.graph.nodes import HookNode -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.node_types import NodeType, RunHookType from dbt.parser.base import SimpleParser from dbt.parser.search import FileBlock diff --git a/core/dbt/parser/manifest.py b/core/dbt/parser/manifest.py index 1518ba5d514..e10342b9f21 100644 --- a/core/dbt/parser/manifest.py +++ b/core/dbt/parser/manifest.py @@ -40,7 +40,7 @@ PARTIAL_PARSE_FILE_NAME, SEMANTIC_MANIFEST_FILE_NAME, ) -from dbt.helper_types import PathSet +from dbt.common.helper_types import PathSet from dbt.common.events.functions import fire_event, get_invocation_id, warn_or_error from dbt.common.events.types import ( PartialParsingErrorProcessingFile, diff --git a/core/dbt/parser/models.py b/core/dbt/parser/models.py index 60fe83f7b65..367fb68d237 100644 --- a/core/dbt/parser/models.py +++ b/core/dbt/parser/models.py @@ -25,8 +25,8 @@ ParsingError, PythonLiteralEvalError, PythonParsingError, - UndefinedMacroError, ) +from dbt.common.exceptions.macros import UndefinedMacroError dbt_function_key_words = set(["ref", "source", "config", "get"]) dbt_function_full_names = set(["dbt.ref", "dbt.source", "dbt.config", "dbt.config.get"]) diff --git a/core/dbt/parser/schema_yaml_readers.py b/core/dbt/parser/schema_yaml_readers.py index 63a5808b8c9..01504d32e05 100644 --- a/core/dbt/parser/schema_yaml_readers.py +++ b/core/dbt/parser/schema_yaml_readers.py @@ -39,7 +39,8 @@ Measure, NonAdditiveDimension, ) -from dbt.exceptions import DbtInternalError, YamlParseDictError, JSONValidationError +from dbt.common.exceptions import DbtInternalError +from dbt.exceptions import YamlParseDictError, JSONValidationError from dbt.context.providers import generate_parse_exposure, generate_parse_semantic_models from dbt.contracts.graph.model_config import MetricConfig, ExposureConfig diff --git a/core/dbt/parser/schemas.py b/core/dbt/parser/schemas.py index b5aa82bff00..391d5847674 100644 --- a/core/dbt/parser/schemas.py +++ b/core/dbt/parser/schemas.py @@ -38,12 +38,12 @@ JSONValidationError, DbtInternalError, ParsingError, - DbtValidationError, YamlLoadError, YamlParseDictError, YamlParseListError, InvalidAccessTypeError, ) +from dbt.common.exceptions import DbtValidationError from dbt.common.events.functions import warn_or_error from dbt.common.events.types import ( MacroNotFoundForPatch, diff --git a/core/dbt/parser/sources.py b/core/dbt/parser/sources.py index 531cf5d942c..a0d43d0f9ea 100644 --- a/core/dbt/parser/sources.py +++ b/core/dbt/parser/sources.py @@ -29,7 +29,7 @@ from dbt.common.events.functions import warn_or_error, fire_event from dbt.common.events.types import UnusedTables, FreshnessConfigProblem -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.node_types import NodeType from dbt.parser.common import ParserRef diff --git a/core/dbt/parser/sql.py b/core/dbt/parser/sql.py index 98e28aadc19..67dc71f731a 100644 --- a/core/dbt/parser/sql.py +++ b/core/dbt/parser/sql.py @@ -5,7 +5,7 @@ from dbt.contracts.graph.manifest import SourceFile from dbt.contracts.graph.nodes import SqlNode, Macro from dbt.contracts.graph.unparsed import UnparsedMacro -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.node_types import NodeType from dbt.parser.base import SimpleSQLParser from dbt.parser.macros import MacroParser diff --git a/core/dbt/plugins/manager.py b/core/dbt/plugins/manager.py index cc71d5ab6bf..ea3629e5860 100644 --- a/core/dbt/plugins/manager.py +++ b/core/dbt/plugins/manager.py @@ -3,7 +3,7 @@ from typing import Dict, List, Callable from dbt.contracts.graph.manifest import Manifest -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError from dbt.plugins.contracts import PluginArtifacts from dbt.plugins.manifest import PluginNodes import dbt.tracking diff --git a/core/dbt/semver.py b/core/dbt/semver.py index e216716a9f0..ba23f06975b 100644 --- a/core/dbt/semver.py +++ b/core/dbt/semver.py @@ -2,7 +2,8 @@ import re from typing import List -from dbt.exceptions import VersionsNotCompatibleError +import dbt.common.exceptions.base +from dbt.common.exceptions import VersionsNotCompatibleError import dbt.utils from dbt.common.dataclass_schema import dbtClassMixin, StrEnum @@ -96,7 +97,7 @@ def from_version_string(cls, version_string): match = _VERSION_REGEX.match(version_string) if not match: - raise dbt.exceptions.SemverError( + raise dbt.common.exceptions.base.SemverError( f'"{version_string}" is not a valid semantic version.' ) diff --git a/core/dbt/task/base.py b/core/dbt/task/base.py index fc3bd61893b..49ba909eeff 100644 --- a/core/dbt/task/base.py +++ b/core/dbt/task/base.py @@ -8,6 +8,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional, Type, Union +import dbt.common.exceptions.base import dbt.exceptions from dbt import tracking from dbt.adapters.factory import get_adapter @@ -38,11 +39,11 @@ NodeCompiling, NodeExecuting, ) -from dbt.exceptions import ( - NotImplementedError, - CompilationError, +from dbt.common.exceptions import ( DbtRuntimeError, DbtInternalError, + CompilationError, + NotImplementedError, ) from dbt.flags import get_flags from dbt.graph import Graph @@ -103,17 +104,17 @@ def from_args(cls, args, *pargs, **kwargs): fire_event(LogDbtProjectError(exc=str(exc))) tracking.track_invalid_invocation(args=args, result_type=exc.result_type) - raise dbt.exceptions.DbtRuntimeError("Could not run dbt") from exc + raise dbt.common.exceptions.DbtRuntimeError("Could not run dbt") from exc except dbt.exceptions.DbtProfileError as exc: all_profile_names = list(read_profiles(get_flags().PROFILES_DIR).keys()) fire_event(LogDbtProfileError(exc=str(exc), profiles=all_profile_names)) tracking.track_invalid_invocation(args=args, result_type=exc.result_type) - raise dbt.exceptions.DbtRuntimeError("Could not run dbt") from exc + raise dbt.common.exceptions.DbtRuntimeError("Could not run dbt") from exc return cls(args, config, *pargs, **kwargs) @abstractmethod def run(self): - raise dbt.exceptions.NotImplementedError("Not Implemented") + raise dbt.common.exceptions.base.NotImplementedError("Not Implemented") def interpret_results(self, results): return True @@ -128,7 +129,7 @@ def get_nearest_project_dir(project_dir: Optional[str]) -> Path: if project_file.is_file(): return cur_dir else: - raise dbt.exceptions.DbtRuntimeError( + raise dbt.common.exceptions.DbtRuntimeError( "fatal: Invalid --project-dir flag. Not a dbt project. " "Missing dbt_project.yml file" ) @@ -138,7 +139,7 @@ def get_nearest_project_dir(project_dir: Optional[str]) -> Path: if project_file.is_file(): return cur_dir else: - raise dbt.exceptions.DbtRuntimeError( + raise dbt.common.exceptions.DbtRuntimeError( "fatal: Not a dbt project (or any of the parent directories). " "Missing dbt_project.yml file" ) diff --git a/core/dbt/task/build.py b/core/dbt/task/build.py index d001a429f4c..83fc5262803 100644 --- a/core/dbt/task/build.py +++ b/core/dbt/task/build.py @@ -7,7 +7,7 @@ from dbt.adapters.factory import get_adapter from dbt.contracts.results import NodeStatus -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.graph import ResourceTypeSelector from dbt.node_types import NodeType from dbt.task.test import TestSelector diff --git a/core/dbt/task/clean.py b/core/dbt/task/clean.py index c54d7c5544c..c5e6d476d98 100644 --- a/core/dbt/task/clean.py +++ b/core/dbt/task/clean.py @@ -8,7 +8,7 @@ ConfirmCleanPath, FinishedCleanPaths, ) -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError from dbt.task.base import ( BaseTask, move_to_nearest_project_dir, diff --git a/core/dbt/task/clone.py b/core/dbt/task/clone.py index efa533b4af5..089bc7be265 100644 --- a/core/dbt/task/clone.py +++ b/core/dbt/task/clone.py @@ -6,7 +6,7 @@ from dbt.context.providers import generate_runtime_model_context from dbt.contracts.results import RunStatus, RunResult from dbt.common.dataclass_schema import dbtClassMixin -from dbt.exceptions import DbtInternalError, CompilationError +from dbt.common.exceptions import DbtInternalError, CompilationError from dbt.graph import ResourceTypeSelector from dbt.node_types import NodeType from dbt.parser.manifest import write_manifest diff --git a/core/dbt/task/compile.py b/core/dbt/task/compile.py index 002400a4d71..829127a9ad4 100644 --- a/core/dbt/task/compile.py +++ b/core/dbt/task/compile.py @@ -6,10 +6,10 @@ from dbt.common.events.base_types import EventLevel from dbt.common.events.functions import fire_event from dbt.common.events.types import CompiledNode, Note, ParseInlineNodeError -from dbt.exceptions import ( +from dbt.common.exceptions import ( CompilationError, DbtInternalError, - Exception as DbtException, + DbtBaseException as DbtException, ) from dbt.graph import ResourceTypeSelector diff --git a/core/dbt/task/debug.py b/core/dbt/task/debug.py index 971ebbc3e18..ba297d5fef4 100644 --- a/core/dbt/task/debug.py +++ b/core/dbt/task/debug.py @@ -17,13 +17,14 @@ ) import dbt.clients.system import dbt.exceptions +import dbt.common.exceptions from dbt.adapters.factory import get_adapter, register_adapter from dbt.config import PartialProject, Project, Profile from dbt.config.renderer import DbtProjectYamlRenderer, ProfileRenderer from dbt.contracts.results import RunStatus from dbt.clients.yaml_helper import load_yaml_text from dbt.links import ProfileConfigDocs -from dbt.ui import green, red +from dbt.common.ui import green, red from dbt.common.events.format import pluralize from dbt.version import get_installed_version @@ -81,7 +82,7 @@ def __init__(self, args, config) -> None: self.profile_path = os.path.join(self.profiles_dir, "profiles.yml") try: self.project_dir = get_nearest_project_dir(self.args.project_dir) - except dbt.exceptions.Exception: + except dbt.common.exceptions.DbtBaseException: # we probably couldn't find a project directory. Set project dir # to whatever was given, or default to the current directory. if args.project_dir: @@ -126,7 +127,7 @@ def run(self) -> bool: fire_event(DebugCmdOut(msg="Using dbt_project.yml file at {}".format(self.project_path))) if load_profile_status.run_status == RunStatus.Success: if self.profile is None: - raise dbt.exceptions.DbtInternalError( + raise dbt.common.exceptions.DbtInternalError( "Profile should not be None if loading profile completed" ) else: @@ -218,7 +219,7 @@ def _load_profile(self) -> SubtaskStatus: # https://github.com/dbt-labs/dbt-core/issues/6259 getattr(self.args, "threads", None), ) - except dbt.exceptions.DbtConfigError as exc: + except dbt.common.exceptions.DbtConfigError as exc: profile_errors.append(str(exc)) else: if len(profile_names) == 1: @@ -263,7 +264,7 @@ def _choose_profile_names(self) -> Tuple[List[str], str]: try: return [Profile.pick_profile_name(args_profile, project_profile)], "" - except dbt.exceptions.DbtConfigError: + except dbt.common.exceptions.DbtConfigError: pass # try to guess @@ -345,7 +346,7 @@ def _load_project(self) -> SubtaskStatus: renderer, verify_version=self.args.VERSION_CHECK, ) - except dbt.exceptions.DbtConfigError as exc: + except dbt.common.exceptions.DbtConfigError as exc: return SubtaskStatus( log_msg=red("ERROR invalid"), run_status=RunStatus.Error, diff --git a/core/dbt/task/freshness.py b/core/dbt/task/freshness.py index 291e918a0f9..3e71345db6c 100644 --- a/core/dbt/task/freshness.py +++ b/core/dbt/task/freshness.py @@ -15,7 +15,7 @@ SourceFreshnessResult, FreshnessStatus, ) -from dbt.exceptions import DbtRuntimeError, DbtInternalError +from dbt.common.exceptions import DbtRuntimeError, DbtInternalError from dbt.common.events.functions import fire_event from dbt.common.events.types import ( FreshnessCheckComplete, diff --git a/core/dbt/task/generate.py b/core/dbt/task/generate.py index 9e0e609263b..dd4cb837bbc 100644 --- a/core/dbt/task/generate.py +++ b/core/dbt/task/generate.py @@ -24,7 +24,8 @@ ColumnMetadata, CatalogArtifact, ) -from dbt.exceptions import DbtInternalError, AmbiguousCatalogMatchError +from dbt.common.exceptions import DbtInternalError +from dbt.exceptions import AmbiguousCatalogMatchError from dbt.graph import ResourceTypeSelector from dbt.node_types import NodeType from dbt.include.global_project import DOCS_INDEX_FILE_PATH @@ -87,7 +88,7 @@ def get_table(self, data: PrimitiveDict) -> CatalogTable: str(data["table_name"]), ) except KeyError as exc: - raise dbt.exceptions.CompilationError( + raise dbt.common.exceptions.CompilationError( "Catalog information missing required key {} (got {})".format(exc, data) ) table: CatalogTable diff --git a/core/dbt/task/init.py b/core/dbt/task/init.py index 5a41b3f122d..11fe14406b8 100644 --- a/core/dbt/task/init.py +++ b/core/dbt/task/init.py @@ -11,7 +11,7 @@ import dbt.config import dbt.clients.system from dbt.config.profile import read_profile -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError from dbt.flags import get_flags from dbt.version import _get_adapter_plugin_names from dbt.adapters.factory import load_plugin, get_include_paths @@ -300,7 +300,7 @@ def run(self): try: move_to_nearest_project_dir(self.args.project_dir) in_project = True - except dbt.exceptions.DbtRuntimeError: + except dbt.common.exceptions.DbtRuntimeError: in_project = False if in_project: diff --git a/core/dbt/task/list.py b/core/dbt/task/list.py index 29b4fd8dd4a..c395d5c3f31 100644 --- a/core/dbt/task/list.py +++ b/core/dbt/task/list.py @@ -14,7 +14,7 @@ NoNodesSelected, ListCmdOut, ) -from dbt.exceptions import DbtRuntimeError, DbtInternalError +from dbt.common.exceptions import DbtRuntimeError, DbtInternalError from dbt.common.events.contextvars import task_contextvars diff --git a/core/dbt/task/retry.py b/core/dbt/task/retry.py index af6a46b776e..a982880adaa 100644 --- a/core/dbt/task/retry.py +++ b/core/dbt/task/retry.py @@ -5,7 +5,7 @@ from dbt.config import RuntimeConfig from dbt.contracts.results import NodeStatus from dbt.contracts.state import PreviousState -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError from dbt.graph import GraphQueue from dbt.task.base import ConfiguredTask from dbt.task.build import BuildTask diff --git a/core/dbt/task/run.py b/core/dbt/task/run.py index 246a39c3b08..76c2d456a95 100644 --- a/core/dbt/task/run.py +++ b/core/dbt/task/run.py @@ -23,10 +23,10 @@ from dbt.exceptions import ( CompilationError, DbtInternalError, - MissingMaterializationError, DbtRuntimeError, - DbtValidationError, ) +from dbt.common.exceptions import DbtValidationError +from dbt.adapters.exceptions import MissingMaterializationError from dbt.common.events.functions import fire_event, get_invocation_id from dbt.common.events.types import ( DatabaseErrorRunningHook, diff --git a/core/dbt/task/run_operation.py b/core/dbt/task/run_operation.py index db2d103aa90..caa1f1c7b7e 100644 --- a/core/dbt/task/run_operation.py +++ b/core/dbt/task/run_operation.py @@ -5,7 +5,7 @@ import agate -import dbt.exceptions +import dbt.common.exceptions from dbt.adapters.factory import get_adapter from dbt.contracts.files import FileHash from dbt.contracts.graph.nodes import HookNode @@ -16,7 +16,7 @@ RunningOperationUncaughtError, LogDebugStackTrace, ) -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.node_types import NodeType from dbt.task.base import ConfiguredTask @@ -56,7 +56,7 @@ def run(self) -> RunResultsArtifact: try: self._run_unsafe(package_name, macro_name) - except dbt.exceptions.Exception as exc: + except dbt.common.exceptions.DbtBaseException as exc: fire_event(RunningOperationCaughtError(exc=str(exc))) fire_event(LogDebugStackTrace(exc_info=traceback.format_exc())) success = False diff --git a/core/dbt/task/runnable.py b/core/dbt/task/runnable.py index 288d15032d3..a2b76a35bc8 100644 --- a/core/dbt/task/runnable.py +++ b/core/dbt/task/runnable.py @@ -38,10 +38,10 @@ ) from dbt.exceptions import ( DbtInternalError, - NotImplementedError, DbtRuntimeError, FailFastError, ) +from dbt.common.exceptions import NotImplementedError from dbt.flags import get_flags from dbt.graph import GraphQueue, NodeSelector, SelectionSpec, parse_difference, UniqueId from dbt.logger import ( diff --git a/core/dbt/task/seed.py b/core/dbt/task/seed.py index 02907af7746..07619141079 100644 --- a/core/dbt/task/seed.py +++ b/core/dbt/task/seed.py @@ -6,7 +6,7 @@ ) from dbt.contracts.results import RunStatus -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.graph import ResourceTypeSelector from dbt.logger import TextOnly from dbt.common.events.functions import fire_event diff --git a/core/dbt/task/show.py b/core/dbt/task/show.py index 630c2d7235a..d6d140898a9 100644 --- a/core/dbt/task/show.py +++ b/core/dbt/task/show.py @@ -8,7 +8,7 @@ from dbt.common.events.base_types import EventLevel from dbt.common.events.functions import fire_event from dbt.common.events.types import ShowNode, Note -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError from dbt.task.compile import CompileTask, CompileRunner from dbt.task.seed import SeedRunner diff --git a/core/dbt/task/snapshot.py b/core/dbt/task/snapshot.py index 7ff5b11828b..b9f517bd662 100644 --- a/core/dbt/task/snapshot.py +++ b/core/dbt/task/snapshot.py @@ -1,6 +1,6 @@ from .run import ModelRunner, RunTask -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.common.events.functions import fire_event from dbt.common.events.base_types import EventLevel from dbt.common.events.types import LogSnapshotResult diff --git a/core/dbt/task/sql.py b/core/dbt/task/sql.py index c0f82e3e748..18c402ebac9 100644 --- a/core/dbt/task/sql.py +++ b/core/dbt/task/sql.py @@ -3,6 +3,7 @@ from typing import Generic, TypeVar import traceback +import dbt.common.exceptions.base import dbt.exceptions from dbt.contracts.sql import ( RemoteCompileResult, @@ -25,7 +26,7 @@ def __init__(self, config, adapter, node, node_index, num_nodes) -> None: def handle_exception(self, e, ctx): fire_event(SQLRunnerException(exc=str(e), exc_info=traceback.format_exc())) if isinstance(e, dbt.exceptions.Exception): - if isinstance(e, dbt.exceptions.DbtRuntimeError): + if isinstance(e, dbt.common.exceptions.DbtRuntimeError): e.add_node(ctx.node) return e @@ -51,7 +52,9 @@ def error_result(self, node, error, start_time, timing_info): raise error def ephemeral_result(self, node, start_time, timing_info): - raise dbt.exceptions.NotImplementedError("cannot execute ephemeral nodes remotely!") + raise dbt.common.exceptions.base.NotImplementedError( + "cannot execute ephemeral nodes remotely!" + ) class SqlCompileRunner(GenericSqlRunner[RemoteCompileResult]): diff --git a/core/dbt/task/test.py b/core/dbt/task/test.py index 13d67afaa0b..93695b5c3bf 100644 --- a/core/dbt/task/test.py +++ b/core/dbt/task/test.py @@ -25,8 +25,8 @@ from dbt.exceptions import ( DbtInternalError, BooleanError, - MissingMaterializationError, ) +from ..adapters.exceptions import MissingMaterializationError from dbt.graph import ( ResourceTypeSelector, ) diff --git a/core/dbt/tests/fixtures/project.py b/core/dbt/tests/fixtures/project.py index c3511e7c671..cf617eeee4f 100644 --- a/core/dbt/tests/fixtures/project.py +++ b/core/dbt/tests/fixtures/project.py @@ -1,4 +1,5 @@ import os +from pathlib import Path import pytest # type: ignore import random from argparse import Namespace @@ -6,7 +7,7 @@ import warnings import yaml -from dbt.exceptions import CompilationError, DbtDatabaseError +from dbt.common.exceptions import CompilationError, DbtDatabaseError import dbt.flags as flags from dbt.config.runtime import RuntimeConfig from dbt.adapters.factory import get_adapter, register_adapter, reset_adapters, get_adapter_by_type @@ -374,7 +375,7 @@ def project_files(project_root, models, macros, snapshots, properties, seeds, te def logs_dir(request, prefix): dbt_log_dir = os.path.join(request.config.rootdir, "logs", prefix) os.environ["DBT_LOG_PATH"] = str(dbt_log_dir) - yield dbt_log_dir + yield str(Path(dbt_log_dir)) del os.environ["DBT_LOG_PATH"] diff --git a/core/dbt/tracking.py b/core/dbt/tracking.py index 120f1dc0d18..ae9754e621e 100644 --- a/core/dbt/tracking.py +++ b/core/dbt/tracking.py @@ -24,7 +24,8 @@ SendingEvent, TrackingInitializeFailure, ) -from dbt.exceptions import FailedToConnectError, NotImplementedError +from dbt.adapters.exceptions import FailedToConnectError +from dbt.common.exceptions import NotImplementedError sp_logger.setLevel(100) diff --git a/core/dbt/utils.py b/core/dbt/utils.py index 8d31a266f2c..57a650a4902 100644 --- a/core/dbt/utils.py +++ b/core/dbt/utils.py @@ -13,13 +13,12 @@ from dbt.common.utils import md5 from dbt.common.events.types import RetryExternalCall, RecordRetryException -from dbt.exceptions import ( - ConnectionError, +from dbt.common.exceptions import ( DbtInternalError, RecursionError, - DuplicateAliasError, ) -from dbt.helper_types import WarnErrorOptions +from dbt.exceptions import ConnectionError, DuplicateAliasError +from dbt.common.helper_types import WarnErrorOptions from dbt import flags from enum import Enum from typing import ( @@ -215,16 +214,6 @@ def default(self, obj): return super().default(obj) -class ForgivingJSONEncoder(JSONEncoder): - def default(self, obj): - # let dbt's default JSON encoder handle it if possible, fallback to - # str() - try: - return super().default(obj) - except TypeError: - return str(obj) - - class Translator: def __init__(self, aliases: Mapping[str, str], recursive: bool = False) -> None: self.aliases = aliases diff --git a/core/dbt/version.py b/core/dbt/version.py index 523a9874fab..ffbe7416c44 100644 --- a/core/dbt/version.py +++ b/core/dbt/version.py @@ -10,7 +10,7 @@ import dbt.exceptions import dbt.semver -from dbt.ui import green, red, yellow +from dbt.common.ui import green, red, yellow PYPI_VERSION_URL = "https://pypi.org/pypi/dbt-core/json" diff --git a/plugins/postgres/dbt/adapters/postgres/connections.py b/plugins/postgres/dbt/adapters/postgres/connections.py index 1fe83645beb..3ba81c11a8c 100644 --- a/plugins/postgres/dbt/adapters/postgres/connections.py +++ b/plugins/postgres/dbt/adapters/postgres/connections.py @@ -3,13 +3,13 @@ import psycopg2 from psycopg2.extensions import string_types -import dbt.exceptions +import dbt.common.exceptions from dbt.adapters.base import Credentials from dbt.adapters.sql import SQLConnectionManager from dbt.adapters.contracts.connection import AdapterResponse from dbt.common.events import AdapterLogger -from dbt.helper_types import Port +from dbt.common.helper_types import Port from dataclasses import dataclass from typing import Optional from typing_extensions import Annotated @@ -84,19 +84,19 @@ def exception_handler(self, sql): logger.debug("Failed to release connection!") pass - raise dbt.exceptions.DbtDatabaseError(str(e).strip()) from e + raise dbt.common.exceptions.DbtDatabaseError(str(e).strip()) from e except Exception as e: logger.debug("Error running SQL: {}", sql) logger.debug("Rolling back transaction.") self.rollback_if_open() - if isinstance(e, dbt.exceptions.DbtRuntimeError): + if isinstance(e, dbt.common.exceptions.DbtRuntimeError): # during a sql query, an internal to dbt exception was raised. # this sounds a lot like a signal handler and probably has # useful information, so raise it without modification. raise - raise dbt.exceptions.DbtRuntimeError(e) from e + raise dbt.common.exceptions.DbtRuntimeError(e) from e @classmethod def open(cls, connection): diff --git a/plugins/postgres/dbt/adapters/postgres/impl.py b/plugins/postgres/dbt/adapters/postgres/impl.py index 5ce2da12301..2cc9e5079a4 100644 --- a/plugins/postgres/dbt/adapters/postgres/impl.py +++ b/plugins/postgres/dbt/adapters/postgres/impl.py @@ -10,12 +10,12 @@ from dbt.adapters.postgres.column import PostgresColumn from dbt.adapters.postgres import PostgresRelation from dbt.common.dataclass_schema import dbtClassMixin, ValidationError +from dbt.common.exceptions import DbtRuntimeError from dbt.contracts.graph.nodes import ConstraintType -from dbt.exceptions import ( +from dbt.adapters.exceptions import ( CrossDbReferenceProhibitedError, IndexConfigNotDictError, IndexConfigError, - DbtRuntimeError, UnexpectedDbReferenceError, ) import dbt.utils diff --git a/plugins/postgres/dbt/adapters/postgres/relation.py b/plugins/postgres/dbt/adapters/postgres/relation.py index e6a302d4143..fbb358cde43 100644 --- a/plugins/postgres/dbt/adapters/postgres/relation.py +++ b/plugins/postgres/dbt/adapters/postgres/relation.py @@ -8,7 +8,7 @@ ) from dbt.context.providers import RuntimeConfigObject from dbt.contracts.relation import RelationType -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError from dbt.adapters.postgres.relation_configs import ( PostgresIndexConfig, diff --git a/plugins/postgres/dbt/adapters/postgres/relation_configs/index.py b/plugins/postgres/dbt/adapters/postgres/relation_configs/index.py index ee9a1c4c061..7d3a0ce7189 100644 --- a/plugins/postgres/dbt/adapters/postgres/relation_configs/index.py +++ b/plugins/postgres/dbt/adapters/postgres/relation_configs/index.py @@ -3,7 +3,7 @@ import agate from dbt.common.dataclass_schema import StrEnum -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError from dbt.adapters.relation_configs import ( RelationConfigBase, RelationConfigValidationMixin, diff --git a/plugins/postgres/dbt/adapters/postgres/relation_configs/materialized_view.py b/plugins/postgres/dbt/adapters/postgres/relation_configs/materialized_view.py index 15e700e777a..70a0805f79e 100644 --- a/plugins/postgres/dbt/adapters/postgres/relation_configs/materialized_view.py +++ b/plugins/postgres/dbt/adapters/postgres/relation_configs/materialized_view.py @@ -9,7 +9,7 @@ RelationConfigValidationRule, ) from dbt.contracts.graph.nodes import ModelNode -from dbt.exceptions import DbtRuntimeError +from dbt.common.exceptions import DbtRuntimeError from dbt.adapters.postgres.relation_configs.constants import MAX_CHARACTERS_IN_IDENTIFIER from dbt.adapters.postgres.relation_configs.index import ( diff --git a/tests/adapter/dbt/tests/adapter/hooks/test_model_hooks.py b/tests/adapter/dbt/tests/adapter/hooks/test_model_hooks.py index 73995be3b33..a5bbfccb3c4 100644 --- a/tests/adapter/dbt/tests/adapter/hooks/test_model_hooks.py +++ b/tests/adapter/dbt/tests/adapter/hooks/test_model_hooks.py @@ -2,7 +2,8 @@ from pathlib import Path -from dbt.exceptions import CompilationError, ParsingError +from dbt.common.exceptions import CompilationError +from dbt.exceptions import ParsingError from dbt.tests.util import ( run_dbt, diff --git a/tests/adapter/dbt/tests/adapter/utils/test_validate_sql.py b/tests/adapter/dbt/tests/adapter/utils/test_validate_sql.py index 24bdd287f94..86b041539fe 100644 --- a/tests/adapter/dbt/tests/adapter/utils/test_validate_sql.py +++ b/tests/adapter/dbt/tests/adapter/utils/test_validate_sql.py @@ -3,7 +3,8 @@ import pytest from dbt.adapters.base.impl import BaseAdapter -from dbt.exceptions import DbtRuntimeError, InvalidConnectionError +from dbt.exceptions import DbtRuntimeError +from dbt.adapters.exceptions import InvalidConnectionError class BaseValidateSqlMethod: @@ -41,7 +42,7 @@ def invalid_sql(self) -> str: def expected_exception(self) -> Type[Exception]: """Returns the Exception type thrown by a failed query. - Defaults to dbt.exceptions.DbtRuntimeError because that is the most common + Defaults to dbt.common.exceptions.DbtRuntimeError because that is the most common base exception for adapters to throw.""" return DbtRuntimeError diff --git a/tests/functional/compile/test_compile.py b/tests/functional/compile/test_compile.py index ca03904901f..722c69536c1 100644 --- a/tests/functional/compile/test_compile.py +++ b/tests/functional/compile/test_compile.py @@ -3,7 +3,7 @@ import pytest import re -from dbt.exceptions import DbtRuntimeError, Exception as DbtException +from dbt.common.exceptions import DbtRuntimeError, DbtBaseException as DbtException from dbt.tests.util import run_dbt, run_dbt_and_capture, read_file from tests.functional.compile.fixtures import ( first_model_sql, diff --git a/tests/functional/dependencies/test_local_dependency.py b/tests/functional/dependencies/test_local_dependency.py index 5305659b95b..8d0ff0a1617 100644 --- a/tests/functional/dependencies/test_local_dependency.py +++ b/tests/functional/dependencies/test_local_dependency.py @@ -197,7 +197,7 @@ def models(self): def test_missing_dependency(self, project): # dbt should raise a runtime exception - with pytest.raises(dbt.exceptions.DbtRuntimeError): + with pytest.raises(dbt.common.exceptions.DbtRuntimeError): run_dbt(["compile"]) @@ -352,7 +352,7 @@ def test_local_dependency_same_name(self, prepare_dependencies, project): def test_local_dependency_same_name_sneaky(self, prepare_dependencies, project): shutil.copytree("duplicate_dependency", "./dbt_packages/duplicate_dependency") - with pytest.raises(dbt.exceptions.CompilationError): + with pytest.raises(dbt.common.exceptions.CompilationError): run_dbt(["compile"]) # needed to avoid compilation errors from duplicate package names in test autocleanup diff --git a/tests/functional/deprecations/test_deprecations.py b/tests/functional/deprecations/test_deprecations.py index 6c2678433b0..d95fc99da88 100644 --- a/tests/functional/deprecations/test_deprecations.py +++ b/tests/functional/deprecations/test_deprecations.py @@ -59,7 +59,7 @@ def test_data_path(self, project): def test_data_path_fail(self, project): deprecations.reset_deprecations() assert deprecations.active_deprecations == set() - with pytest.raises(dbt.exceptions.CompilationError) as exc: + with pytest.raises(dbt.common.exceptions.CompilationError) as exc: run_dbt(["--warn-error", "debug"]) exc_str = " ".join(str(exc.value).split()) # flatten all whitespace expected_msg = "The `data-paths` config has been renamed" @@ -103,7 +103,7 @@ def test_package_path(self, project): def test_package_path_not_set(self, project): deprecations.reset_deprecations() assert deprecations.active_deprecations == set() - with pytest.raises(dbt.exceptions.CompilationError) as exc: + with pytest.raises(dbt.common.exceptions.CompilationError) as exc: run_dbt(["--warn-error", "clean"]) exc_str = " ".join(str(exc.value).split()) # flatten all whitespace expected_msg = "path has changed from `dbt_modules` to `dbt_packages`." @@ -130,7 +130,7 @@ def test_package_redirect(self, project): def test_package_redirect_fail(self, project): deprecations.reset_deprecations() assert deprecations.active_deprecations == set() - with pytest.raises(dbt.exceptions.CompilationError) as exc: + with pytest.raises(dbt.common.exceptions.CompilationError) as exc: run_dbt(["--warn-error", "deps"]) exc_str = " ".join(str(exc.value).split()) # flatten all whitespace expected_msg = "The `fishtown-analytics/dbt_utils` package is deprecated in favor of `dbt-labs/dbt_utils`" @@ -152,7 +152,7 @@ def test_exposure_name(self, project): def test_exposure_name_fail(self, project): deprecations.reset_deprecations() assert deprecations.active_deprecations == set() - with pytest.raises(dbt.exceptions.CompilationError) as exc: + with pytest.raises(dbt.common.exceptions.CompilationError) as exc: run_dbt(["--warn-error", "--no-partial-parse", "parse"]) exc_str = " ".join(str(exc.value).split()) # flatten all whitespace expected_msg = "Starting in v1.3, the 'name' of an exposure should contain only letters, numbers, and underscores." diff --git a/tests/functional/docs/test_duplicate_docs_block.py b/tests/functional/docs/test_duplicate_docs_block.py index 2ff9459e4b3..7a0d8d9d602 100644 --- a/tests/functional/docs/test_duplicate_docs_block.py +++ b/tests/functional/docs/test_duplicate_docs_block.py @@ -31,5 +31,5 @@ def models(self): } def test_duplicate_doc_ref(self, project): - with pytest.raises(dbt.exceptions.CompilationError): + with pytest.raises(dbt.common.exceptions.CompilationError): run_dbt(expect_pass=False) diff --git a/tests/functional/docs/test_invalid_doc_ref.py b/tests/functional/docs/test_invalid_doc_ref.py index 7c486938124..0a6574216b4 100644 --- a/tests/functional/docs/test_invalid_doc_ref.py +++ b/tests/functional/docs/test_invalid_doc_ref.py @@ -43,5 +43,5 @@ def models(self): def test_invalid_doc_ref(self, project): # The run should fail since we could not find the docs reference. - with pytest.raises(dbt.exceptions.CompilationError): + with pytest.raises(dbt.common.exceptions.CompilationError): run_dbt(expect_pass=False) diff --git a/tests/functional/docs/test_missing_docs_blocks.py b/tests/functional/docs/test_missing_docs_blocks.py index 3b6f4e540b9..fb5ae9cc7ee 100644 --- a/tests/functional/docs/test_missing_docs_blocks.py +++ b/tests/functional/docs/test_missing_docs_blocks.py @@ -39,5 +39,5 @@ def models(self): def test_missing_doc_ref(self, project): # The run should fail since we could not find the docs reference. - with pytest.raises(dbt.exceptions.CompilationError): + with pytest.raises(dbt.common.exceptions.CompilationError): run_dbt() diff --git a/tests/functional/macros/test_macros.py b/tests/functional/macros/test_macros.py index a93a7d76c85..1619c2d6992 100644 --- a/tests/functional/macros/test_macros.py +++ b/tests/functional/macros/test_macros.py @@ -118,7 +118,7 @@ def macros(self): return {"my_macros.sql": macros__no_default_macros} def test_invalid_macro(self, project): - with pytest.raises(dbt.exceptions.CompilationError) as exc: + with pytest.raises(dbt.common.exceptions.CompilationError) as exc: run_dbt() assert "In dispatch: No macro named 'dispatch_to_nowhere' found" in str(exc.value) @@ -255,7 +255,7 @@ def test_misnamed_macro_namespace( ): run_dbt(["deps"]) - with pytest.raises(dbt.exceptions.CompilationError) as exc: + with pytest.raises(dbt.common.exceptions.CompilationError) as exc: run_dbt() assert "In dispatch: No macro named 'cowsay' found" in str(exc.value) @@ -271,7 +271,7 @@ def macros(self): return {"macro.sql": macros__deprecated_adapter_macro} def test_invalid_macro(self, project): - with pytest.raises(dbt.exceptions.CompilationError) as exc: + with pytest.raises(dbt.common.exceptions.CompilationError) as exc: run_dbt() assert 'The "adapter_macro" macro has been deprecated' in str(exc.value) diff --git a/tests/functional/partial_parsing/test_pp_vars.py b/tests/functional/partial_parsing/test_pp_vars.py index f57fca06b1e..a01e78c6458 100644 --- a/tests/functional/partial_parsing/test_pp_vars.py +++ b/tests/functional/partial_parsing/test_pp_vars.py @@ -3,7 +3,8 @@ import pytest from dbt.constants import SECRET_ENV_PREFIX -from dbt.exceptions import FailedToConnectError, ParsingError +from dbt.exceptions import ParsingError +from dbt.adapters.exceptions import FailedToConnectError from dbt.tests.util import get_manifest, run_dbt, run_dbt_and_capture, write_file from tests.functional.partial_parsing.fixtures import ( diff --git a/tests/functional/run_operations/test_run_operations.py b/tests/functional/run_operations/test_run_operations.py index aa6d908b8ce..b9d026b3bb5 100644 --- a/tests/functional/run_operations/test_run_operations.py +++ b/tests/functional/run_operations/test_run_operations.py @@ -3,7 +3,7 @@ import pytest import yaml -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.tests.util import ( check_table_does_exist, run_dbt, diff --git a/tests/functional/show/test_show.py b/tests/functional/show/test_show.py index fa4e2acca53..97947f00bd1 100644 --- a/tests/functional/show/test_show.py +++ b/tests/functional/show/test_show.py @@ -1,6 +1,6 @@ import pytest -from dbt.exceptions import DbtRuntimeError, Exception as DbtException +from dbt.common.exceptions import DbtRuntimeError, DbtBaseException as DbtException from dbt.tests.util import run_dbt_and_capture, run_dbt from tests.functional.show.fixtures import ( models__second_ephemeral_model, diff --git a/tests/functional/sources/test_source_fresher_state.py b/tests/functional/sources/test_source_fresher_state.py index d060072da17..c33cef5a8e5 100644 --- a/tests/functional/sources/test_source_fresher_state.py +++ b/tests/functional/sources/test_source_fresher_state.py @@ -4,7 +4,7 @@ import pytest from datetime import datetime, timedelta -from dbt.exceptions import DbtInternalError +from dbt.common.exceptions import DbtInternalError from dbt.tests.util import AnyStringWith, AnyFloat diff --git a/tests/unit/test_adapter_connection_manager.py b/tests/unit/test_adapter_connection_manager.py index 961ffe9f831..dfcd76a7b18 100644 --- a/tests/unit/test_adapter_connection_manager.py +++ b/tests/unit/test_adapter_connection_manager.py @@ -2,6 +2,7 @@ from unittest import mock import sys +import dbt.adapters.exceptions import dbt.exceptions import psycopg2 @@ -75,7 +76,8 @@ def connect(): raise ValueError("Something went horribly wrong") with self.assertRaisesRegex( - dbt.exceptions.FailedToConnectError, "Something went horribly wrong" + dbt.adapters.exceptions.FailedToConnectError, + "Something went horribly wrong", ): BaseConnectionManager.retry_connection( @@ -110,7 +112,8 @@ def connect(): raise ValueError("Something went horribly wrong") with self.assertRaisesRegex( - dbt.exceptions.FailedToConnectError, "Something went horribly wrong" + dbt.adapters.exceptions.FailedToConnectError, + "Something went horribly wrong", ): BaseConnectionManager.retry_connection( @@ -185,7 +188,8 @@ def connect(): raise ValueError("Something went horribly wrong") with self.assertRaisesRegex( - dbt.exceptions.FailedToConnectError, "Something went horribly wrong" + dbt.adapters.exceptions.FailedToConnectError, + "Something went horribly wrong", ): BaseConnectionManager.retry_connection( conn, @@ -220,7 +224,8 @@ def connect(): raise TypeError("An unhandled thing went horribly wrong") with self.assertRaisesRegex( - dbt.exceptions.FailedToConnectError, "An unhandled thing went horribly wrong" + dbt.adapters.exceptions.FailedToConnectError, + "An unhandled thing went horribly wrong", ): BaseConnectionManager.retry_connection( conn, @@ -338,7 +343,8 @@ def connect(): return True with self.assertRaisesRegex( - dbt.exceptions.FailedToConnectError, "retry_limit cannot be negative" + dbt.adapters.exceptions.FailedToConnectError, + "retry_limit cannot be negative", ): BaseConnectionManager.retry_connection( conn, @@ -365,7 +371,7 @@ def connect(): for retry_timeout in [-10, -2.5, lambda _: -100, lambda _: -10.1]: with self.assertRaisesRegex( - dbt.exceptions.FailedToConnectError, + dbt.adapters.exceptions.FailedToConnectError, "retry_timeout cannot be negative or return a negative time", ): BaseConnectionManager.retry_connection( @@ -392,7 +398,7 @@ def connect(): return True with self.assertRaisesRegex( - dbt.exceptions.FailedToConnectError, + dbt.adapters.exceptions.FailedToConnectError, "retry_limit cannot be negative", ): BaseConnectionManager.retry_connection( diff --git a/tests/unit/test_cli_flags.py b/tests/unit/test_cli_flags.py index e6c860315c1..31f9ffa836f 100644 --- a/tests/unit/test_cli_flags.py +++ b/tests/unit/test_cli_flags.py @@ -9,8 +9,8 @@ from dbt.cli.main import cli from dbt.cli.types import Command from dbt.contracts.project import UserConfig -from dbt.exceptions import DbtInternalError -from dbt.helper_types import WarnErrorOptions +from dbt.common.exceptions import DbtInternalError +from dbt.common.helper_types import WarnErrorOptions from dbt.tests.util import rm_file, write_file diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index 99dc672b608..8c56e9bca3e 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -986,7 +986,7 @@ def test_configured_task_dir_change(self): def test_configured_task_dir_change_with_bad_path(self): self.args.project_dir = "bad_path" - with self.assertRaises(dbt.exceptions.DbtRuntimeError): + with self.assertRaises(dbt.common.exceptions.DbtRuntimeError): InheritsFromConfiguredTask.from_args(self.args) diff --git a/tests/unit/test_context.py b/tests/unit/test_context.py index 2e7d5612f57..58941553de3 100644 --- a/tests/unit/test_context.py +++ b/tests/unit/test_context.py @@ -91,7 +91,7 @@ def test_var_not_defined(self): var = providers.RuntimeVar(self.context, self.config, self.model) self.assertEqual(var("foo", "bar"), "bar") - with self.assertRaises(dbt.exceptions.CompilationError): + with self.assertRaises(dbt.common.exceptions.CompilationError): var("foo") def test_parser_var_default_something(self): @@ -459,7 +459,7 @@ def test_macro_namespace_duplicates(config_postgres, manifest_fx): mn.add_macros(manifest_fx.macros.values(), {}) # same pkg, same name: error - with pytest.raises(dbt.exceptions.CompilationError): + with pytest.raises(dbt.common.exceptions.CompilationError): mn.add_macro(mock_macro("macro_a", "root"), {}) # different pkg, same name: no error diff --git a/tests/unit/test_flags.py b/tests/unit/test_flags.py index 69d8913b675..db496dbf952 100644 --- a/tests/unit/test_flags.py +++ b/tests/unit/test_flags.py @@ -6,7 +6,7 @@ from dbt import flags from dbt.contracts.project import UserConfig from dbt.graph.selector_spec import IndirectSelection -from dbt.helper_types import WarnErrorOptions +from dbt.common.helper_types import WarnErrorOptions # Skip due to interface for flag updated pytestmark = pytest.mark.skip diff --git a/tests/unit/test_functions.py b/tests/unit/test_functions.py index 2128e41fc05..cfd371db1d2 100644 --- a/tests/unit/test_functions.py +++ b/tests/unit/test_functions.py @@ -4,7 +4,7 @@ import dbt.flags as flags from dbt.common.events.functions import msg_to_dict, warn_or_error, setup_event_logger from dbt.common.events.types import InfoLevel, NoNodesForSelectionCriteria -from dbt.exceptions import EventCompilationError +from dbt.common.exceptions import EventCompilationError @pytest.mark.parametrize( diff --git a/tests/unit/test_graph_selection.py b/tests/unit/test_graph_selection.py index 572c8fed10d..d43a0e38c88 100644 --- a/tests/unit/test_graph_selection.py +++ b/tests/unit/test_graph_selection.py @@ -214,5 +214,5 @@ def test_parse_specs( @pytest.mark.parametrize("invalid", invalid_specs, ids=lambda k: str(k)) def test_invalid_specs(invalid): - with pytest.raises(dbt.exceptions.DbtRuntimeError): + with pytest.raises(dbt.common.exceptions.DbtRuntimeError): graph_selector.SelectionCriteria.from_single_spec(invalid) diff --git a/tests/unit/test_graph_selector_methods.py b/tests/unit/test_graph_selector_methods.py index 7cc863c52c8..aa89ca417e2 100644 --- a/tests/unit/test_graph_selector_methods.py +++ b/tests/unit/test_graph_selector_methods.py @@ -1418,19 +1418,19 @@ def test_select_state_no_change(manifest, previous_state): def test_select_state_nothing(manifest, previous_state): previous_state.manifest = None method = statemethod(manifest, previous_state) - with pytest.raises(dbt.exceptions.DbtRuntimeError) as exc: + with pytest.raises(dbt.common.exceptions.DbtRuntimeError) as exc: search_manifest_using_method(manifest, method, "modified") assert "no comparison manifest" in str(exc.value) - with pytest.raises(dbt.exceptions.DbtRuntimeError) as exc: + with pytest.raises(dbt.common.exceptions.DbtRuntimeError) as exc: search_manifest_using_method(manifest, method, "new") assert "no comparison manifest" in str(exc.value) - with pytest.raises(dbt.exceptions.DbtRuntimeError) as exc: + with pytest.raises(dbt.common.exceptions.DbtRuntimeError) as exc: search_manifest_using_method(manifest, method, "unmodified") assert "no comparison manifest" in str(exc.value) - with pytest.raises(dbt.exceptions.DbtRuntimeError) as exc: + with pytest.raises(dbt.common.exceptions.DbtRuntimeError) as exc: search_manifest_using_method(manifest, method, "old") assert "no comparison manifest" in str(exc.value) diff --git a/tests/unit/test_helper_types.py b/tests/unit/test_helper_types.py index c2931c7efa9..17bd669911c 100644 --- a/tests/unit/test_helper_types.py +++ b/tests/unit/test_helper_types.py @@ -1,6 +1,6 @@ import pytest -from dbt.helper_types import IncludeExclude, WarnErrorOptions +from dbt.common.helper_types import IncludeExclude, WarnErrorOptions from dbt.common.dataclass_schema import ValidationError diff --git a/tests/unit/test_postgres_adapter.py b/tests/unit/test_postgres_adapter.py index 77d4cfc2325..8739e3e2784 100644 --- a/tests/unit/test_postgres_adapter.py +++ b/tests/unit/test_postgres_adapter.py @@ -16,7 +16,8 @@ from dbt.contracts.files import FileHash from dbt.contracts.graph.manifest import ManifestStateCheck from dbt.common.clients import agate_helper -from dbt.exceptions import DbtValidationError, DbtConfigError +from dbt.exceptions import DbtConfigError +from dbt.common.exceptions import DbtValidationError from psycopg2 import extensions as psycopg2_extensions from psycopg2 import DatabaseError diff --git a/tests/unit/test_semver.py b/tests/unit/test_semver.py index bdd8c6ae9c3..f2417ad5c15 100644 --- a/tests/unit/test_semver.py +++ b/tests/unit/test_semver.py @@ -2,7 +2,7 @@ import itertools from typing import List -from dbt.exceptions import VersionsNotCompatibleError +from dbt.common.exceptions import VersionsNotCompatibleError from dbt.semver import ( VersionSpecifier, UnboundedVersionSpecifier, diff --git a/tests/unit/test_version.py b/tests/unit/test_version.py index 217988ba5e2..35f4518464f 100644 --- a/tests/unit/test_version.py +++ b/tests/unit/test_version.py @@ -1,5 +1,5 @@ import dbt.version -from dbt.ui import green, red, yellow +from dbt.common.ui import green, red, yellow class TestGetVersionInformation: