ddeutil-workflow 0.0.80__py3-none-any.whl → 0.0.81__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.
@@ -1 +1 @@
1
- __version__: str = "0.0.80"
1
+ __version__: str = "0.0.81"
ddeutil/workflow/cli.py CHANGED
@@ -54,29 +54,30 @@ def init() -> None:
54
54
  dedent(
55
55
  """
56
56
  # Example workflow template.
57
- wf-example:
58
- type: Workflow
59
- desc: |
60
- An example workflow template that provide the demo of workflow.
61
- params:
62
- name:
63
- type: str
64
- default: "World"
65
- jobs:
66
- first-job:
67
- stages:
68
-
69
- - name: "Hello Stage"
70
- echo: "Start say hi to the console"
71
-
72
- - name: "Call tasks"
73
- uses: tasks/say-hello-func@example
74
- with:
75
- name: ${{ params.name }}
76
- second-job:
77
-
78
- - name: "Hello Env"
79
- echo: "Start say hi with ${ WORKFLOW_DEMO_HELLO }"
57
+ name: wf-example:
58
+ type: Workflow
59
+ desc: |
60
+ An example workflow template that provide the demo of workflow.
61
+ params:
62
+ name:
63
+ type: str
64
+ default: "World"
65
+ jobs:
66
+ first-job:
67
+ stages:
68
+
69
+ - name: "Hello Stage"
70
+ echo: "Start say hi to the console"
71
+
72
+ - name: "Call tasks"
73
+ uses: tasks/say-hello-func@example
74
+ with:
75
+ name: ${{ params.name }}
76
+
77
+ second-job:
78
+
79
+ - name: "Hello Env"
80
+ echo: "Start say hi with ${ WORKFLOW_DEMO_HELLO }"
80
81
  """
81
82
  ).lstrip("\n")
82
83
  )
@@ -89,12 +90,20 @@ def init() -> None:
89
90
  dummy_tasks_path.write_text(
90
91
  dedent(
91
92
  """
93
+ from typing import Any, Optional
94
+
92
95
  from ddeutil.workflow import Result, tag
93
96
 
94
97
  @tag(name="example", alias="say-hello-func")
95
- def hello_world_task(name: str, rs: Result) -> dict[str, str]:
98
+ def hello_world_task(name: str, rs: Result, extras: Optional[dict[str, Any]] = None) -> dict[str, str]:
96
99
  \"\"\"Logging hello task function\"\"\"
97
- rs.trace.info(f"Hello, {name}")
100
+ _extras = extras or {}
101
+ # NOTE: I will use custom newline logging if you pass `||`.
102
+ rs.trace.info(
103
+ f"Hello, {name}||"
104
+ f"> running ID: {rs.run_id}"
105
+ f"> extras: {_extras}"
106
+ )
98
107
  return {"name": name}
99
108
  """
100
109
  ).lstrip("\n")
@@ -106,18 +115,19 @@ def init() -> None:
106
115
  dotenv_file = Path(".env")
107
116
  mode: str = "a" if dotenv_file.exists() else "w"
108
117
  with dotenv_file.open(mode=mode) as f:
109
- f.write("\n# Workflow env vars\n")
118
+ f.write("\n# Workflow Environment Variables\n")
110
119
  f.write(
111
120
  "WORKFLOW_DEMO_HELLO=foo\n"
112
121
  "WORKFLOW_CORE_DEBUG_MODE=true\n"
113
122
  "WORKFLOW_LOG_TIMEZONE=Asia/Bangkok\n"
114
- "WORKFLOW_LOG_TRACE_ENABLE_WRITE=false\n"
123
+ 'WORKFLOW_LOG_TRACE_HANDLERS=\'[{"type": "console"}]\'\n'
124
+ 'WORKFLOW_LOG_AUDIT_CONF=\'{"type": "file", "path": "./audits"}\''
115
125
  "WORKFLOW_LOG_AUDIT_ENABLE_WRITE=true\n"
116
126
  )
117
127
 
118
128
  typer.echo("Starter command:")
119
129
  typer.echo(
120
- "> `source .env && workflow-cli workflows execute --name=wf-example`"
130
+ ">>> `source .env && workflow-cli workflows execute --name=wf-example`"
121
131
  )
122
132
 
123
133
 
@@ -163,7 +173,7 @@ def api(
163
173
  debug: Annotated[bool, typer.Option(help="A debug mode flag")] = True,
164
174
  workers: Annotated[int, typer.Option(help="A worker number")] = None,
165
175
  reload: Annotated[bool, typer.Option(help="A reload flag")] = False,
166
- ):
176
+ ) -> None:
167
177
  """
168
178
  Provision API application from the FastAPI.
169
179
  """
