ddeutil-workflow 0.0.37__py3-none-any.whl → 0.0.38__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.
ddeutil/workflow/utils.py CHANGED
@@ -5,10 +5,9 @@
5
5
  # ------------------------------------------------------------------------------
6
6
  from __future__ import annotations
7
7
 
8
- import logging
9
8
  import stat
10
9
  import time
11
- from collections.abc import Iterator, Mapping
10
+ from collections.abc import Iterator
12
11
  from datetime import date, datetime, timedelta
13
12
  from hashlib import md5
14
13
  from inspect import isfunction
@@ -24,7 +23,6 @@ from .__types import DictData, Matrix
24
23
 
25
24
  T = TypeVar("T")
26
25
  UTC = ZoneInfo("UTC")
27
- logger = logging.getLogger("ddeutil.workflow")
28
26
 
29
27
 
30
28
  def get_dt_now(
@@ -188,29 +186,6 @@ def filter_func(value: T) -> T:
188
186
  return value
189
187
 
190
188
 
191
- def dash2underscore(
192
- key: str,
193
- values: DictData,
194
- *,
195
- fixed: str | None = None,
196
- ) -> DictData:
197
- """Change key name that has dash to underscore.
198
-
199
- :param key:
200
- :param values:
201
- :param fixed:
202
-
203
- Examples:
204
- >>> dash2underscore('foo-bar', {"foo-bar": "demo"})
205
- {'foo_bar': 'demo'}
206
-
207
- :rtype: DictData
208
- """
209
- if key in values:
210
- values[(fixed or key.replace("-", "_"))] = values.pop(key)
211
- return values
212
-
213
-
214
189
  def cross_product(matrix: Matrix) -> Iterator[DictData]:
215
190
  """Iterator of products value from matrix.
216
191
 
@@ -267,21 +242,3 @@ def cut_id(run_id: str, *, num: int = 6) -> str:
267
242
  :rtype: str
268
243
  """
269
244
  return run_id[-num:]
270
-
271
-
272
- def deep_update(origin: DictData, u: Mapping) -> DictData:
273
- """Deep update dict.
274
-
275
- Example:
276
- >>> deep_update(
277
- ... origin={"jobs": {"job01": "foo"}},
278
- ... u={"jobs": {"job02": "bar"}},
279
- ... )
280
- {"jobs": {"job01": "foo", "job02": "bar"}}
281
- """
282
- for k, value in u.items():
283
- if isinstance(value, Mapping) and value:
284
- origin[k] = deep_update(origin.get(k, {}), value)
285
- else:
286
- origin[k] = value
287
- return origin
@@ -20,6 +20,7 @@ from functools import partial, total_ordering
20
20
  from heapq import heappop, heappush
21
21
  from queue import Queue
22
22
  from textwrap import dedent
23
+ from threading import Event
23
24
  from typing import Optional
24
25
 
25
26
  from pydantic import BaseModel, ConfigDict, Field
@@ -68,34 +69,45 @@ class ReleaseType(str, Enum):
68
69
  config=ConfigDict(arbitrary_types_allowed=True, use_enum_values=True)
69
70
  )
70
71
  class Release:
71
- """Release Pydantic dataclass object that use for represent
72
- the release data that use with the `workflow.release` method."""
72
+ """Release Pydantic dataclass object that use for represent the release data
73
+ that use with the `workflow.release` method.
74
+ """
73
75
 
74
- date: datetime
75
- offset: float
76
- end_date: datetime
77
- runner: CronRunner
76
+ date: datetime = field()
77
+ offset: float = field()
78
+ end_date: datetime = field()
79
+ runner: CronRunner = field()
78
80
  type: ReleaseType = field(default=ReleaseType.DEFAULT)
79
81
 
80
82
  def __repr__(self) -> str:
83
+ """Represent string"""
81
84
  return repr(f"{self.date:%Y-%m-%d %H:%M:%S}")
82
85
 
83
86
  def __str__(self) -> str:
87
+ """Override string value of this release object with the date field.
88
+
89
+ :rtype: str
90
+ """
84
91
  return f"{self.date:%Y-%m-%d %H:%M:%S}"
85
92
 
86
93
  @classmethod
87
94
  def from_dt(cls, dt: datetime | str) -> Self:
