Skip to content

Commit 4bd60bf

Browse files
authored
Allow to pass functions to PythonNode(hash=...). (#410)
1 parent 453faf0 commit 4bd60bf

File tree

3 files changed

+27
-4
lines changed

3 files changed

+27
-4
lines changed

docs/source/changes.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
2424
- {pull}`402` replaces ABCs with protocols allowing for more flexibility for users
2525
implementing their own nodes.
2626
- {pull}`404` allows to use function returns to define task products.
27-
- {pull}`405` allows to match function returns to node annotations with prefix trees.
28-
- {pull}`406` removes `.value` from `Node` protocol.
29-
- {pull}`407` make `.from_annot` an optional feature of nodes.
27+
- {pull}`406` allows to match function returns to node annotations with prefix trees.
28+
- {pull}`408` removes `.value` from `Node` protocol.
29+
- {pull}`409` make `.from_annot` an optional feature of nodes.
30+
- {pull}`410` allows to pass functions to `PythonNode(hash=...)`.
3031

3132
## 0.3.2 - 2023-06-07
3233

src/_pytask/nodes.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ class PythonNode(Node):
174174
"""Name of the node."""
175175
value: Any = None
176176
"""Value of the node."""
177-
hash: bool = False # noqa: A003
177+
hash: bool | Callable[[Any], bool] = False # noqa: A003
178178
"""Whether the value should be hashed to determine the state."""
179179

180180
def load(self) -> Any:
@@ -206,6 +206,8 @@ def state(self) -> str | None:
206206
If ``hash = False``, the function returns ``"0"``, a constant hash value, so the
207207
:class:`PythonNode` is ignored when checking for a changed state of the task.
208208
209+
If ``hash`` is a callable, then use this function to calculate a hash.
210+
209211
If ``hash = True``, :func:`hash` is used for all types except strings.
210212
211213
The hash for strings is calculated using hashlib because ``hash("asd")`` returns
@@ -214,6 +216,8 @@ def state(self) -> str | None:
214216
215217
"""
216218
if self.hash:
219+
if callable(self.hash):
220+
return str(self.hash(self.value))
217221
if isinstance(self.value, str):
218222
return str(hashlib.sha256(self.value.encode()).hexdigest())
219223
return str(hash(self.value))

tests/test_nodes.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pathlib import Path
55

66
import pytest
7+
from _pytask.nodes import PythonNode
78
from _pytask.shared import reduce_node_name
89
from pytask import PathNode
910

@@ -35,3 +36,20 @@ def test_reduce_node_name(node, paths, expectation, expected):
3536
with expectation:
3637
result = reduce_node_name(node, paths)
3738
assert result == expected
39+
40+
41+
@pytest.mark.unit()
42+
@pytest.mark.parametrize(
43+
("value", "hash_", "expected"),
44+
[
45+
(0, False, "0"),
46+
(0, True, "0"),
47+
(0, lambda x: 1, "1"), # noqa: ARG005
48+
("0", False, "0"),
49+
("0", True, "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9"),
50+
],
51+
)
52+
def test_hash_of_python_node(value, hash_, expected):
53+
node = PythonNode(name="test", value=value, hash=hash_)
54+
state = node.state()
55+
assert state == expected

0 commit comments

Comments
 (0)