ddeutil/workflow/conf.py CHANGED
@@ -22,19 +22,6 @@ Functions:
22
22
  pass_env: Process environment variable substitution
23
23
  api_config: Get API-specific configuration settings
24
24
 
25
- Example:
26
- ```python
27
- from ddeutil.workflow.conf import Config, YamlParser
28
-
29
- # Load workflow configuration
30
- parser = YamlParser("my-workflow")
31
- workflow_config = parser.data
32
-
33
- # Access dynamic configuration
34
- from ddeutil.workflow.conf import dynamic
35
- log_level = dynamic("log_level", default="INFO")
36
- ```
37
-
38
25
  Note:
39
26
  Configuration files support environment variable substitution using
40
27
  ${VAR_NAME} syntax and provide extensive validation capabilities.
@@ -155,7 +142,7 @@ class Config: # pragma: no cov
155
142
  )
156
143
 
157
144
  @property
158
- def audit_conf(self) -> str:
145
+ def audit_conf(self) -> dict[str, Any]:
159
146
  return json.loads(
160
147
  env("LOG_AUDIT_URL", '{"type": "file", "path": "./audits"}')
161
148
  )
@@ -288,9 +275,12 @@ class YamlParser:
288
275
  continue
289
276
 
290
277
  if data := cls.filter_yaml(file, name=name):
278
+
279
+ # NOTE: Start adding file metadata.
291
280
  file_stat: os.stat_result = file.lstat()
292
281
  data["created_at"] = file_stat.st_ctime
293
282
  data["updated_at"] = file_stat.st_mtime
283
+
294
284
  if not obj_type:
295
285
  all_data.append((file_stat.st_mtime, data))
296
286
  elif (t := data.get("type")) and t == obj_type:
@@ -324,9 +314,8 @@ class YamlParser:
324
314
  extras: (DictData) An extra parameter that use to override core
325
315
  config values.
326
316
  ignore_filename: (str) An ignore filename. Default is
327
- ``.confignore`` filename.
328
- tags: (list[str])
329
- A list of tag that want to filter.
317
+ ``.confignore`` filename.
318
+ tags (list[str]): A list of tag that want to filter.
330
319
 
331
320
  :rtype: Iterator[tuple[str, DictData]]
332
321
  """
@@ -365,6 +354,8 @@ class YamlParser:
365
354
  continue
366
355
 
367
356
  if (t := data.get("type")) and t == obj_type:
357
+
358
+ # NOTE: Start adding file metadata.
368
359
  file_stat: os.stat_result = file.lstat()
369
360
  data["created_at"] = file_stat.st_ctime
370
361
  data["updated_at"] = file_stat.st_mtime
@@ -372,6 +363,7 @@ class YamlParser:
372
363
  file.lstat().st_mtime,
373
364
  data,
374
365
  )
366
+
375
367
  if key in all_data:
376
368
  all_data[key].append(marking)
377
369
  else:
@@ -405,15 +397,23 @@ class YamlParser:
405
397
  def filter_yaml(cls, file: Path, name: Optional[str] = None) -> DictData:
406
398
  """Read a YAML file context from an input file path and specific name.
407
399
 
408
- :param file: (Path) A file path that want to extract YAML context.
409
- :param name: (str) A key name that search on a YAML context.
400
+ Args:
401
+ file (Path): A file path that want to extract YAML context.
402
+ name (str): A key name that search on a YAML context.
410
403
 