88
95
  """Construct Release via datetime object only.
89
96
 
90
- :param dt: A datetime object.
97
+ :param dt: (datetime | str) A datetime object or string that want to
98
+ construct to the Release object.
91
99
 
92
- :rtype: Self
100
+ :raise TypeError: If the type of the dt argument does not valid with
101
+ datetime or str object.
102
+
103
+ :rtype: Release
93
104
  """
94
105
  if isinstance(dt, str):
95
106
  dt: datetime = datetime.fromisoformat(dt)
96
107
  elif not isinstance(dt, datetime):
97
108
  raise TypeError(
98
- "The `from_dt` need argument type be str or datetime only."
109
+ "The `from_dt` need the `dt` argument type be str or datetime "
110
+ "only."
99
111
  )
100
112
 
101
113
  return cls(
@@ -108,6 +120,8 @@ class Release:
108
120
  def __eq__(self, other: Release | datetime) -> bool:
109
121
  """Override equal property that will compare only the same type or
110
122
  datetime.
123
+
124
+ :rtype: bool
111
125
  """
112
126
  if isinstance(other, self.__class__):
113
127
  return self.date == other.date
@@ -118,6 +132,8 @@ class Release:
118
132
  def __lt__(self, other: Release | datetime) -> bool:
119
133
  """Override equal property that will compare only the same type or
120
134
  datetime.
135
+
136
+ :rtype: bool
121
137
  """
122
138
  if isinstance(other, self.__class__):
123
139
  return self.date < other.date
@@ -143,7 +159,7 @@ class ReleaseQueue:
143
159
 
144
160
  :raise TypeError: If the type of input queue does not valid.
145
161
 
146
- :rtype: Self
162
+ :rtype: ReleaseQueue
147
163
  """
148
164
  if queue is None:
149
165
  return cls()
@@ -174,7 +190,7 @@ class ReleaseQueue:
174
190
  """Check an input Release object is the first value of the
175
191
  waiting queue.
176
192
 
177
- :rtype: bool
193
+ :rtype: Release
178
194
  """
179
195
  return self.queue[0]
180
196
 
@@ -265,12 +281,12 @@ class Workflow(BaseModel):
265
281
  an input workflow name. The loader object will use this workflow name to
266
282
  searching configuration data of this workflow model in conf path.
267
283
 
268
- :raise ValueError: If the type does not match with current object.
269
-
270
284
  :param name: A workflow name that want to pass to Loader object.
271
285
  :param externals: An external parameters that want to pass to Loader
272
286
  object.
273
287
 
288
+ :raise ValueError: If the type does not match with current object.
289
+
274
290
  :rtype: Self
275
291
  """
276
292
  loader: Loader = Loader(name, externals=(externals or {}))
@@ -285,11 +301,11 @@ class Workflow(BaseModel):
285
301
  loader_data["name"] = name.replace(" ", "_")
286
302
 
287
303
  # NOTE: Prepare `on` data
288
- cls.__bypass_on(loader_data, externals=externals)
304
+ cls.__bypass_on__(loader_data, externals=externals)
289
305
  return cls.model_validate(obj=loader_data)
290
306
 
291
307
  @classmethod
292
- def __bypass_on(
308
+ def __bypass_on__(
293
309
  cls,
294
310
  data: DictData,
295
311
  externals: DictData | None = None,
@@ -405,10 +421,13 @@ class Workflow(BaseModel):
405
421
  return self
406
422
 
407
423
  def job(self, name: str) -> Job:
408
- """Return this workflow's jobs that passing with the Job model.
424
+ """Return the workflow's job model that searching with an input job's
425
+ name or job's ID.
409
426
 
410
- :param name: A job name that want to get from a mapping of job models.
411
- :type name: str
427
+ :param name: (str) A job name or ID that want to get from a mapping of
428
+ job models.
429
+
430
+ :raise ValueError: If a name or ID does not exist on the jobs field.
412
431
 
413
432
  :rtype: Job
414
433
  :return: A job model that exists on this workflow by input name.
@@ -505,18 +524,19 @@ class Workflow(BaseModel):
505
524
  :param result: (Result) A result object for keeping context and status
506
525
  data.
507
526
 
527
+ :raise TypeError: If a queue parameter does not match with ReleaseQueue
528
+ type.
529
+
508
530
  :rtype: Result
509
531
  """
510
532
  audit: type[Audit] = audit or get_audit()
511
533
  name: str = override_log_name or self.name
512
-
513
- if result is None:
514
- result: Result = Result(
515
- run_id=(run_id or gen_id(name, unique=True)),
516
- parent_run_id=parent_run_id,
517
- )
518
- elif parent_run_id: # pragma: no cov
519
- result.set_parent_run_id(parent_run_id)
534
+ result: Result = Result.construct_with_rs_or_id(
535
+ result,
536
+ run_id=run_id,
537
+ parent_run_id=parent_run_id,
538
+ id_logic=name,
539
+ )
520
540
 
521
541
  if queue is not None and not isinstance(queue, ReleaseQueue):
522
542
  raise TypeError(
@@ -795,9 +815,7 @@ class Workflow(BaseModel):
795
815
  partial_queue(q)
796
816
  continue
797
817
 
798
- # NOTE: Push the latest Release to the running queue.
799
818
  heappush(q.running, release)
800
-
801
819
  futures.append(
802
820
  executor.submit(
803
821
  self.release,
@@ -827,6 +845,7 @@ class Workflow(BaseModel):
827
845
  params: DictData,
828
846
  *,
829
847
  result: Result | None = None,
848
+ event: Event | None = None,
830
849
  raise_error: bool = True,
831
850
  ) -> Result:
832
851
  """Job execution with passing dynamic parameters from the main workflow
@@ -842,10 +861,12 @@ class Workflow(BaseModel):
842
861
 
843
862
  :param job_id: A job ID that want to execute.
844
863
  :param params: A params that was parameterized from workflow execution.
845
- :param raise_error: A flag that raise error instead catching to result
846
- if it gets exception from job execution.
847
864
  :param result: (Result) A result object for keeping context and status
848
865
  data.
866
+ :param event: (Event) An event manager that pass to the
867
+ PoolThreadExecutor.
868
+ :param raise_error: A flag that raise error instead catching to result
869
+ if it gets exception from job execution.
849
870
 
850
871
  :rtype: Result
851
872
  :return: Return the result object that receive the job execution result
@@ -863,6 +884,12 @@ class Workflow(BaseModel):
863
884
 
864
885
  result.trace.info(f"[WORKFLOW]: Start execute job: {job_id!r}")
865
886
 
887
+ if event and event.is_set(): # pragma: no cov
888
+ raise WorkflowException(
889
+ "Workflow job was canceled from event that had set before "
890
+ "job execution."
891
+ )
892
+
866
893
  # IMPORTANT:
867
894
  # This execution change all job running IDs to the current workflow
868
895
  # running ID, but it still trac log to the same parent running ID
@@ -876,6 +903,7 @@ class Workflow(BaseModel):
876
903
  params=params,
877
904
  run_id=result.run_id,
878
905
  parent_run_id=result.parent_run_id,
906
+ event=event,
879
907
  ).context,
880
908
  to=params,
881
909
  )
@@ -889,7 +917,7 @@ class Workflow(BaseModel):
889
917
  "Handle error from the job execution does not support yet."
890
918
  ) from None
