jobflow 0.2.1__py3-none-any.whl → 0.3.0__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/__init__.py +1 -1
- jobflow/core/flow.py +110 -1
- jobflow/core/job.py +40 -0
- jobflow/core/reference.py +39 -4
- jobflow/core/store.py +19 -5
- {jobflow-0.2.1.dist-info → jobflow-0.3.0.dist-info}/METADATA +12 -12
- {jobflow-0.2.1.dist-info → jobflow-0.3.0.dist-info}/RECORD +10 -10
- {jobflow-0.2.1.dist-info → jobflow-0.3.0.dist-info}/WHEEL +1 -1
- {jobflow-0.2.1.dist-info → jobflow-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {jobflow-0.2.1.dist-info → jobflow-0.3.0.dist-info}/top_level.txt +0 -0
jobflow/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Jobflow is a package for writing dynamic and connected workflows."""
|
|
2
2
|
|
|
3
3
|
from jobflow._version import __version__
|
|
4
|
-
from jobflow.core.flow import Flow, JobOrder
|
|
4
|
+
from jobflow.core.flow import Flow, JobOrder, flow
|
|
5
5
|
from jobflow.core.job import Job, JobConfig, Response, job
|
|
6
6
|
from jobflow.core.maker import Maker
|
|
7
7
|
from jobflow.core.reference import OnMissing, OutputReference
|
jobflow/core/flow.py
CHANGED
|
@@ -4,6 +4,8 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import warnings
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from contextvars import ContextVar
|
|
7
9
|
from copy import deepcopy
|
|
8
10
|
from typing import TYPE_CHECKING
|
|
9
11
|
|
|
@@ -155,6 +157,12 @@ class Flow(MSONable):
|
|
|
155
157
|
self.add_jobs(jobs)
|
|
156
158
|
self.output = output
|
|
157
159
|
|
|
160
|
+
# If we're running inside a `DecoratedFlow`, add *this* Flow to the
|
|
161
|
+
# context.
|
|
162
|
+
current_flow_children_list = _current_flow_context.get()
|
|
163
|
+
if current_flow_children_list is not None:
|
|
164
|
+
current_flow_children_list.append(self)
|
|
165
|
+
|
|
158
166
|
def __len__(self) -> int:
|
|
159
167
|
"""Get the number of jobs or subflows in the flow."""
|
|
160
168
|
return len(self.jobs)
|
|
@@ -828,7 +836,7 @@ class Flow(MSONable):
|
|
|
828
836
|
if job.host is not None and job.host != self.uuid:
|
|
829
837
|
raise ValueError(
|
|
830
838
|
f"{type(job).__name__} {job.name} ({job.uuid}) already belongs "
|
|
831
|
-
f"to another flow."
|
|
839
|
+
f"to another flow: {job.host}."
|
|
832
840
|
)
|
|
833
841
|
if job.uuid in job_ids:
|
|
834
842
|
raise ValueError(
|
|
@@ -921,3 +929,104 @@ def get_flow(
|
|
|
921
929
|
)
|
|
922
930
|
|
|
923
931
|
return flow
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
class DecoratedFlow(Flow):
|
|
935
|
+
"""A DecoratedFlow is a Flow that is returned on using the @flow decorator."""
|
|
936
|
+
|
|
937
|
+
def __init__(self, fn, *args, **kwargs):
|
|
938
|
+
from jobflow import Maker
|
|
939
|
+
|
|
940
|
+
self.fn = fn
|
|
941
|
+
self.args = args
|
|
942
|
+
self.kwargs = kwargs
|
|
943
|
+
|
|
944
|
+
# Collect the jobs and flows that are used in the function
|
|
945
|
+
children_list = []
|
|
946
|
+
with flow_build_context(children_list):
|
|
947
|
+
output = self.fn(*self.args, **self.kwargs)
|
|
948
|
+
|
|
949
|
+
# From the collected items, remove those that have already been assigned
|
|
950
|
+
# to another Flow during the call of the function.
|
|
951
|
+
# This handles the case where a Flow object is instantiated inside
|
|
952
|
+
# the decorated function
|
|
953
|
+
children_list = [c for c in children_list if c.host is None]
|
|
954
|
+
|
|
955
|
+
name = getattr(self.fn, "__qualname__", self.fn.__name__)
|
|
956
|
+
|
|
957
|
+
# if decorates a make() in a Maker use that as a name
|
|
958
|
+
if (
|
|
959
|
+
len(self.args) > 0
|
|
960
|
+
and name.split(".")[-1] == "make"
|
|
961
|
+
and getattr(args[0], self.fn.__name__, None)
|
|
962
|
+
and isinstance(args[0], Maker)
|
|
963
|
+
):
|
|
964
|
+
name = args[0].name
|
|
965
|
+
|
|
966
|
+
if isinstance(output, (jobflow.Job, jobflow.Flow)):
|
|
967
|
+
warnings.warn(
|
|
968
|
+
f"@flow decorated function '{name}' contains a Flow or"
|
|
969
|
+
f"Job as an output. Usually the output should be the output of"
|
|
970
|
+
f"a Job or another Flow (e.g. job.output). Replacing the"
|
|
971
|
+
f"output of the @flow with the output of the Flow/Job."
|
|
972
|
+
f"If this message is unexpected then double check the outputs"
|
|
973
|
+
f"of your @flow decorated function.",
|
|
974
|
+
stacklevel=2,
|
|
975
|
+
)
|
|
976
|
+
output = output.output
|
|
977
|
+
|
|
978
|
+
super().__init__(name=name, jobs=children_list, output=output)
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
def flow(fn):
|
|
982
|
+
"""
|
|
983
|
+
Turn a function into a DecoratedFlow object.
|
|
984
|
+
|
|
985
|
+
Parameters
|
|
986
|
+
----------
|
|
987
|
+
fn (Callable): The function to be wrapped in a DecoratedFlow object.
|
|
988
|
+
|
|
989
|
+
Returns
|
|
990
|
+
-------
|
|
991
|
+
Callable: A wrapper function that, when called, creates and returns
|
|
992
|
+
an instance of DecoratedFlow initialized with the provided function
|
|
993
|
+
and its arguments.
|
|
994
|
+
"""
|
|
995
|
+
from functools import wraps
|
|
996
|
+
|
|
997
|
+
@wraps(fn)
|
|
998
|
+
def wrapper(*args, **kwargs):
|
|
999
|
+
return DecoratedFlow(fn, *args, **kwargs)
|
|
1000
|
+
|
|
1001
|
+
return wrapper
|
|
1002
|
+
|
|
1003
|
+
|
|
1004
|
+
@contextmanager
|
|
1005
|
+
def flow_build_context(children_list):
|
|
1006
|
+
"""Provide a context manager for flows.
|
|
1007
|
+
|
|
1008
|
+
Provides a context manager for setting and resetting the `Job` and `Flow`
|
|
1009
|
+
objects in the current flow context.
|
|
1010
|
+
|
|
1011
|
+
Parameters
|
|
1012
|
+
----------
|
|
1013
|
+
children_list: The `Job` or `Flow` objects that are part of the current
|
|
1014
|
+
flow context.
|
|
1015
|
+
|
|
1016
|
+
Yields
|
|
1017
|
+
------
|
|
1018
|
+
None: Temporarily sets the provided `Job` or `Flow` objects as
|
|
1019
|
+
belonging to the current flow context within the managed block.
|
|
1020
|
+
|
|
1021
|
+
Raises
|
|
1022
|
+
------
|
|
1023
|
+
None
|
|
1024
|
+
"""
|
|
1025
|
+
token = _current_flow_context.set(children_list)
|
|
1026
|
+
try:
|
|
1027
|
+
yield
|
|
1028
|
+
finally:
|
|
1029
|
+
_current_flow_context.reset(token)
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
_current_flow_context = ContextVar("current_flow_context", default=None)
|
jobflow/core/job.py
CHANGED
|
@@ -11,6 +11,7 @@ from typing import cast, overload
|
|
|
11
11
|
from monty.json import MSONable, jsanitize
|
|
12
12
|
from typing_extensions import Self
|
|
13
13
|
|
|
14
|
+
from jobflow.core.flow import _current_flow_context
|
|
14
15
|
from jobflow.core.reference import OnMissing, OutputReference
|
|
15
16
|
from jobflow.utils.uid import suid
|
|
16
17
|
|
|
@@ -384,6 +385,30 @@ class Job(MSONable):
|
|
|
384
385
|
stacklevel=2,
|
|
385
386
|
)
|
|
386
387
|
|
|
388
|
+
# If we're running inside a `DecoratedFlow`, add *this* Job to the
|
|
389
|
+
# context.
|
|
390
|
+
current_flow_children_list = _current_flow_context.get()
|
|
391
|
+
if current_flow_children_list is not None:
|
|
392
|
+
current_flow_children_list.append(self)
|
|
393
|
+
|
|
394
|
+
def __getitem__(self, key: Any) -> OutputReference:
|
|
395
|
+
"""
|
|
396
|
+
Get the corresponding `OutputReference` for the `Job`.
|
|
397
|
+
|
|
398
|
+
This is for when it is indexed like a dictionary or list.
|
|
399
|
+
|
|
400
|
+
Parameters
|
|
401
|
+
----------
|
|
402
|
+
key
|
|
403
|
+
The index/key.
|
|
404
|
+
|
|
405
|
+
Returns
|
|
406
|
+
-------
|
|
407
|
+
OutputReference
|
|
408
|
+
The equivalent of `Job.output[k]`
|
|
409
|
+
"""
|
|
410
|
+
return self.output[key]
|
|
411
|
+
|
|
387
412
|
def __repr__(self):
|
|
388
413
|
"""Get a string representation of the job."""
|
|
389
414
|
name, uuid = self.name, self.uuid
|
|
@@ -1279,6 +1304,21 @@ class Response(typing.Generic[T]):
|
|
|
1279
1304
|
Response
|
|
1280
1305
|
The job response controlling the data to store and flow execution options.
|
|
1281
1306
|
"""
|
|
1307
|
+
# If the Job returns another Job, or something that can be interpreted
|
|
1308
|
+
# as an iterable of jobs, interpret it as a replace.
|
|
1309
|
+
from jobflow import Flow
|
|
1310
|
+
|
|
1311
|
+
def is_job_or_flow(x):
|
|
1312
|
+
return isinstance(x, Job | Flow)
|
|
1313
|
+
|
|
1314
|
+
should_replace = is_job_or_flow(job_returns)
|
|
1315
|
+
if isinstance(job_returns, (list, tuple)):
|
|
1316
|
+
should_replace = all(is_job_or_flow(resp) for resp in job_returns)
|
|
1317
|
+
|
|
1318
|
+
if should_replace:
|
|
1319
|
+
job_returns = Response(replace=job_returns)
|
|
1320
|
+
|
|
1321
|
+
# only apply output schema if there is no replace.
|
|
1282
1322
|
if isinstance(job_returns, Response):
|
|
1283
1323
|
if job_returns.replace is None:
|
|
1284
1324
|
# only apply output schema if there is no replace.
|
jobflow/core/reference.py
CHANGED
|
@@ -514,12 +514,47 @@ def validate_schema_access(
|
|
|
514
514
|
The BaseModel class associated with the item, if any.
|
|
515
515
|
"""
|
|
516
516
|
schema_dict = schema.model_json_schema()
|
|
517
|
-
|
|
517
|
+
item_in_schema = item in schema_dict["properties"]
|
|
518
|
+
property_like = isinstance(item, str) and has_property_like(schema, item)
|
|
519
|
+
if not item_in_schema and not property_like:
|
|
518
520
|
raise AttributeError(f"{schema.__name__} does not have attribute '{item}'.")
|
|
519
521
|
|
|
520
522
|
subschema = None
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
523
|
+
if item_in_schema:
|
|
524
|
+
item_type = schema.model_fields[item].annotation
|
|
525
|
+
if lenient_issubclass(item_type, BaseModel):
|
|
526
|
+
subschema = item_type
|
|
524
527
|
|
|
525
528
|
return True, subschema
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def has_property_like(obj_type: type, name: str) -> bool:
|
|
532
|
+
"""
|
|
533
|
+
Check if a class has an attribute and if it is property-like.
|
|
534
|
+
|
|
535
|
+
Parameters
|
|
536
|
+
----------
|
|
537
|
+
obj_type
|
|
538
|
+
The class that needs to be checked
|
|
539
|
+
name
|
|
540
|
+
The name of the attribute to be verified
|
|
541
|
+
|
|
542
|
+
Returns
|
|
543
|
+
-------
|
|
544
|
+
bool
|
|
545
|
+
True if the property corresponding to the name is property-like.
|
|
546
|
+
"""
|
|
547
|
+
if not hasattr(obj_type, name):
|
|
548
|
+
return False
|
|
549
|
+
|
|
550
|
+
attr = getattr(obj_type, name)
|
|
551
|
+
|
|
552
|
+
if isinstance(attr, property):
|
|
553
|
+
return True
|
|
554
|
+
|
|
555
|
+
if callable(attr):
|
|
556
|
+
return False
|
|
557
|
+
|
|
558
|
+
# Check for custom property-like descriptors with __get__ but not callable
|
|
559
|
+
# If not, is not property-like.
|
|
560
|
+
return hasattr(attr, "__get__")
|
jobflow/core/store.py
CHANGED
|
@@ -21,9 +21,17 @@ if typing.TYPE_CHECKING:
|
|
|
21
21
|
|
|
22
22
|
from jobflow.core.schemas import JobStoreDocument
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
obj_save_type = Union[
|
|
25
|
+
str,
|
|
26
|
+
Enum,
|
|
27
|
+
type[MSONable],
|
|
28
|
+
list[Union[Enum, str, type[MSONable], list[Union[str, type[MSONable]]]]],
|
|
29
|
+
]
|
|
30
|
+
save_type = Optional[dict[str, obj_save_type]]
|
|
31
|
+
obj_load_type = Union[
|
|
32
|
+
str, Enum, type[MSONable], list[Union[Enum, str, type[MSONable]]]
|
|
33
|
+
]
|
|
34
|
+
load_type = Union[bool, dict[str, Union[bool, obj_load_type]]]
|
|
27
35
|
|
|
28
36
|
|
|
29
37
|
T = typing.TypeVar("T", bound="JobStore")
|
|
@@ -43,6 +51,9 @@ class JobStore(Store):
|
|
|
43
51
|
Which items to save in additional stores when uploading documents. Given as a
|
|
44
52
|
mapping of ``{store name: store_type}`` where ``store_type`` can be a dictionary
|
|
45
53
|
key (string or enum), an :obj:`.MSONable` class, or a list of keys/classes.
|
|
54
|
+
If the list of keys/classes itself contains a list/tuple, this will be treated
|
|
55
|
+
as the path to the save key. Can be used if a key is duplicate in the output
|
|
56
|
+
and only a single occurrence shall be put in the additional store.
|
|
46
57
|
load
|
|
47
58
|
Which items to load from additional stores when querying documents. Given as a
|
|
48
59
|
mapping of ``{store name: store_type}`` where ``store_type`` can be `True``, in
|
|
@@ -304,7 +315,10 @@ class JobStore(Store):
|
|
|
304
315
|
locations = []
|
|
305
316
|
for store_name, store_save in save_keys.items():
|
|
306
317
|
for save_key in store_save:
|
|
307
|
-
|
|
318
|
+
if isinstance(save_key, (list, tuple)):
|
|
319
|
+
locations.append(list(save_key))
|
|
320
|
+
else:
|
|
321
|
+
locations.extend(find_key(doc, save_key, include_end=True))
|
|
308
322
|
|
|
309
323
|
locations = get_root_locations(locations)
|
|
310
324
|
objects = [get(doc, list(loc)) for loc in locations]
|
|
@@ -726,7 +740,7 @@ def _prepare_load(
|
|
|
726
740
|
|
|
727
741
|
def _prepare_save(
|
|
728
742
|
save: bool | save_type,
|
|
729
|
-
) -> dict[str, list[str | type[MSONable]]]:
|
|
743
|
+
) -> dict[str, list[str | type[MSONable] | list[str | type[MSONable]]]]:
|
|
730
744
|
"""Standardize save type."""
|
|
731
745
|
from enum import Enum
|
|
732
746
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jobflow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
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
|
|
@@ -36,35 +36,35 @@ Provides-Extra: ulid
|
|
|
36
36
|
Requires-Dist: python-ulid; extra == "ulid"
|
|
37
37
|
Provides-Extra: docs
|
|
38
38
|
Requires-Dist: autodoc_pydantic==2.2.0; extra == "docs"
|
|
39
|
-
Requires-Dist: furo==2025.
|
|
40
|
-
Requires-Dist: ipython==9.
|
|
39
|
+
Requires-Dist: furo==2025.12.19; extra == "docs"
|
|
40
|
+
Requires-Dist: ipython==9.9.0; extra == "docs"
|
|
41
41
|
Requires-Dist: myst_parser==4.0.1; extra == "docs"
|
|
42
|
-
Requires-Dist: nbsphinx==0.9.
|
|
42
|
+
Requires-Dist: nbsphinx==0.9.8; extra == "docs"
|
|
43
43
|
Requires-Dist: sphinx-copybutton==0.5.2; extra == "docs"
|
|
44
44
|
Requires-Dist: sphinx==8.1.3; extra == "docs"
|
|
45
45
|
Provides-Extra: dev
|
|
46
46
|
Requires-Dist: pre-commit>=2.12.1; extra == "dev"
|
|
47
47
|
Requires-Dist: typing_extensions; python_version < "3.11" and extra == "dev"
|
|
48
48
|
Provides-Extra: tests
|
|
49
|
-
Requires-Dist: moto==5.1.
|
|
49
|
+
Requires-Dist: moto==5.1.20; extra == "tests"
|
|
50
50
|
Requires-Dist: pytest-cov==7.0.0; extra == "tests"
|
|
51
|
-
Requires-Dist: pytest==
|
|
51
|
+
Requires-Dist: pytest==9.0.2; extra == "tests"
|
|
52
52
|
Provides-Extra: vis
|
|
53
53
|
Requires-Dist: matplotlib; extra == "vis"
|
|
54
54
|
Requires-Dist: pydot; extra == "vis"
|
|
55
55
|
Provides-Extra: fireworks
|
|
56
56
|
Requires-Dist: FireWorks; extra == "fireworks"
|
|
57
57
|
Provides-Extra: strict
|
|
58
|
-
Requires-Dist: FireWorks==2.0.
|
|
58
|
+
Requires-Dist: FireWorks==2.0.8; extra == "strict"
|
|
59
59
|
Requires-Dist: PyYAML==6.0.3; extra == "strict"
|
|
60
60
|
Requires-Dist: maggma==0.72.0; extra == "strict"
|
|
61
|
-
Requires-Dist: matplotlib==3.10.
|
|
61
|
+
Requires-Dist: matplotlib==3.10.8; extra == "strict"
|
|
62
62
|
Requires-Dist: monty==2025.3.3; extra == "strict"
|
|
63
|
-
Requires-Dist: moto==5.1.
|
|
63
|
+
Requires-Dist: moto==5.1.20; extra == "strict"
|
|
64
64
|
Requires-Dist: networkx==3.4.2; extra == "strict"
|
|
65
|
-
Requires-Dist: pydantic-settings==2.
|
|
66
|
-
Requires-Dist: pydantic==2.12.
|
|
67
|
-
Requires-Dist: pydash==8.0.
|
|
65
|
+
Requires-Dist: pydantic-settings==2.12.0; extra == "strict"
|
|
66
|
+
Requires-Dist: pydantic==2.12.5; extra == "strict"
|
|
67
|
+
Requires-Dist: pydash==8.0.6; extra == "strict"
|
|
68
68
|
Requires-Dist: pydot==4.0.1; extra == "strict"
|
|
69
69
|
Requires-Dist: python-ulid==3.1.0; extra == "strict"
|
|
70
70
|
Requires-Dist: typing-extensions==4.15.0; extra == "strict"
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
jobflow/__init__.py,sha256=
|
|
1
|
+
jobflow/__init__.py,sha256=gKxdw3Yi4gjq6218fMBTHSBnVq-kZ4uZ9fLoKbC1xr0,576
|
|
2
2
|
jobflow/_version.py,sha256=Ym07PBD7sAmpqVpX8tuzWma3P_Hv6KXbDKXWkw8OwaI,205
|
|
3
3
|
jobflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
jobflow/settings.py,sha256=yinHpix-DPwzcBhQCO8zDXFuv048rmBgpYMPa9wcs8c,6094
|
|
5
5
|
jobflow/core/__init__.py,sha256=3sx5t1Gysejc4c_fPrhvCjPUg0p_384Zko8ms2c_NnY,98
|
|
6
|
-
jobflow/core/flow.py,sha256=
|
|
7
|
-
jobflow/core/job.py,sha256=
|
|
6
|
+
jobflow/core/flow.py,sha256=ernJoGkts5hi3ruReJHDqioB5N2wpl6zjiwCQqhqGYM,34337
|
|
7
|
+
jobflow/core/job.py,sha256=9Tqv4u9Fc_3Ou1qBizHLvrgLB_WjVKcyjodlvMAnAMg,50319
|
|
8
8
|
jobflow/core/maker.py,sha256=WhsYw2wDNVIyAEeRUoikOQMzXzHuXfFVwXrJpwGCD1E,11162
|
|
9
|
-
jobflow/core/reference.py,sha256=
|
|
9
|
+
jobflow/core/reference.py,sha256=ZATBhaB9HDhPujsfscOcHw7nJf7QfKvozvII47_q1UU,18012
|
|
10
10
|
jobflow/core/schemas.py,sha256=Oi5-PnZpI8S9jSY7Q4f8H7xUybbRZDXlgugeVewVsrA,968
|
|
11
11
|
jobflow/core/state.py,sha256=IGJTtmpotDKEcgDEnsT5x20ZeyvQT68Mr3teTjkgYnM,709
|
|
12
|
-
jobflow/core/store.py,sha256=
|
|
12
|
+
jobflow/core/store.py,sha256=njeKh_F-sHGt-hk0lsETmgK9H1I7hAqCeJY4LzStAxo,27627
|
|
13
13
|
jobflow/managers/__init__.py,sha256=KkA5cVDe2os2_2aTa8eiB9SnkGLZNybcci-Lo4tbaWM,55
|
|
14
14
|
jobflow/managers/fireworks.py,sha256=xKPcL1BX59jnF6LpyEwcsxvCCShLqep-9UHRyv9FmYE,7146
|
|
15
15
|
jobflow/managers/local.py,sha256=iI1QI6Dg7P7C3vVd4Mt1xXAmcQ-o37Ox_gQRnafsBHE,6142
|
|
@@ -21,8 +21,8 @@ jobflow/utils/graph.py,sha256=kweAowEzv8ZGjJ9KZvJ-G5ueAqGPWbU0z7Xd4e-q8no,6596
|
|
|
21
21
|
jobflow/utils/log.py,sha256=4-_1OUSQ8I4Q6CgQ4pxNBeveBdpXla7nibZNF7Vk3pw,1110
|
|
22
22
|
jobflow/utils/uid.py,sha256=hNkpJ5AYhKd_sPWE_iGPcn6YD_AyizKX_swWksFr-_M,2537
|
|
23
23
|
jobflow/utils/uuid.py,sha256=m0fInOs1yklpavf7jId85luDOHjZqfIIUzEtd89Bk_s,440
|
|
24
|
-
jobflow-0.
|
|
25
|
-
jobflow-0.
|
|
26
|
-
jobflow-0.
|
|
27
|
-
jobflow-0.
|
|
28
|
-
jobflow-0.
|
|
24
|
+
jobflow-0.3.0.dist-info/licenses/LICENSE,sha256=jUEiENfZNQZh9RE9ixtUWgVkLRD85ScZ6iv1WREf19w,2418
|
|
25
|
+
jobflow-0.3.0.dist-info/METADATA,sha256=5lPrcdt01_rFBFjVSCxQpbHvH7N9JQwSOaODeG-Z6Po,10040
|
|
26
|
+
jobflow-0.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
27
|
+
jobflow-0.3.0.dist-info/top_level.txt,sha256=IanNooU88OupQPDrWnT0rbL3E27P2wEy7Jsfx9_j8zc,8
|
|
28
|
+
jobflow-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|