Skip to content

Commit 3d60186

Browse files
committed
dereference_from_node_arguments: add original node id to nodes
for Open-EO/openeo-python-driver#479
1 parent 43ea500 commit 3d60186

4 files changed

Lines changed: 175 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Changed
1515

16+
- `ProcessGraphVisitor.dereference_from_node_arguments()`: add original node id (by default) to the dereferenced nodes in the process graph ([Open-EO/openeo-python-driver#479](https://github.com/Open-EO/openeo-python-driver/issues/479))
17+
1618
### Removed
1719

1820
### Fixed

openeo/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.50.0a1"
1+
__version__ = "0.50.0a2"

openeo/internal/process_graph_visitor.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ class ProcessGraphVisitException(OpenEoClientException):
1212
pass
1313

1414

15-
# TODO: this key is non-standard and mainly specific to a particular openeo-python-driver use case,
15+
# TODO: these keys are non-standard and mainly specific to a particular openeo-python-driver use case,
1616
# which probably should be refactored out from this more generic implementation.
1717
# Making it a named constant at least makes this more visible and trackable.
1818
DEREFERENCED_NODE_KEY = "node"
19+
ORIG_NODE_ID_KEY = "_node_id"
1920

2021

2122
class ProcessGraphVisitor(ABC):
@@ -27,18 +28,23 @@ def __init__(self):
2728
self.process_stack = []
2829

2930
@classmethod
30-
def dereference_from_node_arguments(cls, process_graph: dict) -> str:
31+
def dereference_from_node_arguments(cls, process_graph: dict, add_node_id: bool = True) -> str:
3132
"""
3233
Walk through the given (flat) process graph and replace (in-place) "from_node" references in
3334
process arguments (dictionaries or lists) with the corresponding resolved subgraphs
3435
3536
:param process_graph: process graph dictionary (flat representation) to be manipulated in-place
37+
:param add_node_id: whether to add the original node id in the node dict (alongside "process_id", "arguments", ...)
3638
:return: name of the "result" node of the graph
3739
"""
3840

3941
# TODO avoid manipulating process graph in place? make it more explicit? work on a copy?
4042
# TODO call it more something like "unflatten"?. Split this off of ProcessGraphVisitor?
4143
# TODO implement this through `ProcessGraphUnflattener` ?
44+
# TODO the "there can only be one result node" view is outdated and is getting counter-produtive
45+
# or more generally, the dereferencing aspect in the current process graph processing approach
46+
# makes some things unnecessarily complicated (e.g. caching, or trying to preserve
47+
# the original node id per https://github.com/Open-EO/openeo-python-driver/issues/479)
4248

4349
def resolve_from_node(process_graph: dict, node_id: str, from_node: str):
4450
if from_node not in process_graph:
@@ -53,6 +59,8 @@ def resolve_from_node(process_graph: dict, node_id: str, from_node: str):
5359
if result_node:
5460
raise ProcessGraphVisitException(f"Multiple result nodes: {result_node!r}, {node_id!r}")
5561
result_node = node_id
62+
if add_node_id:
63+
node_data[ORIG_NODE_ID_KEY] = node_id
5664
arguments = node_data.get("arguments", {})
5765
for arg in arguments.values():
5866
if isinstance(arg, dict):

tests/internal/test_process_graph_visitor.py

Lines changed: 162 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,34 @@ def test_visit_array_with_dereferenced_nodes():
148148
"process_id": "array_element",
149149
"arguments": {"data": {"from_parameter": "data"}, "index": 2},
150150
"result": False,
151+
"_node_id": "arrayelement1",
151152
}
152153
)
153154
]
154155
assert visitor.constantArrayElement.call_args_list == [call(-1)]
155156

156157

158+
def test_dereference_simple():
159+
graph = {
160+
"foo1": {
161+
"process_id": "foo",
162+
"arguments": {"bar": 123},
163+
"result": True,
164+
}
165+
}
166+
result = ProcessGraphVisitor.dereference_from_node_arguments(graph)
167+
168+
assert result == "foo1"
169+
assert graph == {
170+
"foo1": {
171+
"process_id": "foo",
172+
"arguments": {"bar": 123},
173+
"result": True,
174+
"_node_id": "foo1",
175+
},
176+
}
177+
178+
157179
def test_dereference_basic():
158180
graph = {
159181
"node1": {},
@@ -184,25 +206,39 @@ def test_dereference_basic():
184206
assert graph["node3"] == graph["node2"]["arguments"]["data2"]["node"]
185207
assert graph["node4"] == graph["node3"]["arguments"]["data"]["node"]
186208
assert graph == {
187-
"node1": {},
209+
"node1": {"_node_id": "node1"},
188210
"node2": {
189211
"arguments": {
190-
"data1": {"from_node": "node1", "node": {}},
191-
"data2": {"from_node": "node3", "node": {
192-
"arguments": {
193-
"data": {"from_node": "node4", "node": {}},
194-
}
195-
}},
212+
"data1": {
213+
"from_node": "node1",
214+
"node": {"_node_id": "node1"},
215+
},
216+
"data2": {
217+
"from_node": "node3",
218+
"node": {
219+
"arguments": {
220+
"data": {
221+
"from_node": "node4",
222+
"node": {"_node_id": "node4"},
223+
},
224+
},
225+
"_node_id": "node3",
226+
},
227+
},
196228
},
197-
"result": True
229+
"result": True,
230+
"_node_id": "node2",
198231
},
199232
"node3": {
200233
"arguments": {
201-
"data": {"from_node": "node4", "node": {}},
202-
}
234+
"data": {
235+
"from_node": "node4",
236+
"node": {"_node_id": "node4"},
237+
},
238+
},
239+
"_node_id": "node3",
203240
},
204-
"node4": {}
205-
241+
"node4": {"_node_id": "node4"},
206242
}
207243

