diff --git a/CHANGELOG.md b/CHANGELOG.md index de4db566..18953991 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and ## Unreleased +- [#995](https://github.com/pytask-dev/pytask/pull/995) documents that return values + from task generators are ignored and adds a regression test for this behavior. - [#977](https://github.com/pytask-dev/pytask/pull/977) allows marker expressions passed to `-m` to select tasks by marker keyword arguments. - [#976](https://github.com/pytask-dev/pytask/pull/976) makes invalid marker and diff --git a/docs/source/how_to_guides/provisional_nodes_and_task_generators.md b/docs/source/how_to_guides/provisional_nodes_and_task_generators.md index b43d0fa7..f0310a5f 100644 --- a/docs/source/how_to_guides/provisional_nodes_and_task_generators.md +++ b/docs/source/how_to_guides/provisional_nodes_and_task_generators.md @@ -95,3 +95,6 @@ content to a `.txt` file. !!! important The generated tasks need to be decorated with `@task` to be collected. + +Task generators are only used to define other tasks. Their return values are ignored +and cannot be used to define products. Define products on the generated tasks instead. diff --git a/src/_pytask/provisional.py b/src/_pytask/provisional.py index 76f724b0..e72ccc05 100644 --- a/src/_pytask/provisional.py +++ b/src/_pytask/provisional.py @@ -57,7 +57,7 @@ def _safe_load(node: PNode | PProvisionalNode, task: PTask, is_product: bool) -> @hookimpl -def pytask_execute_task(session: Session, task: PTask) -> None: +def pytask_execute_task(session: Session, task: PTask) -> bool | None: """Execute task generators and collect the tasks.""" if is_task_generator(task): kwargs = {} @@ -114,9 +114,12 @@ def pytask_execute_task(session: Session, task: PTask) -> None: ) session.execution_reports.append(exec_report) session.should_stop = True - return + return None recreate_dag(session, task) + return True + + return None @hookimpl diff --git a/tests/test_provisional.py b/tests/test_provisional.py index 2fcd78a6..f181e548 100644 --- a/tests/test_provisional.py +++ b/tests/test_provisional.py @@ -197,6 +197,31 @@ def task_copy( assert tmp_path.joinpath("b-copy.txt").exists() +def test_task_generator_return_value_is_ignored(runner, tmp_path): + source = """ + from pathlib import Path + from typing import Annotated + from pytask import task + + @task(is_generator=True) + def task_generator() -> Annotated[ + str, Path("returned.txt") + ]: + @task + def task_child(produces=Path("child.txt")): + produces.write_text("child") + + return "ignored" + """ + tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(source)) + + result = runner.invoke(cli, [tmp_path.as_posix()]) + + assert result.exit_code == ExitCode.OK + assert tmp_path.joinpath("child.txt").read_text() == "child" + assert not tmp_path.joinpath("returned.txt").exists() + + def test_gracefully_fail_when_task_generator_raises_error(runner, tmp_path): source = """ from typing import Annotated