891
919
 
892
- return result.catch(status=0, context=params)
920
+ return result.catch(status=Status.SUCCESS, context=params)
893
921
 
894
922
  def execute(
895
923
  self,
@@ -927,22 +955,21 @@ class Workflow(BaseModel):
927
955
  :rtype: Result
928
956
  """
929
957
  ts: float = time.monotonic()
930
- if result is None: # pragma: no cov
931
- result: Result = Result(
932
- run_id=(run_id or gen_id(self.name, unique=True)),
933
- parent_run_id=parent_run_id,
934
- )
935
- elif parent_run_id:
936
- result.set_parent_run_id(parent_run_id)
958
+ result: Result = Result.construct_with_rs_or_id(
959
+ result,
960
+ run_id=run_id,
961
+ parent_run_id=parent_run_id,
962
+ id_logic=self.name,
963
+ )
937
964
 
938
965
  result.trace.info(f"[WORKFLOW]: Start Execute: {self.name!r} ...")
939
966
 
940
967
  # NOTE: It should not do anything if it does not have job.
941
968
  if not self.jobs:
942
969
  result.trace.warning(
943
- f"[WORKFLOW]: Workflow: {self.name!r} does not have any jobs"
970
+ f"[WORKFLOW]: {self.name!r} does not have any jobs"
944
971
  )
945
- return result.catch(status=0, context=params)
972
+ return result.catch(status=Status.SUCCESS, context=params)
946
973
 
947
974
  # NOTE: Create a job queue that keep the job that want to run after
948
975
  # its dependency condition.
@@ -959,7 +986,7 @@ class Workflow(BaseModel):
959
986
  # }
960
987
  #
961
988
  context: DictData = self.parameterize(params)
962
- status: int = 0
989
+ status: Status = Status.SUCCESS
963
990
  try:
964
991
  if config.max_job_parallel == 1:
965
992
  self.__exec_non_threading(
@@ -978,16 +1005,9 @@ class Workflow(BaseModel):
978
1005
  timeout=timeout,
979
1006
  )
980
1007
  except WorkflowException as err:
981
- status: int = 1
982
- context.update(
983
- {
984
- "errors": {
985
- "class": err,
986
- "name": err.__class__.__name__,
987
- "message": f"{err.__class__.__name__}: {err}",
988
- },
989
- },
990
- )
1008
+ status = Status.FAILED
1009
+ context.update({"errors": err.to_dict()})
1010
+
991
1011
  return result.catch(status=status, context=context)
992
1012
 
993
1013
  def __exec_threading(
@@ -1005,18 +1025,19 @@ class Workflow(BaseModel):
1005
1025
  If a job need dependency, it will check dependency job ID from
1006
1026
  context data before allow it run.
1007
1027
 
1008
- :param result: A result model.
1028
+ :param result: (Result) A result model.
1009
1029
  :param context: A context workflow data that want to downstream passing.
1010
1030
  :param ts: A start timestamp that use for checking execute time should
1011
1031
  time out.
1012
- :param job_queue: A job queue object.
1013
- :param timeout: A second value unit that bounding running time.
1032
+ :param job_queue: (Queue) A job queue object.
1033
+ :param timeout: (int) A second value unit that bounding running time.
1014
1034
  :param thread_timeout: A timeout to waiting all futures complete.
1015
1035
 
1016
1036
  :rtype: DictData
1017
1037
  """
1018
1038
  not_timeout_flag: bool = True
1019
1039
  timeout: int = timeout or config.max_job_exec_timeout
1040
+ event: Event = Event()
1020
1041
  result.trace.debug(f"[WORKFLOW]: Run {self.name!r} with threading.")
1021
1042
 
1022
1043
  # IMPORTANT: The job execution can run parallel and waiting by
@@ -1036,7 +1057,7 @@ class Workflow(BaseModel):
1036
1057
  if not job.check_needs(context["jobs"]):
1037
1058
  job_queue.task_done()
1038
1059
  job_queue.put(job_id)
1039
- time.sleep(0.25)
1060
+ time.sleep(0.15)
1040
1061
  continue
1041
1062
 
1042
1063
  # NOTE: Start workflow job execution with deep copy context data
@@ -1055,6 +1076,7 @@ class Workflow(BaseModel):
1055
1076
  job_id=job_id,
1056
1077
  params=context,
1057
1078
  result=result,
1079
+ event=event,
1058
1080
  ),
