easylink 0.1.10__py3-none-any.whl → 0.1.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- easylink/_version.py +1 -1
- easylink/pipeline_schema.py +21 -1
- easylink/pipeline_schema_constants/development.py +23 -2
- easylink/step.py +244 -87
- {easylink-0.1.10.dist-info → easylink-0.1.12.dist-info}/METADATA +1 -1
- {easylink-0.1.10.dist-info → easylink-0.1.12.dist-info}/RECORD +9 -9
- {easylink-0.1.10.dist-info → easylink-0.1.12.dist-info}/WHEEL +1 -1
- {easylink-0.1.10.dist-info → easylink-0.1.12.dist-info}/entry_points.txt +0 -0
- {easylink-0.1.10.dist-info → easylink-0.1.12.dist-info}/top_level.txt +0 -0
easylink/_version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.1.
|
1
|
+
__version__ = "0.1.12"
|
easylink/pipeline_schema.py
CHANGED
@@ -13,7 +13,7 @@ from pathlib import Path
|
|
13
13
|
|
14
14
|
from layered_config_tree import LayeredConfigTree
|
15
15
|
|
16
|
-
from easylink.graph_components import EdgeParams
|
16
|
+
from easylink.graph_components import EdgeParams, ImplementationGraph
|
17
17
|
from easylink.pipeline_schema_constants import ALLOWED_SCHEMA_PARAMS
|
18
18
|
from easylink.step import HierarchicalStep, NonLeafConfigurationState, Step
|
19
19
|
|
@@ -54,6 +54,26 @@ class PipelineSchema(HierarchicalStep):
|
|
54
54
|
def __repr__(self) -> str:
|
55
55
|
return f"PipelineSchema.{self.name}"
|
56
56
|
|
57
|
+
def get_implementation_graph(self) -> ImplementationGraph:
|
58
|
+
"""Gets the :class:`~easylink.graph_components.ImplementationGraph`.
|
59
|
+
|
60
|
+
The ``PipelineSchema`` is by definition a :class:`~easylink.step.HierarchicalStep`
|
61
|
+
which has a :class:`~easylink.graph_components.StepGraph` containing
|
62
|
+
sub-:class:`Steps<easylink.step.Step>` that need to be unrolled. This method
|
63
|
+
recursively traverses that ``StepGraph`` and its childrens' ``StepGraphs``
|
64
|
+
until all sub-``Steps`` are in a :class:`~easylink.step.LeafConfigurationState`,
|
65
|
+
i.e. all ``Steps`` are implemented by a single ``Implementation`` and we
|
66
|
+
have the desired ``ImplementationGraph``.
|
67
|
+
|
68
|
+
Returns
|
69
|
+
-------
|
70
|
+
The ``ImplementationGraph`` of this ``PipelineSchema``.
|
71
|
+
"""
|
72
|
+
implementation_graph = ImplementationGraph()
|
73
|
+
self.add_nodes_to_implementation_graph(implementation_graph)
|
74
|
+
self.add_edges_to_implementation_graph(implementation_graph)
|
75
|
+
return implementation_graph
|
76
|
+
|
57
77
|
def validate_step(
|
58
78
|
self, pipeline_config: LayeredConfigTree, input_data_config: LayeredConfigTree
|
59
79
|
) -> dict[str, list[str]]:
|
@@ -66,16 +66,37 @@ NODES = [
|
|
66
66
|
name="step_3_main_input",
|
67
67
|
env_var="DUMMY_CONTAINER_MAIN_INPUT_FILE_PATHS",
|
68
68
|
validator=validate_input_file_dummy,
|
69
|
-
splitter=split_data_by_size,
|
70
69
|
),
|
71
70
|
],
|
72
71
|
output_slots=[
|
73
72
|
OutputSlot(
|
74
73
|
name="step_3_main_output",
|
75
|
-
aggregator=concatenate_datasets,
|
76
74
|
),
|
77
75
|
],
|
78
76
|
),
|
77
|
+
input_slots=[
|
78
|
+
InputSlot(
|
79
|
+
name="step_3_main_input",
|
80
|
+
env_var="DUMMY_CONTAINER_MAIN_INPUT_FILE_PATHS",
|
81
|
+
validator=validate_input_file_dummy,
|
82
|
+
splitter=split_data_by_size,
|
83
|
+
),
|
84
|
+
],
|
85
|
+
output_slots=[OutputSlot("step_3_main_output", aggregator=concatenate_datasets)],
|
86
|
+
input_slot_mappings=[
|
87
|
+
InputSlotMapping(
|
88
|
+
parent_slot="step_3_main_input",
|
89
|
+
child_node="step_3",
|
90
|
+
child_slot="step_3_main_input",
|
91
|
+
),
|
92
|
+
],
|
93
|
+
output_slot_mappings=[
|
94
|
+
OutputSlotMapping(
|
95
|
+
parent_slot="step_3_main_output",
|
96
|
+
child_node="step_3",
|
97
|
+
child_slot="step_3_main_output",
|
98
|
+
),
|
99
|
+
],
|
79
100
|
),
|
80
101
|
self_edges=[
|
81
102
|
EdgeParams(
|
easylink/step.py
CHANGED
@@ -15,7 +15,6 @@ import copy
|
|
15
15
|
from abc import ABC, abstractmethod
|
16
16
|
from collections import defaultdict
|
17
17
|
from collections.abc import Iterable
|
18
|
-
from typing import Any
|
19
18
|
|
20
19
|
from layered_config_tree import LayeredConfigTree
|
21
20
|
|
@@ -88,6 +87,7 @@ class Step:
|
|
88
87
|
output_slots: Iterable[OutputSlot] = (),
|
89
88
|
input_slot_mappings: Iterable[InputSlotMapping] = (),
|
90
89
|
output_slot_mappings: Iterable[OutputSlotMapping] = (),
|
90
|
+
is_embarrassingly_parallel: bool = False,
|
91
91
|
) -> None:
|
92
92
|
self.step_name = step_name
|
93
93
|
"""The name of the pipeline step in the ``PipelineSchema``. It must also match
|
@@ -108,6 +108,8 @@ class Step:
|
|
108
108
|
}
|
109
109
|
"""A combined dictionary containing both the ``InputSlotMappings`` and
|
110
110
|
``OutputSlotMappings`` of this ``Step``."""
|
111
|
+
self.is_embarrassingly_parallel = is_embarrassingly_parallel
|
112
|
+
"""Whether or not this ``Step`` is to be run in an embarrassingly parallel manner."""
|
111
113
|
self.parent_step = None
|
112
114
|
"""This ``Step's`` parent ``Step``, if applicable."""
|
113
115
|
self._configuration_state = None
|
@@ -250,18 +252,25 @@ class Step:
|
|
250
252
|
]
|
251
253
|
return errors
|
252
254
|
|
253
|
-
def
|
254
|
-
|
255
|
+
def add_nodes_to_implementation_graph(
|
256
|
+
self, implementation_graph: ImplementationGraph
|
257
|
+
) -> None:
|
258
|
+
"""Adds the ``Implementations`` related to this ``Step`` as nodes to the :class:`~easylink.graph_components.ImplementationGraph`.
|
255
259
|
|
256
|
-
|
257
|
-
|
258
|
-
|
260
|
+
How the nodes get added depends on whether this ``Step`` is a leaf or a non-leaf,
|
261
|
+
i.e. what its :attr:`configuration_state` is.
|
262
|
+
"""
|
263
|
+
self.configuration_state.add_nodes_to_implementation_graph(implementation_graph)
|
259
264
|
|
260
|
-
|
261
|
-
|
262
|
-
|
265
|
+
def add_edges_to_implementation_graph(
|
266
|
+
self, implementation_graph: ImplementationGraph
|
267
|
+
) -> None:
|
268
|
+
"""Adds the edges of this ``Step's`` ``Implementation(s)`` to the :class:`~easylink.graph_components.ImplementationGraph`.
|
269
|
+
|
270
|
+
How the edges get added depends on whether this ``Step`` is a leaf or a non-leaf,
|
271
|
+
i.e. what its :attr:`configuration_state` is.
|
263
272
|
"""
|
264
|
-
|
273
|
+
self.configuration_state.add_edges_to_implementation_graph(implementation_graph)
|
265
274
|
|
266
275
|
def get_implementation_edges(self, edge: EdgeParams) -> list[EdgeParams]:
|
267
276
|
"""Gets the edge information for the ``Implementation`` related to this ``Step``.
|
@@ -387,7 +396,7 @@ class IOStep(Step):
|
|
387
396
|
The internal configuration of this ``Step``, i.e. it should not include
|
388
397
|
the ``Step's`` name.
|
389
398
|
combined_implementations
|
390
|
-
The configuration for any
|
399
|
+
The configuration for any ``Implementations`` to be combined.
|
391
400
|
input_data_config
|
392
401
|
The input data configuration for the entire pipeline.
|
393
402
|
"""
|
@@ -395,8 +404,10 @@ class IOStep(Step):
|
|
395
404
|
self, step_config, combined_implementations, input_data_config
|
396
405
|
)
|
397
406
|
|
398
|
-
def
|
399
|
-
|
407
|
+
def add_nodes_to_implementation_graph(
|
408
|
+
self, implementation_graph: ImplementationGraph
|
409
|
+
) -> None:
|
410
|
+
"""Adds this ``IOStep's`` ``Implementation`` as a node to the :class:`~easylink.graph_components.ImplementationGraph`.
|
400
411
|
|
401
412
|
Notes
|
402
413
|
-----
|
@@ -404,19 +415,22 @@ class IOStep(Step):
|
|
404
415
|
via an :class:`~easylink.implementation.Implementation`. As such, we
|
405
416
|
leverage the :class:`~easylink.implementation.NullImplementation` class
|
406
417
|
to generate the graph node.
|
407
|
-
|
408
|
-
Returns
|
409
|
-
-------
|
410
|
-
The ``ImplementationGraph`` of this ``Step``.
|
411
418
|
"""
|
412
|
-
implementation_graph = ImplementationGraph()
|
413
419
|
implementation_graph.add_node_from_implementation(
|
414
420
|
self.name,
|
415
421
|
implementation=NullImplementation(
|
416
422
|
self.name, self.input_slots.values(), self.output_slots.values()
|
417
423
|
),
|
418
424
|
)
|
419
|
-
|
425
|
+
|
426
|
+
def add_edges_to_implementation_graph(self, implementation_graph):
|
427
|
+
"""Adds the edges of this ``Step's`` ``Implementation`` to the ``ImplementationGraph``.
|
428
|
+
|
429
|
+
``IOSteps`` do not have edges within them in the ``ImplementationGraph``,
|
430
|
+
since they are represented by a single ``NullImplementation`` node, and so we
|
431
|
+
simply pass.
|
432
|
+
"""
|
433
|
+
pass
|
420
434
|
|
421
435
|
|
422
436
|
class InputStep(IOStep):
|
@@ -873,7 +887,7 @@ class TemplatedStep(Step, ABC):
|
|
873
887
|
self.step_graph = StepGraph()
|
874
888
|
self.template_step.name = self.name
|
875
889
|
self.step_graph.add_node_from_step(self.template_step)
|
876
|
-
#
|
890
|
+
# Update the slot mappings with renamed children
|
877
891
|
input_mappings = [
|
878
892
|
InputSlotMapping(slot, self.name, slot) for slot in self.input_slots
|
879
893
|
]
|
@@ -888,10 +902,8 @@ class TemplatedStep(Step, ABC):
|
|
888
902
|
num_repeats = len(expanded_config)
|
889
903
|
self.step_graph = self._update_step_graph(num_repeats)
|
890
904
|
self.slot_mappings = self._update_slot_mappings(num_repeats)
|
891
|
-
|
892
|
-
#
|
893
|
-
# set to leaf state in the event the user didn't include the config_key
|
894
|
-
# in the pipeline specification.
|
905
|
+
|
906
|
+
# TemplatedSteps are by definition non-leaf steps.
|
895
907
|
self._configuration_state = NonLeafConfigurationState(
|
896
908
|
self, expanded_config, combined_implementations, input_data_config
|
897
909
|
)
|
@@ -1153,10 +1165,22 @@ class EmbarrassinglyParallelStep(Step):
|
|
1153
1165
|
def __init__(
|
1154
1166
|
self,
|
1155
1167
|
step: Step,
|
1168
|
+
input_slots: Iterable[InputSlot],
|
1169
|
+
output_slots: Iterable[OutputSlot],
|
1170
|
+
input_slot_mappings: Iterable[InputSlotMapping],
|
1171
|
+
output_slot_mappings: Iterable[OutputSlotMapping],
|
1156
1172
|
) -> None:
|
1157
1173
|
super().__init__(
|
1158
|
-
step.step_name,
|
1174
|
+
step.step_name,
|
1175
|
+
step.name,
|
1176
|
+
input_slots,
|
1177
|
+
output_slots,
|
1178
|
+
input_slot_mappings,
|
1179
|
+
output_slot_mappings,
|
1180
|
+
is_embarrassingly_parallel=True,
|
1159
1181
|
)
|
1182
|
+
self.step_graph = None
|
1183
|
+
self.step = step
|
1160
1184
|
self._validate()
|
1161
1185
|
|
1162
1186
|
def _validate(self) -> None:
|
@@ -1199,6 +1223,50 @@ class EmbarrassinglyParallelStep(Step):
|
|
1199
1223
|
if errors:
|
1200
1224
|
raise ValueError("\n".join(errors))
|
1201
1225
|
|
1226
|
+
def set_configuration_state(
|
1227
|
+
self,
|
1228
|
+
step_config: LayeredConfigTree,
|
1229
|
+
combined_implementations: LayeredConfigTree,
|
1230
|
+
input_data_config: LayeredConfigTree,
|
1231
|
+
):
|
1232
|
+
"""Sets the configuration state to 'non-leaf'.
|
1233
|
+
|
1234
|
+
In addition to setting the configuration state, this also updates the
|
1235
|
+
:class:`~easylink.graph_components.StepGraph` and
|
1236
|
+
:class:`SlotMappings<easylink.graph_components.SlotMapping>`.
|
1237
|
+
|
1238
|
+
Parameters
|
1239
|
+
----------
|
1240
|
+
step_config
|
1241
|
+
The internal configuration of this ``Step``, i.e. it should not include
|
1242
|
+
the ``Step's`` name.
|
1243
|
+
combined_implementations
|
1244
|
+
The configuration for any implementations to be combined.
|
1245
|
+
input_data_config
|
1246
|
+
The input data configuration for the entire pipeline.
|
1247
|
+
"""
|
1248
|
+
if self.step.name != self.name:
|
1249
|
+
# Update the step name if the parent got renamed, e.g. a parent LoopStep
|
1250
|
+
# 'step_1' that got expanded to 'step_1_loop_1', etc.
|
1251
|
+
self.step.name = self.name
|
1252
|
+
input_mappings = [
|
1253
|
+
InputSlotMapping(slot, self.name, slot) for slot in self.input_slots
|
1254
|
+
]
|
1255
|
+
output_mappings = [
|
1256
|
+
OutputSlotMapping(slot, self.name, slot) for slot in self.output_slots
|
1257
|
+
]
|
1258
|
+
self.slot_mappings = {"input": input_mappings, "output": output_mappings}
|
1259
|
+
# Generate step graph from the single ``step`` attr
|
1260
|
+
self.step_graph = StepGraph()
|
1261
|
+
self.step_graph.add_node_from_step(self.step)
|
1262
|
+
# Add the key back to the expanded config
|
1263
|
+
expanded_config = LayeredConfigTree({self.name: step_config})
|
1264
|
+
|
1265
|
+
# EmbarrassinglyParallelSteps are by definition non-leaf steps
|
1266
|
+
self._configuration_state = NonLeafConfigurationState(
|
1267
|
+
self, expanded_config, combined_implementations, input_data_config
|
1268
|
+
)
|
1269
|
+
|
1202
1270
|
|
1203
1271
|
class ChoiceStep(Step):
|
1204
1272
|
"""A type of :class:`Step` that allows for choosing from a set of options.
|
@@ -1394,8 +1462,17 @@ class ConfigurationState(ABC):
|
|
1394
1462
|
"""The input data configuration for the entire pipeline."""
|
1395
1463
|
|
1396
1464
|
@abstractmethod
|
1397
|
-
def
|
1398
|
-
|
1465
|
+
def add_nodes_to_implementation_graph(
|
1466
|
+
self, implementation_graph: ImplementationGraph
|
1467
|
+
) -> None:
|
1468
|
+
"""Adds this ``Step's`` ``Implementation(s)`` as nodes to the :class:`~easylink.graph_components.ImplementationGraph`."""
|
1469
|
+
pass
|
1470
|
+
|
1471
|
+
@abstractmethod
|
1472
|
+
def add_edges_to_implementation_graph(
|
1473
|
+
self, implementation_graph: ImplementationGraph
|
1474
|
+
) -> None:
|
1475
|
+
"""Adds the edges of this ``Step's`` ``Implementation(s)`` to the :class:`~easylink.graph_components.ImplementationGraph`."""
|
1399
1476
|
pass
|
1400
1477
|
|
1401
1478
|
@abstractmethod
|
@@ -1438,24 +1515,21 @@ class LeafConfigurationState(ConfigurationState):
|
|
1438
1515
|
else self.step_config.implementation
|
1439
1516
|
)
|
1440
1517
|
|
1441
|
-
def
|
1442
|
-
|
1518
|
+
def add_nodes_to_implementation_graph(
|
1519
|
+
self, implementation_graph: ImplementationGraph
|
1520
|
+
) -> None:
|
1521
|
+
"""Adds this ``Step's`` :class:`~easylink.implementation.Implementation` as a node to the :class:`~easylink.graph_components.ImplementationGraph`.
|
1443
1522
|
|
1444
1523
|
A ``Step`` in a leaf configuration state by definition has no sub-``Steps``
|
1445
1524
|
to unravel; we are able to directly instantiate an :class:`~easylink.implementation.Implementation`
|
1446
|
-
and
|
1447
|
-
|
1448
|
-
Returns
|
1449
|
-
-------
|
1450
|
-
The ``ImplementationGraph`` related to this ``Step``.
|
1525
|
+
and add it to the ``ImplementationGraph``.
|
1451
1526
|
"""
|
1452
1527
|
step = self._step
|
1453
|
-
implementation_graph = ImplementationGraph()
|
1454
1528
|
if self.is_combined:
|
1455
|
-
if
|
1529
|
+
if step.is_embarrassingly_parallel:
|
1456
1530
|
raise NotImplementedError(
|
1457
1531
|
"Combining implementations with embarrassingly parallel steps "
|
1458
|
-
"is not
|
1532
|
+
"is not supported."
|
1459
1533
|
)
|
1460
1534
|
implementation = PartialImplementation(
|
1461
1535
|
combined_name=self.step_config[COMBINED_IMPLEMENTATION_KEY],
|
@@ -1469,13 +1543,21 @@ class LeafConfigurationState(ConfigurationState):
|
|
1469
1543
|
implementation_config=self.implementation_config,
|
1470
1544
|
input_slots=step.input_slots.values(),
|
1471
1545
|
output_slots=step.output_slots.values(),
|
1472
|
-
is_embarrassingly_parallel=
|
1546
|
+
is_embarrassingly_parallel=step.is_embarrassingly_parallel,
|
1473
1547
|
)
|
1474
1548
|
implementation_graph.add_node_from_implementation(
|
1475
1549
|
step.implementation_node_name,
|
1476
1550
|
implementation=implementation,
|
1477
1551
|
)
|
1478
|
-
|
1552
|
+
|
1553
|
+
def add_edges_to_implementation_graph(self, implementation_graph) -> None:
|
1554
|
+
"""Adds the edges for this ``Step's`` ``Implementation`` to the ``ImplementationGraph``.
|
1555
|
+
|
1556
|
+
``Steps`` in a ``LeafConfigurationState`` do not actually have edges within them
|
1557
|
+
(they are represented by a single node in the ``ImplementationGraph``) and so
|
1558
|
+
we simply pass.
|
1559
|
+
"""
|
1560
|
+
pass
|
1479
1561
|
|
1480
1562
|
def get_implementation_edges(self, edge: EdgeParams) -> list[EdgeParams]:
|
1481
1563
|
"""Gets the edge information for the ``Implementation`` related to this ``Step``.
|
@@ -1543,7 +1625,8 @@ class NonLeafConfigurationState(ConfigurationState):
|
|
1543
1625
|
for; it should not include the ``Step's`` name (though it must include
|
1544
1626
|
the sub-step names).
|
1545
1627
|
combined_implementations
|
1546
|
-
The configuration for any
|
1628
|
+
The configuration for any :class:`Implementations<easylink.implementation.Implementation>`
|
1629
|
+
to be combined.
|
1547
1630
|
input_data_config
|
1548
1631
|
The input data configuration for the entire pipeline.
|
1549
1632
|
|
@@ -1582,64 +1665,115 @@ class NonLeafConfigurationState(ConfigurationState):
|
|
1582
1665
|
"NonLeafConfigurationState requires a subgraph upon which to operate, "
|
1583
1666
|
f"but Step {step.name} has no step graph."
|
1584
1667
|
)
|
1585
|
-
self._nodes = step.step_graph.nodes
|
1586
1668
|
self._configure_subgraph_steps()
|
1587
1669
|
|
1588
|
-
def
|
1589
|
-
|
1670
|
+
def add_nodes_to_implementation_graph(
|
1671
|
+
self, implementation_graph: ImplementationGraph
|
1672
|
+
) -> None:
|
1673
|
+
"""Adds this ``Step's`` ``Implementations`` as nodes to the ``ImplementationGraph``.
|
1590
1674
|
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1675
|
+
This is a recursive function; it calls itself until all sub-``Steps``
|
1676
|
+
are of a ``LeafConfigurationState`` and have had their corresponding
|
1677
|
+
``Implementations`` added as nodes to the ``ImplementationGraph``.
|
1678
|
+
"""
|
1679
|
+
for node in self._step.step_graph.nodes:
|
1680
|
+
substep = self._step.step_graph.nodes[node]["step"]
|
1681
|
+
if self._step.is_embarrassingly_parallel:
|
1682
|
+
substep.is_embarrassingly_parallel = True
|
1683
|
+
self._propagate_splitter_aggregators(self._step, substep)
|
1684
|
+
substep.add_nodes_to_implementation_graph(implementation_graph)
|
1685
|
+
|
1686
|
+
@staticmethod
|
1687
|
+
def _propagate_splitter_aggregators(parent: Step, child: Step):
|
1688
|
+
"""Propagates splitters and aggregators to child ``Steps``.
|
1689
|
+
|
1690
|
+
This method adds the :meth:`~easylink.graph_components.InputSlot.splitter`
|
1691
|
+
and :meth:`~easylink.graph_components.OutputSlot.aggregator` methods from a
|
1692
|
+
parent ``Step's`` :class:`~easylink.graph_components.InputSlot` and
|
1693
|
+
:class:`OutputSlots<easylink.graph_components.OutputSlot>` to the corresponding
|
1694
|
+
child steps' slots.
|
1597
1695
|
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1696
|
+
Parameters
|
1697
|
+
----------
|
1698
|
+
parent
|
1699
|
+
The parent ``Step`` whose ``splitter`` and ``aggregator`` methods are
|
1700
|
+
to be propagated to the appropriate child ``Step``.
|
1701
|
+
child
|
1702
|
+
A child ``Step`` to potentially have its parent's ``splitter`` and
|
1703
|
+
``aggregators`` assigned to its ``InputSlot`` and ``OutputSlots``,
|
1704
|
+
respectively.
|
1705
|
+
"""
|
1706
|
+
for parent_input_slot_name, parent_input_slot in parent.input_slots.items():
|
1707
|
+
if parent_input_slot.splitter:
|
1708
|
+
# Extract the appropriate child slot name from the mapping
|
1709
|
+
mappings_with_splitter = [
|
1710
|
+
mapping
|
1711
|
+
for mapping in parent.slot_mappings["input"]
|
1712
|
+
if mapping.parent_slot == parent_input_slot_name
|
1713
|
+
]
|
1714
|
+
for mapping in mappings_with_splitter:
|
1715
|
+
child_node = mapping.child_node
|
1716
|
+
child_slot = mapping.child_slot
|
1717
|
+
# Assign the splitter to the appropriate child slot
|
1718
|
+
if child_slot in child.input_slots and child_node == child.name:
|
1719
|
+
child.input_slots[child_slot].splitter = parent_input_slot.splitter
|
1720
|
+
for parent_output_slot_name, parent_output_slot in parent.output_slots.items():
|
1721
|
+
# Extract the appropriate child slot name from the mapping
|
1722
|
+
mappings_from_parent = [
|
1723
|
+
mapping
|
1724
|
+
for mapping in parent.slot_mappings["output"]
|
1725
|
+
if mapping.parent_slot == parent_output_slot_name
|
1726
|
+
]
|
1727
|
+
for mapping in mappings_from_parent:
|
1728
|
+
child_node = mapping.child_node
|
1729
|
+
child_slot = mapping.child_slot
|
1730
|
+
# Assign the aggregator to the appropriate child slot
|
1731
|
+
if child_slot in child.output_slots and child_node == child.name:
|
1732
|
+
child.output_slots[child_slot].aggregator = parent_output_slot.aggregator
|
1733
|
+
|
1734
|
+
def add_edges_to_implementation_graph(
|
1735
|
+
self, implementation_graph: ImplementationGraph
|
1736
|
+
) -> None:
|
1737
|
+
"""Adds the edges of this ``Step's`` ``Implementations`` to the ``ImplementationGraph``.
|
1601
1738
|
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1739
|
+
This method does two things:
|
1740
|
+
1. Adds the edges *at this level* (i.e. at the ``Step`` tied to this
|
1741
|
+
``NonLeafConfigurationState``) to the ``ImplementationGraph``.
|
1742
|
+
2. Recursively traverses all sub-steps and adds their edges to the
|
1743
|
+
``ImplementationGraph``.
|
1607
1744
|
|
1745
|
+
Note that to achieve (1), edges must be mapped from being between steps at
|
1746
|
+
this level of the hierarchy, all the way down to being between concrete implementations.
|
1747
|
+
Mapping each edge down to the implementation level is *itself* a recursive
|
1748
|
+
operation (see ``get_implementation_edges``).
|
1608
1749
|
"""
|
1609
|
-
|
1610
|
-
self.add_nodes(implementation_graph)
|
1611
|
-
self.add_edges(implementation_graph)
|
1612
|
-
return implementation_graph
|
1613
|
-
|
1614
|
-
def add_nodes(self, implementation_graph: ImplementationGraph) -> None:
|
1615
|
-
"""Adds nodes for each ``Step`` to the ``ImplementationGraph``."""
|
1616
|
-
for node in self._nodes:
|
1617
|
-
step = self._nodes[node]["step"]
|
1618
|
-
implementation_graph.update(step.get_implementation_graph())
|
1619
|
-
|
1620
|
-
def add_edges(self, implementation_graph: ImplementationGraph) -> None:
|
1621
|
-
"""Adds the edges to the ``ImplementationGraph``."""
|
1750
|
+
# Add the edges at this level (i.e. the edges at this `self._step`)
|
1622
1751
|
for source, target, edge_attrs in self._step.step_graph.edges(data=True):
|
1623
|
-
all_edges = []
|
1624
1752
|
edge = EdgeParams.from_graph_edge(source, target, edge_attrs)
|
1625
|
-
|
1626
|
-
|
1753
|
+
source_step = self._step.step_graph.nodes[source]["step"]
|
1754
|
+
target_step = self._step.step_graph.nodes[target]["step"]
|
1627
1755
|
|
1628
|
-
source_edges =
|
1756
|
+
source_edges = source_step.get_implementation_edges(edge)
|
1629
1757
|
for source_edge in source_edges:
|
1630
|
-
for target_edge in
|
1631
|
-
|
1758
|
+
for target_edge in target_step.get_implementation_edges(source_edge):
|
1759
|
+
implementation_graph.add_edge_from_params(target_edge)
|
1632
1760
|
|
1633
|
-
|
1634
|
-
|
1761
|
+
# Recurse through all sub-steps and add the edges between them
|
1762
|
+
for node in self._step.step_graph.nodes:
|
1763
|
+
substep = self._step.step_graph.nodes[node]["step"]
|
1764
|
+
substep.add_edges_to_implementation_graph(implementation_graph)
|
1635
1765
|
|
1636
1766
|
def get_implementation_edges(self, edge: EdgeParams) -> list[EdgeParams]:
|
1637
|
-
"""Gets the
|
1767
|
+
"""Gets the edges for the ``Implementation`` related to this ``Step``.
|
1768
|
+
|
1769
|
+
This method maps an edge between ``Steps`` in this ``Step's`` ``StepGraph``
|
1770
|
+
to one or more edges between ``Implementations`` by applying ``SlotMappings``.
|
1638
1771
|
|
1639
1772
|
Parameters
|
1640
1773
|
----------
|
1641
1774
|
edge
|
1642
|
-
The
|
1775
|
+
The edge information of the edge in the ``StepGraph`` to be mapped to
|
1776
|
+
the ``Implementation`` level.
|
1643
1777
|
|
1644
1778
|
Raises
|
1645
1779
|
------
|
@@ -1648,7 +1782,28 @@ class NonLeafConfigurationState(ConfigurationState):
|
|
1648
1782
|
|
1649
1783
|
Returns
|
1650
1784
|
-------
|
1651
|
-
|
1785
|
+
A list of edges between ``Implementations`` which are ready to add to
|
1786
|
+
the ``ImplementationGraph``.
|
1787
|
+
|
1788
|
+
Notes
|
1789
|
+
-----
|
1790
|
+
In EasyLink, an edge (in either a ``StepGraph`` or ``ImplementationGraph``)
|
1791
|
+
sconnects two ``Slot``.
|
1792
|
+
|
1793
|
+
The core of this method is to map the ``Slots`` on the ``StepGraph`` edge
|
1794
|
+
to the corresponding ``Slots`` on ``Implementations``.
|
1795
|
+
|
1796
|
+
At each level in the step hierarchy, ``SlotMappings`` indicate how to map
|
1797
|
+
a ``Slot`` to the level below in the hierarchy.
|
1798
|
+
|
1799
|
+
This method recurses through the step hierarchy until it reaches the leaf
|
1800
|
+
``Steps`` relevant to this edge in order to compose all the ``SlotMappings``
|
1801
|
+
that should apply to it.
|
1802
|
+
|
1803
|
+
Because a single ``Step`` can become multiple nodes in the ``ImplementationGraph``
|
1804
|
+
(e.g. a :class:`TemplatedStep`), a single edge between ``Steps`` may actually
|
1805
|
+
become multiple edges between ``Implementations``, which is why this method
|
1806
|
+
can return a list.
|
1652
1807
|
"""
|
1653
1808
|
implementation_edges = []
|
1654
1809
|
if edge.source_node == self._step.name:
|
@@ -1659,7 +1814,7 @@ class NonLeafConfigurationState(ConfigurationState):
|
|
1659
1814
|
]
|
1660
1815
|
for mapping in mappings:
|
1661
1816
|
new_edge = mapping.remap_edge(edge)
|
1662
|
-
new_step = self.
|
1817
|
+
new_step = self._step.step_graph.nodes[mapping.child_node]["step"]
|
1663
1818
|
imp_edges = new_step.get_implementation_edges(new_edge)
|
1664
1819
|
implementation_edges.extend(imp_edges)
|
1665
1820
|
elif edge.target_node == self._step.name:
|
@@ -1670,7 +1825,7 @@ class NonLeafConfigurationState(ConfigurationState):
|
|
1670
1825
|
]
|
1671
1826
|
for mapping in mappings:
|
1672
1827
|
new_edge = mapping.remap_edge(edge)
|
1673
|
-
new_step = self.
|
1828
|
+
new_step = self._step.step_graph.nodes[mapping.child_node]["step"]
|
1674
1829
|
imp_edges = new_step.get_implementation_edges(new_edge)
|
1675
1830
|
implementation_edges.extend(imp_edges)
|
1676
1831
|
else:
|
@@ -1685,12 +1840,14 @@ class NonLeafConfigurationState(ConfigurationState):
|
|
1685
1840
|
This method recursively traverses the ``StepGraph`` and sets the configuration
|
1686
1841
|
state for each ``Step`` until reaching all leaf nodes.
|
1687
1842
|
"""
|
1688
|
-
for
|
1689
|
-
|
1843
|
+
for sub_node in self._step.step_graph.nodes:
|
1844
|
+
sub_step = self._step.step_graph.nodes[sub_node]["step"]
|
1690
1845
|
# IOStep names never appear in configuration
|
1691
1846
|
step_config = (
|
1692
|
-
self.step_config
|
1847
|
+
self.step_config
|
1848
|
+
if isinstance(sub_step, IOStep)
|
1849
|
+
else self.step_config[sub_step.name]
|
1693
1850
|
)
|
1694
|
-
|
1851
|
+
sub_step.set_configuration_state(
|
1695
1852
|
step_config, self.combined_implementations, self.input_data_config
|
1696
1853
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
easylink/__about__.py,sha256=2-oxCfu9t9yUJouLDwqYRZ0eii8kN25SxRzsawjWjho,440
|
2
2
|
easylink/__init__.py,sha256=gGMcIVfiVnHtlDw5mZwhevcDb2wt-kuP6F64gnkFack,159
|
3
|
-
easylink/_version.py,sha256=
|
3
|
+
easylink/_version.py,sha256=LcIlFjHZFfiF9Rd4UHoakmombOFkxIYk00I181frGBM,23
|
4
4
|
easylink/cli.py,sha256=ARSKAljepNOEYd1VCS_QqBJQIBLzE3IgKiOb5-OROdY,6380
|
5
5
|
easylink/configuration.py,sha256=Ire2pMZNZ6wtSwhcWnQpYa-snX4KrhXgovlQwQ2Wxf4,12530
|
6
6
|
easylink/graph_components.py,sha256=PhMKxpgZjorhubS7vcta1pgXgXSGplmPulQpV0YZhqo,14811
|
@@ -8,14 +8,14 @@ easylink/implementation.py,sha256=AwGl5YCKCSQo91owWj-gg9_5lBz7H_4q2z7jF0BhXs4,89
|
|
8
8
|
easylink/implementation_metadata.yaml,sha256=VvlEu3Dvlmeh1MpzeYx91j22GiV-9mu3hZP5yVuW04o,6763
|
9
9
|
easylink/pipeline.py,sha256=EyCXv5p9WzTqcndXK6ukBJE6jY_fWIP_DGZQUl1wRcY,12284
|
10
10
|
easylink/pipeline_graph.py,sha256=vsY6nW_iEwZCNf_N_3CsixsKBUy_5JxGEi61-1Q-KAw,22842
|
11
|
-
easylink/pipeline_schema.py,sha256=
|
11
|
+
easylink/pipeline_schema.py,sha256=Q2sCpsC-F2W0yxVP7ufunowDepOBrRVENXOdap9J5iY,6921
|
12
12
|
easylink/rule.py,sha256=W97LMI-vkEPipJbnSZLn2BxfYfFtvzGTKzq6YgDVri0,19913
|
13
13
|
easylink/runner.py,sha256=k9ICTToHj2xr6MGIuvlWf6YMeZ47UGgseaMByMgUGac,6271
|
14
|
-
easylink/step.py,sha256=
|
14
|
+
easylink/step.py,sha256=Hweg1OAGcmrNAt95C-M4ksOAtc_db0oeibbF3cnqhq0,74951
|
15
15
|
easylink/images/spark_cluster/Dockerfile,sha256=3PHotbR4jdjVYRHOJ0VQW55b5Qd4tQ1pLLQMrTKWVA0,576
|
16
16
|
easylink/images/spark_cluster/README.md,sha256=KdgSttZRplNNWqHn4K1GTsTIab3dTOSG4V99QPLxSp8,569
|
17
17
|
easylink/pipeline_schema_constants/__init__.py,sha256=uRVjQw7_Ff5IBQw0_Jc93Fzfa-MnbPVPKsy18CCaW7E,1021
|
18
|
-
easylink/pipeline_schema_constants/development.py,sha256=
|
18
|
+
easylink/pipeline_schema_constants/development.py,sha256=0fc6xWRBr5e_xDaldR9sY2vMQJU1wnlhDQS_-yUOT6g,12339
|
19
19
|
easylink/pipeline_schema_constants/testing.py,sha256=ohcTlT_viZYxS1GkO46mjkb8IzXo6yIOqvBbb4YrOhA,10897
|
20
20
|
easylink/steps/dev/README.md,sha256=u9dZUggpY2Lf2qb-xkDLWWgHjcmi4osbQtzSNo4uklE,4549
|
21
21
|
easylink/steps/dev/build-containers-local.sh,sha256=Wy3pfcyt7I-BNvHcr7ZXDe0g5Ihd00BIPqt9YuRbLeA,259
|
@@ -43,8 +43,8 @@ easylink/utilities/paths.py,sha256=KM1GlnsAcKbUJrC4LZKpeJfPljxe_aXP1ZhVp43TYRA,9
|
|
43
43
|
easylink/utilities/spark.smk,sha256=tQ7RArNQzhjbaBQQcRORB4IxxkuDx4gPHUBcWHDYJ_U,5795
|
44
44
|
easylink/utilities/splitter_utils.py,sha256=y4CbbTBgRaoXFxy-9Eu5eWx4lA4ZEcbrYpxgLIzG_kc,2602
|
45
45
|
easylink/utilities/validation_utils.py,sha256=W9r_RXcivJjfpioLhONirfwdByYttxNsVY489_sbrYQ,1683
|
46
|
-
easylink-0.1.
|
47
|
-
easylink-0.1.
|
48
|
-
easylink-0.1.
|
49
|
-
easylink-0.1.
|
50
|
-
easylink-0.1.
|
46
|
+
easylink-0.1.12.dist-info/METADATA,sha256=jBuzMoJJREYmGnXNbuUEmHs_SgqcibeIcKiTxjZ1Ky8,2805
|
47
|
+
easylink-0.1.12.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
48
|
+
easylink-0.1.12.dist-info/entry_points.txt,sha256=OGMZDFltg3yMboT7XjJt3joiPhRfV_7jnREVtrAIQNU,51
|
49
|
+
easylink-0.1.12.dist-info/top_level.txt,sha256=oHcOpcF_jDMWFiJRzfGQvuskENGDjSPC_Agu9Z_Xvik,9
|
50
|
+
easylink-0.1.12.dist-info/RECORD,,
|
File without changes
|
File without changes
|