ddeutil-workflow 0.0.59__py3-none-any.whl → 0.0.61__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.
@@ -39,7 +39,7 @@ from pydantic.functional_validators import field_validator, model_validator
39
39
  from typing_extensions import Self
40
40
 
41
41
  from .__cron import CronRunner
42
- from .__types import DictData, TupleStr
42
+ from .__types import DictData
43
43
  from .conf import FileLoad, Loader, dynamic
44
44
  from .event import Crontab
45
45
  from .exceptions import WorkflowException
@@ -57,14 +57,6 @@ from .utils import (
57
57
  wait_until_next_minute,
58
58
  )
59
59
 
60
- __all__: TupleStr = (
61
- "Release",
62
- "ReleaseQueue",
63
- "ReleaseType",
64
- "Workflow",
65
- "WorkflowTask",
66
- )
67
-
68
60
 
69
61
  class ReleaseType(str, Enum):
70
62
  """Release Type Enum support the type field on the Release dataclass."""
@@ -563,11 +555,12 @@ class Workflow(BaseModel):
563
555
  adding jobs key to this parameter.
564
556
  """
565
557
  # VALIDATE: Incoming params should have keys that set on this workflow.
566
- if check_key := [
558
+ check_key: list[str] = [
567
559
  f"{k!r}"
568
560
  for k in self.params
569
561
  if (k not in params and self.params[k].required)
570
- ]:
562
+ ]
563
+ if check_key:
571
564
  raise WorkflowException(
572
565
  f"Required Param on this workflow setting does not set: "
573
566
  f"{', '.join(check_key)}."
@@ -670,7 +663,7 @@ class Workflow(BaseModel):
670
663
  rs: Result = self.execute(
671
664
  params=values,
672
665
  result=result,
673
- parent_run_id=result.parent_run_id,
666
+ parent_run_id=result.run_id,
674
667
  timeout=timeout,
675
668
  )
676
669
  result.trace.info(
@@ -710,198 +703,6 @@ class Workflow(BaseModel):
710
703
  },
711
704
  )
712
705
 
713
- def queue(
714
- self,
715
- offset: float,
716
- end_date: datetime,
717
- queue: ReleaseQueue,
718
- audit: type[Audit],
719
- *,
720
- force_run: bool = False,
721
- ) -> ReleaseQueue:
722
- """Generate Release from all on values from the on field and store them
723
- to the ReleaseQueue object.
724
-
725
- :param offset: An offset in second unit for time travel.
726
- :param end_date: An end datetime object.
727
- :param queue: A workflow queue object.
728
- :param audit: An audit class that want to make audit object.
729
- :param force_run: A flag that allow to release workflow if the audit
730
- with that release was pointed.
731
-
732
- :rtype: ReleaseQueue
733
- """
734
- for on in self.on:
735
-
736
- queue.gen(
737
- end_date,
738
- audit,
739
- on.next(get_dt_now(offset=offset).replace(microsecond=0)),
740
- self.name,
741
- force_run=force_run,
742
- )
743
-
744
- return queue
745
-
746
- def poke(
747
- self,
748
- params: Optional[DictData] = None,
749
- start_date: Optional[datetime] = None,
750
- *,
751
- run_id: Optional[str] = None,
752
- periods: int = 1,
753
- audit: Optional[Audit] = None,
754
- force_run: bool = False,
755
- timeout: int = 1800,
756
- max_poking_pool_worker: int = 2,
757
- ) -> Result:
758
- """Poke workflow with a start datetime value that will pass to its
759
- `on` field on the threading executor pool for execute the `release`
760
- method (It run all schedules that was set on the `on` values).
761
-
762
- This method will observe its `on` field that nearing to run with the
763
- `self.release()` method.
764
-
765
- The limitation of this method is not allow run a date that gather
766
- than the current date.
767
-
768
- :param params: (DictData) A parameter data.
769
- :param start_date: (datetime) A start datetime object.
770
- :param run_id: (str) A workflow running ID for this poke.
771
- :param periods: (int) A periods in minutes value that use to run this
772
- poking. (Default is 1)
773
- :param audit: (Audit) An audit object that want to use on this poking
774
- process.
775
- :param force_run: (bool) A flag that allow to release workflow if the
776
- audit with that release was pointed. (Default is False)
777
- :param timeout: (int) A second value for timeout while waiting all
778
- futures run completely.
779
- :param max_poking_pool_worker: (int) The maximum poking pool worker.
780
- (Default is 2 workers)
781
-
782
- :raise WorkflowException: If the periods parameter less or equal than 0.
783
-
784
- :rtype: Result
785
- :return: A list of all results that return from `self.release` method.
786
- """
787
- audit: type[Audit] = audit or get_audit(extras=self.extras)
788
- result: Result = Result(
789
- run_id=(run_id or gen_id(self.name, unique=True))
790
- )
791
-
792
- # VALIDATE: Check the periods value should gather than 0.
793
- if periods <= 0:
794
- raise WorkflowException(
795
- "The period of poking should be `int` and grater or equal "
796
- "than 1."
797
- )
798
-
799
- if len(self.on) == 0:
800
- result.trace.warning(
801
- f"[POKING]: {self.name!r} not have any schedule!!!"
802
- )
803
- return result.catch(status=SUCCESS, context={"outputs": []})
804
-
805
- # NOTE: Create the current date that change microsecond to 0
806
- current_date: datetime = datetime.now().replace(microsecond=0)
807
-
808
- if start_date is None:
809
- # NOTE: Force change start date if it gathers than the current date,
810
- # or it does not pass to this method.
811
- start_date: datetime = current_date
812
- offset: float = 0
813
- elif start_date <= current_date:
814
- start_date = start_date.replace(microsecond=0)
815
- offset: float = (current_date - start_date).total_seconds()
816
- else:
817
- raise WorkflowException(
818
- f"The start datetime should less than or equal the current "
819
- f"datetime, {current_date:%Y-%m-%d %H:%M:%S}."
820
- )
821
-
822
- # NOTE: The end date is using to stop generate queue with an input
823
- # periods value. It will change to MM:59.
824
- # For example:
825
- # (input) start_date = 12:04:12, offset = 2
826
- # (output) end_date = 12:06:59
827
- end_date: datetime = start_date.replace(second=0) + timedelta(
828
- minutes=periods + 1, seconds=-1
829
- )
830
-
831
- result.trace.info(
832
- f"[POKING]: Execute Poking: {self.name!r} ("
833
- f"{start_date:%Y-%m-%d %H:%M:%S} ==> {end_date:%Y-%m-%d %H:%M:%S})"
834
- )
835
-
836
- params: DictData = {} if params is None else params
837
- context: list[Result] = []
838
- q: ReleaseQueue = ReleaseQueue()
839
-
840
- # NOTE: Create reusable partial function and add Release to the release
841
- # queue object.
842
- partial_queue = partial(
843
- self.queue, offset, end_date, audit=audit, force_run=force_run
844
- )
845
- partial_queue(q)
846
- if not q.is_queued:
847
- result.trace.warning(
848
- f"[POKING]: Skip {self.name!r}, not have any queue!!!"
849
- )
850
- return result.catch(status=SUCCESS, context={"outputs": []})
851
-
852
- with ThreadPoolExecutor(
853
- max_workers=dynamic(
854
- "max_poking_pool_worker",
855
- f=max_poking_pool_worker,
856
- extras=self.extras,
857
- ),
858
- thread_name_prefix="wf_poking_",
859
- ) as executor:
860
-
861
- futures: list[Future] = []
862
-
863
- while q.is_queued:
864
-
865
- # NOTE: Pop the latest Release object from the release queue.
866
- release: Release = heappop(q.queue)
867
-
868
- if reach_next_minute(release.date, offset=offset):
869
- result.trace.debug(
870
- f"[POKING]: Skip Release: "
871
- f"{release.date:%Y-%m-%d %H:%M:%S}"
872
- )
873
- heappush(q.queue, release)
874
- wait_until_next_minute(get_dt_now(offset=offset))
875
-
876
- # WARNING: I already call queue poking again because issue
877
- # about the every minute crontab.
878
- partial_queue(q)
879
- continue
880
-
881
- heappush(q.running, release)
882
- futures.append(
883
- executor.submit(
884
- self.release,
885
- release=release,
886
- params=params,
887
- audit=audit,
888
- queue=q,
889
- parent_run_id=result.run_id,
890
- )
891
- )
892
-
893
- partial_queue(q)
894
-
895
- # WARNING: This poking method does not allow to use fail-fast
896
- # logic to catching parallel execution result.
897
- for future in as_completed(futures, timeout=timeout):
898
- context.append(future.result())
899
-
900
- return result.catch(
901
- status=SUCCESS,
902
- context={"outputs": context},
903
- )
904
-
905
706
  def execute_job(
906
707
  self,
907
708
  job: Job,
@@ -1139,6 +940,202 @@ class Workflow(BaseModel):
1139
940
  )
1140
941
 
1141
942
 
943
+ class WorkflowPoke(Workflow):
944
+ """Workflow Poke model that was implemented the poke method."""
945
+
946
+ def queue(
947
+ self,
948
+ offset: float,
949
+ end_date: datetime,
950
+ queue: ReleaseQueue,
951
+ audit: type[Audit],
952
+ *,
953
+ force_run: bool = False,
954
+ ) -> ReleaseQueue:
955
+ """Generate Release from all on values from the on field and store them
956
+ to the ReleaseQueue object.
957
+
958
+ :param offset: An offset in second unit for time travel.
959
+ :param end_date: An end datetime object.
960
+ :param queue: A workflow queue object.
961
+ :param audit: An audit class that want to make audit object.
962
+ :param force_run: A flag that allow to release workflow if the audit
963
+ with that release was pointed.
964
+
965
+ :rtype: ReleaseQueue
966
+ """
967
+ for on in self.on:
968
+
969
+ queue.gen(
970
+ end_date,
971
+ audit,
972
+ on.next(get_dt_now(offset=offset).replace(microsecond=0)),
973
+ self.name,
974
+ force_run=force_run,
975
+ )
976
+
977
+ return queue
978
+
979
+ def poke(
980
+ self,
981
+ params: Optional[DictData] = None,
982
+ start_date: Optional[datetime] = None,
983
+ *,
984
+ run_id: Optional[str] = None,
985
+ periods: int = 1,
986
+ audit: Optional[Audit] = None,
987
+ force_run: bool = False,
988
+ timeout: int = 1800,
989
+ max_poking_pool_worker: int = 2,
990
+ ) -> Result:
991
+ """Poke workflow with a start datetime value that will pass to its
992
+ `on` field on the threading executor pool for execute the `release`
993
+ method (It run all schedules that was set on the `on` values).
994
+
995
+ This method will observe its `on` field that nearing to run with the
996
+ `self.release()` method.
997
+
998
+ The limitation of this method is not allow run a date that gather
999
+ than the current date.
1000
+
1001
+ :param params: (DictData) A parameter data.
1002
+ :param start_date: (datetime) A start datetime object.
1003
+ :param run_id: (str) A workflow running ID for this poke.
1004
+ :param periods: (int) A periods in minutes value that use to run this
1005
+ poking. (Default is 1)
1006
+ :param audit: (Audit) An audit object that want to use on this poking
1007
+ process.
1008
+ :param force_run: (bool) A flag that allow to release workflow if the
1009
+ audit with that release was pointed. (Default is False)
1010
+ :param timeout: (int) A second value for timeout while waiting all
1011
+ futures run completely.
1012
+ :param max_poking_pool_worker: (int) The maximum poking pool worker.
1013
+ (Default is 2 workers)
1014
+
1015
+ :raise WorkflowException: If the periods parameter less or equal than 0.
1016
+
1017
+ :rtype: Result
1018
+ :return: A list of all results that return from `self.release` method.
1019
+ """
1020
+ audit: type[Audit] = audit or get_audit(extras=self.extras)
1021
+ result: Result = Result(
1022
+ run_id=(run_id or gen_id(self.name, unique=True))
1023
+ )
1024
+
1025
+ # VALIDATE: Check the periods value should gather than 0.
1026
+ if periods <= 0:
1027
+ raise WorkflowException(
1028
+ "The period of poking should be `int` and grater or equal "
1029
+ "than 1."
1030
+ )
1031
+
1032
+ if len(self.on) == 0:
1033
+ result.trace.warning(
1034
+ f"[POKING]: {self.name!r} not have any schedule!!!"
1035
+ )
1036
+ return result.catch(status=SUCCESS, context={"outputs": []})
1037
+
1038
+ # NOTE: Create the current date that change microsecond to 0
1039
+ current_date: datetime = datetime.now().replace(microsecond=0)
1040
+
1041
+ if start_date is None:
1042
+ # NOTE: Force change start date if it gathers than the current date,
1043
+ # or it does not pass to this method.
1044
+ start_date: datetime = current_date
1045
+ offset: float = 0
1046
+ elif start_date <= current_date:
1047
+ start_date = start_date.replace(microsecond=0)
1048
+ offset: float = (current_date - start_date).total_seconds()
1049
+ else:
1050
+ raise WorkflowException(
1051
+ f"The start datetime should less than or equal the current "
1052
+ f"datetime, {current_date:%Y-%m-%d %H:%M:%S}."
1053
+ )
1054
+
1055
+ # NOTE: The end date is using to stop generate queue with an input
1056
+ # periods value. It will change to MM:59.
1057
+ # For example:
1058
+ # (input) start_date = 12:04:12, offset = 2
1059
+ # (output) end_date = 12:06:59
1060
+ end_date: datetime = start_date.replace(second=0) + timedelta(
1061
+ minutes=periods + 1, seconds=-1
1062
+ )
1063
+
1064
+ result.trace.info(
1065
+ f"[POKING]: Execute Poking: {self.name!r} "
1066
+ f"({start_date:%Y-%m-%d %H:%M:%S} ==> {end_date:%Y-%m-%d %H:%M:%S})"
1067
+ )
1068
+
1069
+ params: DictData = {} if params is None else params
1070
+ context: list[Result] = []
1071
+ q: ReleaseQueue = ReleaseQueue()
1072
+
1073
+ # NOTE: Create reusable partial function and add Release to the release
1074
+ # queue object.
1075
+ partial_queue = partial(
1076
+ self.queue, offset, end_date, audit=audit, force_run=force_run
1077
+ )
1078
+ partial_queue(q)
1079
+ if not q.is_queued:
1080
+ result.trace.warning(
1081
+ f"[POKING]: Skip {self.name!r}, not have any queue!!!"
1082
+ )
1083
+ return result.catch(status=SUCCESS, context={"outputs": []})
1084
+
1085
+ with ThreadPoolExecutor(
1086
+ max_workers=dynamic(
1087
+ "max_poking_pool_worker",
1088
+ f=max_poking_pool_worker,
1089
+ extras=self.extras,
1090
+ ),
1091
+ thread_name_prefix="wf_poking_",
1092
+ ) as executor:
1093
+
1094
+ futures: list[Future] = []
1095
+
1096
+ while q.is_queued:
1097
+
1098
+ # NOTE: Pop the latest Release object from the release queue.
1099
+ release: Release = heappop(q.queue)
1100
+
1101
+ if reach_next_minute(release.date, offset=offset):
1102
+ result.trace.debug(
1103
+ f"[POKING]: Skip Release: "
1104
+ f"{release.date:%Y-%m-%d %H:%M:%S}"
1105
+ )
1106
+ heappush(q.queue, release)
1107
+ wait_until_next_minute(get_dt_now(offset=offset))
1108
+
1109
+ # WARNING: I already call queue poking again because issue
1110
+ # about the every minute crontab.
1111
+ partial_queue(q)
1112
+ continue
1113
+
1114
+ heappush(q.running, release)
1115
+ futures.append(
1116
+ executor.submit(
1117
+ self.release,
1118
+ release=release,
1119
+ params=params,
1120
+ audit=audit,
1121
+ queue=q,
1122
+ parent_run_id=result.run_id,
1123
+ )
1124
+ )
1125
+
1126
+ partial_queue(q)
1127
+
1128
+ # WARNING: This poking method does not allow to use fail-fast
1129
+ # logic to catching parallel execution result.
1130
+ for future in as_completed(futures, timeout=timeout):
1131
+ context.append(future.result())
1132
+
1133
+ return result.catch(
1134
+ status=SUCCESS,
1135
+ context={"outputs": context},
1136
+ )
1137
+
1138
+
1142
1139
  @dataclass(config=ConfigDict(arbitrary_types_allowed=True))
1143
1140
  class WorkflowTask:
1144
1141
  """Workflow task Pydantic dataclass object that use to keep mapping data and
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.59
3
+ Version: 0.0.61
4
4
  Summary: Lightweight workflow orchestration
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -44,6 +44,8 @@ Requires-Dist: aiofiles; extra == "async"
44
44
  Requires-Dist: aiohttp; extra == "async"
