ddeutil-workflow 0.0.56__tar.gz → 0.0.58__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 (71) hide show
  1. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/PKG-INFO +41 -35
  2. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/README.md +38 -32
  3. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/pyproject.toml +4 -3
  4. ddeutil_workflow-0.0.58/src/ddeutil/workflow/__about__.py +1 -0
  5. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/__cron.py +26 -12
  6. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/__types.py +1 -0
  7. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/conf.py +21 -9
  8. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/event.py +11 -10
  9. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/exceptions.py +33 -12
  10. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/job.py +89 -58
  11. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/logs.py +59 -37
  12. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/params.py +4 -0
  13. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/result.py +9 -4
  14. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/scheduler.py +15 -9
  15. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/stages.py +441 -171
  16. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/utils.py +37 -6
  17. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/workflow.py +218 -243
  18. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil_workflow.egg-info/PKG-INFO +41 -35
  19. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil_workflow.egg-info/SOURCES.txt +2 -2
  20. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil_workflow.egg-info/requires.txt +2 -2
  21. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test__cron.py +28 -16
  22. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_conf.py +81 -10
  23. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_event.py +52 -27
  24. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_job.py +20 -21
  25. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_job_exec.py +93 -68
  26. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_job_exec_strategy.py +9 -20
  27. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_params.py +1 -4
  28. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_release.py +7 -20
  29. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_release_queue.py +1 -1
  30. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_result.py +16 -28
  31. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_schedule.py +3 -3
  32. ddeutil_workflow-0.0.58/tests/test_stage_handler_exec.py +966 -0
  33. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_utils.py +28 -1
  34. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_workflow.py +13 -3
  35. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_workflow_exec.py +158 -137
  36. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_workflow_exec_job.py +10 -28
  37. ddeutil_workflow-0.0.58/tests/test_workflow_poke.py +168 -0
  38. ddeutil_workflow-0.0.58/tests/test_workflow_release.py +153 -0
  39. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_workflow_task.py +44 -16
  40. ddeutil_workflow-0.0.56/src/ddeutil/workflow/__about__.py +0 -1
  41. ddeutil_workflow-0.0.56/tests/test_stage_handler_exec.py +0 -1149
  42. ddeutil_workflow-0.0.56/tests/test_workflow_exec_poke.py +0 -171
  43. ddeutil_workflow-0.0.56/tests/test_workflow_exec_release.py +0 -125
  44. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/LICENSE +0 -0
  45. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/setup.cfg +0 -0
  46. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/__init__.py +0 -0
  47. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/__main__.py +0 -0
  48. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/api/__init__.py +0 -0
  49. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/api/logs.py +0 -0
  50. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/api/routes/__init__.py +0 -0
  51. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/api/routes/job.py +0 -0
  52. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/api/routes/logs.py +0 -0
  53. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/api/routes/schedules.py +0 -0
  54. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/api/routes/workflows.py +0 -0
  55. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/api/utils.py +0 -0
  56. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil/workflow/reusables.py +0 -0
  57. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
  58. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil_workflow.egg-info/entry_points.txt +0 -0
  59. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
  60. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test__regex.py +0 -0
  61. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_logs_audit.py +0 -0
  62. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_logs_trace.py +0 -0
  63. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_reusables_call_tag.py +0 -0
  64. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_reusables_template.py +0 -0
  65. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_reusables_template_filter.py +0 -0
  66. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_schedule_pending.py +0 -0
  67. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_schedule_tasks.py +0 -0
  68. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_schedule_workflow.py +0 -0
  69. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_scheduler_control.py +0 -0
  70. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_stage.py +0 -0
  71. {ddeutil_workflow-0.0.56 → ddeutil_workflow-0.0.58}/tests/test_strategy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.56
3
+ Version: 0.0.58
4
4
  Summary: Lightweight workflow orchestration
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -22,8 +22,8 @@ Classifier: Programming Language :: Python :: 3.13
22
22
  Requires-Python: >=3.9.13
23
23
  Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
