ddeutil-workflow 0.0.35__py3-none-any.whl → 0.0.37__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.
@@ -14,8 +14,8 @@ use-case, and it does not worry when I want to create a new one.
14
14
 
15
15
  Execution --> Ok --> Result with 0
16
16
 
17
- --> Error --> Result with 1 (if env var was set)
18
- --> Raise StageException(...)
17
+ --> Error ┬-> Result with 1 (if env var was set)
18
+ ╰-> Raise StageException(...)
19
19
 
20
20
  On the context I/O that pass to a stage object at execute process. The
21
21
  execute method receives a `params={"params": {...}}` value for mapping to
@@ -733,6 +733,26 @@ Stage = Union[
733
733
 
734
734
  # TODO: Not implement this stages yet
735
735
  class ParallelStage(BaseStage): # pragma: no cov
736
+ """Parallel execution stage that execute child stages with parallel.
737
+
738
+ Data Validate:
739
+ >>> stage = {
740
+ ... "name": "Parallel stage execution.",
741
+ ... "parallel": [
742
+ ... {
743
+ ... "name": "Echo first stage",
744
+ ... "echo": "Start run with branch 1",
745
+ ... "sleep": 3,
746
+ ... },
747
+ ... {
748
+ ... "name": "Echo second stage",
749
+ ... "echo": "Start run with branch 2",
750
+ ... "sleep": 1,
751
+ ... },
752
+ ... ]
753
+ ... }
754
+ """
755
+
736
756
  parallel: list[Stage]
737
757
  max_parallel_core: int = Field(default=2)
738
758
 
@@ -743,6 +763,22 @@ class ParallelStage(BaseStage): # pragma: no cov
743
763
 
744
764
  # TODO: Not implement this stages yet
745
765
  class ForEachStage(BaseStage): # pragma: no cov
766
+ """For-Each execution stage that execute child stages with an item in list of
767
+ item values.
768
+
769
+ Data Validate:
770
+ >>> stage = {
771
+ ... "name": "For-each stage execution",
772
+ ... "foreach": [1, 2, 3]
773
+ ... "stages": [
774
+ ... {
775
+ ... "name": "Echo stage",
776
+ ... "echo": "Start run with item {{ item }}"
777
+ ... },
778
+ ... ],
779
+ ... }
780
+ """
781
+
746
782
  foreach: list[str]
747
783
  stages: list[Stage]
748
784
 
@@ -753,9 +789,34 @@ class ForEachStage(BaseStage): # pragma: no cov
753
789
 
754
790
  # TODO: Not implement this stages yet
755
791
  class HookStage(BaseStage): # pragma: no cov
756
- foreach: list[str]
757
- stages: list[Stage]
792
+ hook: str
793
+ args: DictData
794
+ callback: str
758
795
 
759
796
  def execute(
760
797
  self, params: DictData, *, result: Result | None = None
761
798
  ) -> Result: ...
799
+
800
+
801
+ # TODO: Not implement this stages yet
802
+ class DockerStage(BaseStage): # pragma: no cov
803
+ """Docker container stage execution."""
804
+
805
+ image: str
806
+ env: DictData = Field(default_factory=dict)
807
+ volume: DictData = Field(default_factory=dict)
808
+ auth: DictData = Field(default_factory=dict)
809
+
810
+ def execute(
811
+ self, params: DictData, *, result: Result | None = None
812
+ ) -> Result: ...
813
+
814
+
815
+ # TODO: Not implement this stages yet
816
+ class VirtualPyStage(PyStage): # pragma: no cov
817
+ """Python Virtual Environment stage execution."""
818
+
819
+ run: str
820
+ vars: DictData
821
+
822
+ def create_py_file(self, py: str, run_id: str | None): ...
ddeutil/workflow/utils.py CHANGED
@@ -9,7 +9,7 @@ import logging
9
9
  import stat
10
10
  import time
11
11
  from collections.abc import Iterator, Mapping
12
- from datetime import datetime, timedelta
12
+ from datetime import date, datetime, timedelta
13
13
  from hashlib import md5
14
14
  from inspect import isfunction
15
15
  from itertools import chain, islice, product
@@ -32,13 +32,29 @@ def get_dt_now(
32
32
  ) -> datetime: # pragma: no cov
33
33
  """Return the current datetime object.
34
34
 
35
- :param tz:
36
- :param offset:
35
+ :param tz: A ZoneInfo object for replace timezone of return datetime object.
36
+ :param offset: An offset second value.
37
+
38
+ :rtype: datetime
37
39
  :return: The current datetime object that use an input timezone or UTC.
38
40
  """
39
41
  return datetime.now(tz=(tz or UTC)) - timedelta(seconds=offset)
40
42
 
41
43
 
44
+ def get_d_now(
45
+ tz: ZoneInfo | None = None, offset: float = 0.0
46
+ ) -> date: # pragma: no cov
47
+ """Return the current date object.
48
+
49
+ :param tz: A ZoneInfo object for replace timezone of return date object.
50
+ :param offset: An offset second value.
51
+
52
+ :rtype: date
53
+ :return: The current date object that use an input timezone or UTC.
54
+ """
55
+ return (datetime.now(tz=(tz or UTC)) - timedelta(seconds=offset)).date()
56
+
57
+
42
58
  def get_diff_sec(
43
59
  dt: datetime, tz: ZoneInfo | None = None, offset: float = 0.0
44
60
  ) -> int: # pragma: no cov
@@ -46,8 +62,10 @@ def get_diff_sec(
46
62
  current datetime with specific timezone.
47
63
 
48
64
  :param dt:
49
- :param tz:
50
- :param offset:
65
+ :param tz: A ZoneInfo object for replace timezone of return datetime object.
66
+ :param offset: An offset second value.
67
+
68
+ :rtype: int
51
69
  """
52
70
  return round(
53
71
  (
@@ -61,6 +79,10 @@ def reach_next_minute(
61
79
  ) -> bool:
62
80
  """Check this datetime object is not in range of minute level on the current
63
81
  datetime.
82
+
83
+ :param dt:
84
+ :param tz: A ZoneInfo object for replace timezone of return datetime object.
85
+ :param offset: An offset second value.
64
86
  """
65
87
  diff: float = (
66
88
  dt.replace(second=0, microsecond=0)
@@ -122,13 +144,14 @@ def gen_id(
122
144
  value: str = str(value)
123
145
 
124
146
  if config.gen_id_simple_mode:
125
- return hash_str(f"{(value if sensitive else value.lower())}", n=10) + (
126
- f"{datetime.now(tz=config.tz):%Y%m%d%H%M%S%f}" if unique else ""
127
- )
147
+ return (
148
+ f"{datetime.now(tz=config.tz):%Y%m%d%H%M%S%f}T" if unique else ""
149
+ ) + hash_str(f"{(value if sensitive else value.lower())}", n=10)
150
+
128
151
  return md5(
129
152
  (
130
- f"{(value if sensitive else value.lower())}"
131
- + (f"{datetime.now(tz=config.tz):%Y%m%d%H%M%S%f}" if unique else "")
153
+ (f"{datetime.now(tz=config.tz):%Y%m%d%H%M%S%f}T" if unique else "")
154
+ + f"{(value if sensitive else value.lower())}"
132
155
  ).encode()
133
156
  ).hexdigest()
134
157
 
@@ -173,9 +196,13 @@ def dash2underscore(
173
196
  ) -> DictData:
174
197
  """Change key name that has dash to underscore.
175
198
 
176
- :param key
177
- :param values
178
- :param fixed
199
+ :param key:
200
+ :param values:
201
+ :param fixed:
202
+
203
+ Examples:
204
+ >>> dash2underscore('foo-bar', {"foo-bar": "demo"})
205
+ {'foo_bar': 'demo'}
179
206
 
180
207
  :rtype: DictData
181
208
  """
@@ -3,21 +3,7 @@
3
3
  # Licensed under the MIT License. See LICENSE in the project root for
4
4
  # license information.
5
5
  # ------------------------------------------------------------------------------
6
- """The main schedule running is `workflow_runner` function that trigger the
7
- multiprocess of `workflow_control` function for listing schedules on the
8
- config by `Loader.finds(Schedule)`.
9
-
10
- The `workflow_control` is the scheduler function that release 2 schedule
11
- functions; `workflow_task`, and ``workflow_monitor``.
12
-
13
- ``workflow_control`` --- Every minute at :02 --> ``workflow_task``
14
-
15
- --- Every 5 minutes --> ``workflow_monitor``
16
-
17
- The ``workflow_task`` will run ``task.release`` method in threading object
18
- for multithreading strategy. This ``release`` method will run only one crontab
19
- value with the on field.
20
- """
6
+ """A Workflow module."""
21
7
  from __future__ import annotations
22
8
 
23
9
  import copy
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.35
3
+ Version: 0.0.37
4
4
  Summary: Lightweight workflow orchestration
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -29,6 +29,8 @@ Requires-Dist: python-dotenv==1.0.1
29
29
  Requires-Dist: schedule<2.0.0,==1.2.2
30
30
  Provides-Extra: api
31
31
  Requires-Dist: fastapi<1.0.0,>=0.115.0; extra == "api"
32
+ Requires-Dist: httpx; extra == "api"
33
+ Requires-Dist: ujson; extra == "api"
32
34
 
33
35
  # Workflow Orchestration
34
36
 
@@ -59,10 +61,10 @@ configuration. It called **Metadata Driven Data Workflow**.
59
61
 
60
62
  **:pushpin: <u>Rules of This Workflow engine</u>**:
61
63
 
62
- 1. The Minimum frequency unit of scheduling is **1 minute** :warning:
63
- 2. Can not re-run only failed stage and its pending downstream :rotating_light:
64
- 3. All parallel tasks inside workflow engine use Multi-Threading
65
- (🐍 Python 3.13 unlock GIL :unlock:)
64
+ 1. The Minimum frequency unit of scheduling is **1 Minute** 🕘
65
+ 2. **Can not** re-run only failed stage and its pending downstream ↩️
66
+ 3. All parallel tasks inside workflow engine use **Multi-Threading**
67
+ (Python 3.13 unlock GIL 🐍🔓)
66
68
 
67
69
  ---
68
70
 
@@ -78,12 +80,12 @@ flowchart LR
78
80
 
79
81
  subgraph Docker Container
80
82
  direction TB
81
- G@{ shape: rounded, label: "Observe<br>Application" }
83
+ G@{ shape: rounded, label: "📡Observe<br>Application" }
82
84
  end
83
85
 
84
86
  subgraph Docker Container
85
87
  direction TB
86
- B@{ shape: rounded, label: "Workflow<br>Application" }
88
+ B@{ shape: rounded, label: "🏃Workflow<br>Application" }
87
89
  end
88
90
 
89
91
  A <-->|action &<br>response| B
@@ -95,7 +97,7 @@ flowchart LR
95
97
  E@{ shape: lin-cyl, label: "Audit<br>Logs" }
96
98
  end
97
99
 
98
- subgraph Git Context
100
+ subgraph Config Context
99
101
  F@{ shape: tag-rect, label: "YAML<br>files" }
100
102
  end
101
103
 
@@ -130,10 +132,10 @@ This project need `ddeutil` and `ddeutil-io` extension namespace packages.
130
132
  If you want to install this package with application add-ons, you should add
131
133
  `app` in installation;
132
134
 
133
- | Use-case | Install Optional | Support |
134
- |----------------|--------------------------|--------------------|
135
- | Python | `ddeutil-workflow` | :heavy_check_mark: |
136
- | FastAPI Server | `ddeutil-workflow[api]` | :heavy_check_mark: |
135
+ | Use-case | Install Optional | Support |
136
+ |----------------|--------------------------|:-------------------:|
137
+ | Python | `ddeutil-workflow` | :heavy_check_mark: |
138
+ | FastAPI Server | `ddeutil-workflow[api]` | :heavy_check_mark: |
137
139
 
138
140
  ## :beers: Usage
139
141
 
@@ -295,7 +297,7 @@ like crontab job but via Python API.
295
297
  ### API Server
296
298
 
297
299
  ```shell
298
- (venv) $ uvicorn src.ddeutil.workflow.api:app \
300
+ (venv) $ uvicorn ddeutil.workflow.api:app \
299
301
  --host 127.0.0.1 \
300
302
  --port 80 \
301
303
  --no-access-log
@@ -0,0 +1,32 @@
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.1)
2
+ Generator: setuptools (76.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,30 +0,0 @@
1
- ddeutil/workflow/__about__.py,sha256=nntH3Ja8ABeB5HcYg4Fy7-Z6jDBj67mjILrr2_dJHiw,28
2
- ddeutil/workflow/__cron.py,sha256=3i-wmjTlh0ADCzN9pLKaWHzJkXzC72aIBmVEQSbyCCE,26895
3
- ddeutil/workflow/__init__.py,sha256=SX48GHuEMoxJiKKXbBWT8IxcIk9AnibB-T8cUj1Dx1k,1818
4
- ddeutil/workflow/__types.py,sha256=CK1jfzyHP9P-MB0ElhpJZ59ZFGJC9MkQuAop5739_9k,4304
5
- ddeutil/workflow/audit.py,sha256=kWymGFEy4WBUXDez1QLRU3FvqLMYQhKJHAzAvIms_Vk,8077
6
- ddeutil/workflow/caller.py,sha256=qNfrr0B2ykLm6Y8JfhU1rnf5XGmp-usXrPmXM5uGRnM,5473
7
- ddeutil/workflow/conf.py,sha256=cFc2cd_SGXg9PMrkvCT7WWE85a5UN-DdH53_JIbFyzs,14031
8
- ddeutil/workflow/cron.py,sha256=j8EeoHst70toRfnD_frix41vrI-eLYVJkZ9yeJtpfnI,8871
9
- ddeutil/workflow/exceptions.py,sha256=5ghT443VLq0IeU87loHNEqqrrrctklP7YfxwJ51ImWU,949
10
- ddeutil/workflow/job.py,sha256=iUrmNbpBCpxMJyK_hAxpnyLBtrVhyDCoua4X12CH_BI,24702
11
- ddeutil/workflow/logs.py,sha256=fmtZl7lQYhKre_auADN64jqD2cqN4aMkNUZdMdhKioM,6398
12
- ddeutil/workflow/params.py,sha256=bqFDOcfAGcZ-HYa2n_5tO31uw3KXc66wHpUZ5cgpsTk,6702
13
- ddeutil/workflow/result.py,sha256=iuMvKv5OAzKWDbQf3yfrMHNEse6VdqvsKjv-DXKk-aQ,4349
14
- ddeutil/workflow/scheduler.py,sha256=DTD5HbCB8oQaxvAQ51-hvpec-NmDIYpyAXsMTODWuoc,25401
15
- ddeutil/workflow/stages.py,sha256=4SJcseBw4hv9WahJIXfaPdgn9-1Rbti_awMxO-AiP8s,26438
16
- ddeutil/workflow/templates.py,sha256=A0JgZFGkBv-AX-EskZj656nG5zFd3j1PpLpyXihf6Xg,10967
17
- ddeutil/workflow/utils.py,sha256=MctJmvklTYtiqZ-nZ7fazQeDoe77UvU0YUEqQZSlbCs,7225
18
- ddeutil/workflow/workflow.py,sha256=-n-8C0P-SY0BRc1ak6YhP_J2tT924O6ZkCnDJQFu-z8,45156
19
- ddeutil/workflow/api/__init__.py,sha256=F53NMBWtb9IKaDWkPU5KvybGGfKAcbehgn6TLBwHuuM,21
20
- ddeutil/workflow/api/api.py,sha256=q7wE0CDPCzpwmhnqKO5qSVO2Mjl1folqwF5E4W-l_UE,4042
21
- ddeutil/workflow/api/repeat.py,sha256=zyvsrXKk-3-_N8ZRZSki0Mueshugum2jtqctEOp9QSc,4927
22
- ddeutil/workflow/api/routes/__init__.py,sha256=HRUg4yB3023Iml8FQKuY0X6u9FavJe-HqEl6V8N_4hs,450
23
- ddeutil/workflow/api/routes/logs.py,sha256=uC8daeCrH_vpVJ-9Um5dMGfHR8JVtwwrDH5I1-Vp6Sw,971
24
- ddeutil/workflow/api/routes/schedules.py,sha256=9Q4cPYQWOyiZ1lnanpwtIaLWQtLRX1Vwx7uE30cd_1w,4644
25
- ddeutil/workflow/api/routes/workflows.py,sha256=LQiLlB3tESUtOZWcuLUKIHKvnit_yX3fKzYB4FqhavI,4354
26
- ddeutil_workflow-0.0.35.dist-info/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
27
- ddeutil_workflow-0.0.35.dist-info/METADATA,sha256=FGRFLyia45WdWsiJin8fuTmLoD5-MqKDO63tflCJCzI,19269
28
- ddeutil_workflow-0.0.35.dist-info/WHEEL,sha256=nn6H5-ilmfVryoAQl3ZQ2l8SH5imPWFpm1A5FgEuFV4,91
29
- ddeutil_workflow-0.0.35.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
30
- ddeutil_workflow-0.0.35.dist-info/RECORD,,