ddeutil-workflow 0.0.48__tar.gz → 0.0.49__tar.gz

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.
Files changed (69) hide show
  1. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/PKG-INFO +3 -5
  2. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/README.md +2 -4
  3. ddeutil_workflow-0.0.49/src/ddeutil/workflow/__about__.py +1 -0
  4. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/__init__.py +4 -1
  5. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/api/routes/logs.py +6 -5
  6. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/conf.py +31 -31
  7. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/job.py +10 -5
  8. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/logs.py +144 -80
  9. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/result.py +8 -6
  10. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/reusables.py +3 -3
  11. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/scheduler.py +54 -44
  12. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/stages.py +278 -78
  13. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/utils.py +3 -3
  14. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/workflow.py +107 -87
  15. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil_workflow.egg-info/PKG-INFO +3 -5
  16. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_conf.py +2 -2
  17. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_job.py +10 -11
  18. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_logs_audit.py +2 -2
  19. ddeutil_workflow-0.0.49/tests/test_logs_trace.py +6 -0
  20. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_reusables_template_filter.py +2 -2
  21. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_stage_handler_exec.py +208 -0
  22. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_workflow_exec.py +1 -1
  23. ddeutil_workflow-0.0.48/src/ddeutil/workflow/__about__.py +0 -1
  24. ddeutil_workflow-0.0.48/tests/test_logs_trace.py +0 -6
  25. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/LICENSE +0 -0
  26. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/pyproject.toml +0 -0
  27. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/setup.cfg +0 -0
  28. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/__cron.py +0 -0
  29. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/__main__.py +0 -0
  30. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/__types.py +0 -0
  31. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/api/__init__.py +0 -0
  32. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/api/api.py +0 -0
  33. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/api/log.py +0 -0
  34. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/api/repeat.py +0 -0
  35. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/api/routes/__init__.py +0 -0
  36. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/api/routes/job.py +0 -0
  37. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/api/routes/schedules.py +0 -0
  38. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/api/routes/workflows.py +0 -0
  39. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/cron.py +0 -0
  40. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/exceptions.py +0 -0
  41. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil/workflow/params.py +0 -0
  42. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil_workflow.egg-info/SOURCES.txt +0 -0
  43. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
  44. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil_workflow.egg-info/requires.txt +0 -0
  45. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
  46. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test__cron.py +0 -0
  47. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test__regex.py +0 -0
  48. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_cron_on.py +0 -0
  49. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_job_exec.py +0 -0
  50. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_job_exec_strategy.py +0 -0
  51. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_job_strategy.py +0 -0
  52. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_params.py +0 -0
  53. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_release.py +0 -0
  54. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_release_queue.py +0 -0
  55. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_result.py +0 -0
  56. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_reusables_call_tag.py +0 -0
  57. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_reusables_template.py +0 -0
  58. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_schedule.py +0 -0
  59. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_schedule_pending.py +0 -0
  60. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_schedule_tasks.py +0 -0
  61. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_schedule_workflow.py +0 -0
  62. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_scheduler_control.py +0 -0
  63. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_stage.py +0 -0
  64. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_utils.py +0 -0
  65. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_workflow.py +0 -0
  66. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_workflow_exec_job.py +0 -0
  67. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_workflow_exec_poke.py +0 -0
  68. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_workflow_exec_release.py +0 -0
  69. {ddeutil_workflow-0.0.48 → ddeutil_workflow-0.0.49}/tests/test_workflow_task.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.48
3
+ Version: 0.0.49
4
4
  Summary: Lightweight workflow orchestration
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -262,14 +262,12 @@ it will use default value and do not raise any error to you.
262
262
 
263
263
  | Name | Component | Default | Description |
264
264
  |:-----------------------------|:---------:|:--------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|
265
- | **ROOT_PATH** | Core | `.` | Root path or the project path for this workflow engine. |
266
265
  | **REGISTRY_CALLER** | Core | `.` | List of importable string for the call stage. |
267
266
  | **REGISTRY_FILTER** | Core | `ddeutil.workflow.templates` | List of importable string for the filter template. |
268
- | **CONF_PATH** | Core | `conf` | The config path that keep all template `.yaml` files. |
267
+ | **CONF_PATH** | Core | `./conf` | The config path that keep all template `.yaml` files. |
269
268
  | **TIMEZONE** | Core | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. |