- Requires-Dist: ddeutil[checksum]>=0.4.7
26
- Requires-Dist: ddeutil-io[toml,yaml]>=0.2.11
25
+ Requires-Dist: ddeutil[checksum]>=0.4.8
26
+ Requires-Dist: ddeutil-io[toml,yaml]>=0.2.12
27
27
  Requires-Dist: pydantic==2.11.1
28
28
  Requires-Dist: python-dotenv==1.1.0
29
29
  Requires-Dist: schedule<2.0.0,==1.2.2
@@ -250,42 +250,48 @@ from ddeutil.workflow import Workflow, Result
250
250
 
251
251
  workflow: Workflow = Workflow.from_conf('run-py-local')
252
252
  result: Result = workflow.execute(
253
- params={"source-extract": "USD-THB", "asat-dt": "2024-01-01"}
253
+ params={"source-extract": "USD-THB", "run-date": "2024-01-01"}
254
254
  )
255
255
  ```
256
256
 
257
- So, this package provide the `Schedule` template for this action, and you can dynamic
258
- pass the parameters for changing align with that running time by the `release` prefix.
259
-
260
- ```yaml
261
- schedule-run-local-wf:
262
-
263
- # Validate model that use to parsing exists for template file
264
- type: Schedule
265
- workflows:
266
-
267
- # Map existing workflow that want to deploy with scheduler application.
268
- # It allows you to pass release parameter that dynamic change depend on the
269
- # current context of this scheduler application releasing that time.
270
- - name: run-py-local
271
- params:
272
- source-extract: "USD-THB"
273
- asat-dt: "${{ release.logical_date }}"
274
- ```
275
-
276
- The main method of the `Schedule` model that use to running is `pending`. If you
277
- do not pass the `stop` date on this method, it will use config with `WORKFLOW_APP_STOP_BOUNDARY_DELTA`
278
- key for generate this stop date.
279
-
280
- ```python
281
- from ddeutil.workflow import Schedule
257
+ > [!NOTE]
258
+ > So, this package provide the `Schedule` template for this action, and you can
259
+ > pass the parameters dynamically for changing align with that running time by
260
+ > the `release` prefix.
261
+ >
262
+ > ```yaml
263
+ > schedule-run-local-wf:
264
+ >
265
+ > # Validate model that use to parsing exists for template file
266
+ > type: Schedule
267
+ > workflows:
268
+ >
269
+ > # Map existing workflow that want to deploy with scheduler application.
270
+ > # It allows you to pass release parameter that dynamic change depend on the
271
+ > # current context of this scheduler application releasing that time.
272
+ > - name: run-py-local
273
+ > params:
274
+ > source-extract: "USD-THB"
275
+ > run-date: "${{ release.logical_date }}"
276
+ > ```
277
+ >
278
+ > The main method of the `Schedule` model that use to running is `pending`. If you
279
+ > do not pass the `stop` date on this method, it will use config with
280
+ > `WORKFLOW_APP_STOP_BOUNDARY_DELTA` key for generate this stop date.
281
+ >
282
+ > ```python
283
+ > from ddeutil.workflow import Schedule
284
+ >
285
+ > (
286
+ > Schedule
287
+ > .from_conf("schedule-run-local-wf")
288
+ > .pending(stop=None)
289
+ > )
290
+ > ```
282
291
 
283
- (
284
- Schedule
285
- .from_conf("schedule-run-local-wf")
286
- .pending(stop=None)
287
- )
288
- ```
292
+ > [!WARNING]
293
+ > The scheduler feature is the expensive feature of this project. You should
294
+ > avoid to use it and find a scheduler tool instead.
289
295
 
290
296
  ## :cookie: Configuration
291
297
 
@@ -205,42 +205,48 @@ from ddeutil.workflow import Workflow, Result
205
205
 
206
206
  workflow: Workflow = Workflow.from_conf('run-py-local')
207
207
  result: Result = workflow.execute(
208
- params={"source-extract": "USD-THB", "asat-dt": "2024-01-01"}
208
+ params={"source-extract": "USD-THB", "run-date": "2024-01-01"}
209
209
  )
210
210
  ```