1059
1081
  )
1060
1082
 
@@ -1077,11 +1099,13 @@ class Workflow(BaseModel):
1077
1099
 
1078
1100
  return context
1079
1101
 
1102
+ result.trace.error(
1103
+ f"[WORKFLOW]: Execution: {self.name!r} was timeout."
1104
+ )
1105
+ event.set()
1080
1106
  for future in futures:
1081
1107
  future.cancel()
1082
1108
 
1083
- # NOTE: Raise timeout error.
1084
- result.trace.error(f"[WORKFLOW]: Execution: {self.name!r} was timeout.")
1085
1109
  raise WorkflowException(f"Execution: {self.name!r} was timeout.")
1086
1110
 
1087
1111
  def __exec_non_threading(
@@ -1099,18 +1123,25 @@ class Workflow(BaseModel):
1099
1123
  If a job need dependency, it will check dependency job ID from
1100
1124
  context data before allow it run.
1101
1125
 
1102
- :param result: A result model.
1126
+ :param result: (Result) A result model.
1103
1127
  :param context: A context workflow data that want to downstream passing.
1104
- :param ts: A start timestamp that use for checking execute time should
1105
- time out.
1106
- :param timeout: A second value unit that bounding running time.
1128
+ :param ts: (float) A start timestamp that use for checking execute time
1129
+ should time out.
1130
+ :param timeout: (int) A second value unit that bounding running time.
1107
1131
 
1108
1132
  :rtype: DictData
1109
1133
  """
1110
1134
  not_timeout_flag: bool = True
1111
1135
  timeout: int = timeout or config.max_job_exec_timeout
1136
+ event: Event = Event()
1137
+ future: Future | None = None
1112
1138
  result.trace.debug(f"[WORKFLOW]: Run {self.name!r} with non-threading.")
1113
1139
 
1140
+ executor = ThreadPoolExecutor(
1141
+ max_workers=1,
1142
+ thread_name_prefix="wf_exec_non_threading_",
1143
+ )
1144
+
1114
1145
  while not job_queue.empty() and (
1115
1146
  not_timeout_flag := ((time.monotonic() - ts) < timeout)
1116
1147
  ):
@@ -1132,7 +1163,32 @@ class Workflow(BaseModel):
1132
1163
  # 'params': <input-params>,
1133
1164
  # 'jobs': {},
1134
1165
  # }
1135
- self.execute_job(job_id=job_id, params=context, result=result)
1166
+ if future is None:
1167
+ future: Future = executor.submit(
1168
+ self.execute_job,
1169
+ job_id=job_id,
1170
+ params=context,
1171
+ result=result,
1172
+ event=event,
1173
+ )
1174
+ result.trace.debug(f"[WORKFLOW]: Make future: {future}")
1175
+ time.sleep(0.025)
1176
+ elif future.done():
1177
+ if err := future.exception():
1178
+ result.trace.error(f"[WORKFLOW]: {err}")
1179
+ raise WorkflowException(str(err))
1180
+
1181
+ future = None
1182
+ job_queue.put(job_id)
1183
+ elif future.running():
1184
+ time.sleep(0.075)
1185
+ job_queue.put(job_id)
1186
+ else: # pragma: no cov
1187
+ job_queue.put(job_id)
1188
+ result.trace.debug(
1189
+ f"Execution non-threading does not handle case: {future} "
1190
+ f"that not running."
1191
+ )
1136
1192
 
1137
1193
  # NOTE: Mark this job queue done.
1138
1194
  job_queue.task_done()
@@ -1142,11 +1198,12 @@ class Workflow(BaseModel):
1142
1198
  # NOTE: Wait for all items to finish processing by `task_done()`
1143
1199
  # method.
1144
1200
  job_queue.join()
1145
-
1201
+ executor.shutdown()
1146
1202
  return context
1147
1203
 
1148
- # NOTE: Raise timeout error.
1149
1204
  result.trace.error(f"[WORKFLOW]: Execution: {self.name!r} was timeout.")
1205
+ event.set()
1206
+ executor.shutdown()
1150
1207
  raise WorkflowException(f"Execution: {self.name!r} was timeout.")
1151
1208
 
1152
1209
 
@@ -1162,9 +1219,9 @@ class WorkflowTask:
1162
1219
  arguments before passing to the parent release method.
1163
1220
  """
1164
1221
 
1165
- alias: str
1166
- workflow: Workflow
1167
- runner: CronRunner
1222
+ alias: str = field()
1223
+ workflow: Workflow = field()
1224
+ runner: CronRunner = field()
1168
1225
  values: DictData = field(default_factory=dict)
1169
1226
 
1170
1227
  def release(
@@ -1185,6 +1242,11 @@ class WorkflowTask:
1185
1242
  :param audit: An audit class that want to save the execution result.
1186
1243
  :param queue: A ReleaseQueue object that use to mark complete.
1187
1244
 
1245
+ :raise ValueError: If a queue parameter does not pass while release
1246
+ is None.
1247
+ :raise TypeError: If a queue parameter does not match with ReleaseQueue
1248
+ type.
1249
+
1188
1250
  :rtype: Result
1189
1251
  """
1190
1252
  audit: type[Audit] = audit or get_audit()
@@ -1209,7 +1271,6 @@ class WorkflowTask:
1209
1271
  else:
1210
1272
  release = self.runner.date
1211
1273
 
1212
- # NOTE: Call the workflow release method.
1213
1274
  return self.workflow.release(
1214
1275
  release=release,
1215
1276
  params=self.values,
@@ -1264,13 +1325,14 @@ class WorkflowTask:
1264
1325
  if self.runner.date > end_date:
1265
1326
  return queue
1266
1327
 
1267
- # NOTE: Push the Release object to queue.
1268
1328
  heappush(queue.queue, workflow_release)
1269
-
1270
1329
  return queue
1271
1330
 
1272
1331
  def __repr__(self) -> str:
1273
- """Override the `__repr__` method."""
1332
+ """Override the `__repr__` method.
1333
+
1334
+ :rtype: str
1335
+ """
1274
1336
  return (
1275
1337
  f"{self.__class__.__name__}(alias={self.alias!r}, "
1276
1338
  f"workflow={self.workflow.name!r}, runner={self.runner!r}, "
@@ -1278,7 +1340,10 @@ class WorkflowTask:
1278
1340
  )
1279
1341
 
1280
1342
  def __eq__(self, other: WorkflowTask) -> bool:
1281
- """Override equal property that will compare only the same type."""
1343
+ """Override the equal property that will compare only the same type.
1344
+
1345
+ :rtype: bool
1346
+ """
1282
1347
  if isinstance(other, WorkflowTask):
1283
1348
  return (
1284
1349
  self.workflow.name == other.workflow.name
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.37
3
+ Version: 0.0.38
4
4
  Summary: Lightweight workflow orchestration
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -31,6 +31,10 @@ Provides-Extra: api
31
31
  Requires-Dist: fastapi<1.0.0,>=0.115.0; extra == "api"
32
32
  Requires-Dist: httpx; extra == "api"
33
33
  Requires-Dist: ujson; extra == "api"
34
+ Provides-Extra: async
35
+ Requires-Dist: aiofiles; extra == "async"
36
+ Requires-Dist: aiohttp; extra == "async"
37
+ Dynamic: license-file
34
38
 
35
39
  # Workflow Orchestration
36
40
 
@@ -165,6 +169,8 @@ run-py-local:
165
169
  run-date: datetime
166
170
  jobs:
167
171
  getting-api-data:
172
+ runs-on:
173
+ type: local
168
174
  stages:
169
175
  - name: "Retrieve API Data"
170
176
  id: retrieve-api
@@ -0,0 +1,33 @@
1
+ ddeutil/workflow/__about__.py,sha256=nUvuf0abbrBoZoHVyqcyvfYEilSx2oTLDwA7Az8m5UU,28
2
+ ddeutil/workflow/__cron.py,sha256=3i-wmjTlh0ADCzN9pLKaWHzJkXzC72aIBmVEQSbyCCE,26895
3
+ ddeutil/workflow/__init__.py,sha256=hIM2Ha-7F3YF3aLsu4IY3N-UvD_EE1ai6DO6WL2mILg,1908
4
+ ddeutil/workflow/__types.py,sha256=CK1jfzyHP9P-MB0ElhpJZ59ZFGJC9MkQuAop5739_9k,4304
5
+ ddeutil/workflow/audit.py,sha256=wx70RKRdHj1d2431ilpt9OPTInMByjqXkYff7l5pvF4,8230
6
+ ddeutil/workflow/caller.py,sha256=oFjB9zpG93aeVd-T_4QMz5kk64lo-iyxaPmCo_1_AzU,5693
7
+ ddeutil/workflow/conf.py,sha256=MHzBeLZukFeIQ-YhxOz5uKCnGYqbhYdpwAEh9A9h_OM,12216
8
+ ddeutil/workflow/context.py,sha256=fdGUuQnsjDCDGibPHKMWc5PA3pArHpfv6Aff2nVvZ0U,1618
9
+ ddeutil/workflow/cron.py,sha256=j8EeoHst70toRfnD_frix41vrI-eLYVJkZ9yeJtpfnI,8871
10
+ ddeutil/workflow/exceptions.py,sha256=fO37f9p7lOjIJgVOpKE_1X44yJTwBepyukZV9a7NNm4,1241
11
+ ddeutil/workflow/job.py,sha256=3oDY-THXYMvmW0kKkHN2VMJyq0u0_4s9H0eX40Iiw6E,24267
12
+ ddeutil/workflow/logs.py,sha256=mAKQjNri-oourd3dBLLG3Fqoo4QG27U9IO2h6IqCOU0,10196
13
+ ddeutil/workflow/params.py,sha256=qw9XJyjh2ocf9pf6h_XiYHLOvQN4R5TMqPElmItKnRM,8019
14
+ ddeutil/workflow/result.py,sha256=cDkItrhpzZfMS1Oj8IZX8O-KBD4KZYDi43XJZvvC3Gc,4318
15
+ ddeutil/workflow/scheduler.py,sha256=YMebYpNjqg6RWaE17sicwM3uthupeBGSGCnDGy4aKd8,26286
16
+ ddeutil/workflow/stages.py,sha256=aHMPOPxRiSo2jBFqQOlua01sQ18RMvjaKk2Dotnu7v4,36480
17
+ ddeutil/workflow/templates.py,sha256=A0JgZFGkBv-AX-EskZj656nG5zFd3j1PpLpyXihf6Xg,10967
18
+ ddeutil/workflow/utils.py,sha256=Fz5y-LK_JfikDfvMKcFaxad_VvCnr7UC2C9KFCbzPNA,7105
19
+ ddeutil/workflow/workflow.py,sha256=v6LTbXZ34yWmddZquBxpahxCW1PFb3177q1T3Ssf5C0,46636
20
+ ddeutil/workflow/api/__init__.py,sha256=F53NMBWtb9IKaDWkPU5KvybGGfKAcbehgn6TLBwHuuM,21
21
+ ddeutil/workflow/api/api.py,sha256=gGQtqkzyJNaJIfka_w2M1lrCS3Ep46re2Dznsk9RxYQ,5191
22
+ ddeutil/workflow/api/log.py,sha256=NMTnOnsBrDB5129329xF2myLdrb-z9k1MQrmrP7qXJw,1818
23
+ ddeutil/workflow/api/repeat.py,sha256=cycd1-91j-4v6uY1SkrZHd9l95e-YgVC4UCSNNFuGJ8,5277
24
+ ddeutil/workflow/api/routes/__init__.py,sha256=qoGtOMyVgQ5nTUc8J8wH27A8isaxl3IFCX8qoyibeCY,484
25
+ ddeutil/workflow/api/routes/job.py,sha256=YVta083i8vU8-o4WdKFwDpfdC9vN1dZ6goZSmNlQXHA,1954
26
+ ddeutil/workflow/api/routes/logs.py,sha256=uEQ6k5PwRg-k0eSiSFArXfyeDq5xzepxILrRnwgAe1I,5373
27
+ ddeutil/workflow/api/routes/schedules.py,sha256=uWYDOwlV8w56hKQmfkQFwdZ6t2gZSJeCdBIzMmJenAQ,4824
28
+ ddeutil/workflow/api/routes/workflows.py,sha256=KVywA7vD9b4QrfmWBdSFF5chj34yJe1zNCzl6iBMeGI,4538
29
+ ddeutil_workflow-0.0.38.dist-info/licenses/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
30
+ ddeutil_workflow-0.0.38.dist-info/METADATA,sha256=haHYX6Ui8EXTi2qJA5x_SOWSmVqmXRKsHLdgyMp7Two,19511
31
+ ddeutil_workflow-0.0.38.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
32
+ ddeutil_workflow-0.0.38.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
33
+ ddeutil_workflow-0.0.38.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (76.1.0)
2
+ Generator: setuptools (77.0.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,32 +0,0 @@
1
- ddeutil/workflow/__about__.py,sha256=UJZ9dzvQ9h4mY_1tAZ0imJIZbhNW3TD-wWNEVs22HwA,28
2
- ddeutil/workflow/__cron.py,sha256=3i-wmjTlh0ADCzN9pLKaWHzJkXzC72aIBmVEQSbyCCE,26895
3
- ddeutil/workflow/__init__.py,sha256=d643WDkk93MjCt9ujD46hX07Mb7yc9eTzKV3QwnEZQg,1845
4
- ddeutil/workflow/__types.py,sha256=CK1jfzyHP9P-MB0ElhpJZ59ZFGJC9MkQuAop5739_9k,4304
5
- ddeutil/workflow/audit.py,sha256=wx70RKRdHj1d2431ilpt9OPTInMByjqXkYff7l5pvF4,8230
6
- ddeutil/workflow/caller.py,sha256=pmZ9a5m1JlBTzR_xePOWZa98zyFE7jgJUlAXCx874Fs,5521
7
- ddeutil/workflow/conf.py,sha256=MHzBeLZukFeIQ-YhxOz5uKCnGYqbhYdpwAEh9A9h_OM,12216
8
- ddeutil/workflow/cron.py,sha256=j8EeoHst70toRfnD_frix41vrI-eLYVJkZ9yeJtpfnI,8871
9
- ddeutil/workflow/exceptions.py,sha256=5ghT443VLq0IeU87loHNEqqrrrctklP7YfxwJ51ImWU,949
10
- ddeutil/workflow/job.py,sha256=WnNkk_XhZytmLPzN6Kb41_BdfvBdtYhbn0SnDJ5ZgEw,25417
11
- ddeutil/workflow/logs.py,sha256=EJDb9Xt3XWjTGE8CeEvw0eDU8kyaeStALQNAtTl5HQw,10027
12
- ddeutil/workflow/params.py,sha256=qw9XJyjh2ocf9pf6h_XiYHLOvQN4R5TMqPElmItKnRM,8019
13
- ddeutil/workflow/result.py,sha256=fbM2An3VyweMjAy4Iw7h8H-KkoQsDrZe_KjGztXAFkE,4319
14
- ddeutil/workflow/scheduler.py,sha256=YMebYpNjqg6RWaE17sicwM3uthupeBGSGCnDGy4aKd8,26286
15
- ddeutil/workflow/stages.py,sha256=SJD7T7BdIhxoPSaDf8s5I8U6sv7wJsG2BynG-_aZ004,28165
16
- ddeutil/workflow/templates.py,sha256=A0JgZFGkBv-AX-EskZj656nG5zFd3j1PpLpyXihf6Xg,10967
17
- ddeutil/workflow/utils.py,sha256=JppsS2c545hPqog0GWjpQnTVMnzjqnhx4K8GkMV_CP0,8132
18
- ddeutil/workflow/workflow.py,sha256=_LYfs15AcXWVpM8CaO4oH6SWoJnqzF5FU08QTDoHT5w,44529
19
- ddeutil/workflow/api/__init__.py,sha256=F53NMBWtb9IKaDWkPU5KvybGGfKAcbehgn6TLBwHuuM,21
20
- ddeutil/workflow/api/api.py,sha256=gGQtqkzyJNaJIfka_w2M1lrCS3Ep46re2Dznsk9RxYQ,5191
21
- ddeutil/workflow/api/log.py,sha256=NMTnOnsBrDB5129329xF2myLdrb-z9k1MQrmrP7qXJw,1818
22
- ddeutil/workflow/api/repeat.py,sha256=cycd1-91j-4v6uY1SkrZHd9l95e-YgVC4UCSNNFuGJ8,5277
23
- ddeutil/workflow/api/routes/__init__.py,sha256=qoGtOMyVgQ5nTUc8J8wH27A8isaxl3IFCX8qoyibeCY,484
24
- ddeutil/workflow/api/routes/job.py,sha256=vCUTtsoCOtubVqjgk6MYUcGYim_l5Vh_NdtnQGx1SYM,1898
25
- ddeutil/workflow/api/routes/logs.py,sha256=7xPKu814PGxaMsij8zB3MLeXBfTC7NKT7GlTOJ-PV2U,5173
26
- ddeutil/workflow/api/routes/schedules.py,sha256=uWYDOwlV8w56hKQmfkQFwdZ6t2gZSJeCdBIzMmJenAQ,4824
27
- ddeutil/workflow/api/routes/workflows.py,sha256=KVywA7vD9b4QrfmWBdSFF5chj34yJe1zNCzl6iBMeGI,4538
28
- ddeutil_workflow-0.0.37.dist-info/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
29
- ddeutil_workflow-0.0.37.dist-info/METADATA,sha256=lazCqQyB5Cm-UWkYN9bGz3RnJpv_XhT0JOcmMf-7i5Y,19342
30
- ddeutil_workflow-0.0.37.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
31
- ddeutil_workflow-0.0.37.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
32
- ddeutil_workflow-0.0.37.dist-info/RECORD,,