Skip to content

Show all files that are not produced by a task. #287

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 5 commits into from
Jun 27, 2022
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 @@ -12,6 +12,8 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
- {pull}`283` fixes an issue with coverage and tests using pexpect + `pdb.set_trace()`.
- {pull}`285` implements that pytask does not show the traceback of tasks which are
skipped because its previous task failed. Fixes {issue}`284`.
- {pull}`287` changes that all files that are not produced by a task are displayed in
the error message. Fixes {issue}`262`.

## 0.2.3 - 2022-05-30

Expand Down
24 changes: 17 additions & 7 deletions src/_pytask/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from _pytask.console import console
from _pytask.console import create_summary_panel
from _pytask.console import create_url_style_for_task
from _pytask.console import format_strings_as_flat_tree
from _pytask.console import format_task_id
from _pytask.console import unify_styles
from _pytask.dag import descending_tasks
Expand All @@ -27,6 +28,7 @@
from _pytask.report import ExecutionReport
from _pytask.session import Session
from _pytask.shared import get_first_non_none_value
from _pytask.shared import reduce_node_name
from _pytask.traceback import format_exception_without_traceback
from _pytask.traceback import remove_traceback_from_exc_info
from _pytask.traceback import render_exc_info
Expand Down Expand Up @@ -167,15 +169,23 @@ def pytask_execute_task(task: Task) -> bool:

@hookimpl
def pytask_execute_task_teardown(session: Session, task: Task) -> None:
"""Check if each produced node was indeed produced."""
"""Check if :class:`_pytask.nodes.FilePathNode` are produced by a task."""
missing_nodes = []
for product in session.dag.successors(task.name):
node = session.dag.nodes[product]["node"]
try:
node.state()
except NodeNotFoundError as e:
raise NodeNotFoundError(
f"{node.name} was not produced by {task.name}."
) from e
if isinstance(node, FilePathNode):

try:
node.state()
except NodeNotFoundError:
missing_nodes.append(node)

if missing_nodes:
paths = [reduce_node_name(i, session.config["paths"]) for i in missing_nodes]
formatted = format_strings_as_flat_tree(
paths, "The task did not produce the following files:\n", ""
)
raise NodeNotFoundError(formatted)


@hookimpl(trylast=True)
Expand Down
19 changes: 19 additions & 0 deletions tests/test_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,25 @@ def task_example():
assert isinstance(session.execution_reports[0].exc_info[1], NodeNotFoundError)


@pytest.mark.end_to_end
def test_task_did_not_produce_multiple_nodes_and_all_are_shown(runner, tmp_path):
source = """
import pytask

@pytask.mark.produces(["1.txt", "2.txt"])
def task_example():
pass
"""
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(source))

result = runner.invoke(cli, [tmp_path.as_posix()])

assert result.exit_code == ExitCode.FAILED
assert "NodeNotFoundError" in result.output
assert "1.txt" in result.output
assert "2.txt" in result.output


@pytest.mark.end_to_end
def test_node_not_found_in_task_setup(tmp_path):
"""Test for :class:`_pytask.exceptions.NodeNotFoundError` in task setup.
Expand Down