211
211
 
212
- So, this package provide the `Schedule` template for this action, and you can dynamic
213
- pass the parameters for changing align with that running time by the `release` prefix.
214
-
215
- ```yaml
216
- schedule-run-local-wf:
217
-
218
- # Validate model that use to parsing exists for template file
219
- type: Schedule
220
- workflows:
221
-
222
- # Map existing workflow that want to deploy with scheduler application.
223
- # It allows you to pass release parameter that dynamic change depend on the
224
- # current context of this scheduler application releasing that time.
225
- - name: run-py-local
226
- params:
227
- source-extract: "USD-THB"
228
- asat-dt: "${{ release.logical_date }}"
229
- ```
230
-
231
- The main method of the `Schedule` model that use to running is `pending`. If you
232
- do not pass the `stop` date on this method, it will use config with `WORKFLOW_APP_STOP_BOUNDARY_DELTA`
233
- key for generate this stop date.
234
-
235
- ```python
236
- from ddeutil.workflow import Schedule
212
+ > [!NOTE]
213
+ > So, this package provide the `Schedule` template for this action, and you can
214
+ > pass the parameters dynamically for changing align with that running time by
215
+ > the `release` prefix.
216
+ >
217
+ > ```yaml
218
+ > schedule-run-local-wf:
219
+ >
220
+ > # Validate model that use to parsing exists for template file
221
+ > type: Schedule
222
+ > workflows:
223
+ >
224
+ > # Map existing workflow that want to deploy with scheduler application.
225
+ > # It allows you to pass release parameter that dynamic change depend on the
226
+ > # current context of this scheduler application releasing that time.
227
+ > - name: run-py-local
228
+ > params:
229
+ > source-extract: "USD-THB"
230
+ > run-date: "${{ release.logical_date }}"
231
+ > ```
232
+ >
233
+ > The main method of the `Schedule` model that use to running is `pending`. If you
234
+ > do not pass the `stop` date on this method, it will use config with
235
+ > `WORKFLOW_APP_STOP_BOUNDARY_DELTA` key for generate this stop date.
236
+ >
237
+ > ```python
238
+ > from ddeutil.workflow import Schedule
239
+ >
240
+ > (
241
+ > Schedule
242
+ > .from_conf("schedule-run-local-wf")
243
+ > .pending(stop=None)
244
+ > )
245
+ > ```
237
246
 
238
- (
239
- Schedule
240
- .from_conf("schedule-run-local-wf")
241
- .pending(stop=None)
242
- )
243
- ```
247
+ > [!WARNING]
248
+ > The scheduler feature is the expensive feature of this project. You should
249
+ > avoid to use it and find a scheduler tool instead.
244
250
 
245
251
  ## :cookie: Configuration
246
252
 
@@ -25,8 +25,8 @@ classifiers = [
25
25
  ]
26
26
  requires-python = ">=3.9.13"