411
- :rtype: DictData
404
+ Returns:
405
+ DictData: A data that read from this file if it is YAML format.
412
406
  """
413
407
  if any(file.suffix.endswith(s) for s in (".yml", ".yaml")):
414
408
  values: DictData = YamlFlResolve(file).read()
415
409
  if values is not None:
416
- return values.get(name, {}) if name else values
410
+ if name:
411
+ if "name" in values and values.get("name") == name:
412
+ return values
413
+ return (
414
+ values[name] | {"name": name} if name in values else {}
415
+ )
416
+ return values
417
417
  return {}
418
418
 
419
419
  @cached_property
@@ -135,11 +135,12 @@ class BaseError(Exception):
135
135
 
136
136
  Example:
137
137
  >>> error = BaseError("Something failed", refs="stage-1")
138
- >>> # Simple format
138
+
139
+ Simple format
139
140
  >>> error.to_dict()
140
141
  >>> # Returns: {"name": "BaseError", "message": "Something failed"}
141
142
 
142
- >>> # With reference mapping
143
+ With reference mapping
143
144
  >>> error.to_dict(with_refs=True)
144
145
  >>> # Returns: {"stage-1": {"name": "BaseError", "message": "Something failed"}}
145
146
  ```
@@ -25,22 +25,20 @@ Functions:
25
25
  create_model_from_caller: Generate Pydantic models from function signatures
26
26
 
27
27
  Example:
28
- ```python
29
- from ddeutil.workflow.reusables import tag
30
-
31
- @tag("data-processing", alias="process-csv")
32
- def process_csv_file(input_path: str, output_path: str) -> dict:
33
- # Custom processing logic
34
- return {"status": "completed", "rows_processed": 1000}
35
-
36
- # Use in workflow YAML:
37
- # stages:
38
- # - name: "Process data"
39
- # uses: "data-processing/process-csv@latest"
40
- # args:
41
- # input_path: "/data/input.csv"
42
- # output_path: "/data/output.csv"
43
- ```
28
+
29
+ >>> from ddeutil.workflow.reusables import tag
30
+ >>>
31
+ >>> @tag("data-processing", alias="process-csv")
32
+ >>> def process_csv_file(input_path: str, output_path: str) -> dict:
33
+ >>> return {"status": "completed", "rows_processed": 1000}
34
+
35
+ >>> # Use in workflow YAML:
36
+ >>> # stages:
37
+ >>> # - name: "Process data"
38
+ >>> # uses: "data-processing/process-csv@latest"
39
+ >>> # args:
40
+ >>> # input_path: "/data/input.csv"
41
+ >>> # output_path: "/data/output.csv"
44
42
 
45
43
  Note:
46
44
  The registry system supports versioning and aliasing for better function
@@ -64,6 +62,7 @@ from typing import (
64
62
  Protocol,
65
63
  TypeVar,
66
64
  Union,
65
+ cast,
67
66
  get_type_hints,
68
67
  )
69
68
 
@@ -201,7 +200,7 @@ def get_args_const(
201
200
  f"Post-filter: {expr} does not valid because it raise syntax error."
202
201
  ) from None
203
202
 
204
- body: list[Expr] = mod.body
203
+ body: list[Expr] = cast(list[Expr], mod.body)
205
204
  if len(body) > 1:
206
205
  raise UtilError(
207
206
  "Post-filter function should be only one calling per workflow."
ddeutil/workflow/utils.py CHANGED
@@ -28,17 +28,6 @@ Functions:
28
28
  cut_id: Cut running ID to specified length
29
29
  dump_all: Serialize nested BaseModel objects to dictionaries
30
30
  obj_name: Get object name or class name
31
-
32
- Example:
33
- ```python
34
- from ddeutil.workflow.utils import gen_id, get_dt_now
35
-
36
- # Generate unique ID
37
- run_id = gen_id("workflow")
38
-
39
- # Get current datetime
40
- now = get_dt_now()
41
- ```
42
31
  """
43
32
  from __future__ import annotations
44
33
 