270
- | **STAGE_DEFAULT_ID** | Core | `true` | A flag that enable default stage ID that use for catch an execution output. |
269
+ | **STAGE_DEFAULT_ID** | Core | `false` | A flag that enable default stage ID that use for catch an execution output. |
271
270
  | **STAGE_RAISE_ERROR** | Core | `false` | A flag that all stage raise StageException from stage execution. |
272
- | **JOB_DEFAULT_ID** | Core | `false` | A flag that enable default job ID that use for catch an execution output. The ID that use will be sequence number. |
273
271
  | **JOB_RAISE_ERROR** | Core | `true` | A flag that all job raise JobException from job strategy execution. |
274
272
  | **MAX_CRON_PER_WORKFLOW** | Core | `5` | |
275
273
  | **MAX_QUEUE_COMPLETE_HIST** | Core | `16` | |
@@ -219,14 +219,12 @@ it will use default value and do not raise any error to you.
219
219
 
220
220
  | Name | Component | Default | Description |
221
221
  |:-----------------------------|:---------:|:--------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|
222
- | **ROOT_PATH** | Core | `.` | Root path or the project path for this workflow engine. |
223
222
  | **REGISTRY_CALLER** | Core | `.` | List of importable string for the call stage. |
224
223
  | **REGISTRY_FILTER** | Core | `ddeutil.workflow.templates` | List of importable string for the filter template. |
225
- | **CONF_PATH** | Core | `conf` | The config path that keep all template `.yaml` files. |
224
+ | **CONF_PATH** | Core | `./conf` | The config path that keep all template `.yaml` files. |
226
225
  | **TIMEZONE** | Core | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. |
227
- | **STAGE_DEFAULT_ID** | Core | `true` | A flag that enable default stage ID that use for catch an execution output. |
226
+ | **STAGE_DEFAULT_ID** | Core | `false` | A flag that enable default stage ID that use for catch an execution output. |
228
227
  | **STAGE_RAISE_ERROR** | Core | `false` | A flag that all stage raise StageException from stage execution. |
229
- | **JOB_DEFAULT_ID** | Core | `false` | A flag that enable default job ID that use for catch an execution output. The ID that use will be sequence number. |
230
228
  | **JOB_RAISE_ERROR** | Core | `true` | A flag that all job raise JobException from job strategy execution. |
231
229
  | **MAX_CRON_PER_WORKFLOW** | Core | `5` | |
232
230
  | **MAX_QUEUE_COMPLETE_HIST** | Core | `16` | |
@@ -0,0 +1 @@
1
+ __version__: str = "0.0.49"
@@ -32,8 +32,11 @@ from .job import (
32
32
  )