27
27
  dependencies = [
28
- "ddeutil[checksum]>=0.4.7",
29
- "ddeutil-io[yaml,toml]>=0.2.11",
28
+ "ddeutil[checksum]>=0.4.8",
29
+ "ddeutil-io[yaml,toml]>=0.2.12",
30
30
  "pydantic==2.11.1",
31
31
  "python-dotenv==1.1.0",
32
32
  "schedule==1.2.2,<2.0.0",
@@ -82,8 +82,9 @@ source = ["src.ddeutil.workflow"]
82
82
  omit = [
83
83
  "src/ddeutil/workflow/__about__.py",
84
84
  "src/ddeutil/workflow/__cron.py",
85
+ "src/ddeutil/workflow/__main__.py",
85
86
  "src/ddeutil/workflow/api/__init__.py",
86
- "src/ddeutil/workflow/api/log.py",
87
+ "src/ddeutil/workflow/api/logs.py",
87
88
  "src/ddeutil/workflow/api/utils.py",
88
89
  "src/ddeutil/workflow/api/routes/__init__.py",
89
90
  "src/ddeutil/workflow/api/routes/job.py",
@@ -0,0 +1 @@
1
+ __version__: str = "0.0.58"
@@ -502,10 +502,10 @@ class CronPart:
502
502
  except IndexError:
503
503
  next_value: int = -1
504
504
  if value != (next_value - 1):
505
- # NOTE: ``next_value`` is not the subsequent number
505
+ # NOTE: `next_value` is not the subsequent number
506
506
  if start_number is None:
507
507
  # NOTE:
508
- # The last number of the list ``self.values`` is not in a
508
+ # The last number of the list `self.values` is not in a
509
509
  # range.
510
510
  multi_dim_values.append(value)
511
511
  else:
@@ -703,11 +703,14 @@ class CronJob:
703
703
  *,
704
704
  tz: str | None = None,
705
705
  ) -> CronRunner:
706
- """Returns the schedule datetime runner with this cronjob. It would run
707
- ``next``, ``prev``, or ``reset`` to generate running date that you want.
706
+ """Returns CronRunner instance that be datetime runner with this
707
+ cronjob. It can use `next`, `prev`, or `reset` methods to generate
708
+ running date.
708
709
 
709
- :param date: An initial date that want to mark as the start point.
710
- :param tz: A string timezone that want to change on runner.
710
+ :param date: (datetime) An initial date that want to mark as the start
711
+ point. (Default is use the current datetime)
712
+ :param tz: (str) A string timezone that want to change on runner.
713
+ (Default is None)
711
714
 
712
715
  :rtype: CronRunner
713
716
  """
@@ -743,6 +746,10 @@ class CronJobYear(CronJob):
743
746
  class CronRunner:
744
747
  """Create an instance of Date Runner object for datetime generate with
745
748
  cron schedule object value.
749
+
750
+ :param cron: (CronJob | CronJobYear)
751
+ :param date: (datetime)
752
+ :param tz: (str)
746
753
  """
747
754
 
748
755
  shift_limit: ClassVar[int] = 25
@@ -761,11 +768,17 @@ class CronRunner:
761
768
  cron: CronJob | CronJobYear,
762
769
  date: datetime | None = None,
763
770
  *,
764
- tz: str | None = None,
771
+ tz: str | ZoneInfo | None = None,
765
772
  ) -> None:
766
- # NOTE: Prepare timezone if this value does not set, it will use UTC.
767
- self.tz: ZoneInfo = ZoneInfo("UTC")
773
+ self.tz: ZoneInfo | None = None
768
774
  if tz:
775
+ if isinstance(tz, ZoneInfo):
776
+ self.tz = tz
777
+ elif not isinstance(tz, str):
778
+ raise TypeError(
779
+ "Invalid type of `tz` parameter, it should be str or "
780
+ "ZoneInfo instance."
781
+ )
769
782
  try:
770
783
  self.tz = ZoneInfo(tz)
771
784
  except ZoneInfoNotFoundError as err:
@@ -777,9 +790,10 @@ class CronRunner:
777
790
  raise ValueError(
778
791
  "Input schedule start time is not a valid datetime object."
779
792
  )
780
- if tz is None:
781
- self.tz = date.tzinfo
782
- self.date: datetime = date.astimezone(self.tz)
793
+ if tz is not None:
794
+ self.date: datetime = date.astimezone(self.tz)
795
+ else:
796
+ self.date: datetime = date
783
797
  else:
784
798
  self.date: datetime = datetime.now(tz=self.tz)
785
799
 
@@ -20,6 +20,7 @@ from typing import Any, Optional, TypedDict, Union
20
20
 
21
21
  from typing_extensions import Self
22
22
 
23
+ StrOrInt = Union[str, int]
23
24
  TupleStr = tuple[str, ...]
24
25
  DictData = dict[str, Any]
25
26
  DictStr = dict[str, str]
@@ -200,7 +200,10 @@ class APIConfig:
200
200
  return str2bool(env("API_ENABLE_ROUTE_SCHEDULE", "true"))
201
201
 
202
202
 
203
- class BaseLoad(ABC):
203
+ class BaseLoad(ABC): # pragma: no cov
204
+ """Base Load object is the abstraction object for any Load object that
205
+ should to inherit from this base class.
206
+ """
204
207
 
205
208
  @classmethod
206
209
  @abstractmethod
@@ -215,7 +218,7 @@ class BaseLoad(ABC):
215
218
 
216
219
  class FileLoad(BaseLoad):
217
220
  """Base Load object that use to search config data by given some identity
218
- value like name of `Workflow` or `On` templates.
221
+ value like name of `Workflow` or `Crontab` templates.
219
222
 
220
223
  :param name: (str) A name of key of config data that read with YAML
221
224
  Environment object.
@@ -335,8 +338,13 @@ class FileLoad(BaseLoad):
335
338
  """
336
339
  excluded: list[str] = excluded or []
337
340
  path: Path = dynamic("conf_path", f=path, extras=extras)
341
+ paths: Optional[list[Path]] = paths or (extras or {}).get("conf_paths")
338
342
  if not paths:
339
343
  paths: list[Path] = [path]
344
+ elif not isinstance(paths, list):
345
+ raise TypeError(
346
+ f"Multi-config paths does not support for type: {type(paths)}"
347
+ )
340
348
  else:
341
349
  paths.append(path)
342
350
 
@@ -431,17 +439,21 @@ def dynamic(
431
439
  """Dynamic get config if extra value was passed at run-time.
432
440
 
433
441
  :param key: (str) A config key that get from Config object.
434
- :param f: An inner config function scope.
442
+ :param f: (T) An inner config function scope.
435
443
  :param extras: An extra values that pass at run-time.
444
+
445
+ :rtype: T
436
446
  """
437
- rsx: Optional[T] = extras[key] if extras and key in extras else None
438
- rs: Optional[T] = getattr(config, key, None) if f is None else f
439
- if rsx is not None and not isinstance(rsx, type(rs)):
447
+ extra: Optional[T] = (extras or {}).get(key, None)
448
+ conf: Optional[T] = getattr(config, key, None) if f is None else f
449
+ if extra is None:
450
+ return conf
451
+ if not isinstance(extra, type(conf)):
440
452
  raise TypeError(
441
- f"Type of config {key!r} from extras: {rsx!r} does not valid "
442
- f"as config {type(rs)}."
453
+ f"Type of config {key!r} from extras: {extra!r} does not valid "
454
+ f"as config {type(conf)}."
443
455
  )
444
- return rsx if rsx is not None else rs
456
+ return extra
445
457
 
446
458
 
447
459
  class Loader(Protocol): # pragma: no cov
@@ -3,8 +3,8 @@
3
3
  # Licensed under the MIT License. See LICENSE in the project root for
4
4
  # license information.
5
5
  # ------------------------------------------------------------------------------
6
- """Event module that store all event object. Now, it has only `On` and `OnYear`
7
- model these are schedule with crontab event.
6
+ """Event module that store all event object. Now, it has only `Crontab` and
7
+ `CrontabYear` model these are schedule with crontab event.
8
8
  """
9
9
  from __future__ import annotations
10
10
 
@@ -63,9 +63,9 @@ def interval2crontab(
63
63
  return f"{h} {m} {'1' if interval == 'monthly' else '*'} * {d}"
64
64
 
65
65
 
66
- class On(BaseModel):
67
- """On model (Warped crontab object by Pydantic model) to keep crontab value
68
- and generate CronRunner object from this crontab value.
66
+ class Crontab(BaseModel):
67
+ """Cron event model (Warped the CronJob object by Pydantic model) to keep
68
+ crontab value and generate CronRunner object from this crontab value.
69
69
 
70
70
  Methods:
71
71
  - generate: is the main use-case of this schedule object.
@@ -117,6 +117,7 @@ class On(BaseModel):
117
117
  passing["cronjob"] = interval2crontab(
118
118
  **{v: value[v] for v in value if v in ("interval", "day", "time")}
119
119
  )
120
+ print(passing)
120
121
  return cls(extras=extras | passing.pop("extras", {}), **passing)
121
122
 
122
123
  @classmethod
@@ -127,7 +128,7 @@ class On(BaseModel):
127
128
  extras: DictData | None = None,
128
129
  ) -> Self:
129
130
  """Constructor from the name of config loader that will use loader
130
- object for getting the `On` data.
131
+ object for getting the `Crontab` data.
131
132
 
132
133
  :param name: (str) A name of config that will get from loader.
133
134
  :param extras: (DictData) An extra parameter that use to override core
@@ -171,7 +172,7 @@ class On(BaseModel):
171
172
  def __prepare_values(cls, data: Any) -> Any:
172
173
  """Extract tz key from value and change name to timezone key.
173
174
 
174
- :param data: (DictData) A data that want to pass for create an On
175
+ :param data: (DictData) A data that want to pass for create an Crontab
175
176
  model.
176
177
 
177
178
  :rtype: DictData
@@ -264,9 +265,9 @@ class On(BaseModel):
264
265
  return runner
265
266
 
266
267
 
267
- class YearOn(On):
268
- """On with enhance Year Pydantic model for limit year matrix that use by
269
- some data schedule tools like AWS Glue.
268
+ class CrontabYear(Crontab):
269
+ """Cron event with enhance Year Pydantic model for limit year matrix that
270
+ use by some data schedule tools like AWS Glue.
270
271
  """
271
272
 
272
273
  model_config = ConfigDict(arbitrary_types_allowed=True)
@@ -9,16 +9,16 @@ annotate for handle error only.
9
9
  """
10
10
  from __future__ import annotations
11
11
 
12
- from typing import TypedDict
12
+ from typing import Literal, TypedDict, overload
13
13
 
14
- ErrorData = TypedDict(
15
- "ErrorData",
16
- {
17
- "class": Exception,
18
- "name": str,
19
- "message": str,
20
- },
21
- )
14
+
15
+ class ErrorData(TypedDict):
16
+ """Error data type dict for typing necessary keys of return of to_dict func
17
+ and method.
18
+ """
19
+
20
+ name: str
21
+ message: str
22
22
 
23
23
 
24
24
  def to_dict(exception: Exception) -> ErrorData: # pragma: no cov
@@ -29,20 +29,41 @@ def to_dict(exception: Exception) -> ErrorData: # pragma: no cov
29
29
  :rtype: ErrorData
30
30
  """
31
31
  return {
32
- "class": exception,
33
32
  "name": exception.__class__.__name__,
34
33
  "message": str(exception),
35
34
  }
36
35
 
37
36
 
38
37
  class BaseWorkflowException(Exception):
38
+ """Base Workflow exception class will implement the `refs` argument for
39
+ making an error context to the result context.
40
+ """
41
+
42
+ def __init__(self, message: str, *, refs: str | None = None):
43
+ super().__init__(message)
44
+ self.refs: str | None = refs
45
+
46
+ @overload
47
+ def to_dict(
48
+ self, with_refs: Literal[True] = ...
49
+ ) -> dict[str, ErrorData]: ... # pragma: no cov
50
+
51
+ @overload
52
+ def to_dict(
53
+ self, with_refs: Literal[False] = ...
54
+ ) -> ErrorData: ... # pragma: no cov
39
55
 
40
- def to_dict(self) -> ErrorData:
56
+ def to_dict(
57
+ self, with_refs: bool = False
58
+ ) -> ErrorData | dict[str, ErrorData]:
41
59
  """Return ErrorData data from the current exception object.
42
60
 
43
61
  :rtype: ErrorData
44
62
  """
45
- return to_dict(self)
63
+ data: ErrorData = to_dict(self)
64
+ if with_refs and (self.refs is not None and self.refs != "EMPTY"):
65
+ return {self.refs: data}
66
+ return data
46
67
 
47
68
 
48
69
  class UtilException(BaseWorkflowException): ...