Skip to content

Commit e55ecb6

Browse files
committed
[resolved] embelish resolver exceptions
1 parent 1a7af08 commit e55ecb6

File tree

5 files changed

+68
-10
lines changed

5 files changed

+68
-10
lines changed

python_modules/libraries/dagster-components/dagster_components/resolved/context.py

+20
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from jinja2.exceptions import UndefinedError
1414
from jinja2.nativetypes import NativeTemplate
1515

16+
from dagster_components.resolved.model import ResolvableModel
17+
1618
T = TypeVar("T")
1719

1820

@@ -91,6 +93,24 @@ def _scope_threw_exc(self, fmt_exc: list[str]):
9193

9294
return ResolutionException("".join(msg_parts))
9395

96+
def build_resolve_fn_exc(
97+
self,
98+
fmt_exc: list[str],
99+
field_name: str,
100+
model: ResolvableModel,
101+
) -> ResolutionException:
102+
msg_parts = []
103+
loc = self._location()
104+
if loc:
105+
msg_parts.append(loc + "\n")
106+
107+
msg_parts.append(
108+
f"Exception occurred in Resolver for field '{field_name}' resolving from {model.__class__.__name__}({model}).\n"
109+
)
110+
msg_parts.extend(fmt_exc)
111+
112+
return ResolutionException("".join(msg_parts))
113+
94114
def _resolve_inner_value(self, val: Any) -> Any:
95115
"""Resolves a single value, if it is a templated string."""
96116
if isinstance(val, str):

python_modules/libraries/dagster-components/dagster_components/resolved/model.py

+22-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import sys
2+
import traceback
13
from abc import ABC, abstractmethod
24
from collections.abc import Mapping, Sequence
35
from dataclasses import dataclass
@@ -305,14 +307,26 @@ def from_model(fn: Callable[["ResolutionContext", Any], Any]):
305307
"""Resolve this field by invoking the function which will receive the entire parent ResolvableModel."""
306308
return Resolver(ParentFn(fn))
307309

308-
def execute(self, context: "ResolutionContext", model: ResolvableModel, field_name: str) -> Any:
309-
if isinstance(self.fn, ParentFn):
310-
return self.fn.callable(context, model)
311-
elif isinstance(self.fn, AttrWithContextFn):
312-
attr = getattr(model, field_name)
313-
return self.fn.callable(context.at_path(field_name), attr)
314-
else:
315-
raise ValueError(f"Unsupported DSLFieldResolver type: {self.fn}")
310+
def execute(
311+
self,
312+
context: "ResolutionContext",
313+
model: ResolvableModel,
314+
field_name: str,
315+
) -> Any:
316+
try:
317+
if isinstance(self.fn, ParentFn):
318+
return self.fn.callable(context, model)
319+
elif isinstance(self.fn, AttrWithContextFn):
320+
attr = getattr(model, field_name)
321+
return self.fn.callable(context.at_path(field_name), attr)
322+
except Exception:
323+
raise context.build_resolve_fn_exc(
324+
traceback.format_exception(*sys.exc_info()),
325+
field_name=field_name,
326+
model=model,
327+
)
328+
329+
raise ValueError(f"Unsupported DSLFieldResolver type: {self.fn}")
316330

317331

318332
ResolvedType: TypeAlias = Union[type[ResolvedKwargs], type[ResolvedFrom]]

python_modules/libraries/dagster-components/dagster_components/test/basic_components.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
from collections.abc import Mapping
66
from dataclasses import dataclass
7-
from typing import Any
7+
from typing import Annotated, Any
88

99
from dagster._core.definitions.definitions_class import Definitions
1010
from pydantic import BaseModel, ConfigDict
1111

12-
from dagster_components import Component, ResolvableModel, ResolvedFrom
12+
from dagster_components import Component, ResolvableModel, ResolvedFrom, Resolver
1313
from dagster_components.core.component import ComponentLoadContext
1414

1515

@@ -24,12 +24,20 @@ def _error():
2424
class MyComponentModel(ResolvableModel):
2525
a_string: str
2626
an_int: int
27+
throw: bool = False
28+
29+
30+
def _maybe_throw(ctx, throw):
31+
if throw:
32+
_error()
33+
return throw
2734

2835

2936
@dataclass
3037
class MyComponent(Component, ResolvedFrom[MyComponentModel]):
3138
a_string: str
3239
an_int: int
40+
throw: Annotated[bool, Resolver(_maybe_throw)]
3341

3442
def build_defs(self, context: ComponentLoadContext) -> Definitions:
3543
return Definitions()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type: .MyComponent
2+
3+
attributes:
4+
a_string: "hi"
5+
an_int: 3
6+
throw: true

python_modules/libraries/dagster-components/dagster_components_tests/integration_tests/validation_tests/test_component_validation.py

+10
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@
3737
"_inner_error()",
3838
),
3939
),
40+
ComponentValidationTestCase(
41+
component_path="validation/basic_component_resolve_exc",
42+
component_type_filepath=BASIC_COMPONENT_TYPE_FILEPATH,
43+
should_error=True,
44+
validate_error_msg=msg_includes_all_of(
45+
"component.yaml:6",
46+
'raise Exception("boom")',
47+
"_inner_error()",
48+
),
49+
),
4050
]
4151

4252

0 commit comments

Comments
 (0)