Skip to content

Commit 05c3b6d

Browse files
authored
Replace pybaum with optree and add paths to PythonNode names. (#396)
1 parent 47e073e commit 05c3b6d

22 files changed

Lines changed: 436 additions & 174 deletions

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ repos:
4949
rev: 1.5.0
5050
hooks:
5151
- id: interrogate
52-
args: [-v, --fail-under=75]
52+
args: [-vv, --fail-under=75]
5353
exclude: ^(tests/|docs/|scripts/)
5454
- repo: https://github.com/pre-commit/mirrors-mypy
5555
rev: 'v1.3.0'

docs/rtd_environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ dependencies:
2525
- click-default-group
2626
- networkx >=2.4
2727
- pluggy
28-
- pybaum >=0.1.1
28+
- optree >=0.9
2929
- pexpect
3030
- rich
3131
- sqlalchemy >=1.4.36

docs/source/changes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
1515
- {pull}`394` allows to add products with {obj}`typing.Annotation` and
1616
{obj}`~pytask.Product`.
1717
- {pull}`395` refactors all occurrences of pybaum to {mod}`_pytask.tree_util`.
18+
- {pull}`396` replaces pybaum with optree and adds paths to the name of
19+
{class}`pytask.PythonNode`'s allowing for better hashing.
1820

1921
## 0.3.2 - 2023-06-07
2022

docs/source/reference_guides/api.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,12 @@ Then, different kinds of nodes can be implemented.
247247

248248
```{eval-rst}
249249
.. autoclass:: pytask.FilePathNode
250+
:members:
251+
```
252+
253+
```{eval-rst}
254+
.. autoclass:: pytask.PythonNode
255+
:members:
250256
```
251257

252258
To parse dependencies and products from nodes, use the following functions.

environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ dependencies:
1616
- click-default-group
1717
- networkx >=2.4
1818
- pluggy
19-
- pybaum >=0.1.1
19+
- optree >=0.9
2020
- rich
2121
- sqlalchemy >=1.4.36
2222
- tomli >=1.0.0

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ install_requires =
3434
click
3535
click-default-group
3636
networkx>=2.4
37+
optree>=0.9
3738
packaging
3839
pluggy
39-
pybaum>=0.1.1
4040
rich
4141
sqlalchemy>=1.4.36
4242
tomli>=1.0.0

src/_pytask/clean.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from _pytask.session import Session
3030
from _pytask.shared import to_list
3131
from _pytask.traceback import render_exc_info
32-
from _pytask.tree_util import tree_just_yield
32+
from _pytask.tree_util import tree_leaves
3333
from attrs import define
3434

3535

@@ -218,7 +218,7 @@ def _yield_paths_from_task(task: Task) -> Generator[Path, None, None]:
218218
"""Yield all paths attached to a task."""
219219
yield task.path
220220
for attribute in ("depends_on", "produces"):
221-
for node in tree_just_yield(getattr(task, attribute)):
221+
for node in tree_leaves(getattr(task, attribute)):
222222
if hasattr(node, "path") and isinstance(node.path, Path):
223223
yield node.path
224224

src/_pytask/collect.py

Lines changed: 20 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
from typing import Generator
1212
from typing import Iterable
1313

14-
from _pytask.collect_utils import depends_on
1514
from _pytask.collect_utils import parse_dependencies_from_task_function
16-
from _pytask.collect_utils import parse_nodes
1715
from _pytask.collect_utils import parse_products_from_task_function
1816
from _pytask.config import hookimpl
1917
from _pytask.config import IS_FILE_SYSTEM_CASE_SENSITIVE
@@ -22,7 +20,9 @@
2220
from _pytask.console import format_task_id
2321
from _pytask.exceptions import CollectionError
2422
from _pytask.mark_utils import has_mark
23+
from _pytask.models import NodeInfo
2524
from _pytask.nodes import FilePathNode
25+
from _pytask.nodes import MetaNode
2626
from _pytask.nodes import PythonNode
2727
from _pytask.nodes import Task
2828
from _pytask.outcomes import CollectionOutcome
@@ -169,13 +169,7 @@ def pytask_collect_task(
169169
170170
"""
171171
if (name.startswith("task_") or has_mark(obj, "task")) and callable(obj):
172-
if has_mark(obj, "depends_on"):
173-
nodes = parse_nodes(session, path, name, obj, depends_on)
174-
dependencies = {"depends_on": nodes}
175-
else:
176-
dependencies = parse_dependencies_from_task_function(
177-
session, path, name, obj
178-
)
172+
dependencies = parse_dependencies_from_task_function(session, path, name, obj)
179173

180174
products = parse_products_from_task_function(session, path, name, obj)
181175

@@ -210,9 +204,7 @@ def pytask_collect_task(
210204

211205

212206
@hookimpl(trylast=True)
213-
def pytask_collect_node(
214-
session: Session, path: Path, node: str | Path
215-
) -> FilePathNode | PythonNode:
207+
def pytask_collect_node(session: Session, path: Path, node_info: NodeInfo) -> MetaNode:
216208
"""Collect a node of a task as a :class:`pytask.nodes.FilePathNode`.
217209
218210
Strings are assumed to be paths. This might be a strict assumption, but since this
@@ -222,17 +214,18 @@ def pytask_collect_node(
222214
``trylast=True`` might be necessary if other plugins try to parse strings themselves
223215
like a plugin for downloading files which depends on URLs given as strings.
224216
225-
Parameters
226-
----------
227-
session : _pytask.session.Session
228-
The session.
229-
path : Union[str, pathlib.Path]
230-
The path to file where the task and node are specified.
231-
node : Union[str, pathlib.Path]
232-
The value of the node which can be a str, a path or anything which cannot be
233-
handled by this function.
234-
235217
"""
218+
node = node_info.value
219+
220+
if isinstance(node, PythonNode):
221+
if not node.name:
222+
suffix = "-" + "-".join(map(str, node_info.path)) if node_info.path else ""
223+
node.name = node_info.arg_name + suffix
224+
return node
225+
226+
if isinstance(node, MetaNode):
227+
return node
228+
236229
if isinstance(node, Path):
237230
if not node.is_absolute():
238231
node = path.parent.joinpath(node)
@@ -251,7 +244,10 @@ def pytask_collect_node(
251244
raise ValueError(_TEMPLATE_ERROR.format(node, case_sensitive_path))
252245

253246
return FilePathNode.from_path(node)
254-
return PythonNode(value=node)
247+
248+
suffix = "-" + "-".join(map(str, node_info.path)) if node_info.path else ""
249+
node_name = node_info.arg_name + suffix
250+
return PythonNode(value=node, name=node_name)
255251

256252

257253
def _not_ignored_paths(
@@ -263,18 +259,6 @@ def _not_ignored_paths(
263259
directories, all subsequent files and folders are considered, but one level after
264260
another, so that files of ignored folders are not checked.
265261
266-
Parameters
267-
----------
268-
paths : Iterable[pathlib.Path]
269-
List of paths from which tasks are collected.
270-
session : _pytask.session.Session
271-
The session.
272-
273-
Yields
274-
------
275-
path : pathlib.Path
276-
A path which is not ignored.
277-
278262
"""
279263
for path in paths:
280264
if not session.hook.pytask_ignore_collect(path=path, config=session.config):
@@ -287,11 +271,7 @@ def _not_ignored_paths(
287271

288272
@hookimpl(trylast=True)
289273
def pytask_collect_modify_tasks(tasks: list[Task]) -> None:
290-
"""Given all tasks, assign a short uniquely identifiable name to each task.
291-
292-
The shorter ids are necessary to display
293-
294-
"""
274+
"""Given all tasks, assign a short uniquely identifiable name to each task."""
295275
id_to_short_id = _find_shortest_uniquely_identifiable_name_for_tasks(tasks)
296276
for task in tasks:
297277
short_id = id_to_short_id[task.name]

src/_pytask/collect_command.py

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from _pytask.path import relative_to
2727
from _pytask.pluginmanager import get_plugin_manager
2828
from _pytask.session import Session
29-
from _pytask.tree_util import tree_just_flatten
29+
from _pytask.tree_util import tree_leaves
3030
from rich.text import Text
3131
from rich.tree import Tree
3232

@@ -126,10 +126,10 @@ def _find_common_ancestor_of_all_nodes(
126126
if show_nodes:
127127
all_paths.extend(
128128
x.path
129-
for x in tree_just_flatten(task.depends_on)
129+
for x in tree_leaves(task.depends_on)
130130
if isinstance(x, FilePathNode)
131131
)
132-
all_paths.extend(x.path for x in tree_just_flatten(task.produces))
132+
all_paths.extend(x.path for x in tree_leaves(task.produces))
133133

134134
common_ancestor = find_common_ancestor(*all_paths, *paths)
135135

@@ -150,7 +150,7 @@ def _organize_tasks(tasks: list[Task]) -> dict[Path, list[Task]]:
150150

151151
sorted_dict = {}
152152
for k in sorted(dictionary):
153-
sorted_dict[k] = sorted(dictionary[k], key=lambda x: x.path)
153+
sorted_dict[k] = sorted(dictionary[k], key=lambda x: x.name)
154154

155155
return sorted_dict
156156

@@ -202,36 +202,24 @@ def _print_collected_tasks(
202202
)
203203

204204
if show_nodes:
205-
file_path_nodes = [
206-
i
207-
for i in tree_just_flatten(task.depends_on)
208-
if isinstance(i, FilePathNode)
209-
]
210-
sorted_nodes = sorted(file_path_nodes, key=lambda x: x.path)
205+
file_path_nodes = list(tree_leaves(task.depends_on))
206+
sorted_nodes = sorted(file_path_nodes, key=lambda x: x.name)
211207
for node in sorted_nodes:
212-
reduced_node_name = relative_to(node.path, common_ancestor)
213-
url_style = create_url_style_for_path(node.path, editor_url_scheme)
214-
task_branch.add(
215-
Text.assemble(
216-
FILE_ICON,
217-
"<Dependency ",
218-
Text(str(reduced_node_name), style=url_style),
219-
">",
208+
if isinstance(node, FilePathNode):
209+
reduced_node_name = relative_to(node.path, common_ancestor)
210+
url_style = create_url_style_for_path(
211+
node.path, editor_url_scheme
220212
)
221-
)
213+
text = Text(str(reduced_node_name), style=url_style)
214+
else:
215+
text = node.name
222216

223-
for node in sorted(
224-
tree_just_flatten(task.produces), key=lambda x: x.path
225-
):
217+
task_branch.add(Text.assemble(FILE_ICON, "<Dependency ", text, ">"))
218+
219+
for node in sorted(tree_leaves(task.produces), key=lambda x: x.path):
226220
reduced_node_name = relative_to(node.path, common_ancestor)
227221
url_style = create_url_style_for_path(node.path, editor_url_scheme)
228-
task_branch.add(
229-
Text.assemble(
230-
FILE_ICON,
231-
"<Product ",
232-
Text(str(reduced_node_name), style=url_style),
233-
">",
234-
)
235-
)
222+
text = Text(str(reduced_node_name), style=url_style)
223+
task_branch.add(Text.assemble(FILE_ICON, "<Product ", text, ">"))
236224

237225
console.print(tree)

0 commit comments

Comments
 (0)