Skip to content

Commit ac2a5e3

Browse files
authored
Collect automatically created tasks. (#325)
1 parent eaee128 commit ac2a5e3

File tree

5 files changed

+84
-22
lines changed

5 files changed

+84
-22
lines changed

docs/source/changes.md

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
# Changes
22

3-
This is a record of all past pytask releases and what went into them in reverse
3+
The document records all past pytask releases and what went into them in reverse
44
chronological order. Releases follow [semantic versioning](https://semver.org/) and all
55
releases are available on [PyPI](https://pypi.org/project/pytask) and
66
[Anaconda.org](https://anaconda.org/conda-forge/pytask).
77

8-
## 0.2.7 - 2022-xx-xx
8+
## 0.2.7 - 2022-12-14
99

10+
- {pull}`307` adds Python 3.11 to the CI.
1011
- {pull}`308` replaces pydot with pygraphviz.
11-
- {pull}`318` Clarifies an example on nested dependencies and products.
12+
- {pull}`311` fixes a link in the documentation.
13+
- {pull}`311` adds refurb to pre-commit hooks.
14+
- {pull}`318` clarifies an example on nested dependencies and products.
1215
- {pull}`321` converts more choice options to enums.
1316
- {pull}`322` replaces SVGs with animations by termynal.
17+
- {pull}`325` allows to collect dynamically created tasks.
1418

1519
## 0.2.6 - 2022-10-27
1620

@@ -24,16 +28,16 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
2428
- {pull}`289` shortens the task ids when using `pytask collect`. Fixes {issue}`286`.
2529
- {pull}`290` implements a dry-run with `pytask --dry-run` to see which tasks would be
2630
executed.
27-
- {pull}`296` fixes a bug where the source code of wrapped function could not be
31+
- {pull}`296` fixes a bug where the source code of the wrapped function could not be
2832
retrieved.
2933

3034
## 0.2.4 - 2022-06-28
3135

3236
- {pull}`279` enhances some tutorials with spell and grammar checking.
3337
- {pull}`282` updates the tox configuration.
3438
- {pull}`283` fixes an issue with coverage and tests using pexpect + `pdb.set_trace()`.
35-
- {pull}`285` implements that pytask does not show the traceback of tasks which are
36-
skipped because its previous task failed. Fixes {issue}`284`.
39+
- {pull}`285` implements that pytask does not show the traceback of tasks that are
40+
skipped because their previous task failed. Fixes {issue}`284`.
3741
- {pull}`287` changes that all files that are not produced by a task are displayed in
3842
the error message. Fixes {issue}`262`.
3943

@@ -82,7 +86,7 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
8286
- {pull}`237` publish more functions.
8387
- {pull}`238` allows any order of decorators with a `@pytask.mark.task` decorator.
8488
- {pull}`239` adds a warning on globals for parametrizations and some fixes.
85-
- {pull}`241` allows to parametrize over single dicts.
89+
- {pull}`241` allows parametrizing over single dicts.
8690
- {pull}`242` removes tasks from global {obj}`_pytask.task_utils.COLLECTED_TASKS` to
8791
avoid re-collection when the programmatic interface is used.
8892
- {pull}`243` converts choice options to use enums instead of simple strings.
@@ -112,7 +116,7 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
112116

113117
## 0.1.8 - 2022-02-07
114118

115-
- {pull}`210` allows `__tracebackhide__` to be a callable which accepts the current
119+
- {pull}`210` allows `__tracebackhide__` to be a callable that accepts the current
116120
exception as an input. Closes {issue}`145`.
117121
- {pull}`213` improves coverage and reporting.
118122
- {pull}`215` makes the help pages of the CLI prettier.
@@ -135,7 +139,7 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
135139
- {pull}`199` extends the error message when paths are ambiguous on case-insensitive
136140
file systems.
137141
- {pull}`200` implements the {func}`@pytask.mark.task <pytask.mark.task>` decorator to
138-
mark functions as tasks regardless whether they are prefixed with `task_` or not.
142+
mark functions as tasks regardless of whether they are prefixed with `task_` or not.
139143
- {pull}`201` adds tests for `_pytask.mark_utils`.
140144
- {pull}`204` removes internal traceback frames from exceptions raised somewhere in
141145
pytask.
@@ -149,7 +153,7 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
149153
in many places.
150154
- {pull}`185` fix issues with drawing a graph and adds the `--rank-direction` to change
151155
the direction of the DAG.
152-
- {pull}`186` enhance live displays by deactivating auto-refresh among other things.
156+
- {pull}`186` enhance live displays by deactivating auto-refresh, among other things.
153157
- {pull}`187` allows to enable and disable showing tracebacks and potentially different
154158
styles in the future with {confval}`show_traceback=True|False`.
155159
- {pull}`188` refactors some code related to {class}`_pytask.enums.ExitCode`.
@@ -170,10 +174,10 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
170174
information.
171175
- {pull}`173` replaces `ColorCode` with custom rich themes.
172176
- {pull}`174` restructures loosely defined outcomes to clear `enum.Enum`.
173-
- {pull}`176` and {pull}`177` implement a summary panel which holds aggregate
174-
information about the number of successes, fails and other status.
175-
- {pull}`178` makes some stylistic changes like reducing tasks ids even more and dims
176-
the path part.
177+
- {pull}`176` and {pull}`177` implement a summary panel that holds aggregate information
178+
about the number of successes, fails and other status.
179+
- {pull}`178` adds stylistic changes like reducing tasks ids even more and dimming the
180+
path part.
177181
- {pull}`180` fixes parsing relative paths from the configuration file.
178182
- {pull}`181` adds correct formatting of running tasks.
179183
- {pull}`182` introduces that only the starting year is displayed in the license

src/_pytask/console.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,20 @@ def create_url_style_for_task(
172172
"""Create the style to add a link to a task id."""
173173
url_scheme = _EDITOR_URL_SCHEMES.get(edtior_url_scheme, edtior_url_scheme)
174174

175-
info = {
176-
"path": _get_file(task_function),
177-
"line_number": _get_source_lines(task_function),
178-
}
175+
if not url_scheme:
176+
return Style()
177+
178+
try:
179+
info = {
180+
"path": _get_file(task_function),
181+
"line_number": _get_source_lines(task_function),
182+
}
183+
except (OSError, TypeError):
184+
style = Style()
185+
else:
186+
style = Style(link=url_scheme.format(**info))
179187

180-
return Style() if not url_scheme else Style(link=url_scheme.format(**info))
188+
return style
181189

182190

183191
def create_url_style_for_path(path: Path, edtior_url_scheme: str) -> Style:

src/_pytask/task_utils.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,13 @@ def task(
5252

5353
def wrapper(func: Callable[..., Any]) -> None:
5454
unwrapped = inspect.unwrap(func)
55-
path = Path(inspect.getfile(unwrapped)).absolute().resolve()
55+
56+
raw_path = inspect.getfile(unwrapped)
57+
if "<string>" in raw_path:
58+
path = Path(unwrapped.__globals__["__file__"]).absolute().resolve()
59+
else:
60+
path = Path(raw_path).absolute().resolve()
61+
5662
parsed_kwargs = {} if kwargs is None else kwargs
5763
parsed_name = name if isinstance(name, str) else func.__name__
5864

tests/test_execute.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,22 @@ def task_second(): pass
351351
assert ("Task task_example.py::task_second failed" in result.output) is (
352352
verbose == 2
353353
)
354+
355+
356+
@pytest.mark.end_to_end
357+
def test_that_dynamically_creates_tasks_are_captured(runner, tmp_path):
358+
source = """
359+
_DEFINITION = '''
360+
def task_example():
361+
pass
362+
'''
363+
364+
exec(_DEFINITION)
365+
"""
366+
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(source))
367+
368+
result = runner.invoke(cli, [tmp_path.as_posix()])
369+
370+
assert result.exit_code == ExitCode.OK
371+
assert "task_example" in result.output
372+
assert "Collect 1 task"

tests/test_task.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,9 @@ def func(produces, content):
354354

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

357-
assert result.exit_code == ExitCode.FAILED
358-
assert "TypeError: module, class, method" in result.output
357+
assert result.exit_code == ExitCode.OK
358+
assert "Collected 1 task." in result.output
359+
assert "1 Succeeded" in result.output
359360

360361

361362
@pytest.mark.end_to_end
@@ -403,3 +404,27 @@ def task_example():
403404
assert "task_example[1]" in result.output
404405
assert "task_example[hello]" in result.output
405406
assert "Collect 3 tasks"
407+
408+
409+
@pytest.mark.end_to_end
410+
def test_that_dynamically_creates_tasks_are_captured(runner, tmp_path):
411+
source = """
412+
import pytask
413+
414+
_DEFINITION = '''
415+
@pytask.mark.task
416+
def task_example():
417+
pass
418+
'''
419+
420+
for i in range(2):
421+
exec(_DEFINITION)
422+
"""
423+
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(source))
424+
425+
result = runner.invoke(cli, [tmp_path.as_posix()])
426+
427+
assert result.exit_code == ExitCode.OK
428+
assert "task_example[0]" in result.output
429+
assert "task_example[1]" in result.output
430+
assert "Collect 2 tasks"

0 commit comments

Comments
 (0)