jobflow 0.1.17__py3-none-any.whl → 0.1.19__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.
- jobflow/core/flow.py +51 -9
- jobflow/core/job.py +71 -21
- jobflow/core/maker.py +2 -4
- jobflow/core/reference.py +1 -1
- jobflow/core/store.py +5 -4
- jobflow/managers/fireworks.py +15 -0
- jobflow/managers/local.py +13 -8
- jobflow/settings.py +13 -2
- jobflow/utils/__init__.py +1 -0
- jobflow/utils/log.py +15 -3
- jobflow/utils/uid.py +2 -1
- jobflow/utils/uuid.py +1 -0
- {jobflow-0.1.17.dist-info → jobflow-0.1.19.dist-info}/METADATA +48 -41
- jobflow-0.1.19.dist-info/RECORD +28 -0
- {jobflow-0.1.17.dist-info → jobflow-0.1.19.dist-info}/WHEEL +1 -1
- jobflow-0.1.17.dist-info/RECORD +0 -28
- {jobflow-0.1.17.dist-info → jobflow-0.1.19.dist-info}/LICENSE +0 -0
- {jobflow-0.1.17.dist-info → jobflow-0.1.19.dist-info}/top_level.txt +0 -0
jobflow/core/flow.py
CHANGED
|
@@ -67,6 +67,11 @@ class Flow(MSONable):
|
|
|
67
67
|
automatically when a flow is included in the jobs array of another flow.
|
|
68
68
|
The object identified by one UUID of the list should be contained in objects
|
|
69
69
|
identified by its subsequent elements.
|
|
70
|
+
metadata
|
|
71
|
+
A dictionary of information that will get stored in the Flow collection.
|
|
72
|
+
metadata_updates
|
|
73
|
+
A list of updates for the metadata that will be applied to any dynamically
|
|
74
|
+
generated sub Flow/Job.
|
|
70
75
|
|
|
71
76
|
Raises
|
|
72
77
|
------
|
|
@@ -128,6 +133,8 @@ class Flow(MSONable):
|
|
|
128
133
|
order: JobOrder = JobOrder.AUTO,
|
|
129
134
|
uuid: str = None,
|
|
130
135
|
hosts: list[str] = None,
|
|
136
|
+
metadata: dict[str, Any] = None,
|
|
137
|
+
metadata_updates: list[dict[str, Any]] = None,
|
|
131
138
|
):
|
|
132
139
|
from jobflow.core.job import Job
|
|
133
140
|
|
|
@@ -141,6 +148,8 @@ class Flow(MSONable):
|
|
|
141
148
|
self.order = order
|
|
142
149
|
self.uuid = uuid
|
|
143
150
|
self.hosts = hosts or []
|
|
151
|
+
self.metadata = metadata or {}
|
|
152
|
+
self.metadata_updates = metadata_updates or []
|
|
144
153
|
|
|
145
154
|
self._jobs: tuple[Flow | Job, ...] = ()
|
|
146
155
|
self.add_jobs(jobs)
|
|
@@ -158,9 +167,8 @@ class Flow(MSONable):
|
|
|
158
167
|
self, idx: int | slice, value: Flow | Job | Sequence[Flow | Job]
|
|
159
168
|
) -> None:
|
|
160
169
|
"""Set the job(s) or subflow(s) at the given index/slice."""
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
or isinstance(value, (tuple, list))
|
|
170
|
+
if not isinstance(value, (Flow, jobflow.Job, tuple, list)) or (
|
|
171
|
+
isinstance(value, (tuple, list))
|
|
164
172
|
and not all(isinstance(v, (Flow, jobflow.Job)) for v in value)
|
|
165
173
|
):
|
|
166
174
|
raise TypeError(
|
|
@@ -191,7 +199,7 @@ class Flow(MSONable):
|
|
|
191
199
|
if other not in self:
|
|
192
200
|
raise ValueError(f"{other!r} not found in flow")
|
|
193
201
|
new_flow = deepcopy(self)
|
|
194
|
-
new_flow.jobs = tuple(
|
|
202
|
+
new_flow.jobs = tuple(job for job in new_flow if job != other)
|
|
195
203
|
return new_flow
|
|
196
204
|
|
|
197
205
|
def __repr__(self, level: int = 0, prefix: str = "") -> str:
|
|
@@ -583,7 +591,7 @@ class Flow(MSONable):
|
|
|
583
591
|
dict_mod=dict_mod,
|
|
584
592
|
)
|
|
585
593
|
|
|
586
|
-
def append_name(self, append_str: str, prepend: bool = False):
|
|
594
|
+
def append_name(self, append_str: str, prepend: bool = False, dynamic: bool = True):
|
|
587
595
|
"""
|
|
588
596
|
Append a string to the name of the flow and all jobs contained in it.
|
|
589
597
|
|
|
@@ -600,7 +608,7 @@ class Flow(MSONable):
|
|
|
600
608
|
self.name += append_str
|
|
601
609
|
|
|
602
610
|
for job in self:
|
|
603
|
-
job.append_name(append_str, prepend=prepend)
|
|
611
|
+
job.append_name(append_str, prepend=prepend, dynamic=dynamic)
|
|
604
612
|
|
|
605
613
|
def update_metadata(
|
|
606
614
|
self,
|
|
@@ -609,9 +617,10 @@ class Flow(MSONable):
|
|
|
609
617
|
function_filter: Callable = None,
|
|
610
618
|
dict_mod: bool = False,
|
|
611
619
|
dynamic: bool = True,
|
|
620
|
+
callback_filter: Callable[[Flow | Job], bool] = lambda _: True,
|
|
612
621
|
):
|
|
613
622
|
"""
|
|
614
|
-
Update the metadata of
|
|
623
|
+
Update the metadata of the Flow and/or its Jobs.
|
|
615
624
|
|
|
616
625
|
Note that updates will be applied to jobs in nested Flow.
|
|
617
626
|
|
|
@@ -631,6 +640,10 @@ class Flow(MSONable):
|
|
|
631
640
|
dynamic
|
|
632
641
|
The updates will be propagated to Jobs/Flows dynamically generated at
|
|
633
642
|
runtime.
|
|
643
|
+
callback_filter
|
|
644
|
+
A function that takes a Flow or Job instance and returns True if updates
|
|
645
|
+
should be applied to that instance. Allows for custom filtering logic.
|
|
646
|
+
Applies recursively to nested Flows and Jobs so best be specific.
|
|
634
647
|
|
|
635
648
|
Examples
|
|
636
649
|
--------
|
|
@@ -647,16 +660,45 @@ class Flow(MSONable):
|
|
|
647
660
|
The ``metadata`` of both jobs could be updated as follows:
|
|
648
661
|
|
|
649
662
|
>>> flow.update_metadata({"tag": "addition_job"})
|
|
663
|
+
|
|
664
|
+
Or using a callback filter to only update flows containing a specific maker:
|
|
665
|
+
|
|
666
|
+
>>> flow.update_metadata(
|
|
667
|
+
... {"material_id": 42},
|
|
668
|
+
... callback_filter=lambda flow: SomeMaker in map(type, flow)
|
|
669
|
+
... and flow.name == "flow name"
|
|
670
|
+
... )
|
|
650
671
|
"""
|
|
651
|
-
|
|
652
|
-
|
|
672
|
+
from jobflow.utils.dict_mods import apply_mod
|
|
673
|
+
|
|
674
|
+
for job_or_flow in self:
|
|
675
|
+
job_or_flow.update_metadata(
|
|
653
676
|
update,
|
|
654
677
|
name_filter=name_filter,
|
|
655
678
|
function_filter=function_filter,
|
|
656
679
|
dict_mod=dict_mod,
|
|
657
680
|
dynamic=dynamic,
|
|
681
|
+
callback_filter=callback_filter,
|
|
658
682
|
)
|
|
659
683
|
|
|
684
|
+
if callback_filter(self) is False:
|
|
685
|
+
return
|
|
686
|
+
|
|
687
|
+
if dict_mod:
|
|
688
|
+
apply_mod(update, self.metadata)
|
|
689
|
+
else:
|
|
690
|
+
self.metadata.update(update)
|
|
691
|
+
|
|
692
|
+
if dynamic:
|
|
693
|
+
dict_input = {
|
|
694
|
+
"update": update,
|
|
695
|
+
"name_filter": name_filter,
|
|
696
|
+
"function_filter": function_filter,
|
|
697
|
+
"dict_mod": dict_mod,
|
|
698
|
+
"callback_filter": callback_filter,
|
|
699
|
+
}
|
|
700
|
+
self.metadata_updates.append(dict_input)
|
|
701
|
+
|
|
660
702
|
def update_config(
|
|
661
703
|
self,
|
|
662
704
|
config: jobflow.JobConfig | dict,
|
jobflow/core/job.py
CHANGED
|
@@ -6,14 +6,17 @@ import logging
|
|
|
6
6
|
import typing
|
|
7
7
|
import warnings
|
|
8
8
|
from dataclasses import dataclass, field
|
|
9
|
+
from typing import cast, overload
|
|
9
10
|
|
|
10
11
|
from monty.json import MSONable, jsanitize
|
|
12
|
+
from typing_extensions import Self
|
|
11
13
|
|
|
12
14
|
from jobflow.core.reference import OnMissing, OutputReference
|
|
13
15
|
from jobflow.utils.uid import suid
|
|
14
16
|
|
|
15
17
|
if typing.TYPE_CHECKING:
|
|
16
18
|
from collections.abc import Hashable, Sequence
|
|
19
|
+
from pathlib import Path
|
|
17
20
|
from typing import Any, Callable
|
|
18
21
|
|
|
19
22
|
from networkx import DiGraph
|
|
@@ -65,7 +68,24 @@ class JobConfig(MSONable):
|
|
|
65
68
|
response_manager_config: dict = field(default_factory=dict)
|
|
66
69
|
|
|
67
70
|
|
|
68
|
-
|
|
71
|
+
@overload
|
|
72
|
+
def job(method: Callable | None = None) -> Callable[..., Job]:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@overload
|
|
77
|
+
def job(method: Callable, **job_kwargs) -> Callable[..., Job]:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@overload
|
|
82
|
+
def job(method: None = None, **job_kwargs) -> Callable[..., Callable[..., Job]]:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def job(
|
|
87
|
+
method: Callable | None = None, **job_kwargs
|
|
88
|
+
) -> Callable[..., Job] | Callable[..., Callable[..., Job]]:
|
|
69
89
|
"""
|
|
70
90
|
Wrap a function to produce a :obj:`Job`.
|
|
71
91
|
|
|
@@ -277,12 +297,12 @@ class Job(MSONable):
|
|
|
277
297
|
--------
|
|
278
298
|
Builtin functions such as :obj:`print` can be specified.
|
|
279
299
|
|
|
280
|
-
>>> print_task = Job(function=print,
|
|
300
|
+
>>> print_task = Job(function=print, function_args=("I am a job", ))
|
|
281
301
|
|
|
282
302
|
Or other functions of the Python standard library.
|
|
283
303
|
|
|
284
304
|
>>> import os
|
|
285
|
-
>>> Job(function=os.path.join,
|
|
305
|
+
>>> Job(function=os.path.join, function_args=("folder", "filename.txt"))
|
|
286
306
|
|
|
287
307
|
To use custom functions, the functions should be importable (i.e. not
|
|
288
308
|
defined in another function). For example, if the following function is defined
|
|
@@ -290,7 +310,7 @@ class Job(MSONable):
|
|
|
290
310
|
|
|
291
311
|
>>> def add(a, b):
|
|
292
312
|
... return a + b
|
|
293
|
-
>>> add_job = Job(function=add,
|
|
313
|
+
>>> add_job = Job(function=add, function_args=(1, 2))
|
|
294
314
|
|
|
295
315
|
More details are given in the :obj:`job` decorator docstring.
|
|
296
316
|
|
|
@@ -313,6 +333,7 @@ class Job(MSONable):
|
|
|
313
333
|
hosts: list[str] = None,
|
|
314
334
|
metadata_updates: list[dict[str, Any]] = None,
|
|
315
335
|
config_updates: list[dict[str, Any]] = None,
|
|
336
|
+
name_updates: list[dict[str, Any]] = None,
|
|
316
337
|
**kwargs,
|
|
317
338
|
):
|
|
318
339
|
from copy import deepcopy
|
|
@@ -322,7 +343,6 @@ class Job(MSONable):
|
|
|
322
343
|
function_args = () if function_args is None else function_args
|
|
323
344
|
function_kwargs = {} if function_kwargs is None else function_kwargs
|
|
324
345
|
uuid = suid() if uuid is None else uuid
|
|
325
|
-
metadata = {} if metadata is None else metadata
|
|
326
346
|
config = JobConfig() if config is None else config
|
|
327
347
|
|
|
328
348
|
# make a deep copy of the function (means makers do not share the same instance)
|
|
@@ -333,10 +353,11 @@ class Job(MSONable):
|
|
|
333
353
|
self.uuid = uuid
|
|
334
354
|
self.index = index
|
|
335
355
|
self.name = name
|
|
336
|
-
self.metadata = metadata
|
|
356
|
+
self.metadata = metadata or {}
|
|
337
357
|
self.config = config
|
|
338
358
|
self.hosts = hosts or []
|
|
339
359
|
self.metadata_updates = metadata_updates or []
|
|
360
|
+
self.name_updates = name_updates or []
|
|
340
361
|
self.config_updates = config_updates or []
|
|
341
362
|
self._kwargs = kwargs
|
|
342
363
|
|
|
@@ -526,7 +547,7 @@ class Job(MSONable):
|
|
|
526
547
|
self.uuid = uuid
|
|
527
548
|
self.output = self.output.set_uuid(uuid)
|
|
528
549
|
|
|
529
|
-
def run(self, store: jobflow.JobStore) -> Response:
|
|
550
|
+
def run(self, store: jobflow.JobStore, job_dir: Path = None) -> Response:
|
|
530
551
|
"""
|
|
531
552
|
Run the job.
|
|
532
553
|
|
|
@@ -581,7 +602,9 @@ class Job(MSONable):
|
|
|
581
602
|
function = types.MethodType(function, bound)
|
|
582
603
|
|
|
583
604
|
response = function(*self.function_args, **self.function_kwargs)
|
|
584
|
-
response = Response.from_job_returns(
|
|
605
|
+
response = Response.from_job_returns(
|
|
606
|
+
response, self.output_schema, job_dir=job_dir
|
|
607
|
+
)
|
|
585
608
|
|
|
586
609
|
if response.replace is not None:
|
|
587
610
|
response.replace = prepare_replace(response.replace, self)
|
|
@@ -604,6 +627,8 @@ class Job(MSONable):
|
|
|
604
627
|
new_jobs.update_metadata(**metadata_update, dynamic=True)
|
|
605
628
|
for config_update in self.config_updates:
|
|
606
629
|
new_jobs.update_config(**config_update, dynamic=True)
|
|
630
|
+
for name_update in self.name_updates:
|
|
631
|
+
new_jobs.append_name(**name_update, dynamic=True)
|
|
607
632
|
|
|
608
633
|
if self.config.response_manager_config:
|
|
609
634
|
passed_config = self.config.response_manager_config
|
|
@@ -872,7 +897,7 @@ class Job(MSONable):
|
|
|
872
897
|
dict_mod=dict_mod,
|
|
873
898
|
)
|
|
874
899
|
|
|
875
|
-
def append_name(self, append_str: str, prepend: bool = False):
|
|
900
|
+
def append_name(self, append_str: str, prepend: bool = False, dynamic: bool = True):
|
|
876
901
|
"""
|
|
877
902
|
Append a string to the name of the job.
|
|
878
903
|
|
|
@@ -882,12 +907,18 @@ class Job(MSONable):
|
|
|
882
907
|
A string to append.
|
|
883
908
|
prepend
|
|
884
909
|
Prepend the name rather than appending it.
|
|
910
|
+
dynamic
|
|
911
|
+
The updates will be propagated to Jobs/Flows dynamically generated at
|
|
912
|
+
runtime.
|
|
885
913
|
"""
|
|
886
914
|
if prepend:
|
|
887
915
|
self.name = append_str + self.name
|
|
888
916
|
else:
|
|
889
917
|
self.name += append_str
|
|
890
918
|
|
|
919
|
+
if dynamic:
|
|
920
|
+
self.name_updates.append({"append_str": append_str, "prepend": prepend})
|
|
921
|
+
|
|
891
922
|
def update_metadata(
|
|
892
923
|
self,
|
|
893
924
|
update: dict[str, Any],
|
|
@@ -895,6 +926,7 @@ class Job(MSONable):
|
|
|
895
926
|
function_filter: Callable = None,
|
|
896
927
|
dict_mod: bool = False,
|
|
897
928
|
dynamic: bool = True,
|
|
929
|
+
callback_filter: Callable[[jobflow.Flow | Job], bool] = lambda _: True,
|
|
898
930
|
):
|
|
899
931
|
"""
|
|
900
932
|
Update the metadata of the job.
|
|
@@ -918,6 +950,9 @@ class Job(MSONable):
|
|
|
918
950
|
dynamic
|
|
919
951
|
The updates will be propagated to Jobs/Flows dynamically generated at
|
|
920
952
|
runtime.
|
|
953
|
+
callback_filter
|
|
954
|
+
A function that takes a Flow or Job instance and returns True if updates
|
|
955
|
+
should be applied to that instance. Allows for custom filtering logic.
|
|
921
956
|
|
|
922
957
|
Examples
|
|
923
958
|
--------
|
|
@@ -936,11 +971,16 @@ class Job(MSONable):
|
|
|
936
971
|
will not only set the `example` metadata to the `test_job`, but also to all the
|
|
937
972
|
new Jobs that will be generated at runtime by the ExampleMaker.
|
|
938
973
|
|
|
939
|
-
`update_metadata` can be called multiple times with different
|
|
940
|
-
|
|
974
|
+
`update_metadata` can be called multiple times with different filters to control
|
|
975
|
+
which Jobs will be updated. For example, using a callback filter:
|
|
941
976
|
|
|
942
|
-
|
|
943
|
-
|
|
977
|
+
>>> test_job.update_metadata(
|
|
978
|
+
... {"material_id": 42},
|
|
979
|
+
... callback_filter=lambda job: isinstance(job.maker, SomeMaker)
|
|
980
|
+
... )
|
|
981
|
+
|
|
982
|
+
At variance, if `dynamic` is set to `False` the metadata will only be
|
|
983
|
+
added to the filtered Jobs and not to any generated Jobs.
|
|
944
984
|
"""
|
|
945
985
|
from jobflow.utils.dict_mods import apply_mod
|
|
946
986
|
|
|
@@ -950,6 +990,7 @@ class Job(MSONable):
|
|
|
950
990
|
"name_filter": name_filter,
|
|
951
991
|
"function_filter": function_filter,
|
|
952
992
|
"dict_mod": dict_mod,
|
|
993
|
+
"callback_filter": callback_filter,
|
|
953
994
|
}
|
|
954
995
|
self.metadata_updates.append(dict_input)
|
|
955
996
|
|
|
@@ -957,7 +998,6 @@ class Job(MSONable):
|
|
|
957
998
|
function_filter = getattr(function_filter, "__wrapped__", function_filter)
|
|
958
999
|
function = getattr(self.function, "__wrapped__", self.function)
|
|
959
1000
|
|
|
960
|
-
# if function_filter is not None and function_filter != self.function:
|
|
961
1001
|
if function_filter is not None and function_filter != function:
|
|
962
1002
|
return
|
|
963
1003
|
|
|
@@ -966,6 +1006,9 @@ class Job(MSONable):
|
|
|
966
1006
|
):
|
|
967
1007
|
return
|
|
968
1008
|
|
|
1009
|
+
if callback_filter(self) is False:
|
|
1010
|
+
return
|
|
1011
|
+
|
|
969
1012
|
# if we get to here then we pass all the filters
|
|
970
1013
|
if dict_mod:
|
|
971
1014
|
apply_mod(update, self.metadata)
|
|
@@ -1134,8 +1177,8 @@ class Job(MSONable):
|
|
|
1134
1177
|
prepend
|
|
1135
1178
|
Insert the UUIDs at the beginning of the list rather than extending it.
|
|
1136
1179
|
"""
|
|
1137
|
-
if
|
|
1138
|
-
hosts_uuids = [hosts_uuids]
|
|
1180
|
+
if isinstance(hosts_uuids, str):
|
|
1181
|
+
hosts_uuids = [hosts_uuids]
|
|
1139
1182
|
if prepend:
|
|
1140
1183
|
self.hosts[0:0] = hosts_uuids
|
|
1141
1184
|
else:
|
|
@@ -1170,6 +1213,8 @@ class Response(typing.Generic[T]):
|
|
|
1170
1213
|
Stop any children of the current flow.
|
|
1171
1214
|
stop_jobflow
|
|
1172
1215
|
Stop executing all remaining jobs.
|
|
1216
|
+
job_dir
|
|
1217
|
+
The directory where the job was run.
|
|
1173
1218
|
"""
|
|
1174
1219
|
|
|
1175
1220
|
output: T = None
|
|
@@ -1179,13 +1224,15 @@ class Response(typing.Generic[T]):
|
|
|
1179
1224
|
stored_data: dict[Hashable, Any] = None
|
|
1180
1225
|
stop_children: bool = False
|
|
1181
1226
|
stop_jobflow: bool = False
|
|
1227
|
+
job_dir: str | Path = None
|
|
1182
1228
|
|
|
1183
1229
|
@classmethod
|
|
1184
1230
|
def from_job_returns(
|
|
1185
1231
|
cls,
|
|
1186
1232
|
job_returns: Any | None,
|
|
1187
1233
|
output_schema: type[BaseModel] = None,
|
|
1188
|
-
|
|
1234
|
+
job_dir: str | Path = None,
|
|
1235
|
+
) -> Self:
|
|
1189
1236
|
"""
|
|
1190
1237
|
Generate a :obj:`Response` from the outputs of a :obj:`Job`.
|
|
1191
1238
|
|
|
@@ -1199,6 +1246,8 @@ class Response(typing.Generic[T]):
|
|
|
1199
1246
|
output_schema
|
|
1200
1247
|
A pydantic model associated with the job. Used to enforce a schema for the
|
|
1201
1248
|
outputs.
|
|
1249
|
+
job_dir
|
|
1250
|
+
The directory where the job was run.
|
|
1202
1251
|
|
|
1203
1252
|
Raises
|
|
1204
1253
|
------
|
|
@@ -1215,17 +1264,18 @@ class Response(typing.Generic[T]):
|
|
|
1215
1264
|
# only apply output schema if there is no replace.
|
|
1216
1265
|
job_returns.output = apply_schema(job_returns.output, output_schema)
|
|
1217
1266
|
|
|
1218
|
-
|
|
1267
|
+
job_returns.job_dir = job_dir
|
|
1268
|
+
return cast(Self, job_returns)
|
|
1219
1269
|
|
|
1220
1270
|
if isinstance(job_returns, (list, tuple)):
|
|
1221
1271
|
# check that a Response object is not given as one of many outputs
|
|
1222
|
-
for
|
|
1223
|
-
if isinstance(
|
|
1272
|
+
for resp in job_returns:
|
|
1273
|
+
if isinstance(resp, Response):
|
|
1224
1274
|
raise ValueError(
|
|
1225
1275
|
"Response cannot be returned in combination with other outputs."
|
|
1226
1276
|
)
|
|
1227
1277
|
|
|
1228
|
-
return cls(output=apply_schema(job_returns, output_schema))
|
|
1278
|
+
return cls(output=apply_schema(job_returns, output_schema), job_dir=job_dir)
|
|
1229
1279
|
|
|
1230
1280
|
|
|
1231
1281
|
def apply_schema(output: Any, schema: type[BaseModel] | None):
|
jobflow/core/maker.py
CHANGED
|
@@ -261,7 +261,7 @@ def recursive_call(
|
|
|
261
261
|
|
|
262
262
|
if isinstance(class_filter, Maker):
|
|
263
263
|
# Maker instance supplied rather than a Maker class
|
|
264
|
-
class_filter = class_filter.__class__
|
|
264
|
+
class_filter = class_filter.__class__ # type: ignore[assignment]
|
|
265
265
|
|
|
266
266
|
def _filter(nested_obj: Maker):
|
|
267
267
|
# Filter the Maker object
|
|
@@ -269,9 +269,7 @@ def recursive_call(
|
|
|
269
269
|
return False
|
|
270
270
|
if name_filter is not None and name_filter not in nested_obj.name:
|
|
271
271
|
return False
|
|
272
|
-
|
|
273
|
-
return False
|
|
274
|
-
return True
|
|
272
|
+
return class_filter is None or isinstance(nested_obj, class_filter)
|
|
275
273
|
|
|
276
274
|
d = obj.as_dict()
|
|
277
275
|
|
jobflow/core/reference.py
CHANGED
jobflow/core/store.py
CHANGED
|
@@ -17,6 +17,7 @@ if typing.TYPE_CHECKING:
|
|
|
17
17
|
from typing import Any, Optional, Union
|
|
18
18
|
|
|
19
19
|
from maggma.core import Sort
|
|
20
|
+
from typing_extensions import Self
|
|
20
21
|
|
|
21
22
|
from jobflow.core.schemas import JobStoreDocument
|
|
22
23
|
|
|
@@ -545,9 +546,9 @@ class JobStore(Store):
|
|
|
545
546
|
)
|
|
546
547
|
|
|
547
548
|
@classmethod
|
|
548
|
-
def from_file(cls
|
|
549
|
+
def from_file(cls, db_file: str | Path, **kwargs) -> Self:
|
|
549
550
|
"""
|
|
550
|
-
Create
|
|
551
|
+
Create a JobStore from a database file.
|
|
551
552
|
|
|
552
553
|
Two options are supported for the database file. The file should be in json or
|
|
553
554
|
yaml format.
|
|
@@ -555,7 +556,7 @@ class JobStore(Store):
|
|
|
555
556
|
The simplest format is a monty dumped version of the store, generated using:
|
|
556
557
|
|
|
557
558
|
>>> from monty.serialization import dumpfn
|
|
558
|
-
>>> dumpfn("job_store.yaml"
|
|
559
|
+
>>> dumpfn(job_store, "job_store.yaml")
|
|
559
560
|
|
|
560
561
|
Alternatively, the file can contain the keys docs_store, additional_stores and
|
|
561
562
|
any other keyword arguments supported by the :obj:`JobStore` constructor. The
|
|
@@ -605,7 +606,7 @@ class JobStore(Store):
|
|
|
605
606
|
return cls.from_dict_spec(store_info, **kwargs)
|
|
606
607
|
|
|
607
608
|
@classmethod
|
|
608
|
-
def from_dict_spec(cls
|
|
609
|
+
def from_dict_spec(cls, spec: dict, **kwargs) -> Self:
|
|
609
610
|
"""
|
|
610
611
|
Create an JobStore from a dict specification.
|
|
611
612
|
|
jobflow/managers/fireworks.py
CHANGED
|
@@ -5,6 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
import typing
|
|
6
6
|
|
|
7
7
|
from fireworks import FiretaskBase, Firework, FWAction, Workflow, explicit_serialize
|
|
8
|
+
from fireworks.utilities.fw_serializers import recursive_serialize, serialize_fw
|
|
9
|
+
from monty.json import jsanitize
|
|
8
10
|
|
|
9
11
|
if typing.TYPE_CHECKING:
|
|
10
12
|
from collections.abc import Sequence
|
|
@@ -197,3 +199,16 @@ class JobFiretask(FiretaskBase):
|
|
|
197
199
|
defuse_workflow=response.stop_jobflow,
|
|
198
200
|
defuse_children=response.stop_children,
|
|
199
201
|
)
|
|
202
|
+
|
|
203
|
+
@serialize_fw
|
|
204
|
+
@recursive_serialize
|
|
205
|
+
def to_dict(self) -> dict:
|
|
206
|
+
"""
|
|
207
|
+
Serialize version of the FireTask.
|
|
208
|
+
|
|
209
|
+
Overrides the original method to explicitly jsanitize the Job
|
|
210
|
+
to handle cases not properly handled by fireworks, like a Callable.
|
|
211
|
+
"""
|
|
212
|
+
d = dict(self)
|
|
213
|
+
d["job"] = jsanitize(d["job"].as_dict())
|
|
214
|
+
return d
|
jobflow/managers/local.py
CHANGED
|
@@ -15,8 +15,8 @@ logger = logging.getLogger(__name__)
|
|
|
15
15
|
|
|
16
16
|
def run_locally(
|
|
17
17
|
flow: jobflow.Flow | jobflow.Job | list[jobflow.Job],
|
|
18
|
-
log: bool = True,
|
|
19
|
-
store: jobflow.JobStore = None,
|
|
18
|
+
log: bool | str = True,
|
|
19
|
+
store: jobflow.JobStore | None = None,
|
|
20
20
|
create_folders: bool = False,
|
|
21
21
|
root_dir: str | Path | None = None,
|
|
22
22
|
ensure_success: bool = False,
|
|
@@ -30,8 +30,11 @@ def run_locally(
|
|
|
30
30
|
----------
|
|
31
31
|
flow : Flow | Job | list[Job]
|
|
32
32
|
A job or flow.
|
|
33
|
-
log : bool
|
|
34
|
-
|
|
33
|
+
log : bool | str
|
|
34
|
+
Controls logging. Defaults to True. Can be:
|
|
35
|
+
- False: disable logging
|
|
36
|
+
- True: use default logging format (read from ~/.jobflow.yaml)
|
|
37
|
+
- str: custom logging format string (e.g. "%(message)s" for more concise output)
|
|
35
38
|
store : JobStore
|
|
36
39
|
A job store. If a job store is not specified then
|
|
37
40
|
:obj:`JobflowSettings.JOB_STORE` will be used. By default this is a maggma
|
|
@@ -46,7 +49,7 @@ def run_locally(
|
|
|
46
49
|
Raise an error if the flow was not executed successfully.
|
|
47
50
|
allow_external_references : bool
|
|
48
51
|
If False all the references to other outputs should be from other Jobs
|
|
49
|
-
of the Flow.
|
|
52
|
+
of the same Flow.
|
|
50
53
|
raise_immediately : bool
|
|
51
54
|
If True, raise an exception immediately if a job fails. If False, continue
|
|
52
55
|
running the flow and only raise an exception at the end if the flow did not
|
|
@@ -58,7 +61,7 @@ def run_locally(
|
|
|
58
61
|
The responses of the jobs, as a dict of ``{uuid: {index: response}}``.
|
|
59
62
|
"""
|
|
60
63
|
from collections import defaultdict
|
|
61
|
-
from datetime import datetime
|
|
64
|
+
from datetime import datetime, timezone
|
|
62
65
|
from pathlib import Path
|
|
63
66
|
from random import randint
|
|
64
67
|
|
|
@@ -77,7 +80,7 @@ def run_locally(
|
|
|
77
80
|
store.connect()
|
|
78
81
|
|
|
79
82
|
if log:
|
|
80
|
-
initialize_logger()
|
|
83
|
+
initialize_logger(fmt=log if isinstance(log, str) else "")
|
|
81
84
|
|
|
82
85
|
flow = get_flow(flow, allow_external_references=allow_external_references)
|
|
83
86
|
|
|
@@ -152,7 +155,7 @@ def run_locally(
|
|
|
152
155
|
|
|
153
156
|
def _get_job_dir():
|
|
154
157
|
if create_folders:
|
|
155
|
-
time_now = datetime.
|
|
158
|
+
time_now = datetime.now(tz=timezone.utc).strftime(SETTINGS.DIRECTORY_FORMAT)
|
|
156
159
|
job_dir = root_dir / f"job_{time_now}-{randint(10000, 99999)}"
|
|
157
160
|
job_dir.mkdir()
|
|
158
161
|
return job_dir
|
|
@@ -165,6 +168,8 @@ def run_locally(
|
|
|
165
168
|
with cd(job_dir):
|
|
166
169
|
response, jobflow_stopped = _run_job(job, parents)
|
|
167
170
|
|
|
171
|
+
if response is not None:
|
|
172
|
+
response.job_dir = job_dir
|
|
168
173
|
encountered_bad_response = encountered_bad_response or response is None
|
|
169
174
|
if jobflow_stopped:
|
|
170
175
|
return False
|
jobflow/settings.py
CHANGED
|
@@ -11,6 +11,8 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
11
11
|
from jobflow import JobStore
|
|
12
12
|
|
|
13
13
|
DEFAULT_CONFIG_FILE_PATH = Path("~/.jobflow.yaml").expanduser().as_posix()
|
|
14
|
+
DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)s %(message)s"
|
|
15
|
+
DEFAULT_DIRECTORY_FORMAT = "%Y-%m-%d-%H-%M-%S-%f"
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
def _default_additional_store():
|
|
@@ -28,7 +30,7 @@ class JobflowSettings(BaseSettings):
|
|
|
28
30
|
"""
|
|
29
31
|
Settings for jobflow.
|
|
30
32
|
|
|
31
|
-
The default way to modify these is to
|
|
33
|
+
The default way to modify these is to create a ~/.jobflow.yaml. Alternatively,
|
|
32
34
|
the environment variable ``JOBFLOW_CONFIG_FILE`` can be set to point to a yaml file
|
|
33
35
|
with jobflow settings.
|
|
34
36
|
|
|
@@ -114,9 +116,18 @@ class JobflowSettings(BaseSettings):
|
|
|
114
116
|
"accepted formats.",
|
|
115
117
|
)
|
|
116
118
|
DIRECTORY_FORMAT: str = Field(
|
|
117
|
-
|
|
119
|
+
DEFAULT_DIRECTORY_FORMAT,
|
|
118
120
|
description="Date stamp format used to create directories",
|
|
119
121
|
)
|
|
122
|
+
LOG_FORMAT: str = Field(
|
|
123
|
+
DEFAULT_LOG_FORMAT,
|
|
124
|
+
description="""Logging format string. Common format codes:
|
|
125
|
+
- %(message)s - The logged message
|
|
126
|
+
- %(asctime)s - Human-readable time
|
|
127
|
+
- %(levelname)s - DEBUG, INFO, WARNING, ERROR, or CRITICAL
|
|
128
|
+
- %(name)s - Logger name
|
|
129
|
+
See Python logging documentation for more format codes.""",
|
|
130
|
+
)
|
|
120
131
|
|
|
121
132
|
UID_TYPE: str = Field(
|
|
122
133
|
"uuid4", description="Type of unique identifier to use to track jobs. "
|
jobflow/utils/__init__.py
CHANGED
jobflow/utils/log.py
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
"""Tools for logging."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import logging
|
|
4
6
|
|
|
5
7
|
|
|
6
|
-
def initialize_logger(level: int = logging.INFO) -> logging.Logger:
|
|
8
|
+
def initialize_logger(level: int = logging.INFO, fmt: str = "") -> logging.Logger:
|
|
7
9
|
"""Initialize the default logger.
|
|
8
10
|
|
|
9
11
|
Parameters
|
|
10
12
|
----------
|
|
11
13
|
level
|
|
12
14
|
The log level.
|
|
15
|
+
fmt
|
|
16
|
+
Custom logging format string. Defaults to JobflowSettings.LOG_FORMAT.
|
|
17
|
+
Common format codes:
|
|
18
|
+
- %(message)s - The logged message
|
|
19
|
+
- %(asctime)s - Human-readable time
|
|
20
|
+
- %(levelname)s - DEBUG, INFO, WARNING, ERROR, or CRITICAL
|
|
21
|
+
See Python logging documentation for more format codes.
|
|
13
22
|
|
|
14
23
|
Returns
|
|
15
24
|
-------
|
|
@@ -18,12 +27,15 @@ def initialize_logger(level: int = logging.INFO) -> logging.Logger:
|
|
|
18
27
|
"""
|
|
19
28
|
import sys
|
|
20
29
|
|
|
30
|
+
from jobflow import SETTINGS
|
|
31
|
+
|
|
21
32
|
log = logging.getLogger("jobflow")
|
|
22
33
|
log.setLevel(level)
|
|
23
34
|
log.handlers = [] # reset logging handlers if they already exist
|
|
24
35
|
|
|
25
|
-
|
|
36
|
+
formatter = logging.Formatter(fmt or SETTINGS.LOG_FORMAT)
|
|
37
|
+
|
|
26
38
|
screen_handler = logging.StreamHandler(stream=sys.stdout)
|
|
27
|
-
screen_handler.setFormatter(
|
|
39
|
+
screen_handler.setFormatter(formatter)
|
|
28
40
|
log.addHandler(screen_handler)
|
|
29
41
|
return log
|
jobflow/utils/uid.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Tools for generating UUIDs."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from uuid import UUID
|
|
@@ -11,7 +12,7 @@ except ImportError: # pragma: no cover
|
|
|
11
12
|
"Install it with `pip install jobflow[ulid]` or `pip install python-ulid`."
|
|
12
13
|
)
|
|
13
14
|
|
|
14
|
-
class ULID: # type: ignore
|
|
15
|
+
class ULID: # type: ignore[no-redef]
|
|
15
16
|
"""Fake ULID class for raising import error."""
|
|
16
17
|
|
|
17
18
|
def __init__(self, *args, **kwargs):
|
jobflow/utils/uuid.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: jobflow
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.19
|
|
4
4
|
Summary: jobflow is a library for writing computational workflows
|
|
5
5
|
Author-email: Alex Ganose <a.ganose@imperial.ac.uk>
|
|
6
6
|
License: modified BSD
|
|
@@ -25,49 +25,52 @@ Requires-Python: >=3.9
|
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
License-File: LICENSE
|
|
27
27
|
Requires-Dist: PyYAML
|
|
28
|
-
Requires-Dist: maggma
|
|
29
|
-
Requires-Dist: monty
|
|
28
|
+
Requires-Dist: maggma>=0.57.0
|
|
29
|
+
Requires-Dist: monty>=2023.9.25
|
|
30
30
|
Requires-Dist: networkx
|
|
31
|
-
Requires-Dist: pydantic-settings
|
|
32
|
-
Requires-Dist: pydantic
|
|
31
|
+
Requires-Dist: pydantic-settings>=2.0.3
|
|
32
|
+
Requires-Dist: pydantic>=2.0.1
|
|
33
33
|
Requires-Dist: pydash
|
|
34
|
-
Provides-Extra:
|
|
35
|
-
Requires-Dist:
|
|
34
|
+
Provides-Extra: ulid
|
|
35
|
+
Requires-Dist: python-ulid; extra == "ulid"
|
|
36
36
|
Provides-Extra: docs
|
|
37
|
-
Requires-Dist:
|
|
38
|
-
Requires-Dist: furo
|
|
39
|
-
Requires-Dist: ipython
|
|
40
|
-
Requires-Dist:
|
|
41
|
-
Requires-Dist: nbsphinx
|
|
42
|
-
Requires-Dist: sphinx-copybutton
|
|
43
|
-
Requires-Dist: sphinx
|
|
44
|
-
Provides-Extra:
|
|
45
|
-
Requires-Dist:
|
|
46
|
-
|
|
47
|
-
Requires-Dist: FireWorks ==2.0.3 ; extra == 'strict'
|
|
48
|
-
Requires-Dist: PyYAML ==6.0.1 ; extra == 'strict'
|
|
49
|
-
Requires-Dist: maggma ==0.61.0 ; extra == 'strict'
|
|
50
|
-
Requires-Dist: matplotlib ==3.8.2 ; extra == 'strict'
|
|
51
|
-
Requires-Dist: monty ==2023.11.3 ; extra == 'strict'
|
|
52
|
-
Requires-Dist: moto ==4.2.13 ; extra == 'strict'
|
|
53
|
-
Requires-Dist: networkx ==3.2.1 ; extra == 'strict'
|
|
54
|
-
Requires-Dist: pydantic-settings ==2.1.0 ; extra == 'strict'
|
|
55
|
-
Requires-Dist: pydantic ==2.5.3 ; extra == 'strict'
|
|
56
|
-
Requires-Dist: pydash ==7.0.6 ; extra == 'strict'
|
|
57
|
-
Requires-Dist: pydot ==2.0.0 ; extra == 'strict'
|
|
58
|
-
Requires-Dist: typing-extensions ==4.9.0 ; extra == 'strict'
|
|
59
|
-
Requires-Dist: python-ulid ==2.2.0 ; extra == 'strict'
|
|
37
|
+
Requires-Dist: autodoc_pydantic==2.1.0; extra == "docs"
|
|
38
|
+
Requires-Dist: furo==2024.8.6; extra == "docs"
|
|
39
|
+
Requires-Dist: ipython==8.29.0; extra == "docs"
|
|
40
|
+
Requires-Dist: myst_parser==4.0.0; extra == "docs"
|
|
41
|
+
Requires-Dist: nbsphinx==0.9.5; extra == "docs"
|
|
42
|
+
Requires-Dist: sphinx-copybutton==0.5.2; extra == "docs"
|
|
43
|
+
Requires-Dist: sphinx==8.1.3; extra == "docs"
|
|
44
|
+
Provides-Extra: dev
|
|
45
|
+
Requires-Dist: pre-commit>=2.12.1; extra == "dev"
|
|
46
|
+
Requires-Dist: typing_extensions; python_version < "3.11" and extra == "dev"
|
|
60
47
|
Provides-Extra: tests
|
|
61
|
-
Requires-Dist: moto
|
|
62
|
-
Requires-Dist: pytest-cov
|
|
63
|
-
Requires-Dist: pytest
|
|
64
|
-
Provides-Extra: ulid
|
|
65
|
-
Requires-Dist: python-ulid ; extra == 'ulid'
|
|
48
|
+
Requires-Dist: moto==4.2.13; extra == "tests"
|
|
49
|
+
Requires-Dist: pytest-cov==6.0.0; extra == "tests"
|
|
50
|
+
Requires-Dist: pytest==8.3.3; extra == "tests"
|
|
66
51
|
Provides-Extra: vis
|
|
67
|
-
Requires-Dist: matplotlib
|
|
68
|
-
Requires-Dist: pydot
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
Requires-Dist: matplotlib; extra == "vis"
|
|
53
|
+
Requires-Dist: pydot; extra == "vis"
|
|
54
|
+
Provides-Extra: fireworks
|
|
55
|
+
Requires-Dist: FireWorks; extra == "fireworks"
|
|
56
|
+
Provides-Extra: strict
|
|
57
|
+
Requires-Dist: FireWorks==2.0.3; extra == "strict"
|
|
58
|
+
Requires-Dist: PyYAML==6.0.2; extra == "strict"
|
|
59
|
+
Requires-Dist: maggma==0.70.0; extra == "strict"
|
|
60
|
+
Requires-Dist: matplotlib==3.9.2; extra == "strict"
|
|
61
|
+
Requires-Dist: monty==2024.10.21; extra == "strict"
|
|
62
|
+
Requires-Dist: moto==4.2.13; extra == "strict"
|
|
63
|
+
Requires-Dist: networkx==3.2.1; extra == "strict"
|
|
64
|
+
Requires-Dist: pydantic-settings==2.6.1; extra == "strict"
|
|
65
|
+
Requires-Dist: pydantic==2.9.2; extra == "strict"
|
|
66
|
+
Requires-Dist: pydash==8.0.4; extra == "strict"
|
|
67
|
+
Requires-Dist: pydot==2.0.0; extra == "strict"
|
|
68
|
+
Requires-Dist: python-ulid==3.0.0; extra == "strict"
|
|
69
|
+
Requires-Dist: typing-extensions==4.12.2; extra == "strict"
|
|
70
|
+
|
|
71
|
+
<div align="center">
|
|
72
|
+
|
|
73
|
+
# 
|
|
71
74
|
|
|
72
75
|
[](https://github.com/materialsproject/jobflow/actions?query=workflow%3Atesting)
|
|
73
76
|
[](https://codecov.io/gh/materialsproject/jobflow/)
|
|
@@ -75,11 +78,14 @@ Requires-Dist: pydot ; extra == 'vis'
|
|
|
75
78
|

|
|
76
79
|
[](https://doi.org/10.21105/joss.05995)
|
|
77
80
|
|
|
81
|
+
</div>
|
|
82
|
+
|
|
78
83
|
[Documentation](https://materialsproject.github.io/jobflow/) | [PyPI](https://pypi.org/project/jobflow/) | [GitHub](https://github.com/materialsproject/jobflow) | [Paper](https://doi.org/10.21105/joss.05995)
|
|
79
84
|
|
|
80
85
|
Jobflow is a free, open-source library for writing and executing workflows. Complex
|
|
81
86
|
workflows can be defined using simple python functions and executed locally or on
|
|
82
|
-
arbitrary computing resources using the [FireWorks][fireworks]
|
|
87
|
+
arbitrary computing resources using the [jobflow-remote][jfr] or [FireWorks][fireworks]
|
|
88
|
+
workflow managers.
|
|
83
89
|
|
|
84
90
|
Some features that distinguish jobflow are dynamic workflows, easy compositing and
|
|
85
91
|
connecting of workflows, and the ability to store workflow outputs across multiple
|
|
@@ -98,7 +104,7 @@ Some of its features include:
|
|
|
98
104
|
way to build complex workflows.
|
|
99
105
|
- Integration with multiple databases (MongoDB, S3, GridFS, and more) through the
|
|
100
106
|
[Maggma][maggma] package.
|
|
101
|
-
- Support for the [FireWorks][fireworks] workflow management
|
|
107
|
+
- Support for the [jobflow-remote][jfr] and [FireWorks][fireworks] workflow management systems, allowing workflow
|
|
102
108
|
execution on multicore machines or through a queue, on a single machine or multiple
|
|
103
109
|
machines.
|
|
104
110
|
- Support for dynamic workflows — workflows that modify themselves or create new ones
|
|
@@ -192,6 +198,7 @@ Jobflow was designed by Alex Ganose, Anubhav Jain, Gian-Marco Rignanese, David W
|
|
|
192
198
|
|
|
193
199
|
[maggma]: https://materialsproject.github.io/maggma/
|
|
194
200
|
[fireworks]: https://materialsproject.github.io/fireworks/
|
|
201
|
+
[jfr]: https://matgenix.github.io/jobflow-remote
|
|
195
202
|
[help-forum]: https://matsci.org/c/fireworks
|
|
196
203
|
[issues]: https://github.com/materialsproject/jobflow/issues
|
|
197
204
|
[changelog]: https://materialsproject.github.io/jobflow/changelog.html
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
jobflow/__init__.py,sha256=l7o10BaqEQWw5aZziWRg40PsIAgQ4lrlluXs9hIv2mg,570
|
|
2
|
+
jobflow/_version.py,sha256=Ym07PBD7sAmpqVpX8tuzWma3P_Hv6KXbDKXWkw8OwaI,205
|
|
3
|
+
jobflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
jobflow/settings.py,sha256=yinHpix-DPwzcBhQCO8zDXFuv048rmBgpYMPa9wcs8c,6094
|
|
5
|
+
jobflow/core/__init__.py,sha256=3sx5t1Gysejc4c_fPrhvCjPUg0p_384Zko8ms2c_NnY,98
|
|
6
|
+
jobflow/core/flow.py,sha256=XuH0-n8DpSWLHyUdVU7KeewtDzjZw7AIqh9xwLQkqcc,30634
|
|
7
|
+
jobflow/core/job.py,sha256=wAVmJrWO5sf7vze8696lMLz_A-bnPVgIp5TvhmDIGx0,48145
|
|
8
|
+
jobflow/core/maker.py,sha256=WhsYw2wDNVIyAEeRUoikOQMzXzHuXfFVwXrJpwGCD1E,11162
|
|
9
|
+
jobflow/core/reference.py,sha256=hLuuxQcXwMCEQk-N3jpvxnWfxJZ4OFtxK4Fb-PXMJNk,17090
|
|
10
|
+
jobflow/core/schemas.py,sha256=Oi5-PnZpI8S9jSY7Q4f8H7xUybbRZDXlgugeVewVsrA,968
|
|
11
|
+
jobflow/core/state.py,sha256=IGJTtmpotDKEcgDEnsT5x20ZeyvQT68Mr3teTjkgYnM,709
|
|
12
|
+
jobflow/core/store.py,sha256=PAtAhZTLU1b17OD66rqlo8_JP1p-IzH8c2WJwWd1Q2o,26997
|
|
13
|
+
jobflow/managers/__init__.py,sha256=KkA5cVDe2os2_2aTa8eiB9SnkGLZNybcci-Lo4tbaWM,55
|
|
14
|
+
jobflow/managers/fireworks.py,sha256=xKPcL1BX59jnF6LpyEwcsxvCCShLqep-9UHRyv9FmYE,7146
|
|
15
|
+
jobflow/managers/local.py,sha256=iI1QI6Dg7P7C3vVd4Mt1xXAmcQ-o37Ox_gQRnafsBHE,6142
|
|
16
|
+
jobflow/utils/__init__.py,sha256=XrA5IQ2Zm6unFRzgA58Ip9CIPchnlXm8Ei-Ny20f6NA,364
|
|
17
|
+
jobflow/utils/dict_mods.py,sha256=g50aMw-mK3RjXp_hHJBR9xUaWRYXoqqmPTMCPDDluz4,6052
|
|
18
|
+
jobflow/utils/enum.py,sha256=_buiq0NuwyYe82Ajd5-8Pjx9J63XcDDeIvLDS4evM1s,713
|
|
19
|
+
jobflow/utils/find.py,sha256=J57_xQ9boWVVqpbSfTECuZ3TinEXKN24LO4YWIB-7T4,6149
|
|
20
|
+
jobflow/utils/graph.py,sha256=UjwEklPvFz3oTiSY3E_ElYZz21ePY0xLaT7nqWDVfpI,6598
|
|
21
|
+
jobflow/utils/log.py,sha256=4-_1OUSQ8I4Q6CgQ4pxNBeveBdpXla7nibZNF7Vk3pw,1110
|
|
22
|
+
jobflow/utils/uid.py,sha256=hNkpJ5AYhKd_sPWE_iGPcn6YD_AyizKX_swWksFr-_M,2537
|
|
23
|
+
jobflow/utils/uuid.py,sha256=-cN3NpxiZuQMjl_0wq5h8ZjTYpf5o4-o2QQ5g9E7vQM,406
|
|
24
|
+
jobflow-0.1.19.dist-info/LICENSE,sha256=jUEiENfZNQZh9RE9ixtUWgVkLRD85ScZ6iv1WREf19w,2418
|
|
25
|
+
jobflow-0.1.19.dist-info/METADATA,sha256=HrT82LwLIk0FvQTJqg_ZNSyr8xHqe-PV1TL-RiVX_eM,9945
|
|
26
|
+
jobflow-0.1.19.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
27
|
+
jobflow-0.1.19.dist-info/top_level.txt,sha256=IanNooU88OupQPDrWnT0rbL3E27P2wEy7Jsfx9_j8zc,8
|
|
28
|
+
jobflow-0.1.19.dist-info/RECORD,,
|
jobflow-0.1.17.dist-info/RECORD
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
jobflow/__init__.py,sha256=l7o10BaqEQWw5aZziWRg40PsIAgQ4lrlluXs9hIv2mg,570
|
|
2
|
-
jobflow/_version.py,sha256=Ym07PBD7sAmpqVpX8tuzWma3P_Hv6KXbDKXWkw8OwaI,205
|
|
3
|
-
jobflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
jobflow/settings.py,sha256=2tG3FKGTkCDgguwQZrgSt36Ll86nn4ET8ukBr2IN8Ys,5614
|
|
5
|
-
jobflow/core/__init__.py,sha256=3sx5t1Gysejc4c_fPrhvCjPUg0p_384Zko8ms2c_NnY,98
|
|
6
|
-
jobflow/core/flow.py,sha256=tPMo6tCJ742XqNLiEaf0HLIZ1M9uyE8j_Uw0pEm05kg,28912
|
|
7
|
-
jobflow/core/job.py,sha256=xeJhbjh3G7uOGq-3fN5UuLi52dKi9ifuCaogEMlYJT8,46488
|
|
8
|
-
jobflow/core/maker.py,sha256=DKfFXe91v9rRFtgUrm8FMiEcLArmb6jnK7nysuDdZts,11185
|
|
9
|
-
jobflow/core/reference.py,sha256=sPhV3_q8Ru-8xIoryKY8Y9oQN-1cAGVvnY08tgSAxwk,17090
|
|
10
|
-
jobflow/core/schemas.py,sha256=Oi5-PnZpI8S9jSY7Q4f8H7xUybbRZDXlgugeVewVsrA,968
|
|
11
|
-
jobflow/core/state.py,sha256=IGJTtmpotDKEcgDEnsT5x20ZeyvQT68Mr3teTjkgYnM,709
|
|
12
|
-
jobflow/core/store.py,sha256=iuIQR2d4y5lmp6t7vnR2vR6TKcxfygbrKteFCGrZTgA,26971
|
|
13
|
-
jobflow/managers/__init__.py,sha256=KkA5cVDe2os2_2aTa8eiB9SnkGLZNybcci-Lo4tbaWM,55
|
|
14
|
-
jobflow/managers/fireworks.py,sha256=5IKDkE-dppvbhDWTfJKCMmqvxg50zBgCqm6qUqsVZtc,6654
|
|
15
|
-
jobflow/managers/local.py,sha256=huEQBtWkYl3K7x3Ff-PL664aOZ1SXMHTRIvyIG97IgE,5771
|
|
16
|
-
jobflow/utils/__init__.py,sha256=41l-OtHRW5J15-gilJpzilN2Tqejbb_TJYFfqxc0SPU,363
|
|
17
|
-
jobflow/utils/dict_mods.py,sha256=g50aMw-mK3RjXp_hHJBR9xUaWRYXoqqmPTMCPDDluz4,6052
|
|
18
|
-
jobflow/utils/enum.py,sha256=_buiq0NuwyYe82Ajd5-8Pjx9J63XcDDeIvLDS4evM1s,713
|
|
19
|
-
jobflow/utils/find.py,sha256=J57_xQ9boWVVqpbSfTECuZ3TinEXKN24LO4YWIB-7T4,6149
|
|
20
|
-
jobflow/utils/graph.py,sha256=UjwEklPvFz3oTiSY3E_ElYZz21ePY0xLaT7nqWDVfpI,6598
|
|
21
|
-
jobflow/utils/log.py,sha256=tIMpsI4JTlkpxjBZfWqZ0qkEkIxk1-RBasz8JhDcF7E,692
|
|
22
|
-
jobflow/utils/uid.py,sha256=E3L57n4V4KX0Rfd64mnuru0INAR5iQ2o8LIHBAVxXcU,2526
|
|
23
|
-
jobflow/utils/uuid.py,sha256=DA0nEdaYGb3Mk0sHbyCdKXurens-c-HliqTIibQzk2k,405
|
|
24
|
-
jobflow-0.1.17.dist-info/LICENSE,sha256=jUEiENfZNQZh9RE9ixtUWgVkLRD85ScZ6iv1WREf19w,2418
|
|
25
|
-
jobflow-0.1.17.dist-info/METADATA,sha256=HJbIfYiuxQA7MZJrztdPyZftVVZZiyDLGbGYWW_Vmlc,9753
|
|
26
|
-
jobflow-0.1.17.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
27
|
-
jobflow-0.1.17.dist-info/top_level.txt,sha256=IanNooU88OupQPDrWnT0rbL3E27P2wEy7Jsfx9_j8zc,8
|
|
28
|
-
jobflow-0.1.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|