mirror of
https://github.com/dbt-labs/dbt-core
synced 2025-12-20 20:21:28 +00:00
Compare commits
2 Commits
adding-sem
...
er/excepti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad9aabca1d | ||
|
|
ea1304f8bb |
@@ -14,7 +14,7 @@ from dbt.exceptions import DbtProfileError
|
|||||||
from dbt.exceptions import DbtProjectError
|
from dbt.exceptions import DbtProjectError
|
||||||
from dbt.exceptions import ValidationException
|
from dbt.exceptions import ValidationException
|
||||||
from dbt.exceptions import RuntimeException
|
from dbt.exceptions import RuntimeException
|
||||||
from dbt.exceptions import validator_error_message
|
from dbt.exception_messages import validator_error_message
|
||||||
from dbt.events.types import MissingProfileTarget
|
from dbt.events.types import MissingProfileTarget
|
||||||
from dbt.events.functions import fire_event
|
from dbt.events.functions import fire_event
|
||||||
from dbt.utils import coerce_dict_str
|
from dbt.utils import coerce_dict_str
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ from dbt.clients.system import path_exists
|
|||||||
from dbt.clients.system import load_file_contents
|
from dbt.clients.system import load_file_contents
|
||||||
from dbt.clients.yaml_helper import load_yaml_text
|
from dbt.clients.yaml_helper import load_yaml_text
|
||||||
from dbt.contracts.connection import QueryComment
|
from dbt.contracts.connection import QueryComment
|
||||||
|
from dbt.exception_messages import validator_error_message
|
||||||
from dbt.exceptions import DbtProjectError
|
from dbt.exceptions import DbtProjectError
|
||||||
from dbt.exceptions import SemverException
|
from dbt.exceptions import SemverException
|
||||||
from dbt.exceptions import validator_error_message
|
|
||||||
from dbt.exceptions import RuntimeException
|
from dbt.exceptions import RuntimeException
|
||||||
from dbt.graph import SelectionSpec
|
from dbt.graph import SelectionSpec
|
||||||
from dbt.helper_types import NoValue
|
from dbt.helper_types import NoValue
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ from dbt.contracts.graph.manifest import ManifestMetadata
|
|||||||
from dbt.contracts.project import Configuration, UserConfig
|
from dbt.contracts.project import Configuration, UserConfig
|
||||||
from dbt.contracts.relation import ComponentName
|
from dbt.contracts.relation import ComponentName
|
||||||
from dbt.dataclass_schema import ValidationError
|
from dbt.dataclass_schema import ValidationError
|
||||||
|
from dbt.exception_messages import validator_error_message
|
||||||
from dbt.exceptions import (
|
from dbt.exceptions import (
|
||||||
DbtProjectError,
|
DbtProjectError,
|
||||||
RuntimeException,
|
RuntimeException,
|
||||||
raise_compiler_error,
|
raise_compiler_error,
|
||||||
validator_error_message,
|
|
||||||
)
|
)
|
||||||
from dbt.events.functions import warn_or_error
|
from dbt.events.functions import warn_or_error
|
||||||
from dbt.events.types import UnusedResourceConfigPath
|
from dbt.events.types import UnusedResourceConfigPath
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class DBTDeprecation:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
msg = f"Event Class `{class_name}` is not defined in `{module_path}`"
|
msg = f"Event Class `{class_name}` is not defined in `{module_path}`"
|
||||||
raise NameError(msg)
|
raise NameError(msg)
|
||||||
raise NotImplementedError("event not implemented for {}".format(self._event))
|
raise NotImplementedError("event not implemented for {}".format(self))
|
||||||
|
|
||||||
def show(self, *args, **kwargs) -> None:
|
def show(self, *args, **kwargs) -> None:
|
||||||
if self.name not in active_deprecations:
|
if self.name not in active_deprecations:
|
||||||
|
|||||||
237
core/dbt/deps/exceptions.py
Normal file
237
core/dbt/deps/exceptions.py
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
import abc
|
||||||
|
import builtins
|
||||||
|
from typing import ClassVar, NoReturn, Dict
|
||||||
|
|
||||||
|
import dbt.events
|
||||||
|
from dbt.events.helpers import env_secrets, scrub_secrets
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: using this special case here to not break the rest of exceptions but this would normally
|
||||||
|
# live centrally
|
||||||
|
class Exception(builtins.Exception):
|
||||||
|
CODE = -32000
|
||||||
|
MESSAGE = "Server Error"
|
||||||
|
_event: ClassVar[str] = "GeneralException"
|
||||||
|
_category: str = "general exception"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.log()
|
||||||
|
|
||||||
|
def data(self) -> Dict[str, str]:
|
||||||
|
# do not override
|
||||||
|
constant_exception_data = {
|
||||||
|
"type": self.__class__.__name__, # is this used outside logbook logs?
|
||||||
|
"message": str(self),
|
||||||
|
"event": self._event, # does not always match type
|
||||||
|
"category": self._category,
|
||||||
|
}
|
||||||
|
# TODO: can't guarantee this is always serializable...
|
||||||
|
return {**constant_exception_data, **vars(self)}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def event(self) -> abc.ABCMeta:
|
||||||
|
if self._event is not None:
|
||||||
|
module_path = dbt.events.types
|
||||||
|
class_name = self._event
|
||||||
|
|
||||||
|
try:
|
||||||
|
return getattr(module_path, class_name)
|
||||||
|
except AttributeError:
|
||||||
|
msg = f"Event Class `{class_name}` is not defined in `{module_path}`"
|
||||||
|
raise NameError(msg)
|
||||||
|
raise NotImplementedError("event not implemented for {}".format(self))
|
||||||
|
|
||||||
|
def log(self, *args, **kwargs) -> None:
|
||||||
|
log_event = self.event(data=self.data(), **kwargs)
|
||||||
|
dbt.events.functions.fire_event(log_event)
|
||||||
|
|
||||||
|
|
||||||
|
class RuntimeException(RuntimeError, Exception):
|
||||||
|
CODE = 10001
|
||||||
|
MESSAGE = "Runtime error"
|
||||||
|
|
||||||
|
def __init__(self, msg, node=None):
|
||||||
|
self.stack = []
|
||||||
|
self.node = node
|
||||||
|
self.message = 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 "<Unknown>"
|
||||||
|
if not hasattr(node, "name"):
|
||||||
|
# we probably failed to parse a block, so we can't know the name
|
||||||
|
return "{} ({})".format(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 "{}".format(node.path.original_file_path)
|
||||||
|
return "{} {} ({})".format(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("> {} {}".format(msg, self.node_to_string(item)))
|
||||||
|
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def __str__(self, prefix="! "):
|
||||||
|
node_string = ""
|
||||||
|
|
||||||
|
if self.node is not None:
|
||||||
|
node_string = " in {}".format(self.node_to_string(self.node))
|
||||||
|
|
||||||
|
if hasattr(self.message, "split"):
|
||||||
|
split_msg = self.message.split("\n")
|
||||||
|
else:
|
||||||
|
split_msg = str(self.message).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
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: caused some circular imports. copied here. doesn't belong here.
|
||||||
|
class CommandError(RuntimeException):
|
||||||
|
def __init__(self, cwd, cmd, message="Error running command"):
|
||||||
|
cmd_scrubbed = list(scrub_secrets(cmd_txt, env_secrets()) for cmd_txt in cmd)
|
||||||
|
super().__init__(message)
|
||||||
|
self.cwd = cwd
|
||||||
|
self.cmd = cmd_scrubbed
|
||||||
|
self.args = (cwd, cmd_scrubbed, message)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if len(self.cmd) == 0:
|
||||||
|
return "{}: No arguments given".format(self.message)
|
||||||
|
return '{}: "{}"'.format(self.message, self.cmd[0])
|
||||||
|
|
||||||
|
|
||||||
|
# Start actual deps exceptions
|
||||||
|
|
||||||
|
|
||||||
|
class DependencyException(Exception):
|
||||||
|
# this can happen due to raise_dependency_error and its callers
|
||||||
|
CODE = 10006
|
||||||
|
MESSAGE = "Dependency Error"
|
||||||
|
_event: ClassVar[str] = "DependencyException"
|
||||||
|
_category: str = "general deps"
|
||||||
|
|
||||||
|
def __init__(self, message):
|
||||||
|
super().__init__()
|
||||||
|
self.message = scrub_secrets(message, env_secrets())
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: explore where this should live
|
||||||
|
class ExecutableError(CommandError):
|
||||||
|
def __init__(self, cwd, cmd, message):
|
||||||
|
super().__init__(cwd, cmd, message)
|
||||||
|
|
||||||
|
|
||||||
|
class InternalException(DependencyException):
|
||||||
|
_category: str = "internal"
|
||||||
|
|
||||||
|
|
||||||
|
# This was using SemverException previously...
|
||||||
|
class DependencyVersionException(DependencyException):
|
||||||
|
_category: str = "version"
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
msg = "Version error for package {}: {}".format(self.name, self)
|
||||||
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class MultipleDependencyVersionException(DependencyException):
|
||||||
|
_category: str = "git"
|
||||||
|
|
||||||
|
def __init__(self, git, requested):
|
||||||
|
self.git = git
|
||||||
|
self.requested = requested
|
||||||
|
msg = "git dependencies should contain exactly one version. " "{} contains: {}".format(
|
||||||
|
self.git, requested
|
||||||
|
)
|
||||||
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class PackageNotFound(DependencyException):
|
||||||
|
def __init__(self, package_name):
|
||||||
|
self.package_name = package_name
|
||||||
|
msg = f"Package {self.package_name} was not found in the package index"
|
||||||
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class PackageVersionNotFound(DependencyException):
|
||||||
|
_category: str = "config"
|
||||||
|
|
||||||
|
def __init__(self, package_name, version_range, available_versions, should_version_check):
|
||||||
|
self.package_name = package_name
|
||||||
|
self.version_range = str(version_range)
|
||||||
|
self.available_versions = available_versions
|
||||||
|
self.should_version_check = should_version_check
|
||||||
|
msg = self.build_msg()
|
||||||
|
super().__init__(msg)
|
||||||
|
|
||||||
|
def build_msg(self):
|
||||||
|
base_msg = (
|
||||||
|
"Could not find a matching compatible version for package {}\n"
|
||||||
|
" Requested range: {}\n"
|
||||||
|
" Compatible versions: {}\n"
|
||||||
|
)
|
||||||
|
addendum = (
|
||||||
|
(
|
||||||
|
"\n"
|
||||||
|
" Not shown: package versions incompatible with installed version of dbt-core\n"
|
||||||
|
" To include them, run 'dbt --no-version-check deps'"
|
||||||
|
)
|
||||||
|
if self.should_version_check
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
base_msg.format(self.package_name, self.version_range, self.available_versions)
|
||||||
|
+ addendum
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# should these all become their own exceptions? They have to all share a category if not.
|
||||||
|
def raise_dependency_error(msg) -> NoReturn:
|
||||||
|
raise DependencyException(scrub_secrets(msg, env_secrets()))
|
||||||
@@ -9,7 +9,7 @@ from dbt.contracts.project import (
|
|||||||
GitPackage,
|
GitPackage,
|
||||||
)
|
)
|
||||||
from dbt.deps.base import PinnedPackage, UnpinnedPackage, get_downloads_path
|
from dbt.deps.base import PinnedPackage, UnpinnedPackage, get_downloads_path
|
||||||
from dbt.exceptions import ExecutableError, raise_dependency_error
|
from .exceptions import ExecutableError, MultipleDependencyVersionException
|
||||||
from dbt.events.functions import fire_event, warn_or_error
|
from dbt.events.functions import fire_event, warn_or_error
|
||||||
from dbt.events.types import EnsureGitInstalled, DepsUnpinned
|
from dbt.events.types import EnsureGitInstalled, DepsUnpinned
|
||||||
|
|
||||||
@@ -143,10 +143,7 @@ class GitUnpinnedPackage(GitPackageMixin, UnpinnedPackage[GitPinnedPackage]):
|
|||||||
if len(requested) == 0:
|
if len(requested) == 0:
|
||||||
requested = {"HEAD"}
|
requested = {"HEAD"}
|
||||||
elif len(requested) > 1:
|
elif len(requested) > 1:
|
||||||
raise_dependency_error(
|
raise MultipleDependencyVersionException(git=self.git, requested=requested)
|
||||||
"git dependencies should contain exactly one version. "
|
|
||||||
"{} contains: {}".format(self.git, requested)
|
|
||||||
)
|
|
||||||
|
|
||||||
return GitPinnedPackage(
|
return GitPinnedPackage(
|
||||||
git=self.git,
|
git=self.git,
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ from dbt.contracts.project import (
|
|||||||
RegistryPackage,
|
RegistryPackage,
|
||||||
)
|
)
|
||||||
from dbt.deps.base import PinnedPackage, UnpinnedPackage, get_downloads_path
|
from dbt.deps.base import PinnedPackage, UnpinnedPackage, get_downloads_path
|
||||||
from dbt.exceptions import (
|
from dbt.deps.exceptions import (
|
||||||
package_version_not_found,
|
DependencyVersionException,
|
||||||
VersionsNotCompatibleException,
|
)
|
||||||
DependencyException,
|
from dbt.exceptions import VersionsNotCompatibleException
|
||||||
package_not_found,
|
from .exceptions import (
|
||||||
|
PackageVersionNotFound,
|
||||||
|
PackageNotFound,
|
||||||
)
|
)
|
||||||
from dbt.utils import _connection_exception_retry as connection_exception_retry
|
from dbt.utils import _connection_exception_retry as connection_exception_retry
|
||||||
|
|
||||||
@@ -99,7 +101,7 @@ class RegistryUnpinnedPackage(RegistryPackageMixin, UnpinnedPackage[RegistryPinn
|
|||||||
def _check_in_index(self):
|
def _check_in_index(self):
|
||||||
index = registry.index_cached()
|
index = registry.index_cached()
|
||||||
if self.package not in index:
|
if self.package not in index:
|
||||||
package_not_found(self.package)
|
raise PackageNotFound(self.package)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_contract(cls, contract: RegistryPackage) -> "RegistryUnpinnedPackage":
|
def from_contract(cls, contract: RegistryPackage) -> "RegistryUnpinnedPackage":
|
||||||
@@ -124,8 +126,7 @@ class RegistryUnpinnedPackage(RegistryPackageMixin, UnpinnedPackage[RegistryPinn
|
|||||||
try:
|
try:
|
||||||
range_ = semver.reduce_versions(*self.versions)
|
range_ = semver.reduce_versions(*self.versions)
|
||||||
except VersionsNotCompatibleException as e:
|
except VersionsNotCompatibleException as e:
|
||||||
new_msg = "Version error for package {}: {}".format(self.name, e)
|
raise DependencyVersionException(self.name) from e
|
||||||
raise DependencyException(new_msg) from e
|
|
||||||
|
|
||||||
should_version_check = bool(flags.VERSION_CHECK)
|
should_version_check = bool(flags.VERSION_CHECK)
|
||||||
dbt_version = get_installed_version()
|
dbt_version = get_installed_version()
|
||||||
@@ -146,7 +147,12 @@ class RegistryUnpinnedPackage(RegistryPackageMixin, UnpinnedPackage[RegistryPinn
|
|||||||
target = None
|
target = None
|
||||||
if not target:
|
if not target:
|
||||||
# raise an exception if no installable target version is found
|
# raise an exception if no installable target version is found
|
||||||
package_version_not_found(self.package, range_, installable, should_version_check)
|
raise PackageVersionNotFound(
|
||||||
|
package_name=self.package,
|
||||||
|
version_range=range_,
|
||||||
|
available_versions=installable,
|
||||||
|
should_version_check=should_version_check,
|
||||||
|
)
|
||||||
latest_compatible = installable[-1]
|
latest_compatible = installable[-1]
|
||||||
return RegistryPinnedPackage(
|
return RegistryPinnedPackage(
|
||||||
package=self.package, version=target, version_latest=latest_compatible
|
package=self.package, version=target, version_latest=latest_compatible
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Dict, List, NoReturn, Union, Type, Iterator, Set
|
from typing import Dict, List, NoReturn, Union, Type, Iterator, Set
|
||||||
|
|
||||||
from dbt.exceptions import raise_dependency_error, InternalException
|
from .exceptions import raise_dependency_error, InternalException
|
||||||
|
|
||||||
from dbt.config import Project, RuntimeConfig
|
from dbt.config import Project, RuntimeConfig
|
||||||
from dbt.config.renderer import DbtProjectYamlRenderer
|
from dbt.config.renderer import DbtProjectYamlRenderer
|
||||||
|
|||||||
@@ -1495,6 +1495,16 @@ class NoNodesForSelectionCriteria(betterproto.Message):
|
|||||||
spec_raw: str = betterproto.string_field(2)
|
spec_raw: str = betterproto.string_field(2)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DependencyException(betterproto.Message):
|
||||||
|
"""M031"""
|
||||||
|
|
||||||
|
info: "EventInfo" = betterproto.message_field(1)
|
||||||
|
data: Dict[str, str] = betterproto.map_field(
|
||||||
|
3, betterproto.TYPE_STRING, betterproto.TYPE_STRING
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RunningOperationCaughtError(betterproto.Message):
|
class RunningOperationCaughtError(betterproto.Message):
|
||||||
"""Q001"""
|
"""Q001"""
|
||||||
@@ -1827,6 +1837,13 @@ class FoundStats(betterproto.Message):
|
|||||||
stat_line: str = betterproto.string_field(2)
|
stat_line: str = betterproto.string_field(2)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GeneralException(betterproto.Message):
|
||||||
|
"""X001"""
|
||||||
|
|
||||||
|
info: "EventInfo" = betterproto.message_field(1)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MainKeyboardInterrupt(betterproto.Message):
|
class MainKeyboardInterrupt(betterproto.Message):
|
||||||
"""Z001"""
|
"""Z001"""
|
||||||
|
|||||||
@@ -1136,6 +1136,12 @@ message NoNodesForSelectionCriteria {
|
|||||||
string spec_raw = 2;
|
string spec_raw = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// M031
|
||||||
|
message DependencyException {
|
||||||
|
EventInfo info = 1;
|
||||||
|
map<string, string> data = 3;
|
||||||
|
}
|
||||||
|
|
||||||
// Q - Node execution
|
// Q - Node execution
|
||||||
|
|
||||||
// Q001
|
// Q001
|
||||||
@@ -1416,6 +1422,14 @@ message FoundStats {
|
|||||||
string stat_line = 2;
|
string stat_line = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X - Exceptions
|
||||||
|
|
||||||
|
//X001
|
||||||
|
message GeneralException {
|
||||||
|
EventInfo info = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Z - Misc
|
// Z - Misc
|
||||||
|
|
||||||
// Z001
|
// Z001
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from dbt.events.format import format_fancy_output_line, pluralize
|
|||||||
from dbt.events.proto_types import EventInfo, RunResultMsg, ListOfStrings # noqa
|
from dbt.events.proto_types import EventInfo, RunResultMsg, ListOfStrings # noqa
|
||||||
from dbt.events.proto_types import NodeInfo, ReferenceKeyMsg # noqa
|
from dbt.events.proto_types import NodeInfo, ReferenceKeyMsg # noqa
|
||||||
from dbt.events import proto_types as pt
|
from dbt.events import proto_types as pt
|
||||||
|
from dbt.exception_messages import get_not_found_or_disabled_msg_2
|
||||||
|
|
||||||
from dbt.node_types import NodeType
|
from dbt.node_types import NodeType
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ from dbt.node_types import NodeType
|
|||||||
# | M | Deps generation |
|
# | M | Deps generation |
|
||||||
# | Q | Node execution |
|
# | Q | Node execution |
|
||||||
# | W | Node testing |
|
# | W | Node testing |
|
||||||
|
# | X | Exceptions |
|
||||||
# | Z | Misc |
|
# | Z | Misc |
|
||||||
# | T | Test only |
|
# | T | Test only |
|
||||||
#
|
#
|
||||||
@@ -1491,28 +1493,14 @@ class NodeNotFoundOrDisabled(WarnLevel, pt.NodeNotFoundOrDisabled):
|
|||||||
return "I060"
|
return "I060"
|
||||||
|
|
||||||
def message(self) -> str:
|
def message(self) -> str:
|
||||||
# this is duplicated logic from exceptions.get_not_found_or_disabled_msg
|
msg = get_not_found_or_disabled_msg_2(
|
||||||
# when we convert exceptions to be stuctured maybe it can be combined?
|
original_file_path=self.original_file_path,
|
||||||
# convverting the bool to a string since None is also valid
|
unique_id=self.unique_id,
|
||||||
if self.disabled == "None":
|
resource_type_title=self.resource_type_title,
|
||||||
reason = "was not found or is disabled"
|
target_name=self.target_name,
|
||||||
elif self.disabled == "True":
|
target_kind=self.target_kind,
|
||||||
reason = "is disabled"
|
target_package=self.target_package,
|
||||||
else:
|
disabled=self.disabled,
|
||||||
reason = "was not found"
|
|
||||||
|
|
||||||
target_package_string = ""
|
|
||||||
if self.target_package is not None:
|
|
||||||
target_package_string = "in package '{}' ".format(self.target_package)
|
|
||||||
|
|
||||||
msg = "{} '{}' ({}) depends on a {} named '{}' {}which {}".format(
|
|
||||||
self.resource_type_title,
|
|
||||||
self.unique_id,
|
|
||||||
self.original_file_path,
|
|
||||||
self.target_kind,
|
|
||||||
self.target_name,
|
|
||||||
target_package_string,
|
|
||||||
reason,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return warning_tag(msg)
|
return warning_tag(msg)
|
||||||
@@ -1824,6 +1812,15 @@ class NoNodesForSelectionCriteria(WarnLevel, pt.NoNodesForSelectionCriteria):
|
|||||||
return f"The selection criterion '{self.spec_raw}' does not match any nodes"
|
return f"The selection criterion '{self.spec_raw}' does not match any nodes"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DependencyException(ErrorLevel, pt.DependencyException):
|
||||||
|
def code(self):
|
||||||
|
return "M031"
|
||||||
|
|
||||||
|
def message(self) -> str:
|
||||||
|
return f"This is the custom message: {self.data.get('message')}"
|
||||||
|
|
||||||
|
|
||||||
# =======================================================
|
# =======================================================
|
||||||
# Q - Node execution
|
# Q - Node execution
|
||||||
# =======================================================
|
# =======================================================
|
||||||
@@ -2298,6 +2295,20 @@ class FoundStats(InfoLevel, pt.FoundStats):
|
|||||||
return f"Found {self.stat_line}"
|
return f"Found {self.stat_line}"
|
||||||
|
|
||||||
|
|
||||||
|
# =======================================================
|
||||||
|
# X - Exceptions
|
||||||
|
# =======================================================
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GeneralException(ErrorLevel, pt.GeneralException):
|
||||||
|
def code(self):
|
||||||
|
return "X001"
|
||||||
|
|
||||||
|
def message(self) -> str:
|
||||||
|
return "General Exception"
|
||||||
|
|
||||||
|
|
||||||
# =======================================================
|
# =======================================================
|
||||||
# Z - Misc
|
# Z - Misc
|
||||||
# =======================================================
|
# =======================================================
|
||||||
|
|||||||
75
core/dbt/exception_messages.py
Normal file
75
core/dbt/exception_messages.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import dbt.dataclass_schema
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
def validator_error_message(exc):
|
||||||
|
"""Given a dbt.dataclass_schema.ValidationError (which is basically a
|
||||||
|
jsonschema.ValidationError), return the relevant parts as a string
|
||||||
|
"""
|
||||||
|
if not isinstance(exc, dbt.dataclass_schema.ValidationError):
|
||||||
|
return str(exc)
|
||||||
|
path = "[%s]" % "][".join(map(repr, exc.relative_path))
|
||||||
|
return "at path {}: {}".format(path, exc.message)
|
||||||
|
|
||||||
|
|
||||||
|
def get_not_found_or_disabled_msg(
|
||||||
|
original_file_path,
|
||||||
|
unique_id,
|
||||||
|
resource_type_title,
|
||||||
|
target_name: str,
|
||||||
|
target_kind: str,
|
||||||
|
target_package: Optional[str] = None,
|
||||||
|
disabled: Optional[bool] = None,
|
||||||
|
) -> str:
|
||||||
|
if disabled is None:
|
||||||
|
reason = "was not found or is disabled"
|
||||||
|
elif disabled is True:
|
||||||
|
reason = "is disabled"
|
||||||
|
else:
|
||||||
|
reason = "was not found"
|
||||||
|
|
||||||
|
target_package_string = ""
|
||||||
|
if target_package is not None:
|
||||||
|
target_package_string = "in package '{}' ".format(target_package)
|
||||||
|
|
||||||
|
return "{} '{}' ({}) depends on a {} named '{}' {}which {}".format(
|
||||||
|
resource_type_title,
|
||||||
|
unique_id,
|
||||||
|
original_file_path,
|
||||||
|
target_kind,
|
||||||
|
target_name,
|
||||||
|
target_package_string,
|
||||||
|
reason,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: temp fix for mypy/proto sanity - combine later
|
||||||
|
def get_not_found_or_disabled_msg_2(
|
||||||
|
original_file_path,
|
||||||
|
unique_id,
|
||||||
|
resource_type_title,
|
||||||
|
target_name: str,
|
||||||
|
target_kind: str,
|
||||||
|
disabled: str,
|
||||||
|
target_package: Optional[str] = None,
|
||||||
|
) -> str:
|
||||||
|
if disabled == "None":
|
||||||
|
reason = "was not found or is disabled"
|
||||||
|
elif disabled == "True":
|
||||||
|
reason = "is disabled"
|
||||||
|
else:
|
||||||
|
reason = "was not found"
|
||||||
|
|
||||||
|
target_package_string = ""
|
||||||
|
if target_package is not None:
|
||||||
|
target_package_string = "in package '{}' ".format(target_package)
|
||||||
|
|
||||||
|
return "{} '{}' ({}) depends on a {} named '{}' {}which {}".format(
|
||||||
|
resource_type_title,
|
||||||
|
unique_id,
|
||||||
|
original_file_path,
|
||||||
|
target_kind,
|
||||||
|
target_name,
|
||||||
|
target_package_string,
|
||||||
|
reason,
|
||||||
|
)
|
||||||
@@ -1,24 +1,15 @@
|
|||||||
import builtins
|
import builtins
|
||||||
import functools
|
import functools
|
||||||
from typing import NoReturn, Optional, Mapping, Any
|
from typing import Any, Mapping, NoReturn, Optional
|
||||||
|
|
||||||
from dbt.events.helpers import env_secrets, scrub_secrets
|
from dbt.events.helpers import env_secrets, scrub_secrets
|
||||||
from dbt.events.types import JinjaLogWarning
|
from dbt.events.types import JinjaLogWarning
|
||||||
|
from dbt.exception_messages import get_not_found_or_disabled_msg
|
||||||
from dbt.node_types import NodeType
|
from dbt.node_types import NodeType
|
||||||
|
|
||||||
import dbt.dataclass_schema
|
import dbt.dataclass_schema
|
||||||
|
|
||||||
|
|
||||||
def validator_error_message(exc):
|
|
||||||
"""Given a dbt.dataclass_schema.ValidationError (which is basically a
|
|
||||||
jsonschema.ValidationError), return the relevant parts as a string
|
|
||||||
"""
|
|
||||||
if not isinstance(exc, dbt.dataclass_schema.ValidationError):
|
|
||||||
return str(exc)
|
|
||||||
path = "[%s]" % "][".join(map(repr, exc.relative_path))
|
|
||||||
return "at path {}: {}".format(path, exc.message)
|
|
||||||
|
|
||||||
|
|
||||||
class Exception(builtins.Exception):
|
class Exception(builtins.Exception):
|
||||||
CODE = -32000
|
CODE = -32000
|
||||||
MESSAGE = "Server Error"
|
MESSAGE = "Server Error"
|
||||||
@@ -309,12 +300,6 @@ class AliasException(ValidationException):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DependencyException(Exception):
|
|
||||||
# this can happen due to raise_dependency_error and its callers
|
|
||||||
CODE = 10006
|
|
||||||
MESSAGE = "Dependency Error"
|
|
||||||
|
|
||||||
|
|
||||||
class DbtConfigError(RuntimeException):
|
class DbtConfigError(RuntimeException):
|
||||||
CODE = 10007
|
CODE = 10007
|
||||||
MESSAGE = "DBT Configuration Error"
|
MESSAGE = "DBT Configuration Error"
|
||||||
@@ -451,10 +436,6 @@ def raise_database_error(msg, node=None) -> NoReturn:
|
|||||||
raise DatabaseException(msg, node)
|
raise DatabaseException(msg, node)
|
||||||
|
|
||||||
|
|
||||||
def raise_dependency_error(msg) -> NoReturn:
|
|
||||||
raise DependencyException(scrub_secrets(msg, env_secrets()))
|
|
||||||
|
|
||||||
|
|
||||||
def raise_git_cloning_error(error: CommandResultError) -> NoReturn:
|
def raise_git_cloning_error(error: CommandResultError) -> NoReturn:
|
||||||
error.cmd = scrub_secrets(str(error.cmd), env_secrets())
|
error.cmd = scrub_secrets(str(error.cmd), env_secrets())
|
||||||
raise error
|
raise error
|
||||||
@@ -568,37 +549,6 @@ def doc_target_not_found(
|
|||||||
raise_compiler_error(msg, model)
|
raise_compiler_error(msg, model)
|
||||||
|
|
||||||
|
|
||||||
def get_not_found_or_disabled_msg(
|
|
||||||
original_file_path,
|
|
||||||
unique_id,
|
|
||||||
resource_type_title,
|
|
||||||
target_name: str,
|
|
||||||
target_kind: str,
|
|
||||||
target_package: Optional[str] = None,
|
|
||||||
disabled: Optional[bool] = None,
|
|
||||||
) -> str:
|
|
||||||
if disabled is None:
|
|
||||||
reason = "was not found or is disabled"
|
|
||||||
elif disabled is True:
|
|
||||||
reason = "is disabled"
|
|
||||||
else:
|
|
||||||
reason = "was not found"
|
|
||||||
|
|
||||||
target_package_string = ""
|
|
||||||
if target_package is not None:
|
|
||||||
target_package_string = "in package '{}' ".format(target_package)
|
|
||||||
|
|
||||||
return "{} '{}' ({}) depends on a {} named '{}' {}which {}".format(
|
|
||||||
resource_type_title,
|
|
||||||
unique_id,
|
|
||||||
original_file_path,
|
|
||||||
target_kind,
|
|
||||||
target_name,
|
|
||||||
target_package_string,
|
|
||||||
reason,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def target_not_found(
|
def target_not_found(
|
||||||
node,
|
node,
|
||||||
target_name: str,
|
target_name: str,
|
||||||
@@ -718,31 +668,6 @@ def relation_wrong_type(relation, expected_type, model=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def package_not_found(package_name):
|
|
||||||
raise_dependency_error("Package {} was not found in the package index".format(package_name))
|
|
||||||
|
|
||||||
|
|
||||||
def package_version_not_found(
|
|
||||||
package_name, version_range, available_versions, should_version_check
|
|
||||||
):
|
|
||||||
base_msg = (
|
|
||||||
"Could not find a matching compatible version for package {}\n"
|
|
||||||
" Requested range: {}\n"
|
|
||||||
" Compatible versions: {}\n"
|
|
||||||
)
|
|
||||||
addendum = (
|
|
||||||
(
|
|
||||||
"\n"
|
|
||||||
" Not shown: package versions incompatible with installed version of dbt-core\n"
|
|
||||||
" To include them, run 'dbt --no-version-check deps'"
|
|
||||||
)
|
|
||||||
if should_version_check
|
|
||||||
else ""
|
|
||||||
)
|
|
||||||
msg = base_msg.format(package_name, version_range, available_versions) + addendum
|
|
||||||
raise_dependency_error(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def invalid_materialization_argument(name, argument):
|
def invalid_materialization_argument(name, argument):
|
||||||
raise_compiler_error(
|
raise_compiler_error(
|
||||||
"materialization '{}' received unknown argument '{}'.".format(name, argument)
|
"materialization '{}' received unknown argument '{}'.".format(name, argument)
|
||||||
@@ -1000,6 +925,9 @@ def warn(msg, node=None):
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: importing here just to track things separately
|
||||||
|
from dbt.deps.exceptions import raise_dependency_error # noqa
|
||||||
|
|
||||||
# Update this when a new function should be added to the
|
# Update this when a new function should be added to the
|
||||||
# dbt context's `exceptions` key!
|
# dbt context's `exceptions` key!
|
||||||
CONTEXT_EXPORTS = {
|
CONTEXT_EXPORTS = {
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ from dbt.context.context_config import ContextConfig
|
|||||||
from dbt.contracts.graph.manifest import Manifest
|
from dbt.contracts.graph.manifest import Manifest
|
||||||
from dbt.contracts.graph.parsed import HasUniqueID, ManifestNodes
|
from dbt.contracts.graph.parsed import HasUniqueID, ManifestNodes
|
||||||
from dbt.contracts.graph.unparsed import UnparsedNode, Docs
|
from dbt.contracts.graph.unparsed import UnparsedNode, Docs
|
||||||
from dbt.exceptions import ParsingException, validator_error_message, InternalException
|
from dbt.exception_messages import validator_error_message
|
||||||
|
from dbt.exceptions import ParsingException, InternalException
|
||||||
from dbt import hooks
|
from dbt import hooks
|
||||||
from dbt.node_types import NodeType, ModelLanguage
|
from dbt.node_types import NodeType, ModelLanguage
|
||||||
from dbt.parser.search import FileBlock
|
from dbt.parser.search import FileBlock
|
||||||
|
|||||||
@@ -978,7 +978,7 @@ def invalid_target_fail_unless_test(
|
|||||||
target_name=target_name,
|
target_name=target_name,
|
||||||
target_kind=target_kind,
|
target_kind=target_kind,
|
||||||
target_package=target_package if target_package else "",
|
target_package=target_package if target_package else "",
|
||||||
disabled=str(disabled),
|
disabled="None" if disabled is None else str(disabled),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
|
|||||||
# New for Python models :p
|
# New for Python models :p
|
||||||
import ast
|
import ast
|
||||||
from dbt.dataclass_schema import ValidationError
|
from dbt.dataclass_schema import ValidationError
|
||||||
from dbt.exceptions import ParsingException, validator_error_message, UndefinedMacroException
|
from dbt.exception_messages import validator_error_message
|
||||||
|
from dbt.exceptions import ParsingException, UndefinedMacroException
|
||||||
|
|
||||||
|
|
||||||
dbt_function_key_words = set(["ref", "source", "config", "get"])
|
dbt_function_key_words = set(["ref", "source", "config", "get"])
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ from dbt.contracts.graph.unparsed import (
|
|||||||
UnparsedMetric,
|
UnparsedMetric,
|
||||||
UnparsedSourceDefinition,
|
UnparsedSourceDefinition,
|
||||||
)
|
)
|
||||||
|
from dbt.exception_messages import validator_error_message
|
||||||
from dbt.exceptions import (
|
from dbt.exceptions import (
|
||||||
validator_error_message,
|
|
||||||
JSONValidationException,
|
JSONValidationException,
|
||||||
raise_invalid_property_yml_version,
|
raise_invalid_property_yml_version,
|
||||||
ValidationException,
|
ValidationException,
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ from typing import List
|
|||||||
from dbt.dataclass_schema import ValidationError
|
from dbt.dataclass_schema import ValidationError
|
||||||
|
|
||||||
from dbt.contracts.graph.parsed import IntermediateSnapshotNode, ParsedSnapshotNode
|
from dbt.contracts.graph.parsed import IntermediateSnapshotNode, ParsedSnapshotNode
|
||||||
from dbt.exceptions import ParsingException, validator_error_message
|
from dbt.exception_messages import validator_error_message
|
||||||
|
from dbt.exceptions import ParsingException
|
||||||
from dbt.node_types import NodeType
|
from dbt.node_types import NodeType
|
||||||
from dbt.parser.base import SQLParser
|
from dbt.parser.base import SQLParser
|
||||||
from dbt.parser.search import BlockContents, BlockSearcher, FileBlock
|
from dbt.parser.search import BlockContents, BlockSearcher, FileBlock
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ class ModelRunner(CompileRunner):
|
|||||||
return f"{self.node.language} {self.node.get_materialization()} model {self.get_node_representation()}"
|
return f"{self.node.language} {self.node.get_materialization()} model {self.get_node_representation()}"
|
||||||
|
|
||||||
def print_start_line(self):
|
def print_start_line(self):
|
||||||
|
breakpoint()
|
||||||
fire_event(
|
fire_event(
|
||||||
LogStartLine(
|
LogStartLine(
|
||||||
description=self.describe_node(),
|
description=self.describe_node(),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import unittest
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import dbt.deps
|
import dbt.deps
|
||||||
import dbt.exceptions
|
import dbt.deps.exceptions
|
||||||
from dbt.deps.git import GitUnpinnedPackage
|
from dbt.deps.git import GitUnpinnedPackage
|
||||||
from dbt.deps.local import LocalUnpinnedPackage
|
from dbt.deps.local import LocalUnpinnedPackage
|
||||||
from dbt.deps.registry import RegistryUnpinnedPackage
|
from dbt.deps.registry import RegistryUnpinnedPackage
|
||||||
@@ -92,7 +92,7 @@ class TestGitPackage(unittest.TestCase):
|
|||||||
self.assertEqual(c.git, 'http://example.com')
|
self.assertEqual(c.git, 'http://example.com')
|
||||||
self.assertEqual(c.revisions, ['0.0.1', '0.0.2'])
|
self.assertEqual(c.revisions, ['0.0.1', '0.0.2'])
|
||||||
|
|
||||||
with self.assertRaises(dbt.exceptions.DependencyException):
|
with self.assertRaises(dbt.deps.exceptions.DependencyException):
|
||||||
c.resolved()
|
c.resolved()
|
||||||
|
|
||||||
def test_default_revision(self):
|
def test_default_revision(self):
|
||||||
@@ -223,7 +223,7 @@ class TestHubPackage(unittest.TestCase):
|
|||||||
package='dbt-labs-test/b',
|
package='dbt-labs-test/b',
|
||||||
version='0.1.2'
|
version='0.1.2'
|
||||||
))
|
))
|
||||||
with self.assertRaises(dbt.exceptions.DependencyException) as exc:
|
with self.assertRaises(dbt.deps.exceptions.PackageNotFound) as exc:
|
||||||
a.resolved()
|
a.resolved()
|
||||||
|
|
||||||
msg = 'Package dbt-labs-test/b was not found in the package index'
|
msg = 'Package dbt-labs-test/b was not found in the package index'
|
||||||
@@ -235,7 +235,7 @@ class TestHubPackage(unittest.TestCase):
|
|||||||
version='0.1.4'
|
version='0.1.4'
|
||||||
))
|
))
|
||||||
|
|
||||||
with self.assertRaises(dbt.exceptions.DependencyException) as exc:
|
with self.assertRaises(dbt.deps.exceptions.PackageVersionNotFound) as exc:
|
||||||
a.resolved()
|
a.resolved()
|
||||||
msg = (
|
msg = (
|
||||||
"Could not find a matching compatible version for package "
|
"Could not find a matching compatible version for package "
|
||||||
@@ -257,7 +257,7 @@ class TestHubPackage(unittest.TestCase):
|
|||||||
b = RegistryUnpinnedPackage.from_contract(b_contract)
|
b = RegistryUnpinnedPackage.from_contract(b_contract)
|
||||||
c = a.incorporate(b)
|
c = a.incorporate(b)
|
||||||
|
|
||||||
with self.assertRaises(dbt.exceptions.DependencyException) as exc:
|
with self.assertRaises(dbt.deps.exceptions.DependencyVersionException) as exc:
|
||||||
c.resolved()
|
c.resolved()
|
||||||
msg = (
|
msg = (
|
||||||
"Version error for package dbt-labs-test/a: Could not "
|
"Version error for package dbt-labs-test/a: Could not "
|
||||||
|
|||||||
@@ -80,8 +80,11 @@ class BaseAliasErrors:
|
|||||||
def test_alias_dupe_thorews_exeption(self, project):
|
def test_alias_dupe_thorews_exeption(self, project):
|
||||||
message = ".*identical database representation.*"
|
message = ".*identical database representation.*"
|
||||||
with pytest.raises(Exception) as exc:
|
with pytest.raises(Exception) as exc:
|
||||||
assert message in exc
|
# assert message in exc
|
||||||
run_dbt(["run"])
|
run_dbt(["run"])
|
||||||
|
assert message in exc
|
||||||
|
# breakpoint()
|
||||||
|
# print("ab")
|
||||||
|
|
||||||
|
|
||||||
class BaseSameAliasDifferentSchemas:
|
class BaseSameAliasDifferentSchemas:
|
||||||
|
|||||||
13
tests/unit/test_exceptions.py
Normal file
13
tests/unit/test_exceptions.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# flake8: noqa
|
||||||
|
import pytest
|
||||||
|
from dbt.exceptions import Exception
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TestBaseException:
|
||||||
|
|
||||||
|
def test_base_exception(self):
|
||||||
|
with pytest.raises(Exception) as exc_info:
|
||||||
|
raise(Exception())
|
||||||
|
breakpoint()
|
||||||
|
print("hi")
|
||||||
Reference in New Issue
Block a user