Skip to content

Commit bfd19bb

Browse files
committed
fix: Fix handling of ignore_fail option in nested sequences (#253)
Refs: #252
1 parent 881a05a commit bfd19bb

File tree

2 files changed

+96
-26
lines changed

2 files changed

+96
-26
lines changed

poethepoet/task/sequence.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,17 +148,27 @@ def _handle_run(
148148
ignore_fail = self.spec.options.ignore_fail
149149
non_zero_subtasks: List[str] = list()
150150
for subtask in self.subtasks:
151-
task_result = subtask.run(context=context, parent_env=env)
152-
if task_result and not ignore_fail:
153-
raise ExecutionError(
154-
f"Sequence aborted after failed subtask {subtask.name!r}"
155-
)
151+
try:
152+
task_result = subtask.run(context=context, parent_env=env)
153+
except ExecutionError as error:
154+
if ignore_fail:
155+
print("Warning:", error.msg)
156+
non_zero_subtasks.append(subtask.name)
157+
else:
158+
raise
159+
156160
if task_result:
161+
if not ignore_fail:
162+
raise ExecutionError(
163+
f"Sequence aborted after failed subtask {subtask.name!r}"
164+
)
157165
non_zero_subtasks.append(subtask.name)
158166

159167
if non_zero_subtasks and ignore_fail == "return_non_zero":
168+
plural = "s" if len(non_zero_subtasks) > 1 else ""
160169
raise ExecutionError(
161-
f"Subtasks {', '.join(non_zero_subtasks)} returned non-zero exit status"
170+
f"Subtask{plural} {', '.join(repr(st) for st in non_zero_subtasks)} "
171+
"returned non-zero exit status"
162172
)
163173
return 0
164174

tests/test_ignore_fail.py

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,30 @@
33

44
@pytest.fixture()
55
def generate_pyproject(temp_pyproject):
6-
"""Return function which generates pyproject.toml with a given ignore_fail value."""
6+
def generator(lvl1_ignore_fail=False, lvl2_ignore_fail=False):
7+
def fmt_ignore_fail(value):
8+
if value is True:
9+
return "ignore_fail = true"
10+
elif isinstance(value, str):
11+
return f'ignore_fail = "{value}"'
12+
else:
13+
return ""
714

8-
def generator(ignore_fail):
9-
project_tmpl = """
15+
project_tmpl = f"""
1016
[tool.poe.tasks]
11-
task_1 = { shell = "echo 'task 1 error'; exit 1;" }
12-
task_2 = { shell = "echo 'task 2 error'; exit 1;" }
13-
task_3 = { shell = "echo 'task 3 success'; exit 0;" }
17+
task_0 = "echo 'task 0 success'"
18+
task_1.shell = "echo 'task 1 error'; exit 1;"
19+
task_2.shell = "echo 'task 2 error'; exit 1;"
20+
task_3.shell = "echo 'task 3 success'; exit 0;"
1421
15-
[tool.poe.tasks.all_tasks]
22+
[tool.poe.tasks.lvl1_seq]
1623
sequence = ["task_1", "task_2", "task_3"]
24+
{fmt_ignore_fail(lvl1_ignore_fail)}
25+
26+
[tool.poe.tasks.lvl2_seq]
27+
sequence = ["task_0", "lvl1_seq", "task_3"]
28+
{fmt_ignore_fail(lvl2_ignore_fail)}
1729
"""
18-
if isinstance(ignore_fail, bool) and ignore_fail:
19-
project_tmpl += "\nignore_fail = true"
20-
elif not isinstance(ignore_fail, bool):
21-
project_tmpl += f'\nignore_fail = "{ignore_fail}"'
2230

2331
return temp_pyproject(project_tmpl)
2432

@@ -27,17 +35,17 @@ def generator(ignore_fail):
2735

2836
@pytest.mark.parametrize("fail_value", [True, "return_zero"])
2937
def test_full_ignore(generate_pyproject, run_poe, fail_value):
30-
project_path = generate_pyproject(ignore_fail=fail_value)
31-
result = run_poe("all_tasks", cwd=project_path)
38+
project_path = generate_pyproject(lvl1_ignore_fail=fail_value)
39+
result = run_poe("lvl1_seq", cwd=project_path)
3240
assert result.code == 0, "Expected zero result"
3341
assert "task 1 error" in result.capture, "Expected first task in log"
3442
assert "task 2 error" in result.capture, "Expected second task in log"
3543
assert "task 3 success" in result.capture, "Expected third task in log"
3644

3745

3846
def test_without_ignore(generate_pyproject, run_poe):
39-
project_path = generate_pyproject(ignore_fail=False)
40-
result = run_poe("all_tasks", cwd=project_path)
47+
project_path = generate_pyproject(lvl1_ignore_fail=False)
48+
result = run_poe("lvl1_seq", cwd=project_path)
4149
assert result.code == 1, "Expected non-zero result"
4250
assert "task 1 error" in result.capture, "Expected first task in log"
4351
assert "task 2 error" not in result.capture, "Second task shouldn't run"
@@ -46,20 +54,72 @@ def test_without_ignore(generate_pyproject, run_poe):
4654

4755

4856
def test_return_non_zero(generate_pyproject, run_poe):
49-
project_path = generate_pyproject(ignore_fail="return_non_zero")
50-
result = run_poe("all_tasks", cwd=project_path)
57+
project_path = generate_pyproject(lvl1_ignore_fail="return_non_zero")
58+
result = run_poe("lvl1_seq", cwd=project_path)
5159
assert result.code == 1, "Expected non-zero result"
5260
assert "task 1 error" in result.capture, "Expected first task in log"
5361
assert "task 2 error" in result.capture, "Expected second task in log"
5462
assert "task 3 success" in result.capture, "Expected third task in log"
55-
assert "Subtasks task_1, task_2 returned non-zero exit status" in result.capture
63+
assert "Subtasks 'task_1', 'task_2' returned non-zero exit status" in result.capture
5664

5765

5866
def test_invalid_ignore_value(generate_pyproject, run_poe):
59-
project_path = generate_pyproject(ignore_fail="invalid_value")
60-
result = run_poe("all_tasks", cwd=project_path)
67+
project_path = generate_pyproject(lvl1_ignore_fail="invalid_value")
68+
result = run_poe("lvl1_seq", cwd=project_path)
6169
assert result.code == 1, "Expected non-zero result"
6270
assert (
6371
"| Option 'ignore_fail' must be one of "
6472
"(True, False, 'return_zero', 'return_non_zero')\n"
6573
) in result.capture
74+
75+
76+
def test_nested_without_ignore(generate_pyproject, run_poe):
77+
project_path = generate_pyproject()
78+
result = run_poe("lvl2_seq", cwd=project_path)
79+
assert result.code == 1, "Expected non-zero result"
80+
assert "task 0 success" in result.capture, "Expected zeroth task in log"
81+
assert "task 1 error" in result.capture, "Expected first task in log"
82+
assert "task 2 error" not in result.capture, "Second task shouldn't run"
83+
assert "task 3 success" not in result.capture, "Third task shouldn't run"
84+
assert "Sequence aborted after failed subtask 'task_1'" in result.capture
85+
86+
87+
def test_nested_lvl1_return_non_zero(generate_pyproject, run_poe):
88+
project_path = generate_pyproject(lvl1_ignore_fail="return_non_zero")
89+
result = run_poe("lvl2_seq", cwd=project_path)
90+
assert result.code == 1, "Expected non-zero result"
91+
assert "task 1 error" in result.capture, "Expected first task in log"
92+
assert "task 2 error" in result.capture, "Expected second task in log"
93+
assert "task 3 success" in result.capture, "Expected third task in log"
94+
assert "Subtasks 'task_1', 'task_2' returned non-zero exit status" in result.capture
95+
96+
97+
def test_nested_lvl2_return_non_zero(generate_pyproject, run_poe):
98+
project_path = generate_pyproject(lvl2_ignore_fail="return_non_zero")
99+
result = run_poe("lvl2_seq", cwd=project_path)
100+
assert result.code == 1, "Expected non-zero result"
101+
assert "task 1 error" in result.capture, "Expected first task in log"
102+
assert "task 2 error" not in result.capture, "Expected task 2 to be skipped"
103+
assert "task 3 success" in result.capture, "Expected third task in log"
104+
assert (
105+
"Warning: Sequence aborted after failed subtask 'task_1'" in result.stdout
106+
) # TODO log warnings to capture
107+
assert "Error: Subtask 'lvl1_seq' returned non-zero exit status" in result.capture
108+
109+
110+
def test_nested_both_return_non_zero(generate_pyproject, run_poe):
111+
project_path = generate_pyproject(
112+
lvl1_ignore_fail="return_non_zero", lvl2_ignore_fail="return_non_zero"
113+
)
114+
result = run_poe("lvl2_seq", cwd=project_path)
115+
assert result.code == 1, "Expected non-zero result"
116+
assert "task 1 error" in result.capture, "Expected first task in log"
117+
assert "task 2 error" in result.capture, "Expected second task in log"
118+
assert "task 3 success" in result.capture, "Expected third task in log"
119+
assert (
120+
"Warning: Subtasks 'task_1', 'task_2' returned non-zero exit status"
121+
in result.stdout
122+
) # TODO log warnings to capture
123+
assert (
124+
"Error: Subtask 'lvl1_seq' returned non-zero exit status" in result.capture
125+
) # TODO log warnings to capture

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy