Skip to content

Fix issue with rerunning tasks via pytask.build. #626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/source/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
- {pull}`619` makes coiled an optional import for tests. Thanks to {user}`erooke`.
- {pull}`620` makes tests more flexible about their location. Thanks to {user}`erooke`.
- {pull}`621` fixes the pull requests template.
- {pull}`626` resolves an issue with rerunning tasks via the programmatic API. Closes
{issue}`625`. Thanks to @noppelmax for the issue!
- {pull}`627` adds a warning when users explicitly pass files to pytask that pytask is
going to ignore because they do not match a pattern. Happens quite often when the task
module's name does not start with `task_`.
Expand Down
7 changes: 6 additions & 1 deletion src/_pytask/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import os
import sys
import time
from contextlib import suppress
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
Expand Down Expand Up @@ -114,7 +115,11 @@ def _collect_from_tasks(session: Session) -> None:
name = raw_task.pytask_meta.name

if has_mark(raw_task, "task"):
COLLECTED_TASKS[path].remove(raw_task)
# When tasks with @task are passed to the programmatic interface multiple
# times, they are deleted from ``COLLECTED_TASKS`` in the first iteration
# and are missing in the later. See #625.
with suppress(ValueError):
COLLECTED_TASKS[path].remove(raw_task)

# When a task is not a callable, it can be anything or a PTask. Set arbitrary
# values and it will pass without errors and not collected.
Expand Down
40 changes: 39 additions & 1 deletion tests/test_execute.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import json
import os
import pickle
import re
import subprocess
Expand Down Expand Up @@ -261,7 +262,15 @@ def task_example():

@pytest.mark.end_to_end()
def test_task_executed_with_force_although_unchanged(tmp_path):
tmp_path.joinpath("task_module.py").write_text("def task_example(): pass")
source = """
from pytask import task

def task_example(): pass

@task
def task_example_2(): pass
"""
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(source))
session = build(paths=tmp_path)
assert session.execution_reports[0].outcome == TaskOutcome.SUCCESS
session = build(paths=tmp_path, force=True)
Expand Down Expand Up @@ -625,6 +634,35 @@ def create_file(
assert tmp_path.joinpath("file.txt").read_text() == "This is the text."


@pytest.mark.end_to_end()
@pytest.mark.skipif(
sys.platform == "win32" and os.environ.get("CI") == "true",
reason="Windows does not pick up the right Python interpreter.",
)
def test_execute_tasks_multiple_times_via_api(tmp_path):
"""See #625."""
source = """
import pathlib
from typing_extensions import Annotated
from pytask import build, task
import sys

@task
def task1() -> None: pass
def task2() -> None: pass

if __name__ == "__main__":
session1 = build(tasks=[task1, task2])
session2 = build(tasks=[task1, task2])
sys.exit(session2.exit_code)
"""
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(source))
result = subprocess.run(
("python", tmp_path.joinpath("task_module.py").as_posix()), check=False
)
assert result.returncode == ExitCode.OK


@pytest.mark.end_to_end()
def test_pytask_on_a_module_that_uses_the_functional_api(tmp_path):
source = """
Expand Down
Loading