flowrep 0.4.0__tar.gz → 0.5.0__tar.gz
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.
- {flowrep-0.4.0 → flowrep-0.5.0}/.gitignore +2 -0
- {flowrep-0.4.0 → flowrep-0.5.0}/PKG-INFO +22 -19
- {flowrep-0.4.0 → flowrep-0.5.0}/docs/README.md +17 -14
- {flowrep-0.4.0 → flowrep-0.5.0}/pyproject.toml +3 -3
- flowrep-0.5.0/src/flowrep/_version.py +24 -0
- flowrep-0.5.0/src/flowrep/api/schemas.py +50 -0
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/api/tools.py +4 -3
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/base_models.py +2 -2
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/converters/python_workflow_definition.py +18 -18
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/nodes/__init__.py +14 -14
- flowrep-0.4.0/src/flowrep/nodes/atomic_model.py → flowrep-0.5.0/src/flowrep/nodes/atomic_recipe.py +1 -1
- flowrep-0.4.0/src/flowrep/nodes/for_model.py → flowrep-0.5.0/src/flowrep/nodes/for_recipe.py +4 -4
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/nodes/helper_models.py +6 -6
- flowrep-0.4.0/src/flowrep/nodes/if_model.py → flowrep-0.5.0/src/flowrep/nodes/if_recipe.py +4 -4
- flowrep-0.4.0/src/flowrep/nodes/try_model.py → flowrep-0.5.0/src/flowrep/nodes/try_recipe.py +5 -5
- flowrep-0.5.0/src/flowrep/nodes/union_types.py +26 -0
- flowrep-0.4.0/src/flowrep/nodes/while_model.py → flowrep-0.5.0/src/flowrep/nodes/while_recipe.py +3 -3
- flowrep-0.4.0/src/flowrep/nodes/workflow_model.py → flowrep-0.5.0/src/flowrep/nodes/workflow_recipe.py +15 -3
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/atomic_parser.py +19 -19
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/case_helpers.py +8 -6
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/for_parser.py +4 -4
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/if_parser.py +6 -4
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/parser_helpers.py +1 -1
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/parser_protocol.py +3 -3
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/symbol_scope.py +1 -1
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/try_parser.py +3 -3
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/while_parser.py +4 -4
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/workflow_parser.py +24 -20
- flowrep-0.4.0/src/flowrep/live.py → flowrep-0.5.0/src/flowrep/retrospective.py +134 -76
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/storage.py +25 -24
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/storage_widget.py +23 -2
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/subgraph_validation.py +11 -6
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/wfms.py +63 -49
- flowrep-0.4.0/src/flowrep/_version.py +0 -34
- flowrep-0.4.0/src/flowrep/api/schemas.py +0 -41
- flowrep-0.4.0/src/flowrep/nodes/union.py +0 -26
- flowrep-0.4.0/src/flowrep/parsers/dependency_parser.py +0 -166
- flowrep-0.4.0/src/flowrep/parsers/import_parser.py +0 -51
- {flowrep-0.4.0 → flowrep-0.5.0}/LICENSE +0 -0
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/__init__.py +0 -0
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/api/__init__.py +0 -0
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/converters/__init__.py +0 -0
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/edge_models.py +0 -0
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/__init__.py +0 -0
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/label_helpers.py +0 -0
- {flowrep-0.4.0 → flowrep-0.5.0}/src/flowrep/parsers/object_scope.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flowrep
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: flowrep - Your premier tool for workflow representations
|
|
5
5
|
Project-URL: Homepage, https://pyiron.org/
|
|
6
6
|
Project-URL: Documentation, https://flowrep.readthedocs.io
|
|
@@ -50,13 +50,13 @@ Requires-Python: <3.15,>=3.11
|
|
|
50
50
|
Requires-Dist: pydantic<2.13.0,>=2.12.0
|
|
51
51
|
Requires-Dist: pyiron-snippets<2.0.0,>=1.2.1
|
|
52
52
|
Provides-Extra: notebooks
|
|
53
|
-
Requires-Dist: numpy==2.4.
|
|
53
|
+
Requires-Dist: numpy==2.4.6; extra == 'notebooks'
|
|
54
54
|
Provides-Extra: pwd
|
|
55
|
-
Requires-Dist: python-workflow-definition==0.1.
|
|
55
|
+
Requires-Dist: python-workflow-definition==0.1.5; extra == 'pwd'
|
|
56
56
|
Provides-Extra: storage
|
|
57
|
-
Requires-Dist: bagofholding==0.1.
|
|
57
|
+
Requires-Dist: bagofholding==0.1.11; extra == 'storage'
|
|
58
58
|
Provides-Extra: storage-widget
|
|
59
|
-
Requires-Dist: bagofholding==0.1.
|
|
59
|
+
Requires-Dist: bagofholding==0.1.11; extra == 'storage-widget'
|
|
60
60
|
Requires-Dist: ipytree==0.2.2; extra == 'storage-widget'
|
|
61
61
|
Description-Content-Type: text/markdown
|
|
62
62
|
|
|
@@ -76,10 +76,11 @@ Description-Content-Type: text/markdown
|
|
|
76
76
|
## flowrep — Workflow Recipes from Python
|
|
77
77
|
|
|
78
78
|
**flowrep** turns plain Python functions into shareable, versionable *workflow
|
|
79
|
-
recipes* — JSON-serialisable graphs that describe *what* to compute (which
|
|
79
|
+
recipes* — JSON-serialisable "class view" graphs that describe *what* to compute (which
|
|
80
80
|
functions, how they connect) without doing the computation or holding any data.
|
|
81
81
|
Recipes are prospective blueprints that a Workflow Management System (WfMS) can
|
|
82
|
-
digest, visualise, and execute.
|
|
82
|
+
digest, visualise, and execute. To support this, **flowrep** also provides a set of
|
|
83
|
+
retrospective, "instance view" data classes for WfMS to populate when executing recipes.
|
|
83
84
|
|
|
84
85
|
Flowchart-style representations are already the lingua franca for describing
|
|
85
86
|
processes in science and engineering. flowrep gives you a way to author them in
|
|
@@ -209,6 +210,9 @@ control structures you use in real code. Flow control nodes are inherently
|
|
|
209
210
|
dynamic: their exact execution path depends on data and cannot be known until
|
|
210
211
|
run-time, but their IO signature is always fully known a priori.
|
|
211
212
|
|
|
213
|
+
**Clear separation.** Of prospective, "class view" recipes, and retrospective,
|
|
214
|
+
"instance view" data objects.
|
|
215
|
+
|
|
212
216
|
### Example: flow control and nesting
|
|
213
217
|
|
|
214
218
|
So far we've seen `"workflow"` nodes, and alluded to `"atomic"` nodes.
|
|
@@ -307,7 +311,7 @@ the decorated functions are still just python functions; second, to show in the
|
|
|
307
311
|
section that the recipe we parse from this are intended to give the same result as
|
|
308
312
|
these underlying functions when we run the recipe with a WfMS.
|
|
309
313
|
|
|
310
|
-
## Beyond Recipes:
|
|
314
|
+
## Beyond Recipes: Retrospective "Instance View" Data and Execution
|
|
311
315
|
|
|
312
316
|
Recipes are *prospective* — they describe a computation template without holding
|
|
313
317
|
data. For retrospective analysis (inspecting what actually happened during a
|
|
@@ -318,18 +322,18 @@ run), flowrep provides two additional layers accessible through the API:
|
|
|
318
322
|
|
|
319
323
|
```
|
|
320
324
|
|
|
321
|
-
**`flowrep.api.tools.
|
|
322
|
-
data structure whose input and output
|
|
323
|
-
|
|
324
|
-
carry arbitrary data:
|
|
325
|
+
**`flowrep.api.tools.recipe2data`** converts a recipe from the prospective "class view"
|
|
326
|
+
to the retrospective "instance view" — a mutable data structure whose input and output
|
|
327
|
+
ports can hold actual Python values. Retrospective data objects mirror the recipe graph
|
|
328
|
+
but trade JSON-serializability for the ability to carry arbitrary data:
|
|
325
329
|
|
|
326
330
|
```python
|
|
327
|
-
>>>
|
|
331
|
+
>>> wf_data = frt.recipe2data(double_and_add.flowrep_recipe)
|
|
328
332
|
|
|
329
333
|
```
|
|
330
334
|
|
|
331
335
|
**`flowrep.api.tools.run_recipe`** goes one step further: it executes the recipe with
|
|
332
|
-
the provided inputs and returns a fully populated
|
|
336
|
+
the provided inputs and returns a fully populated data object. This is powered
|
|
333
337
|
by a minimal, built-in WfMS intended as a reference implementation and for use
|
|
334
338
|
in tests and documentation (like this!):
|
|
335
339
|
|
|
@@ -342,8 +346,8 @@ in tests and documentation (like this!):
|
|
|
342
346
|
|
|
343
347
|
```
|
|
344
348
|
|
|
345
|
-
Because every child node's ports are populated too, the
|
|
346
|
-
full data provenance — you can walk the tree and inspect exactly what each node
|
|
349
|
+
Because every child node's ports are populated too, the retrospective data graph gives
|
|
350
|
+
you full data provenance — you can walk the tree and inspect exactly what each node
|
|
347
351
|
received and produced.
|
|
348
352
|
For flow control nodes, which are _prospectively_ "black boxes", we find that
|
|
349
353
|
_retrospectively_ they are simple DAGs.
|
|
@@ -360,14 +364,13 @@ each loop iteration:
|
|
|
360
364
|
```
|
|
361
365
|
|
|
362
366
|
For a deeper look at all available node types, edge semantics, version
|
|
363
|
-
provenance, and the
|
|
367
|
+
provenance, and the retropective data/WfMS layer, see the
|
|
364
368
|
[user guide](https://mybinder.org/v2/gh/pyiron/flowrep/HEAD?urlpath=%2Fdoc%2Ftree%2Fuser-guide.ipynb).
|
|
365
369
|
|
|
366
370
|
|
|
367
371
|
## Documentation
|
|
368
372
|
|
|
369
|
-
- The user guide notebook comprehensively covers all node types, edge models, flow control, versioning,
|
|
370
|
-
live/retrospective data formats, a demo WfMS implementation, and recipe format converters.
|
|
373
|
+
- The user guide notebook comprehensively covers all node types, edge models, flow control, versioning, retrospective data formats, a demo WfMS implementation, and recipe format converters.
|
|
371
374
|
Launch it interactively on
|
|
372
375
|
[mybinder](https://mybinder.org/v2/gh/pyiron/flowrep/HEAD?urlpath=%2Fdoc%2Ftree%2Fuser-guide.ipynb).
|
|
373
376
|
- [readthedocs](https://flowrep.readthedocs.io/en/latest/)
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
## flowrep — Workflow Recipes from Python
|
|
15
15
|
|
|
16
16
|
**flowrep** turns plain Python functions into shareable, versionable *workflow
|
|
17
|
-
recipes* — JSON-serialisable graphs that describe *what* to compute (which
|
|
17
|
+
recipes* — JSON-serialisable "class view" graphs that describe *what* to compute (which
|
|
18
18
|
functions, how they connect) without doing the computation or holding any data.
|
|
19
19
|
Recipes are prospective blueprints that a Workflow Management System (WfMS) can
|
|
20
|
-
digest, visualise, and execute.
|
|
20
|
+
digest, visualise, and execute. To support this, **flowrep** also provides a set of
|
|
21
|
+
retrospective, "instance view" data classes for WfMS to populate when executing recipes.
|
|
21
22
|
|
|
22
23
|
Flowchart-style representations are already the lingua franca for describing
|
|
23
24
|
processes in science and engineering. flowrep gives you a way to author them in
|
|
@@ -147,6 +148,9 @@ control structures you use in real code. Flow control nodes are inherently
|
|
|
147
148
|
dynamic: their exact execution path depends on data and cannot be known until
|
|
148
149
|
run-time, but their IO signature is always fully known a priori.
|
|
149
150
|
|
|
151
|
+
**Clear separation.** Of prospective, "class view" recipes, and retrospective,
|
|
152
|
+
"instance view" data objects.
|
|
153
|
+
|
|
150
154
|
### Example: flow control and nesting
|
|
151
155
|
|
|
152
156
|
So far we've seen `"workflow"` nodes, and alluded to `"atomic"` nodes.
|
|
@@ -245,7 +249,7 @@ the decorated functions are still just python functions; second, to show in the
|
|
|
245
249
|
section that the recipe we parse from this are intended to give the same result as
|
|
246
250
|
these underlying functions when we run the recipe with a WfMS.
|
|
247
251
|
|
|
248
|
-
## Beyond Recipes:
|
|
252
|
+
## Beyond Recipes: Retrospective "Instance View" Data and Execution
|
|
249
253
|
|
|
250
254
|
Recipes are *prospective* — they describe a computation template without holding
|
|
251
255
|
data. For retrospective analysis (inspecting what actually happened during a
|
|
@@ -256,18 +260,18 @@ run), flowrep provides two additional layers accessible through the API:
|
|
|
256
260
|
|
|
257
261
|
```
|
|
258
262
|
|
|
259
|
-
**`flowrep.api.tools.
|
|
260
|
-
data structure whose input and output
|
|
261
|
-
|
|
262
|
-
carry arbitrary data:
|
|
263
|
+
**`flowrep.api.tools.recipe2data`** converts a recipe from the prospective "class view"
|
|
264
|
+
to the retrospective "instance view" — a mutable data structure whose input and output
|
|
265
|
+
ports can hold actual Python values. Retrospective data objects mirror the recipe graph
|
|
266
|
+
but trade JSON-serializability for the ability to carry arbitrary data:
|
|
263
267
|
|
|
264
268
|
```python
|
|
265
|
-
>>>
|
|
269
|
+
>>> wf_data = frt.recipe2data(double_and_add.flowrep_recipe)
|
|
266
270
|
|
|
267
271
|
```
|
|
268
272
|
|
|
269
273
|
**`flowrep.api.tools.run_recipe`** goes one step further: it executes the recipe with
|
|
270
|
-
the provided inputs and returns a fully populated
|
|
274
|
+
the provided inputs and returns a fully populated data object. This is powered
|
|
271
275
|
by a minimal, built-in WfMS intended as a reference implementation and for use
|
|
272
276
|
in tests and documentation (like this!):
|
|
273
277
|
|
|
@@ -280,8 +284,8 @@ in tests and documentation (like this!):
|
|
|
280
284
|
|
|
281
285
|
```
|
|
282
286
|
|
|
283
|
-
Because every child node's ports are populated too, the
|
|
284
|
-
full data provenance — you can walk the tree and inspect exactly what each node
|
|
287
|
+
Because every child node's ports are populated too, the retrospective data graph gives
|
|
288
|
+
you full data provenance — you can walk the tree and inspect exactly what each node
|
|
285
289
|
received and produced.
|
|
286
290
|
For flow control nodes, which are _prospectively_ "black boxes", we find that
|
|
287
291
|
_retrospectively_ they are simple DAGs.
|
|
@@ -298,14 +302,13 @@ each loop iteration:
|
|
|
298
302
|
```
|
|
299
303
|
|
|
300
304
|
For a deeper look at all available node types, edge semantics, version
|
|
301
|
-
provenance, and the
|
|
305
|
+
provenance, and the retropective data/WfMS layer, see the
|
|
302
306
|
[user guide](https://mybinder.org/v2/gh/pyiron/flowrep/HEAD?urlpath=%2Fdoc%2Ftree%2Fuser-guide.ipynb).
|
|
303
307
|
|
|
304
308
|
|
|
305
309
|
## Documentation
|
|
306
310
|
|
|
307
|
-
- The user guide notebook comprehensively covers all node types, edge models, flow control, versioning,
|
|
308
|
-
live/retrospective data formats, a demo WfMS implementation, and recipe format converters.
|
|
311
|
+
- The user guide notebook comprehensively covers all node types, edge models, flow control, versioning, retrospective data formats, a demo WfMS implementation, and recipe format converters.
|
|
309
312
|
Launch it interactively on
|
|
310
313
|
[mybinder](https://mybinder.org/v2/gh/pyiron/flowrep/HEAD?urlpath=%2Fdoc%2Ftree%2Fuser-guide.ipynb).
|
|
311
314
|
- [readthedocs](https://flowrep.readthedocs.io/en/latest/)
|
|
@@ -28,9 +28,9 @@ Documentation = "https://flowrep.readthedocs.io"
|
|
|
28
28
|
Repository = "https://github.com/pyiron/flowrep"
|
|
29
29
|
|
|
30
30
|
[project.optional-dependencies]
|
|
31
|
-
pwd = [ "python-workflow-definition==0.1.
|
|
32
|
-
notebooks = [ "numpy==2.4.
|
|
33
|
-
storage = [ "bagofholding==0.1.
|
|
31
|
+
pwd = [ "python-workflow-definition==0.1.5",]
|
|
32
|
+
notebooks = [ "numpy==2.4.6",]
|
|
33
|
+
storage = [ "bagofholding==0.1.11",]
|
|
34
34
|
storage-widget = [ "flowrep[storage]", "ipytree==0.2.2",]
|
|
35
35
|
|
|
36
36
|
[tool.ruff]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.5.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 5, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic models, types and classes, enums, and constants used in constructing or
|
|
3
|
+
inspecting recipe models.
|
|
4
|
+
|
|
5
|
+
Primarily intended for power-users like workflow management system (WfMS) designers to
|
|
6
|
+
work deeply with recipe objects in a structured, well-typed way.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from flowrep.base_models import RESERVED_NAMES as RESERVED_NAMES
|
|
10
|
+
from flowrep.base_models import Label as Label
|
|
11
|
+
from flowrep.base_models import Labels as Labels
|
|
12
|
+
from flowrep.base_models import NodeRecipe as NodeRecipe
|
|
13
|
+
from flowrep.base_models import PythonReference as PythonReference
|
|
14
|
+
from flowrep.base_models import RecipeElementType as RecipeElementType
|
|
15
|
+
from flowrep.base_models import RestrictedParamKind as RestrictedParamKind
|
|
16
|
+
from flowrep.edge_models import Edges as Edges
|
|
17
|
+
from flowrep.edge_models import InputEdges as InputEdges
|
|
18
|
+
from flowrep.edge_models import InputSource as InputSource
|
|
19
|
+
from flowrep.edge_models import OutputEdges as OutputEdges
|
|
20
|
+
from flowrep.edge_models import OutputTarget as OutputTarget
|
|
21
|
+
from flowrep.edge_models import SourceHandle as SourceHandle
|
|
22
|
+
from flowrep.edge_models import TargetHandle as TargetHandle
|
|
23
|
+
from flowrep.nodes.atomic_recipe import AtomicRecipe as AtomicRecipe
|
|
24
|
+
from flowrep.nodes.atomic_recipe import UnpackMode as UnpackMode
|
|
25
|
+
from flowrep.nodes.for_recipe import ForEachRecipe as ForEachRecipe
|
|
26
|
+
from flowrep.nodes.helper_models import ConditionalCase as ConditionalCase
|
|
27
|
+
from flowrep.nodes.helper_models import ExceptionCase as ExceptionCase
|
|
28
|
+
from flowrep.nodes.helper_models import LabeledRecipe as LabeledRecipe
|
|
29
|
+
from flowrep.nodes.if_recipe import IfRecipe as IfRecipe
|
|
30
|
+
from flowrep.nodes.try_recipe import TryRecipe as TryRecipe
|
|
31
|
+
from flowrep.nodes.union_types import RecipeDiscrimination as RecipeDiscrimination
|
|
32
|
+
from flowrep.nodes.union_types import Recipes as Recipes
|
|
33
|
+
from flowrep.nodes.while_recipe import WhileRecipe as WhileRecipe
|
|
34
|
+
from flowrep.nodes.workflow_recipe import WorkflowRecipe as WorkflowRecipe
|
|
35
|
+
from flowrep.retrospective import NOT_DATA as NOT_DATA
|
|
36
|
+
from flowrep.retrospective import AtomicData as AtomicData
|
|
37
|
+
from flowrep.retrospective import CompositeData as CompositeData
|
|
38
|
+
from flowrep.retrospective import DagData as DagData
|
|
39
|
+
from flowrep.retrospective import FlowControlData as FlowControlData
|
|
40
|
+
from flowrep.retrospective import ForEachData as ForEachData
|
|
41
|
+
from flowrep.retrospective import IfData as IfData
|
|
42
|
+
from flowrep.retrospective import InputDataPort as InputDataPort
|
|
43
|
+
from flowrep.retrospective import InputDataPorts as InputDataPorts
|
|
44
|
+
from flowrep.retrospective import NodeData as NodeData
|
|
45
|
+
from flowrep.retrospective import NotData as NotData
|
|
46
|
+
from flowrep.retrospective import OutputDataPort as OutputDataPort
|
|
47
|
+
from flowrep.retrospective import OutputDataPorts as OutputDataPorts
|
|
48
|
+
from flowrep.retrospective import TryData as TryData
|
|
49
|
+
from flowrep.retrospective import WhileData as WhileData
|
|
50
|
+
from flowrep.subgraph_validation import ProspectiveOutputEdges as ProspectiveOutputEdges
|
|
@@ -9,7 +9,7 @@ and the python-workflow-definition format. (When PWD is available in the environ
|
|
|
9
9
|
Where PWD supports the workflow structure; round-trip from flowrep to PWD and back is
|
|
10
10
|
lossy in versioning info.)
|
|
11
11
|
|
|
12
|
-
Use `
|
|
12
|
+
Use `recipe2data` to convert recipes into state-ful data-ful instances of recipes,
|
|
13
13
|
which directly hold python objects (including, potentially, IO data values), but
|
|
14
14
|
which no longer trivially serialize to JSON.
|
|
15
15
|
|
|
@@ -17,15 +17,16 @@ Use decorators (`@atomic` and `@workflow`) to attach a recipe as a `.flowrep_rec
|
|
|
17
17
|
attribute onto a function at definition time, and parsers (`parse_atomic`,
|
|
18
18
|
`parse_workflow`) to return a recipe from an existing function object.
|
|
19
19
|
|
|
20
|
-
Use `run_recipe` to convert recipes into
|
|
20
|
+
Use `run_recipe` to convert recipes into retrospective instance objects with output
|
|
21
|
+
data.
|
|
21
22
|
"""
|
|
22
23
|
|
|
23
24
|
from flowrep.converters.python_workflow_definition import flowrep2pwd as flowrep2pwd
|
|
24
25
|
from flowrep.converters.python_workflow_definition import pwd2flowrep as pwd2flowrep
|
|
25
|
-
from flowrep.live import recipe2live as recipe2live
|
|
26
26
|
from flowrep.parsers.atomic_parser import atomic as atomic
|
|
27
27
|
from flowrep.parsers.atomic_parser import parse_atomic as parse_atomic
|
|
28
28
|
from flowrep.parsers.workflow_parser import parse_workflow as parse_workflow
|
|
29
29
|
from flowrep.parsers.workflow_parser import workflow as workflow
|
|
30
|
+
from flowrep.retrospective import recipe2data as recipe2data
|
|
30
31
|
from flowrep.storage import LexicalBagBrowser as LexicalBagBrowser
|
|
31
32
|
from flowrep.wfms import run_recipe as run_recipe
|
|
@@ -69,7 +69,7 @@ UniqueList = Annotated[list[T], pydantic.AfterValidator(validate_unique)]
|
|
|
69
69
|
Labels = UniqueList[Label]
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
class
|
|
72
|
+
class NodeRecipe(pydantic.BaseModel):
|
|
73
73
|
type: RecipeElementType
|
|
74
74
|
inputs: Labels
|
|
75
75
|
outputs: Labels
|
|
@@ -78,7 +78,7 @@ class NodeModel(pydantic.BaseModel):
|
|
|
78
78
|
@classmethod
|
|
79
79
|
def __pydantic_init_subclass__(cls, **kwargs):
|
|
80
80
|
super().__pydantic_init_subclass__(**kwargs)
|
|
81
|
-
if cls.__name__ !=
|
|
81
|
+
if cls.__name__ != NodeRecipe.__name__: # I.e. for subclasses
|
|
82
82
|
type_field = cls.model_fields["type"]
|
|
83
83
|
if type_field.default is pydantic_core.PydanticUndefined:
|
|
84
84
|
raise TypeError(
|
|
@@ -13,7 +13,7 @@ from typing import Any
|
|
|
13
13
|
from pyiron_snippets import import_alarm, versions
|
|
14
14
|
|
|
15
15
|
from flowrep import base_models, edge_models
|
|
16
|
-
from flowrep.nodes import
|
|
16
|
+
from flowrep.nodes import atomic_recipe, workflow_recipe
|
|
17
17
|
from flowrep.parsers import label_helpers
|
|
18
18
|
|
|
19
19
|
with import_alarm.ImportAlarm(
|
|
@@ -66,7 +66,7 @@ def _desanitize_port(port: str) -> str:
|
|
|
66
66
|
@_import_alarm
|
|
67
67
|
def pwd2flowrep(
|
|
68
68
|
wf: pwd.PythonWorkflowDefinitionWorkflow,
|
|
69
|
-
) -> tuple[
|
|
69
|
+
) -> tuple[workflow_recipe.WorkflowRecipe, dict[str, pwd.AllowableDefaults]]:
|
|
70
70
|
"""
|
|
71
71
|
Convert a *python-workflow-definition* workflow to flowrep.
|
|
72
72
|
|
|
@@ -74,7 +74,7 @@ def pwd2flowrep(
|
|
|
74
74
|
wf: A validated PWD workflow instance.
|
|
75
75
|
|
|
76
76
|
Returns:
|
|
77
|
-
A ``(
|
|
77
|
+
A ``(WorkflowRecipe, defaults)`` pair where *defaults* maps each
|
|
78
78
|
workflow-input name to the default value carried by the corresponding
|
|
79
79
|
PWD input node.
|
|
80
80
|
"""
|
|
@@ -101,7 +101,7 @@ def pwd2flowrep(
|
|
|
101
101
|
label_map,
|
|
102
102
|
)
|
|
103
103
|
|
|
104
|
-
result =
|
|
104
|
+
result = workflow_recipe.WorkflowRecipe(
|
|
105
105
|
inputs=wf_inputs,
|
|
106
106
|
outputs=wf_outputs,
|
|
107
107
|
nodes=nodes,
|
|
@@ -114,13 +114,13 @@ def pwd2flowrep(
|
|
|
114
114
|
|
|
115
115
|
@_import_alarm
|
|
116
116
|
def flowrep2pwd(
|
|
117
|
-
wf:
|
|
117
|
+
wf: workflow_recipe.WorkflowRecipe,
|
|
118
118
|
**terminal_inputs: pwd.AllowableDefaults,
|
|
119
119
|
) -> pwd.PythonWorkflowDefinitionWorkflow:
|
|
120
120
|
"""
|
|
121
|
-
Convert a flowrep :class:`
|
|
121
|
+
Convert a flowrep :class:`WorkflowRecipe` to *python-workflow-definition*.
|
|
122
122
|
|
|
123
|
-
Every child of *wf* must be an :class:`
|
|
123
|
+
Every child of *wf* must be an :class:`AtomicRecipe` (the pwd format does not
|
|
124
124
|
support nested sub-graphs). A default value must be supplied for **every**
|
|
125
125
|
workflow input via *terminal_inputs*.
|
|
126
126
|
|
|
@@ -176,7 +176,7 @@ def flowrep2pwd(
|
|
|
176
176
|
nid = id_counter.next()
|
|
177
177
|
func_node_ids[label] = nid
|
|
178
178
|
# Guaranteed by _validate_flat_workflow
|
|
179
|
-
assert isinstance(node,
|
|
179
|
+
assert isinstance(node, atomic_recipe.AtomicRecipe)
|
|
180
180
|
pwd_nodes.append(
|
|
181
181
|
pwd.PythonWorkflowDefinitionFunctionNode(
|
|
182
182
|
id=nid,
|
|
@@ -280,9 +280,9 @@ def _build_atomic_nodes(
|
|
|
280
280
|
label_map: dict[int, str],
|
|
281
281
|
node_inputs: dict[int, list[str]],
|
|
282
282
|
node_outputs: dict[int, list[str]],
|
|
283
|
-
) -> dict[str,
|
|
284
|
-
"""Build flowrep :class:`
|
|
285
|
-
nodes: dict[str,
|
|
283
|
+
) -> dict[str, atomic_recipe.AtomicRecipe]:
|
|
284
|
+
"""Build flowrep :class:`AtomicRecipe` instances from PWD function nodes."""
|
|
285
|
+
nodes: dict[str, atomic_recipe.AtomicRecipe] = {}
|
|
286
286
|
for node_id in sorted(function_nodes):
|
|
287
287
|
fn_node = function_nodes[node_id]
|
|
288
288
|
label = label_map[node_id]
|
|
@@ -295,7 +295,7 @@ def _build_atomic_nodes(
|
|
|
295
295
|
# This rsplit is at least consistent with the assumptions made in the PWD
|
|
296
296
|
# ecosystem:
|
|
297
297
|
# https://github.com/pythonworkflow/python-workflow-definition/blob/a372769e190176fe49e71740ad899937df0eeb94/src/python_workflow_definition/purepython.py#L80-L82
|
|
298
|
-
nodes[label] =
|
|
298
|
+
nodes[label] = atomic_recipe.AtomicRecipe(
|
|
299
299
|
reference=base_models.PythonReference(
|
|
300
300
|
info=versions.VersionInfo(
|
|
301
301
|
module=module,
|
|
@@ -375,18 +375,18 @@ class _IdCounter:
|
|
|
375
375
|
return nid
|
|
376
376
|
|
|
377
377
|
|
|
378
|
-
def _validate_flat_workflow(wf:
|
|
379
|
-
"""Raise :class:`ValueError` if any child is not an :class:`
|
|
378
|
+
def _validate_flat_workflow(wf: workflow_recipe.WorkflowRecipe) -> None:
|
|
379
|
+
"""Raise :class:`ValueError` if any child is not an :class:`AtomicRecipe`."""
|
|
380
380
|
for label, node in wf.nodes.items():
|
|
381
|
-
if not isinstance(node,
|
|
381
|
+
if not isinstance(node, atomic_recipe.AtomicRecipe):
|
|
382
382
|
raise ValueError(
|
|
383
|
-
f"flowrep2pwd requires all children to be
|
|
383
|
+
f"flowrep2pwd requires all children to be AtomicRecipe, but "
|
|
384
384
|
f"'{label}' is {type(node).__name__}."
|
|
385
385
|
)
|
|
386
386
|
|
|
387
387
|
|
|
388
388
|
def _validate_terminal_inputs(
|
|
389
|
-
wf:
|
|
389
|
+
wf: workflow_recipe.WorkflowRecipe,
|
|
390
390
|
terminal_inputs: dict[str, Any],
|
|
391
391
|
) -> None:
|
|
392
392
|
"""Raise :class:`ValueError` if *terminal_inputs* != workflow inputs."""
|
|
@@ -419,7 +419,7 @@ def _flowrep_port_to_pwd_source_port(port: str) -> str | None:
|
|
|
419
419
|
|
|
420
420
|
|
|
421
421
|
def _build_pwd_edges(
|
|
422
|
-
wf:
|
|
422
|
+
wf: workflow_recipe.WorkflowRecipe,
|
|
423
423
|
input_node_ids: dict[str, int],
|
|
424
424
|
output_node_ids: dict[str, int],
|
|
425
425
|
func_node_ids: dict[str, int],
|
|
@@ -3,15 +3,15 @@ from typing import cast
|
|
|
3
3
|
import pydantic
|
|
4
4
|
|
|
5
5
|
from flowrep.nodes import (
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
atomic_recipe,
|
|
7
|
+
for_recipe,
|
|
8
8
|
helper_models,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
if_recipe,
|
|
10
|
+
try_recipe,
|
|
11
|
+
while_recipe,
|
|
12
|
+
workflow_recipe,
|
|
13
13
|
)
|
|
14
|
-
from flowrep.nodes.
|
|
14
|
+
from flowrep.nodes.union_types import RecipeDiscrimination, Recipes
|
|
15
15
|
|
|
16
16
|
# Subtlety: Anywhere we use `typing.TYPE_CHECKING` to avoid a real import _and_ use
|
|
17
17
|
# the guarded object as a pydantic field annotator, we are going to need to make sure
|
|
@@ -21,12 +21,12 @@ from flowrep.nodes.union import Nodes, NodeType
|
|
|
21
21
|
# security that pydantic can find all the necessary types.
|
|
22
22
|
|
|
23
23
|
for cls in [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
helper_models.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
atomic_recipe.AtomicRecipe,
|
|
25
|
+
for_recipe.ForEachRecipe,
|
|
26
|
+
helper_models.LabeledRecipe,
|
|
27
|
+
if_recipe.IfRecipe,
|
|
28
|
+
try_recipe.TryRecipe,
|
|
29
|
+
while_recipe.WhileRecipe,
|
|
30
|
+
workflow_recipe.WorkflowRecipe,
|
|
31
31
|
]:
|
|
32
32
|
cast(type[pydantic.BaseModel], cls).model_rebuild()
|
flowrep-0.4.0/src/flowrep/nodes/for_model.py → flowrep-0.5.0/src/flowrep/nodes/for_recipe.py
RENAMED
|
@@ -8,10 +8,10 @@ from flowrep import base_models, edge_models, subgraph_validation
|
|
|
8
8
|
from flowrep.nodes import helper_models
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
|
-
from flowrep.nodes.
|
|
11
|
+
from flowrep.nodes.union_types import Recipes
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class
|
|
14
|
+
class ForEachRecipe(base_models.NodeRecipe):
|
|
15
15
|
"""
|
|
16
16
|
Loop over a body node and collect outputs as a list.
|
|
17
17
|
Each loop step is to be treated independently, such that the overall loop behaves
|
|
@@ -76,14 +76,14 @@ class ForEachNode(base_models.NodeModel):
|
|
|
76
76
|
type: Literal[base_models.RecipeElementType.FOR_EACH] = pydantic.Field(
|
|
77
77
|
default=base_models.RecipeElementType.FOR_EACH, frozen=True
|
|
78
78
|
)
|
|
79
|
-
body_node: helper_models.
|
|
79
|
+
body_node: helper_models.LabeledRecipe
|
|
80
80
|
input_edges: edge_models.InputEdges
|
|
81
81
|
output_edges: edge_models.OutputEdges
|
|
82
82
|
nested_ports: base_models.Labels = pydantic.Field(default_factory=list)
|
|
83
83
|
zipped_ports: base_models.Labels = pydantic.Field(default_factory=list)
|
|
84
84
|
|
|
85
85
|
@property
|
|
86
|
-
def prospective_nodes(self) ->
|
|
86
|
+
def prospective_nodes(self) -> Recipes:
|
|
87
87
|
return {self.body_node.label: self.body_node.node}
|
|
88
88
|
|
|
89
89
|
@property
|
|
@@ -8,7 +8,7 @@ from pyiron_snippets import versions
|
|
|
8
8
|
from flowrep import base_models
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
|
-
from flowrep.nodes.
|
|
11
|
+
from flowrep.nodes.union_types import RecipeDiscrimination # Satisfies mypy
|
|
12
12
|
|
|
13
13
|
# Still not enough to satisfy ruff, which doesn't understand the string forward
|
|
14
14
|
# reference, even with the TYPE_CHECKING import
|
|
@@ -17,14 +17,14 @@ if TYPE_CHECKING:
|
|
|
17
17
|
# Ultimately, just silence ruff as needed
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class
|
|
20
|
+
class LabeledRecipe(pydantic.BaseModel):
|
|
21
21
|
label: base_models.Label
|
|
22
|
-
node: "
|
|
22
|
+
node: "RecipeDiscrimination" # noqa: F821, UP037
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class ConditionalCase(pydantic.BaseModel):
|
|
26
|
-
condition:
|
|
27
|
-
body:
|
|
26
|
+
condition: LabeledRecipe
|
|
27
|
+
body: LabeledRecipe
|
|
28
28
|
condition_output: base_models.Label | None = None
|
|
29
29
|
|
|
30
30
|
@pydantic.model_validator(mode="after")
|
|
@@ -70,7 +70,7 @@ class ExceptionCase(pydantic.BaseModel):
|
|
|
70
70
|
"""
|
|
71
71
|
|
|
72
72
|
exceptions: list[versions.VersionInfo]
|
|
73
|
-
body:
|
|
73
|
+
body: LabeledRecipe
|
|
74
74
|
|
|
75
75
|
@pydantic.field_validator("exceptions")
|
|
76
76
|
@classmethod
|
|
@@ -8,10 +8,10 @@ from flowrep import base_models, edge_models, subgraph_validation
|
|
|
8
8
|
from flowrep.nodes import helper_models
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
|
-
from flowrep.nodes.
|
|
11
|
+
from flowrep.nodes.union_types import Recipes
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class
|
|
14
|
+
class IfRecipe(base_models.NodeRecipe):
|
|
15
15
|
"""
|
|
16
16
|
Walk through one or more cases, executing and returning the body result for the
|
|
17
17
|
first case with a positive condition evaluation.
|
|
@@ -51,14 +51,14 @@ class IfNode(base_models.NodeModel):
|
|
|
51
51
|
default=base_models.RecipeElementType.IF, frozen=True
|
|
52
52
|
)
|
|
53
53
|
cases: list[helper_models.ConditionalCase]
|
|
54
|
-
else_case: helper_models.
|
|
54
|
+
else_case: helper_models.LabeledRecipe | None = None
|
|
55
55
|
input_edges: edge_models.InputEdges
|
|
56
56
|
prospective_output_edges: dict[
|
|
57
57
|
edge_models.OutputTarget, base_models.UniqueList[edge_models.SourceHandle]
|
|
58
58
|
]
|
|
59
59
|
|
|
60
60
|
@property
|
|
61
|
-
def prospective_nodes(self) ->
|
|
61
|
+
def prospective_nodes(self) -> Recipes:
|
|
62
62
|
nodes = {}
|
|
63
63
|
for case in self.cases:
|
|
64
64
|
nodes[case.condition.label] = case.condition.node
|
flowrep-0.4.0/src/flowrep/nodes/try_model.py → flowrep-0.5.0/src/flowrep/nodes/try_recipe.py
RENAMED
|
@@ -8,10 +8,10 @@ from flowrep import base_models, edge_models, subgraph_validation
|
|
|
8
8
|
from flowrep.nodes import helper_models
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
|
-
from flowrep.nodes.
|
|
11
|
+
from flowrep.nodes.union_types import Recipes
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class
|
|
14
|
+
class TryRecipe(base_models.NodeRecipe):
|
|
15
15
|
"""
|
|
16
16
|
Try and except your way through a series of exceptions, with the option to perform
|
|
17
17
|
a finally step.
|
|
@@ -49,7 +49,7 @@ class TryNode(base_models.NodeModel):
|
|
|
49
49
|
type: Literal[base_models.RecipeElementType.TRY] = pydantic.Field(
|
|
50
50
|
default=base_models.RecipeElementType.TRY, frozen=True
|
|
51
51
|
)
|
|
52
|
-
try_node: helper_models.
|
|
52
|
+
try_node: helper_models.LabeledRecipe
|
|
53
53
|
exception_cases: list[helper_models.ExceptionCase]
|
|
54
54
|
input_edges: edge_models.InputEdges
|
|
55
55
|
prospective_output_edges: dict[
|
|
@@ -57,7 +57,7 @@ class TryNode(base_models.NodeModel):
|
|
|
57
57
|
]
|
|
58
58
|
|
|
59
59
|
@property
|
|
60
|
-
def prospective_nodes(self) ->
|
|
60
|
+
def prospective_nodes(self) -> Recipes:
|
|
61
61
|
nodes = {self.try_node.label: self.try_node.node}
|
|
62
62
|
for case in self.exception_cases:
|
|
63
63
|
nodes[case.body.label] = case.body.node
|
|
@@ -73,7 +73,7 @@ class TryNode(base_models.NodeModel):
|
|
|
73
73
|
@classmethod
|
|
74
74
|
def validate_exception_cases_not_empty(cls, v):
|
|
75
75
|
if len(v) < 1:
|
|
76
|
-
raise ValueError("
|
|
76
|
+
raise ValueError("TryRecipe must have at least one exception case")
|
|
77
77
|
return v
|
|
78
78
|
|
|
79
79
|
@pydantic.model_validator(mode="after")
|