33
33
  from .logs import (
34
34
  Audit,
35
+ AuditModel,
36
+ Trace,
35
37
  TraceData,
36
- TraceLog,
38
+ TraceMeta,
39
+ TraceModel,
37
40
  get_audit,
38
41
  get_dt_tznow,
39
42
  get_trace,
@@ -10,7 +10,8 @@ from fastapi import APIRouter, Path, Query
10
10
  from fastapi import status as st
11
11
  from fastapi.responses import UJSONResponse
12
12
 
13
- from ...logs import get_audit, get_trace_obj
13
+ from ...logs import get_audit
14
+ from ...result import Result
14
15
 
15
16
  log_route = APIRouter(
16
17
  prefix="/logs",
@@ -33,6 +34,7 @@ async def get_traces(
33
34
  """Return all trace logs from the current trace log path that config with
34
35
  `WORKFLOW_LOG_PATH` environment variable name.
35
36
  """
37
+ result = Result()
36
38
  return {
37
39
  "message": (
38
40
  f"Getting trace logs with offset: {offset} and limit: {limit}"
@@ -44,7 +46,7 @@ async def get_traces(
44
46
  exclude_unset=True,
45
47
  exclude_defaults=True,
46
48
  )
47
- for trace in get_trace_obj().find_logs()
49
+ for trace in result.trace.find_traces()
48
50
  ],
49
51
  }
50
52
 
@@ -63,12 +65,11 @@ async def get_trace_with_id(run_id: str):
63
65
  - **run_id**: A running ID that want to search a trace log from the log
64
66
  path.
65
67
  """
68
+ result = Result()
66
69
  return {
67
70
  "message": f"Getting trace log with specific running ID: {run_id}",
68
71
  "trace": (
69
- get_trace_obj()
70
- .find_log_with_id(run_id)
71
- .model_dump(
72
+ result.trace.find_trace_with_id(run_id).model_dump(
72
73
  by_alias=True,
73
74
  exclude_none=True,
74
75
  exclude_unset=True,
@@ -11,7 +11,7 @@ from collections.abc import Iterator
11
11
  from datetime import timedelta
12
12
  from functools import cached_property
13
13
  from pathlib import Path
14
- from typing import Optional, TypeVar
14
+ from typing import Final, Optional, TypeVar
15
15
  from zoneinfo import ZoneInfo
16
16
 
17
17
  from ddeutil.core import str2bool
@@ -21,12 +21,15 @@ from ddeutil.io.paths import glob_files, is_ignored, read_ignore
21
21
  from .__types import DictData, TupleStr
22
22
 
23
23
  T = TypeVar("T")
24
- PREFIX: str = "WORKFLOW"
24
+ PREFIX: Final[str] = "WORKFLOW"
25
25
 
26
26
 
27
27
  def env(var: str, default: str | None = None) -> str | None: # pragma: no cov
28
28
  """Get environment variable with uppercase and adding prefix string.
29
29
 
30
+ :param var: (str) A env variable name.
31
+ :param default: (str | None) A default value if an env var does not set.
32
+
30
33
  :rtype: str | None
31
34
  """
32
35
  return os.getenv(f"{PREFIX}_{var.upper().replace(' ', '_')}", default)
@@ -51,22 +54,13 @@ class Config: # pragma: no cov
51
54
  """
52
55
 
53
56
  # NOTE: Core
54
- @property
55
- def root_path(self) -> Path:
56
- """Root path or the project path for this workflow engine that use for
57
- combine with `conf_path` value.
58
-
59
- :rtype: Path
60
- """
61
- return Path(env("CORE_ROOT_PATH", "."))
62
-
63
57
  @property
64
58
  def conf_path(self) -> Path:
65
59
  """Config path that keep all workflow template YAML files.
66
60
 
67
61
  :rtype: Path
68
62
  """
69
- return self.root_path / env("CORE_CONF_PATH", "conf")
63
+ return Path(env("CORE_CONF_PATH", "./conf"))
70
64
 
71
65
  @property
72
66
  def tz(self) -> ZoneInfo:
@@ -78,12 +72,12 @@ class Config: # pragma: no cov
78
72
  return ZoneInfo(env("CORE_TIMEZONE", "UTC"))
79
73
 
80
74
  @property
81
- def gen_id_simple_mode(self) -> bool:
75
+ def generate_id_simple_mode(self) -> bool:
82
76
  return str2bool(env("CORE_GENERATE_ID_SIMPLE_MODE", "true"))
83
77
 
84
78
  # NOTE: Register
85
79
  @property
86
- def regis_call(self) -> list[str]:
80
+ def registry_caller(self) -> list[str]:
87
81
  """Register Caller that is a list of importable string for the call
88
82
  stage model can get.
89
83
 
@@ -93,7 +87,7 @@ class Config: # pragma: no cov
93
87
  return [r.strip() for r in regis_call_str.split(",")]
94
88
 
95
89
  @property
96
- def regis_filter(self) -> list[str]:
90
+ def registry_filter(self) -> list[str]:
97
91
  """Register Filter that is a list of importable string for the filter
98
92
  template.
99
93
 
@@ -106,7 +100,7 @@ class Config: # pragma: no cov
106
100
 
107
101
  # NOTE: Log
108
102
  @property
109
- def log_path(self) -> Path:
103
+ def trace_path(self) -> Path:
110
104
  return Path(env("LOG_TRACE_PATH", "./logs"))
111
105
 
112
106
  @property
@@ -165,11 +159,7 @@ class Config: # pragma: no cov
165
159
  return str2bool(env("CORE_JOB_RAISE_ERROR", "true"))
166
160
 
167
161
  @property
168
- def job_default_id(self) -> bool:
169
- return str2bool(env("CORE_JOB_DEFAULT_ID", "false"))
170
-
171
- @property
172
- def max_on_per_workflow(self) -> int:
162
+ def max_cron_per_workflow(self) -> int:
173
163
  """The maximum on value that store in workflow model.
174
164
 
175
165
  :rtype: int
@@ -305,23 +295,31 @@ class SimLoad:
305
295
  )
306
296
 
307
297
  @classmethod
308
- def is_ignore(cls, file: Path, conf_path: Path) -> bool:
298
+ def is_ignore(
299
+ cls,
300
+ file: Path,
301
+ conf_path: Path,
302
+ *,
303
+ ignore_filename: Optional[str] = None,
304
+ ) -> bool:
309
305
  """Check this file was ignored.
310
306
 
311
307
  :param file: (Path) A file path that want to check.
312
308
  :param conf_path: (Path) A config path that want to read the config
313
309
  ignore file.
310
+ :param ignore_filename: (str) An ignore filename.
314
311
 
315
312
  :rtype: bool
316
313
  """
317
- return is_ignored(file, read_ignore(conf_path / ".confignore"))
314
+ ignore_filename: str = ignore_filename or ".confignore"
315
+ return is_ignored(file, read_ignore(conf_path / ignore_filename))
318
316
 
319
317
  @classmethod
320
318
  def filter_yaml(cls, file: Path, name: str | None = None) -> DictData:
321
319
  """Read a YAML file context from an input file path and specific name.
322
320
 
323
- :param file: (Path)
324
- :param name: (str)
321
+ :param file: (Path) A file path that want to extract YAML context.
322
+ :param name: (str) A key name that search on a YAML context.
325
323
 
326
324
  :rtype: DictData
327
325
  """
@@ -374,8 +372,8 @@ def dynamic(
374
372
  class Loader(SimLoad):
375
373
  """Loader Object that get the config `yaml` file from current path.
376
374
 
377
- :param name: A name of config data that will read by Yaml Loader object.
378
- :param externals: An external parameters
375
+ :param name: (str) A name of config data that will read by Yaml Loader object.
376
+ :param externals: (DictData) An external parameters
379
377
  """
380
378
 
381
379
  @classmethod
@@ -383,17 +381,19 @@ class Loader(SimLoad):
383
381
  cls,
384
382
  obj: object,
385
383
  *,
384
+ path: Path | None = None,
386
385
  included: list[str] | None = None,
387
386
  excluded: list[str] | None = None,
388
- path: Path | None = None,
389
387
  **kwargs,
390
388
  ) -> Iterator[tuple[str, DictData]]:
391
389
  """Override the find class method from the Simple Loader object.
392
390
 
393
391
  :param obj: An object that want to validate matching before return.
394
- :param included:
395
- :param excluded:
396
- :param path:
392
+ :param path: (Path) A override config path.
393
+ :param included: An excluded list of data key that want to reject this
394
+ data if any key exist.
395
+ :param excluded: An included list of data key that want to filter from
396
+ data.
397
397
 
398
398
  :rtype: Iterator[tuple[str, DictData]]
399
399
  """
@@ -483,7 +483,13 @@ class Job(BaseModel):
483
483
  except Exception as err:
484
484
  raise JobException(f"{err.__class__.__name__}: {err}") from err
485
485
 
486
- def set_outputs(self, output: DictData, to: DictData) -> DictData:
486
+ def set_outputs(
487
+ self,
488
+ output: DictData,
489
+ to: DictData,
490
+ *,
491
+ job_id: Optional[None] = None,
492
+ ) -> DictData:
487
493
  """Set an outputs from execution process to the received context. The
488
494
  result from execution will pass to value of `strategies` key.
489
495
 
@@ -511,22 +517,21 @@ class Job(BaseModel):
511
517
 
512
518
  :param output: An output context.
513
519
  :param to: A context data that want to add output result.
520
+ :param job_id: A job ID if the id field does not set.
514
521
 
515
522
  :rtype: DictData
516
523
  """
517
524
  if "jobs" not in to:
518
525
  to["jobs"] = {}
519
526
 
520
- if self.id is None and not dynamic(
521
- "job_default_id", extras=self.extras
522
- ):
527
+ if self.id is None and job_id is None:
523
528
  raise JobException(
524
529
  "This job do not set the ID before setting execution output."
525
530
  )
526
531
 
527
532
  # NOTE: If the job ID did not set, it will use index of jobs key
528
533
  # instead.
529
- _id: str = self.id or str(len(to["jobs"]) + 1)
534
+ _id: str = self.id or job_id
530
535
 
531
536
  errors: DictData = (
532
537
  {"errors": output.pop("errors", {})} if "errors" in output else {}