Skip to content

Commit 14f01d3

Browse files
authored
Fix bug and improve test coverage. (#254)
1 parent 4a53f8c commit 14f01d3

17 files changed

+61
-58
lines changed

src/_pytask/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
try:
55
from ._version import version as __version__
6-
except ImportError:
6+
except ImportError: # pragma: no cover
77
# broken installation, we don't even try unknown only works because we do poor mans
88
# version compare
99
__version__ = "unknown"

src/_pytask/compat.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def import_optional_dependency(
7171
version is too old and `errors` is ``'warn'``.
7272
7373
"""
74-
if errors not in ("warn", "raise", "ignore"):
74+
if errors not in ("warn", "raise", "ignore"): # pragma: no cover
7575
raise ValueError("'errors' must be one of 'warn', 'raise' or 'ignore'.")
7676

7777
package_name = _IMPORT_TO_PACKAGE_NAME.get(name)

src/_pytask/config.py

+17-6
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,14 @@ def is_file_system_case_sensitive() -> bool:
7575

7676

7777
_DEPRECATION_MESSAGE = """WARNING: pytask.ini, tox.ini, and setup.cfg will be \
78-
deprecated as configuration files for pytask starting with v0.3 or v1.0. To upgrade \
79-
and silence this warning, copy the content below in a pyproject.toml in the same \
80-
directory as your old configuration file. It is equivalent to your current \
81-
configuration.
78+
deprecated as configuration files for pytask starting with v0.3 or v1.0.
79+
80+
To upgrade and silence this warning, copy the content below in a pyproject.toml in the \
81+
same directory as your old configuration file. This would be the path: {}. The content \
82+
is equivalent to your current configuration.
83+
84+
Even if your configuration just has the header and no values, copy it. pytask needs \
85+
the header to determine the root of your project.
8286
"""
8387

8488

@@ -109,10 +113,17 @@ def pytask_configure(
109113
config_from_file = read_config(config["config"])
110114

111115
if read_config.__name__ == "_read_ini_config":
112-
toml_string = tomli_w.dumps(
116+
toml_string = "# Content of pyproject.toml\n\n" + tomli_w.dumps(
113117
{"tool": {"pytask": {"ini_options": config_from_file}}}
114118
)
115-
console.print(Text(_DEPRECATION_MESSAGE, style="warning"))
119+
console.print(
120+
Text(
121+
_DEPRECATION_MESSAGE.format(
122+
config["config"].with_name("pyproject.toml")
123+
),
124+
style="warning",
125+
)
126+
)
116127
console.print(Syntax(toml_string, "toml"))
117128

118129
# If paths are set in the configuration, process them.

src/_pytask/live.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,9 @@ def pytask_post_parse(config: dict[str, Any]) -> None:
9494

9595
@hookimpl(tryfirst=True)
9696
def pytask_execute_build(session: Session) -> None:
97-
live_execution = session.config["pm"].get_plugin("live_execution")
98-
live_execution.n_tasks = len(session.tasks)
97+
if session.config["verbose"] >= 1:
98+
live_execution = session.config["pm"].get_plugin("live_execution")
99+
live_execution.n_tasks = len(session.tasks)
99100

100101

101102
@attr.s(eq=False)

src/_pytask/outcomes.py

-10
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,6 @@ class CollectionOutcome(Enum):
4242
SUCCESS = auto()
4343
FAIL = auto()
4444

45-
@property
46-
def symbol(self) -> str:
47-
"""The symbol of an outcome."""
48-
symbols = {
49-
CollectionOutcome.SUCCESS: ".",
50-
CollectionOutcome.FAIL: "F",
51-
}
52-
assert len(symbols) == len(CollectionOutcome)
53-
return symbols[self]
54-
5545
@property
5646
def description(self) -> str:
5747
"""A description of an outcome used in the summary panel."""

src/_pytask/profile.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def profile(**config_from_cli: Any) -> NoReturn:
131131
config = pm.hook.pytask_configure(pm=pm, config_from_cli=config_from_cli)
132132
session = Session.from_config(config)
133133

134-
except (ConfigurationError, Exception):
134+
except (ConfigurationError, Exception): # pragma: no cover
135135
session = Session({}, None)
136136
session.exit_code = ExitCode.CONFIGURATION_FAILED
137137
exc_info: tuple[
@@ -159,10 +159,10 @@ def profile(**config_from_cli: Any) -> NoReturn:
159159

160160
console.rule(style="neutral")
161161

162-
except CollectionError:
162+
except CollectionError: # pragma: no cover
163163
session.exit_code = ExitCode.COLLECTION_FAILED
164164

165-
except Exception:
165+
except Exception: # pragma: no cover
166166
session.exit_code = ExitCode.FAILED
167167
console.print_exception()
168168
console.rule(style="failed")
@@ -288,7 +288,7 @@ def pytask_profile_export_profile(
288288
_export_to_json(profile)
289289
elif export == _ExportFormats.NO:
290290
pass
291-
else:
291+
else: # pragma: no cover
292292
raise ValueError(f"The export option {export.value!r} cannot be handled.")
293293

294294

src/_pytask/traceback.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ def render_exc_info(
3434
traceback: str | TracebackType,
3535
show_locals: bool = False,
3636
) -> str | Traceback:
37-
if isinstance(traceback, str):
37+
"""Render an exception info."""
38+
# Can be string if send from subprocess by pytask-parallel.
39+
if isinstance(traceback, str): # pragma: no cover
3840
renderable = traceback
3941
else:
4042
renderable = Traceback.from_exception(

tests/_test_console_helpers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
def empty_decorator(func):
77
@functools.wraps(func)
8-
def wrapped():
8+
def wrapped(): # pragma: no cover
99
return func()
1010

1111
return wrapped

tests/test_capture.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def task_show_capture():
5656
assert "xxxx" in result.output
5757
assert "Captured stderr" in result.output
5858
assert "zzzz" in result.output
59-
else:
59+
else: # pragma: no cover
6060
raise NotImplementedError
6161

6262

@@ -360,7 +360,11 @@ def lsof_check():
360360
pid = os.getpid()
361361
try:
362362
out = subprocess.check_output(("lsof", "-p", str(pid))).decode()
363-
except (OSError, subprocess.CalledProcessError, UnicodeDecodeError) as exc:
363+
except (
364+
OSError,
365+
subprocess.CalledProcessError,
366+
UnicodeDecodeError,
367+
) as exc: # pragma: no cover
364368
# about UnicodeDecodeError, see note on pytester
365369
pytest.skip(f"could not run 'lsof' ({exc!r})")
366370
yield

tests/test_cli.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ def test_version_option():
1919
@pytest.mark.parametrize(
2020
"commands",
2121
[
22-
("pytask",),
23-
("pytask", "build"),
24-
("pytask", "clean"),
25-
("pytask", "collect"),
26-
("pytask", "dag"),
27-
("pytask", "markers"),
28-
("pytask", "profile"),
22+
(),
23+
("build"),
24+
("clean"),
25+
("collect"),
26+
("dag"),
27+
("markers"),
28+
("profile"),
2929
],
3030
)
3131
def test_help_pages(runner, commands, help_option):

tests/test_collect_utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def test_merge_dictionaries(list_of_dicts, expected):
116116
)
117117
def test_extract_args_from_mark(decorator, values, expected):
118118
@decorator(values)
119-
def task_example():
119+
def task_example(): # pragma: no cover
120120
pass
121121

122122
parser = depends_on if decorator.name == "depends_on" else produces
@@ -136,7 +136,7 @@ def task_example():
136136
)
137137
def test_extract_kwargs_from_mark(decorator, values, expected):
138138
@decorator(**values)
139-
def task_example():
139+
def task_example(): # pragma: no cover
140140
pass
141141

142142
parser = depends_on if decorator.name == "depends_on" else produces
@@ -153,7 +153,7 @@ def test_raise_error_for_invalid_args_to_depends_on_and_produces(
153153
decorator, args, kwargs
154154
):
155155
@decorator(*args, **kwargs)
156-
def task_example():
156+
def task_example(): # pragma: no cover
157157
pass
158158

159159
parser = depends_on if decorator.name == "depends_on" else produces

tests/test_debugging.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
try:
1515
import pexpect
16-
except ModuleNotFoundError:
16+
except ModuleNotFoundError: # pragma: no cover
1717
IS_PEXPECT_INSTALLED = False
1818
else:
1919
IS_PEXPECT_INSTALLED = True

tests/test_graph.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
try:
1313
import pydot # noqa: F401
14-
except ImportError:
14+
except ImportError: # pragma: no cover
1515
_IS_PYDOT_INSTALLED = False
1616
else:
1717
_IS_PYDOT_INSTALLED = True
@@ -39,7 +39,7 @@
3939
@pytest.mark.parametrize("format_", _TEST_FORMATS)
4040
@pytest.mark.parametrize("rankdir", ["LR"])
4141
def test_create_graph_via_cli(tmp_path, runner, format_, layout, rankdir):
42-
if sys.platform == "win32" and format_ == "pdf":
42+
if sys.platform == "win32" and format_ == "pdf": # pragma: no cover
4343
pytest.xfail("gvplugin_pango.dll might be missing on Github Actions.")
4444

4545
source = """
@@ -75,7 +75,7 @@ def task_example(): pass
7575
@pytest.mark.parametrize("format_", _TEST_FORMATS)
7676
@pytest.mark.parametrize("rankdir", [_RankDirection.LR.value, _RankDirection.TB])
7777
def test_create_graph_via_task(tmp_path, runner, format_, layout, rankdir):
78-
if sys.platform == "win32" and format_ == "pdf":
78+
if sys.platform == "win32" and format_ == "pdf": # pragma: no cover
7979
pytest.xfail("gvplugin_pango.dll might be missing on Github Actions.")
8080

8181
rankdir_str = rankdir if isinstance(rankdir, str) else rankdir.name

tests/test_live.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,16 @@ def test_parse_n_entries_in_table(value, expectation, expected):
4343

4444

4545
@pytest.mark.end_to_end
46-
@pytest.mark.parametrize("verbose", [False, True])
46+
@pytest.mark.parametrize("verbose", [0, 1])
4747
def test_verbose_mode_execution(tmp_path, runner, verbose):
4848
source = "def task_example(): pass"
4949
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(source))
5050

51-
args = [tmp_path.as_posix()]
52-
if not verbose:
53-
args.append("-v 0")
54-
result = runner.invoke(cli, args)
51+
result = runner.invoke(cli, [tmp_path.as_posix(), "--verbose", verbose])
5552

56-
assert ("Task" in result.output) is verbose
57-
assert ("Outcome" in result.output) is verbose
58-
assert ("task_module.py::task_example" in result.output) is verbose
53+
assert ("Task" in result.output) is (verbose >= 1)
54+
assert ("Outcome" in result.output) is (verbose >= 1)
55+
assert ("task_module.py::task_example" in result.output) is (verbose >= 1)
5956

6057

6158
@pytest.mark.unit

tests/test_mark.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def test_pytask_mark_notcallable() -> None:
2727
@pytest.mark.unit
2828
@pytest.mark.filterwarnings("ignore:Unknown pytask.mark.foo")
2929
def test_mark_with_param():
30-
def some_function():
30+
def some_function(): # pragma: no cover
3131
pass
3232

3333
class SomeClass:

tests/test_parametrize.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def session():
3232
@pytest.mark.integration
3333
def test_pytask_generate_tasks_0(session):
3434
@pytask.mark.parametrize("i", range(2))
35-
def func(i): # noqa: U100
35+
def func(i): # noqa: U100, pragma: no cover
3636
pass
3737

3838
names_and_objs = pytask_parametrize_task(session, "func", func)
@@ -47,7 +47,7 @@ def func(i): # noqa: U100
4747
def test_pytask_generate_tasks_1(session):
4848
@pytask.mark.parametrize("j", range(2))
4949
@pytask.mark.parametrize("i", range(2))
50-
def func(i, j): # noqa: U100
50+
def func(i, j): # noqa: U100, pragma: no cover
5151
pass
5252

5353
pytask_parametrize_task(session, "func", func)
@@ -58,7 +58,7 @@ def func(i, j): # noqa: U100
5858
def test_pytask_generate_tasks_2(session):
5959
@pytask.mark.parametrize("j, k", itertools.product(range(2), range(2)))
6060
@pytask.mark.parametrize("i", range(2))
61-
def func(i, j, k): # noqa: U100
61+
def func(i, j, k): # noqa: U100, pragma: no cover
6262
pass
6363

6464
pytask_parametrize_task(session, "func", func)

tests/test_task.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,6 @@ def task_example(produces):
338338

339339

340340
@pytest.mark.end_to_end
341-
@pytest.mark.xfail(reason="Partialed functions are not supported.")
342341
def test_task_function_with_partialed_args(tmp_path, runner):
343342
source = """
344343
import pytask
@@ -355,9 +354,8 @@ def func(produces, content):
355354

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

358-
assert result.exit_code == ExitCode.OK
359-
assert "task_func" in result.output
360-
assert "1 Skipped" in result.output
357+
assert result.exit_code == ExitCode.FAILED
358+
assert "TypeError: module, class, method" in result.output
361359

362360

363361
@pytest.mark.end_to_end

0 commit comments

Comments
 (0)