@@ -198,17 +198,15 @@ class Workflow(BaseModel):
198
198
  FileNotFoundError: If workflow configuration file not found
199
199
 
200
200
  Example:
201
- ```python
202
- # Load from default config path
203
- workflow = Workflow.from_conf('data-pipeline')
204
-
205
- # Load with custom path and extras
206
- workflow = Workflow.from_conf(
207
- 'data-pipeline',
208
- path=Path('./custom-configs'),
209
- extras={'environment': 'production'}
210
- )
211
- ```
201
+ >>> # Load from default config path
202
+ >>> workflow = Workflow.from_conf('data-pipeline')
203
+
204
+ >>> # Load with custom path and extras
205
+ >>> workflow = Workflow.from_conf(
206
+ ... 'data-pipeline',
207
+ ... path=Path('./custom-configs'),
208
+ ... extras={'env': 'prod'}
209
+ ... )
212
210
  """
213
211
  load: YamlParser = YamlParser(name, path=path, extras=extras, obj=cls)
214
212
  data: DictData = copy.deepcopy(load.data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.80
3
+ Version: 0.0.81
4
4
  Summary: Lightweight workflow orchestration with YAML template
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -1,21 +1,21 @@
1
- ddeutil/workflow/__about__.py,sha256=0fYC3KyobGtx1NtfXlGCbR9mrpL_yrH0UY_kp0NWaN8,28
1
+ ddeutil/workflow/__about__.py,sha256=OgSQucXlwEL1i2ZuW-rDgctTF1ybVDYwWSf4fhzgOx4,28
2
2
  ddeutil/workflow/__cron.py,sha256=avOagaHl9xXOmizeRWm13cOrty9Tw0vRjFq-xoEgpAY,29167
3
3
  ddeutil/workflow/__init__.py,sha256=pRnUNCrwnKGrEQNSmdU9Ybf1tEQKg4LfsCpyj1Y3mhg,3241
4
4
  ddeutil/workflow/__main__.py,sha256=Qd-f8z2Q2vpiEP2x6PBFsJrpACWDVxFKQk820MhFmHo,59
5
5
  ddeutil/workflow/__types.py,sha256=tA2vsr6mzTSzbWB1sb62c5GgxODlfVRz6FvgLNJtQao,4788
6
6
  ddeutil/workflow/audits.py,sha256=h7WVHe3z_FqtmLRvAxhnSqae8fFtZPN0tICrVr39wP4,25456
7
- ddeutil/workflow/cli.py,sha256=aNFOZ3Re_QJBBP6vkT9Lsjrg8wLxrw_LKrl-1SIvSOg,8152
8
- ddeutil/workflow/conf.py,sha256=4bFl1ufX7-p6ely7tJnanpTZ0wJoik81yTcmBrbcyxY,16661
9
- ddeutil/workflow/errors.py,sha256=UpUIqoyqkvzqjuxtUQ9535l1HeAsyh-plEG0PgDVR2w,5541
7
+ ddeutil/workflow/cli.py,sha256=qEfqno3RoHLCKBdaA3IGYXcH40EUk9sSSOxN7iocWNQ,8573
8
+ ddeutil/workflow/conf.py,sha256=mvouKL6MroSCBNiRgPMb9T7_OI26JKHMGiGfYchLkF0,16756
9
+ ddeutil/workflow/errors.py,sha256=U2OHGY46zgcr6POmdJKryTVnCkjrswA7cNqjWiV_kMI,5530
10
10
  ddeutil/workflow/event.py,sha256=qm7QHw-Pozm6oIUzAIxpDkPzzVZVtHgJIUlIle0vEfQ,13943
11
11
  ddeutil/workflow/job.py,sha256=lSmOgh4l3_gBJXrTEWULhSSol648h6zPe6zKzz8jDHQ,46597
12
12
  ddeutil/workflow/params.py,sha256=y9f6DEIyae1j4awbj3Kbeq75-U2UPFlKv9K57Hdo_Go,17188
13
13
  ddeutil/workflow/result.py,sha256=BOk3DZMtmdE7xzQYeEYTGFlIkzJQ4Ed3fYzf0zF8Jo8,8963
14
- ddeutil/workflow/reusables.py,sha256=g_Cac3yHy0H5ffl4Bb8_eGl284ELxOuX4LI8GYPMZgw,24983
14
+ ddeutil/workflow/reusables.py,sha256=SBLJSxR8ELoWJErBfSMZS3Rr1O_93T-fFBpfn2AvxuA,25007
15
15
  ddeutil/workflow/stages.py,sha256=QufIa2b7A_ngOndVoGzyxKm_o5ZrauNeqxAC4vBkKFM,122678
16
16
  ddeutil/workflow/traces.py,sha256=e12_rDnwVo-XR6Ca1KLwCjo3tAwbJP7yXc1YU62YOt8,73415
17
- ddeutil/workflow/utils.py,sha256=-E-Z5hN_UTFuWDk-NpfKhNj0QtLfJSvZNDI5NzJsd5E,12122
18
- ddeutil/workflow/workflow.py,sha256=Uojf7k7l91sqOlsPMeSPwQmbrB5pgbWEmx9QgKYngmI,42924
17
+ ddeutil/workflow/utils.py,sha256=zj4o10Iq3qgNobTQAkTaQzQ5EBBf3uhbqhcOyCo1suo,11923
18
+ ddeutil/workflow/workflow.py,sha256=9oxbJBOtr2jvv1k2BVwAs4xh20yTSqd3EpaKhC9mLlU,42904
19
19
  ddeutil/workflow/api/__init__.py,sha256=5DzYL3ngceoRshh5HYCSVWChqNJSiP01E1bEd8XxPi0,4799
20
20
  ddeutil/workflow/api/log_conf.py,sha256=WfS3udDLSyrP-C80lWOvxxmhd_XWKvQPkwDqKblcH3E,1834
21
21
  ddeutil/workflow/api/routes/__init__.py,sha256=JRaJZB0D6mgR17MbZo8yLtdYDtD62AA8MdKlFqhG84M,420
@@ -28,9 +28,9 @@ ddeutil/workflow/plugins/providers/aws.py,sha256=61uIFBEWt-_D5Sui24qUPier1Hiqlw_
28
28
  ddeutil/workflow/plugins/providers/az.py,sha256=o3dh011lEtmr7-d7FPZJPgXdT0ytFzKfc5xnVxSyXGU,34867
29
29
  ddeutil/workflow/plugins/providers/container.py,sha256=DSN0RWxMjTJN5ANheeMauDaPa3X6Z2E1eGUcctYkENw,22134
30
30
  ddeutil/workflow/plugins/providers/gcs.py,sha256=KgAOdMBvdbMLTH_z_FwVriBFtZfKEYx8_34jzUOVjTY,27460
31
- ddeutil_workflow-0.0.80.dist-info/licenses/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
32
- ddeutil_workflow-0.0.80.dist-info/METADATA,sha256=g43RJsjquJoYyQUl_d5Wfuxdoh00tukjjjgW-lL7Hdw,16087
33
- ddeutil_workflow-0.0.80.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
- ddeutil_workflow-0.0.80.dist-info/entry_points.txt,sha256=qDTpPSauL0ciO6T4iSVt8bJeYrVEkkoEEw_RlGx6Kgk,63
35
- ddeutil_workflow-0.0.80.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
36
- ddeutil_workflow-0.0.80.dist-info/RECORD,,
31
+ ddeutil_workflow-0.0.81.dist-info/licenses/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
32
+ ddeutil_workflow-0.0.81.dist-info/METADATA,sha256=KfQDz9_yjBxdfDMUhbO1kBrwNWS2C6cEiMyWn5b0wws,16087
33
+ ddeutil_workflow-0.0.81.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ ddeutil_workflow-0.0.81.dist-info/entry_points.txt,sha256=qDTpPSauL0ciO6T4iSVt8bJeYrVEkkoEEw_RlGx6Kgk,63
35
+ ddeutil_workflow-0.0.81.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
36
+ ddeutil_workflow-0.0.81.dist-info/RECORD,,