45
45
  Provides-Extra: docker
46
46
  Requires-Dist: docker==7.1.0; extra == "docker"
47
+ Provides-Extra: self-hosted
48
+ Requires-Dist: requests==2.32.3; extra == "self-hosted"
47
49
  Dynamic: license-file
48
50
 
49
51
  # Workflow Orchestration
@@ -0,0 +1,31 @@
1
+ ddeutil/workflow/__about__.py,sha256=MeIsfJkRHwfi0J3-dkYVf-U5ewlNB1-grkzIr3wMoa0,28
2
+ ddeutil/workflow/__cron.py,sha256=5DHQKejG-76L_oREW78RcwMzeyKddJxSMmBzYyMAeeY,28536
3
+ ddeutil/workflow/__init__.py,sha256=NXEhjzKFdIGa-jtIq9HXChLCjSXNPd8VJ8ltggxbBO8,1371
4
+ ddeutil/workflow/__main__.py,sha256=x-sYedl4T8p6054aySk-EQX6vhytvPR0HvaBNYxMzp0,364
5
+ ddeutil/workflow/__types.py,sha256=uNfoRbVmNK5O37UUMVnqcmoghD9oMS1q9fXC0APnjSI,4584
6
+ ddeutil/workflow/conf.py,sha256=NLvjZ8bpDsn4e0MG3m1vgMdAwtmii5hP1D0STKQyZeo,14907
7
+ ddeutil/workflow/event.py,sha256=ATQhCgx4F3I2SPQesXxLgOREEGtwkX6uc6jjViQ5pQg,10716
8
+ ddeutil/workflow/exceptions.py,sha256=TKHBIlfquz3yEb8_kg6UXpxVLKxstt3QA9a1XYsLPJk,2455
9
+ ddeutil/workflow/job.py,sha256=Php1b3n6c-jddel8PTSa61kAW22QBTetzoLVR4XXM4E,35240
10
+ ddeutil/workflow/logs.py,sha256=iVtyl8i69y7t07tAuWkihc54WlkHCcBy_Ur0WtzJ_lM,31367
11
+ ddeutil/workflow/params.py,sha256=1u8gXs1ZyMq-2eD9H8L7Yjfu5t7b_OzjA0fJvhxdYWY,12505
12
+ ddeutil/workflow/result.py,sha256=4M9VCcveI8Yz6ZrnI-67SZlry-Z8G7e0hziy1k-pklk,5906
13
+ ddeutil/workflow/reusables.py,sha256=JIXuAicRXhGuocQy71C7pjK4BTl2wo9lNK2p-zhEA6M,17743
14
+ ddeutil/workflow/scheduler.py,sha256=OsEyj2zscQ-3bDMk2z7UtKlCWLlgoGjaRFt17o1B1ew,27263
15
+ ddeutil/workflow/stages.py,sha256=xH_f7IRohFCnUrtyD-QW86BlJ72p64JmpRTRrLPoF6A,93241
16
+ ddeutil/workflow/utils.py,sha256=rcaDwXaEs4SCdcBKWx4ZCEtpnNfPI8du7Er6b_rg8t4,9569
17
+ ddeutil/workflow/workflow.py,sha256=ty4PUhci7YCPlsNCH8qsaxbzHmBliCVmXczpwlAy_mk,44852
18
+ ddeutil/workflow/api/__init__.py,sha256=kY30dL8HPY8tY_GBmm7y_3OdoXzB1-EA2a96PLU0AQw,5278
19
+ ddeutil/workflow/api/logs.py,sha256=NMTnOnsBrDB5129329xF2myLdrb-z9k1MQrmrP7qXJw,1818
20
+ ddeutil/workflow/api/utils.py,sha256=uTtUFVLpiYYahXvCVx8sueRQ03K2Xw1id_gW3IMmX1U,5295
21
+ ddeutil/workflow/api/routes/__init__.py,sha256=qoGtOMyVgQ5nTUc8J8wH27A8isaxl3IFCX8qoyibeCY,484
22
+ ddeutil/workflow/api/routes/job.py,sha256=8X5VLDJH6PumyNIY6JGRNBsf2gWN0eG9DzxRPSh6n4I,2190
23
+ ddeutil/workflow/api/routes/logs.py,sha256=U6vOni3wd-ZTOwd3yVdSOpgyRmNdcgfngU5KlLM3Cww,5383
24
+ ddeutil/workflow/api/routes/schedules.py,sha256=14RnaJKEGMSJtncI1H_QQVZNBe_jDS40PPRO6qFc3i0,4805
25
+ ddeutil/workflow/api/routes/workflows.py,sha256=GJu5PiXEylswrXylEImpncySjeU9chrvrtjhiMCw2RQ,4529
26
+ ddeutil_workflow-0.0.61.dist-info/licenses/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
27
+ ddeutil_workflow-0.0.61.dist-info/METADATA,sha256=LO7R_mpY7OkPg4J6epm2MwWn4qaI2HIMhTqw7T_LEhU,19427
28
+ ddeutil_workflow-0.0.61.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
29
+ ddeutil_workflow-0.0.61.dist-info/entry_points.txt,sha256=qDTpPSauL0ciO6T4iSVt8bJeYrVEkkoEEw_RlGx6Kgk,63
30
+ ddeutil_workflow-0.0.61.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
31
+ ddeutil_workflow-0.0.61.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,31 +0,0 @@
1
- ddeutil/workflow/__about__.py,sha256=pgt1UgXVQ5NH2bT0-9YyCLh7xzGOl3WFa4CWgM2rMyE,28
2
- ddeutil/workflow/__cron.py,sha256=5DHQKejG-76L_oREW78RcwMzeyKddJxSMmBzYyMAeeY,28536
3
- ddeutil/workflow/__init__.py,sha256=NXEhjzKFdIGa-jtIq9HXChLCjSXNPd8VJ8ltggxbBO8,1371
4
- ddeutil/workflow/__main__.py,sha256=x-sYedl4T8p6054aySk-EQX6vhytvPR0HvaBNYxMzp0,364
5
- ddeutil/workflow/__types.py,sha256=7xXy6ynpT6Do6U5A-XYSVuinE2g-4wlZGGJ1NACK1BE,4343
6
- ddeutil/workflow/conf.py,sha256=NLvjZ8bpDsn4e0MG3m1vgMdAwtmii5hP1D0STKQyZeo,14907
7
- ddeutil/workflow/event.py,sha256=I9CUFAsqUNguCPALVmqwKWaUHcSpwg2S-chGrTZRXFY,10410
8
- ddeutil/workflow/exceptions.py,sha256=Phe5JK-nLDt1Yh-ilWnpLIJl1VRsAzK4TBZ1tTiv9OQ,2359
9
- ddeutil/workflow/job.py,sha256=2GGW_sY3XhZGYJpXWi84k4uTRV9YMPOMagVtDeeDya8,35289
10
- ddeutil/workflow/logs.py,sha256=BFdPKcIsoSPU-tZBMQvBipAdlBur8IjOk7MxyqrTC8Q,28537
11
- ddeutil/workflow/params.py,sha256=tBjKe1_e0TlUrSrlMahDuAdNNBlGBAKMmMMQ9eV-YSs,11616
12
- ddeutil/workflow/result.py,sha256=LJieCsaQJOgZKz68wao2XKXCFm3bXl2jNkeHniP_Y90,5888
13
- ddeutil/workflow/reusables.py,sha256=mw_Fi763B5am0EmntcjLBF7MDEhKqud2BYHcYyno5Ec,17663
14
- ddeutil/workflow/scheduler.py,sha256=OsEyj2zscQ-3bDMk2z7UtKlCWLlgoGjaRFt17o1B1ew,27263
15
- ddeutil/workflow/stages.py,sha256=_GGrI4sayY1HArqV0aWUMwukONnZ_6-QVAiaomP4nbY,92239
16
- ddeutil/workflow/utils.py,sha256=S4TN1qH6t8NiZfIapJed3ZS35aQc18HzDPQ4oLqct7M,8804
17
- ddeutil/workflow/workflow.py,sha256=gq7zbBeJptqb9rmHcR29c8Gbh43N1-cW94NdHGb6Td4,44856
18
- ddeutil/workflow/api/__init__.py,sha256=kY30dL8HPY8tY_GBmm7y_3OdoXzB1-EA2a96PLU0AQw,5278
19
- ddeutil/workflow/api/logs.py,sha256=NMTnOnsBrDB5129329xF2myLdrb-z9k1MQrmrP7qXJw,1818
20
- ddeutil/workflow/api/utils.py,sha256=uTtUFVLpiYYahXvCVx8sueRQ03K2Xw1id_gW3IMmX1U,5295
21
- ddeutil/workflow/api/routes/__init__.py,sha256=qoGtOMyVgQ5nTUc8J8wH27A8isaxl3IFCX8qoyibeCY,484
22
- ddeutil/workflow/api/routes/job.py,sha256=8X5VLDJH6PumyNIY6JGRNBsf2gWN0eG9DzxRPSh6n4I,2190
23
- ddeutil/workflow/api/routes/logs.py,sha256=U6vOni3wd-ZTOwd3yVdSOpgyRmNdcgfngU5KlLM3Cww,5383
24
- ddeutil/workflow/api/routes/schedules.py,sha256=14RnaJKEGMSJtncI1H_QQVZNBe_jDS40PPRO6qFc3i0,4805
25
- ddeutil/workflow/api/routes/workflows.py,sha256=GJu5PiXEylswrXylEImpncySjeU9chrvrtjhiMCw2RQ,4529
26
- ddeutil_workflow-0.0.59.dist-info/licenses/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
27
- ddeutil_workflow-0.0.59.dist-info/METADATA,sha256=JqwaP7hwfaNAlrObHxnHlzDyBDbb_cy186S92GRjXZ4,19343
28
- ddeutil_workflow-0.0.59.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
29
- ddeutil_workflow-0.0.59.dist-info/entry_points.txt,sha256=qDTpPSauL0ciO6T4iSVt8bJeYrVEkkoEEw_RlGx6Kgk,63
30
- ddeutil_workflow-0.0.59.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
31
- ddeutil_workflow-0.0.59.dist-info/RECORD,,