Skip to content

Commit 99167fc

Browse files
hmgaudeckerclaude
andcommitted
Improve error messages from concatenated functions.
Set __name__ on the inner function so signature enforcement produces "The concatenated function is missing ..." instead of "concatenated() missing ...". Drop "()" from all _fail_if_* format strings and add "is" before "missing". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8d6ef96 commit 99167fc

File tree

4 files changed

+55
-14
lines changed

4 files changed

+55
-14
lines changed

docs/getting_started.ipynb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,7 @@
178178
{
179179
"cell_type": "markdown",
180180
"metadata": {},
181-
"source": [
182-
"Calling without arguments now raises an error, because dags needs the external inputs:"
183-
]
181+
"source": "Calling without arguments raises an `InvalidFunctionArgumentsError`, telling you exactly which inputs are missing:"
184182
},
185183
{
186184
"cell_type": "code",

src/dags/dag.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -619,11 +619,6 @@ def _create_concatenated_function(
619619
args = arglist
620620
return_annotation = inspect.Parameter.empty
621621

622-
@with_signature(
623-
args=args,
624-
enforce=enforce_signature,
625-
return_annotation=return_annotation,
626-
)
627622
def concatenated(*args: Any, **kwargs: Any) -> tuple[Any, ...]:
628623
results = {**dict(zip(arglist, args, strict=False)), **kwargs}
629624
for name, info in execution_info.items():
@@ -633,7 +628,13 @@ def concatenated(*args: Any, **kwargs: Any) -> tuple[Any, ...]:
633628

634629
return tuple(results[target] for target in targets)
635630

636-
return concatenated
631+
concatenated.__name__ = "The concatenated function"
632+
return with_signature(
633+
concatenated,
634+
args=args,
635+
enforce=enforce_signature,
636+
return_annotation=return_annotation,
637+
)
637638

638639

639640
def _infer_aggregator_return_type(

src/dags/signature.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ def wrapper_with_signature(*args: P.args, **kwargs: P.kwargs) -> R:
137137
_fail_if_invalid_keyword_arguments(
138138
present_kwargs, valid_kwargs, funcname
139139
)
140+
_fail_if_missing_arguments(
141+
present_args, present_kwargs, valid_kwargs, funcname
142+
)
140143
return func(*args, **kwargs)
141144

142145
wrapper_with_signature.__signature__ = signature # ty: ignore[unresolved-attribute]
@@ -153,7 +156,7 @@ def _fail_if_too_many_positional_arguments(
153156
) -> None:
154157
if len(present_args) > len(argnames):
155158
msg = (
156-
f"{funcname}() takes {len(argnames)} positional arguments "
159+
f"{funcname} takes {len(argnames)} positional arguments "
157160
f"but {len(present_args)} were given"
158161
)
159162
raise InvalidFunctionArgumentsError(msg)
@@ -166,7 +169,7 @@ def _fail_if_duplicated_arguments(
166169
if problematic:
167170
s = "s" if len(problematic) >= 2 else "" # noqa: PLR2004
168171
problem_str = ", ".join(list(problematic))
169-
msg = f"{funcname}() got multiple values for argument{s} {problem_str}"
172+
msg = f"{funcname} got multiple values for argument{s} {problem_str}"
170173
raise InvalidFunctionArgumentsError(msg)
171174

172175

@@ -177,7 +180,22 @@ def _fail_if_invalid_keyword_arguments(
177180
if problematic:
178181
s = "s" if len(problematic) >= 2 else "" # noqa: PLR2004
179182
problem_str = ", ".join(list(problematic))
180-
msg = f"{funcname}() got unexpected keyword argument{s} {problem_str}"
183+
msg = f"{funcname} got unexpected keyword argument{s} {problem_str}"
184+
raise InvalidFunctionArgumentsError(msg)
185+
186+
187+
def _fail_if_missing_arguments(
188+
present_args: set[str],
189+
present_kwargs: set[str],
190+
required_args: set[str],
191+
funcname: str,
192+
) -> None:
193+
provided = present_args | present_kwargs
194+
missing = required_args - provided
195+
if missing:
196+
s = "s" if len(missing) >= 2 else "" # noqa: PLR2004
197+
missing_str = ", ".join(sorted(missing))
198+
msg = f"{funcname} is missing required argument{s}: {missing_str}"
181199
raise InvalidFunctionArgumentsError(msg)
182200

183201

tests/test_signature.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def f(*args, **kwargs):
142142

143143
with pytest.raises(
144144
InvalidFunctionArgumentsError,
145-
match=r"f\(\) got multiple values for argument b",
145+
match=r"f got multiple values for argument b",
146146
):
147147
f(1, 2, b=3)
148148

@@ -154,7 +154,7 @@ def f(*args, **kwargs):
154154

155155
with pytest.raises(
156156
InvalidFunctionArgumentsError,
157-
match=r"f\(\) got unexpected keyword argument d",
157+
match=r"f got unexpected keyword argument d",
158158
):
159159
f(1, 2, d=4)
160160

@@ -207,6 +207,30 @@ def f(d: int, e: float, *, f: bool) -> float:
207207
}
208208

209209

210+
def test_with_signature_decorator_missing_arguments() -> None:
211+
@with_signature(args=["a", "b"], kwargs=["c"])
212+
def f(*args, **kwargs):
213+
return sum(args) + sum(kwargs.values())
214+
215+
with pytest.raises(
216+
InvalidFunctionArgumentsError,
217+
match="missing required argument",
218+
):
219+
f(1)
220+
221+
222+
def test_with_signature_decorator_missing_all_arguments() -> None:
223+
@with_signature(args=["a", "b"], kwargs=["c"])
224+
def f(*args, **kwargs):
225+
return sum(args) + sum(kwargs.values())
226+
227+
with pytest.raises(
228+
InvalidFunctionArgumentsError,
229+
match=r"missing required arguments.*a.*b",
230+
):
231+
f()
232+
233+
210234
def test_with_signature_invalid_args_type() -> None:
211235
with pytest.raises(DagsError, match="Invalid type for arg"):
212236

0 commit comments

Comments
 (0)