208244

@@ -221,17 +257,34 @@ def test_dereference_list_arg():
221257
result = ProcessGraphVisitor.dereference_from_node_arguments(graph)
222258
assert result == "temporal"
223259
assert graph == {
224-
"start": {"process_id": "constant", "arguments": {"x": "2020-02-02"}},
225-
"end": {"process_id": "constant", "arguments": {"x": "2020-03-03"}},
260+
"start": {
261+
"process_id": "constant",
262+
"arguments": {"x": "2020-02-02"},
263+
"_node_id": "start",
264+
},
265+
"end": {
266+
"process_id": "constant",
267+
"arguments": {"x": "2020-03-03"},
268+
"_node_id": "end",
269+
},
226270
"temporal": {
227271
"process_id": "filter_temporal",
228272
"arguments": {
229273
"extent": [
230-
{"process_id": "constant", "arguments": {"x": "2020-02-02"}},
231-
{"process_id": "constant", "arguments": {"x": "2020-03-03"}},
274+
{
275+
"process_id": "constant",
276+
"arguments": {"x": "2020-02-02"},
277+
"_node_id": "start",
278+
},
279+
{
280+
"process_id": "constant",
281+
"arguments": {"x": "2020-03-03"},
282+
"_node_id": "end",
283+
},
232284
],
233285
},
234286
"result": True,
287+
"_node_id": "temporal",
235288
},
236289
}
237290

@@ -254,24 +307,41 @@ def test_dereference_dict_arg():
254307
result = ProcessGraphVisitor.dereference_from_node_arguments(graph)
255308
assert result == "bbox"
256309
assert graph == {
257-
"west": {"process_id": "add", "arguments": {"x": 1, "y": 1}},
258-
"east": {"process_id": "add", "arguments": {"x": 2, "y": 3}},
310+
"west": {
311+
"process_id": "add",
312+
"arguments": {"x": 1, "y": 1},
313+
"_node_id": "west",
314+
},
315+
"east": {
316+
"process_id": "add",
317+
"arguments": {"x": 2, "y": 3},
318+
"_node_id": "east",
319+
},
259320
"bbox": {
260321
"process_id": "filter_bbox",
261322
"arguments": {
262323
"extent": {
263324
"west": {
264325
"from_node": "west",
265-
"node": {"process_id": "add", "arguments": {"x": 1, "y": 1}},
326+
"node": {
327+
"process_id": "add",
328+
"arguments": {"x": 1, "y": 1},
329+
"_node_id": "west",
330+
},
266331
},
267332
"east": {
268333
"from_node": "east",
269-
"node": {"process_id": "add", "arguments": {"x": 2, "y": 3}},
334+
"node": {
335+
"process_id": "add",
336+
"arguments": {"x": 2, "y": 3},
337+
"_node_id": "east",
338+
},
270339
},
271340
}
272341
},
273342
"result": True,
274-
}
343+
"_node_id": "bbox",
344+
},
275345
}
276346

277347

@@ -326,6 +396,77 @@ def test_dereference_cycle():
326396
assert graph["node2"]["arguments"]["data"]["node"] is graph["node1"]
327397

328398

399+
@pytest.mark.parametrize(
400+
["add_node_id", "expected"],
401+
[
402+
(
403+
False,
404+
{
405+
"foo1": {
406+
"process_id": "foo",
407+
"arguments": {"bar": 123},
408+
},
409+
"bar1": {
410+
"process_id": "bar",
411+
"arguments": {
412+
"foo": {
413+
"from_node": "foo1",
414+
"node": {
415+
"process_id": "foo",
416+
"arguments": {"bar": 123},
417+
},
418+
}
419+
},
420+
"result": True,
421+
},
422+
},
423+
),
424+
(
425+
True,
426+
{
427+
"foo1": {
428+
"process_id": "foo",
429+
"arguments": {"bar": 123},
430+
"_node_id": "foo1",
431+
},
432+
"bar1": {
433+
"process_id": "bar",
434+
"arguments": {
435+
"foo": {
436+
"from_node": "foo1",
437+
"node": {
438+
"process_id": "foo",
439+
"arguments": {"bar": 123},
440+
"_node_id": "foo1",
441+
},
442+
}
443+
},
444+
"result": True,
445+
"_node_id": "bar1",
446+
},
447+
},
448+
),
449+
],
450+
)
451+
def test_dereference_add_node_id(add_node_id, expected):
452+
graph = {
453+
"foo1": {
454+
"process_id": "foo",
455+
"arguments": {"bar": 123},
456+
},
457+
"bar1": {
458+
"process_id": "bar",
459+
"arguments": {"foo": {"from_node": "foo1"}},
460+
"result": True,
461+
},
462+
}
463+
result = ProcessGraphVisitor.dereference_from_node_arguments(graph, add_node_id=add_node_id)
464+
465+
assert result == "bar1"
466+
assert graph == expected
467+
468+
469+
329470
class TestProcessGraphUnflattener:
330471
def test_minimal(self):
331472
graph = {

0 commit comments

Comments
 (0)