Skip to content

Commit cb7e27c

Browse files
authored
Allow None return annotation on task execute (#42)
1 parent 2bf9016 commit cb7e27c

3 files changed

Lines changed: 38 additions & 1 deletion

File tree

‎CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- `tilebox-workflows`: Allow task `execute()` methods to use a `-> None` return annotation when postponed annotation
13+
evaluation is enabled.
14+
1015
## [0.55.0] - 2026-07-01
1116

1217
### Added

‎tilebox-workflows/tests/test_task.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import __future__
2+
13
import json
24
from dataclasses import dataclass
35
from typing import Annotated
@@ -46,6 +48,35 @@ def execute(self, context: ExecutionContext) -> None:
4648
assert TaskMeta.for_task(SimpleTask).executable is True
4749

4850

51+
def _compile_task_with_postponed_return_annotation(return_annotation: str) -> type:
52+
source = f"""
53+
class PostponedAnnotationsTask(Task):
54+
def execute(self, context: ExecutionContext) -> {return_annotation}:
55+
pass
56+
"""
57+
namespace: dict[str, type] = {"Task": Task, "ExecutionContext": ExecutionContext}
58+
code = compile(
59+
source,
60+
filename="<postponed-annotations-test>",
61+
mode="exec",
62+
flags=__future__.annotations.compiler_flag,
63+
dont_inherit=True,
64+
)
65+
exec(code, namespace) # noqa: S102
66+
return namespace["PostponedAnnotationsTask"]
67+
68+
69+
def test_task_validation_execute_none_return_type_with_postponed_annotations() -> None:
70+
task_class = _compile_task_with_postponed_return_annotation("None")
71+
72+
assert TaskMeta.for_task(task_class).executable is True
73+
74+
75+
def test_task_validation_execute_invalid_return_type_with_postponed_annotations() -> None:
76+
with pytest.raises(TypeError, match="to not have a return value"):
77+
_compile_task_with_postponed_return_annotation("int")
78+
79+
4980
def test_task_validation_execute_invalid_signature_no_params() -> None:
5081
with pytest.raises(TypeError, match="Expected a function signature of"):
5182
# validation happens at class creation time, that's why we create it in a function

‎tilebox-workflows/tilebox/workflows/task.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ def _validate_execute_method(
136136
f"but got {class_name}.execute{signature}!"
137137
)
138138

139-
if signature.return_annotation is not None and signature.return_annotation != inspect._empty: # noqa: SLF001
139+
# `from __future__ import annotations` stores `-> None` as the string "None".
140+
if signature.return_annotation not in (None, "None", inspect.Signature.empty):
140141
raise TypeError(f"Expected {class_name}.execute{signature} to not have a return value!")
141142

142143
return True

0 commit comments

Comments
 (0)