66from pathlib import Path
77from typing import Any
88from typing import Callable
9+ from typing import NoReturn
910from typing import TYPE_CHECKING
1011
1112from _pytask .node_protocols import MetaNode
1213from _pytask .node_protocols import Node
1314from _pytask .tree_util import PyTree
15+ from _pytask .tree_util import tree_leaves
16+ from _pytask .tree_util import tree_structure
1417from attrs import define
1518from attrs import field
1619
@@ -76,7 +79,18 @@ def execute(self, **kwargs: Any) -> None:
7679 out = self .function (** kwargs )
7780
7881 if "return" in self .produces :
79- self .produces ["return" ].save (out )
82+ structure_out = tree_structure (out )
83+ structure_return = tree_structure (self .produces ["return" ])
84+ if not structure_out == structure_return :
85+ raise ValueError (
86+ "The structure of the function return does not match the structure "
87+ "of the return annotation."
88+ )
89+
90+ for out_ , return_ in zip (
91+ tree_leaves (out ), tree_leaves (self .produces ["return" ])
92+ ):
93+ return_ .save (out_ )
8094
8195 def add_report_section (self , when : str , key : str , content : str ) -> None :
8296 """Add sections which will be displayed in report like stdout or stderr."""
@@ -90,25 +104,20 @@ class PathNode(Node):
90104
91105 name : str = ""
92106 """Name of the node which makes it identifiable in the DAG."""
93- _value : Path | None = None
107+ value : Path | None = None
94108 """Value passed to the decorator which can be requested inside the function."""
95109
96110 @property
97111 def path (self ) -> Path :
98112 return self .value
99113
100- @property
101- def value (self ) -> Path :
102- return self ._value
103-
104- @value .setter
105- def value (self , value : Path ) -> None :
114+ def set_value (self , value : Path ) -> None :
106115 """Set path and if other attributes are not set, set sensible defaults."""
107116 if not isinstance (value , Path ):
108117 raise TypeError ("'value' must be a 'pathlib.Path'." )
109118 if not self .name :
110119 self .name = value .as_posix ()
111- self ._value = value
120+ self .value = value
112121
113122 @classmethod
114123 @functools .lru_cache
@@ -153,11 +162,20 @@ class PythonNode(Node):
153162
154163 name : str = ""
155164 """Name of the node."""
156- value : Any | None = None
165+ value : Any = None
157166 """Value of the node."""
158167 hash : bool = False # noqa: A003
159168 """Whether the value should be hashed to determine the state."""
160169
170+ def load (self ) -> Any :
171+ return self .value
172+
173+ def save (self , value : Any ) -> NoReturn :
174+ raise NotImplementedError
175+
176+ def set_value (self , value : Any ) -> None :
177+ self .value = value
178+
161179 def state (self ) -> str | None :
162180 """Calculate state of the node.
163181
0 commit comments