From 70b5057d7cd0f265e8bceb27b4a6b009d57b3ac8 Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Sat, 19 Sep 2026 15:18:14 +0200 Subject: [PATCH 1/3] Document ignored task generator returns --- CHANGELOG.md | 2 ++ .../provisional_nodes_and_task_generators.md | 4 +++ src/_pytask/provisional.py | 7 ++++-- tests/test_provisional.py | 25 +++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de4db566..bf500e83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and ## Unreleased +- 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..fd184d5f 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,7 @@ 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. +A `return` statement without a value can still be used for ordinary control flow. 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 From 97840b6c26984c4b90fcd93b97339fc3e4f590d5 Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Sat, 19 Sep 2026 15:18:51 +0200 Subject: [PATCH 2/3] Link changelog entry to pull request --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf500e83..18953991 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,8 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and ## Unreleased -- Documents that return values from task generators are ignored and adds a regression - test for this behavior. +- [#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 From d4d02361de4b1c8365e8d872a0542f82aa5f2d6d Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Sat, 19 Sep 2026 15:20:25 +0200 Subject: [PATCH 3/3] Tighten task generator return documentation --- .../how_to_guides/provisional_nodes_and_task_generators.md | 1 - 1 file changed, 1 deletion(-) 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 fd184d5f..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 @@ -98,4 +98,3 @@ content to a `.txt` file. 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. -A `return` statement without a value can still be used for ordinary control flow.