ddeutil-workflow 0.0.17__py3-none-any.whl → 0.0.18__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.17"
1
+ __version__: str = "0.0.18"
@@ -3,7 +3,11 @@
3
3
  # Licensed under the MIT License. See LICENSE in the project root for
4
4
  # license information.
5
5
  # ------------------------------------------------------------------------------
6
- from .conf import Config, FileLog, Loader
6
+ from .conf import (
7
+ Config,
8
+ FileLog,
9
+ Loader,
10
+ )
7
11
  from .exceptions import (
8
12
  JobException,
9
13
  ParamValueException,
@@ -12,14 +16,57 @@ from .exceptions import (
12
16
  WorkflowException,
13
17
  )
14
18
  from .job import Job, Strategy
15
- from .on import On, interval2crontab
19
+ from .on import (
20
+ On,
21
+ YearOn,
22
+ interval2crontab,
23
+ )
16
24
  from .scheduler import (
17
25
  Schedule,
26
+ ScheduleWorkflow,
18
27
  Workflow,
28
+ WorkflowTaskData,
29
+ )
30
+ from .stage import (
31
+ BashStage,
32
+ EmptyStage,
33
+ HookStage,
34
+ PyStage,
35
+ Stage,
36
+ TriggerStage,
37
+ handler_result,
19
38
  )
20
- from .stage import Stage, handler_result
21
39
  from .utils import (
40
+ FILTERS,
41
+ ChoiceParam,
42
+ DatetimeParam,
43
+ DefaultParam,
44
+ FilterFunc,
45
+ FilterRegistry,
46
+ IntParam,
22
47
  Param,
48
+ Result,
49
+ ReturnTagFunc,
50
+ StrParam,
51
+ TagFunc,
52
+ batch,
53
+ cross_product,
54
+ custom_filter,
23
55
  dash2underscore,
56
+ delay,
57
+ filter_func,
58
+ gen_id,
59
+ get_args_const,
60
+ get_diff_sec,
61
+ get_dt_now,
62
+ has_template,
63
+ make_exec,
64
+ make_filter_registry,
65
+ make_registry,
66
+ map_post_filter,
67
+ not_in_template,
24
68
  param2template,
69
+ queue2str,
70
+ str2template,
71
+ tag,
25
72
  )
ddeutil/workflow/conf.py CHANGED
@@ -41,8 +41,8 @@ class Config:
41
41
  # NOTE: Core
42
42
  root_path: Path = Path(os.getenv("WORKFLOW_ROOT_PATH", "."))
43
43
  tz: ZoneInfo = ZoneInfo(env("WORKFLOW_CORE_TIMEZONE", "UTC"))
44
- workflow_id_simple_mode: bool = str2bool(
45
- os.getenv("WORKFLOW_CORE_WORKFLOW_ID_SIMPLE_MODE", "true")
44
+ gen_id_simple_mode: bool = str2bool(
45
+ os.getenv("WORKFLOW_CORE_GENERATE_ID_SIMPLE_MODE", "true")
46
46
  )
47
47
 
48
48
  # NOTE: Register
ddeutil/workflow/job.py CHANGED
@@ -553,8 +553,6 @@ class Job(BaseModel):
553
553
  # NOTE: Create event for cancel executor by trigger stop running event.
554
554
  event: Event = Event()
555
555
 
556
- print("Job Run Fail-Fast:", self.strategy.fail_fast)
557
-
558
556
  # IMPORTANT: Start running strategy execution by multithreading because
559
557
  # it will running by strategy values without waiting previous
560
558
  # execution.
@@ -12,8 +12,8 @@ from functools import wraps
12
12
 
13
13
  from starlette.concurrency import run_in_threadpool
14
14
 
15
+ from .__cron import CronJob
15
16
  from .conf import config, get_logger
16
- from .cron import CronJob
17
17
 
18
18
  logger = get_logger("ddeutil.workflow")
19
19
 
@@ -376,7 +376,7 @@ class Workflow(BaseModel):
376
376
  status=0,
377
377
  context={
378
378
  "params": params,
379
- "poking": {"skipped": [str(on.cronjob)], "run": []},
379
+ "release": {"status": "skipped", "cron": [str(on.cronjob)]},
380
380
  },
381
381
  )
382
382
 
@@ -388,7 +388,7 @@ class Workflow(BaseModel):
388
388
  # NOTE: Release when the time is nearly to schedule time.
389
389
  while (duration := get_diff_sec(next_time, tz=cron_tz)) > (
390
390
  sleep_interval + 5
391
- ):
391
+ ): # pragma: no cov
392
392
  logger.debug(
393
393
  f"({self.run_id}) [CORE]: {self.name!r} : {on.cronjob} : "
394
394
  f"Sleep until: {duration}"
@@ -439,7 +439,7 @@ class Workflow(BaseModel):
439
439
  status=0,
440
440
  context={
441
441
  "params": params,
442
- "poking": {"skipped": [], "run": [str(on.cronjob)]},
442
+ "release": {"status": "run", "cron": [str(on.cronjob)]},
443
443
  },
444
444
  )
445
445
 
@@ -492,7 +492,7 @@ class Workflow(BaseModel):
492
492
  for future in as_completed(futures):
493
493
  results.append(future.result(timeout=60))
494
494
 
495
- if len(queue) > 0:
495
+ if len(queue) > 0: # pragma: no cov
496
496
  logger.error(
497
497
  f"({self.run_id}) [POKING]: Log Queue does empty when poking "
498
498
  f"process was finishing."
@@ -717,11 +717,11 @@ class Workflow(BaseModel):
717
717
  return context
718
718
 
719
719
  # NOTE: Raise timeout error.
720
- logger.warning(
720
+ logger.warning( # pragma: no cov
721
721
  f"({self.run_id}) [WORKFLOW]: Execution of workflow, {self.name!r} "
722
722
  f", was timeout"
723
723
  )
724
- raise WorkflowException(
724
+ raise WorkflowException( # pragma: no cov
725
725
  f"Execution of workflow: {self.name} was timeout"
726
726
  )
727
727
 
@@ -765,7 +765,8 @@ class Workflow(BaseModel):
765
765
  continue
766
766
 
767
767
  # NOTE: Start workflow job execution with deep copy context data
768
- # before release.
768
+ # before release. This job execution process will running until
769
+ # done before checking all execution timeout or not.
769
770
  #
770
771
  # {
771
772
  # 'params': <input-params>,
@@ -783,10 +784,10 @@ class Workflow(BaseModel):
783
784
  return context
784
785
 
785
786
  # NOTE: Raise timeout error.
786
- logger.warning(
787
+ logger.warning( # pragma: no cov
787
788
  f"({self.run_id}) [WORKFLOW]: Execution of workflow was timeout"
788
789
  )
789
- raise WorkflowException(
790
+ raise WorkflowException( # pragma: no cov
790
791
  f"Execution of workflow: {self.name} was timeout"
791
792
  )
792
793
 
@@ -832,12 +833,13 @@ class ScheduleWorkflow(BaseModel):
832
833
  if on := data.pop("on", []):
833
834
 
834
835
  if isinstance(on, str):
835
- on = [on]
836
+ on: list[str] = [on]
836
837
 
837
838
  if any(not isinstance(n, (dict, str)) for n in on):
838
839
  raise TypeError("The ``on`` key should be list of str or dict")
839
840
 
840
- # NOTE: Pass on value to Loader and keep on model object to on field
841
+ # NOTE: Pass on value to Loader and keep on model object to on
842
+ # field.
841
843
  data["on"] = [
842
844
  (
843
845
  Loader(n, externals=(externals or {})).data
@@ -902,12 +904,14 @@ class Schedule(BaseModel):
902
904
  *,
903
905
  externals: DictData | None = None,
904
906
  ) -> list[WorkflowTaskData]:
905
- """Generate Task from the current datetime.
907
+ """Return the list of WorkflowTaskData object from the specific input
908
+ datetime that mapping with the on field.
906
909
 
907
910
  :param start_date: A start date that get from the workflow schedule.
908
911
  :param queue: A mapping of name and list of datetime for queue.
909
912
  :param running: A mapping of name and list of datetime for running.
910
913
  :param externals: An external parameters that pass to the Loader object.
914
+
911
915
  :rtype: list[WorkflowTaskData]
912
916
  """
913
917
 
@@ -922,12 +926,14 @@ class Schedule(BaseModel):
922
926
  queue[wfs.name]: list[datetime] = []
923
927
  running[wfs.name]: list[datetime] = []
924
928
 
925
- # NOTE: Create default on if it does not passing on the Schedule.
929
+ # NOTE: Create the default on value if it does not passing on the
930
+ # Schedule object.
926
931
  _ons: list[On] = wf.on.copy() if len(wfs.on) == 0 else wfs.on
927
932
 
928
933
  for on in _ons:
929
- on_gen = on.generate(start_date)
934
+ on_gen: CronRunner = on.generate(start_date)
930
935
  next_running_date = on_gen.next
936
+
931
937
  while next_running_date in queue[wfs.name]:
932
938
  next_running_date = on_gen.next
933
939
 
@@ -957,13 +963,14 @@ def catch_exceptions(cancel_on_failure: bool = False) -> DecoratorCancelJob:
957
963
 
958
964
  :param cancel_on_failure: A flag that allow to return the CancelJob or not
959
965
  it will raise.
960
- :rtype: Callable[P, Optional[CancelJob]]
966
+
967
+ :rtype: DecoratorCancelJob
961
968
  """
962
969
 
963
970
  def decorator(func: ReturnCancelJob) -> ReturnCancelJob:
964
971
  try:
965
972
  # NOTE: Check the function that want to handle is method or not.
966
- if inspect.ismethod(func):
973
+ if inspect.ismethod(func): # pragma: no cov
967
974
 
968
975
  @wraps(func)
969
976
  def wrapper(self, *args, **kwargs):
@@ -977,7 +984,7 @@ def catch_exceptions(cancel_on_failure: bool = False) -> DecoratorCancelJob:
977
984
 
978
985
  return wrapper
979
986
 
980
- except Exception as err:
987
+ except Exception as err: # pragma: no cov
981
988
  logger.exception(err)
982
989
  if cancel_on_failure:
983
990
  return CancelJob
@@ -1005,7 +1012,7 @@ class WorkflowTaskData:
1005
1012
  *,
1006
1013
  waiting_sec: int = 60,
1007
1014
  sleep_interval: int = 15,
1008
- ) -> None:
1015
+ ) -> None: # pragma: no cov
1009
1016
  """Workflow release, it will use with the same logic of
1010
1017
  `workflow.release` method.
1011
1018
 
@@ -1119,7 +1126,7 @@ class WorkflowTaskData:
1119
1126
  future_running_time in self.running[wf.name]
1120
1127
  or future_running_time in self.queue[wf.name]
1121
1128
  or future_running_time < finish_time
1122
- ):
1129
+ ): # pragma: no cov
1123
1130
  future_running_time: datetime = gen.next
1124
1131
 
1125
1132
  heappush(self.queue[wf.name], future_running_time)
@@ -1134,7 +1141,7 @@ class WorkflowTaskData:
1134
1141
  return NotImplemented
1135
1142
 
1136
1143
 
1137
- @catch_exceptions(cancel_on_failure=True)
1144
+ @catch_exceptions(cancel_on_failure=True) # pragma: no cov
1138
1145
  def workflow_task(
1139
1146
  workflow_tasks: list[WorkflowTaskData],
1140
1147
  stop: datetime,
@@ -1233,7 +1240,7 @@ def workflow_task(
1233
1240
  logger.debug(f"[WORKFLOW]: {'=' * 100}")
1234
1241
 
1235
1242
 
1236
- def workflow_monitor(threads: dict[str, Thread]) -> None:
1243
+ def workflow_monitor(threads: dict[str, Thread]) -> None: # pragma: no cov
1237
1244
  """Workflow schedule for monitoring long running thread from the schedule
1238
1245
  control.
1239
1246
 
@@ -1255,7 +1262,7 @@ def workflow_control(
1255
1262
  schedules: list[str],
1256
1263
  stop: datetime | None = None,
1257
1264
  externals: DictData | None = None,
1258
- ) -> list[str]:
1265
+ ) -> list[str]: # pragma: no cov
1259
1266
  """Workflow scheduler control.
1260
1267
 
1261
1268
  :param schedules: A list of workflow names that want to schedule running.
@@ -1343,7 +1350,7 @@ def workflow_runner(
1343
1350
  stop: datetime | None = None,
1344
1351
  externals: DictData | None = None,
1345
1352
  excluded: list[str] | None = None,
1346
- ) -> list[str]:
1353
+ ) -> list[str]: # pragma: no cov
1347
1354
  """Workflow application that running multiprocessing schedule with chunk of
1348
1355
  workflows that exists in config path.
1349
1356
 
ddeutil/workflow/stage.py CHANGED
@@ -127,7 +127,6 @@ def handler_result(message: str | None = None) -> DecoratorResult:
127
127
  logger.error(
128
128
  f"({self.run_id}) [STAGE]: {err.__class__.__name__}: {err}"
129
129
  )
130
- print("Stage Raise error:", config.stage_raise_error)
131
130
  if config.stage_raise_error:
132
131
  # NOTE: If error that raise from stage execution course by
133
132
  # itself, it will return that error with previous
ddeutil/workflow/utils.py CHANGED
@@ -100,7 +100,7 @@ def gen_id(
100
100
  if not isinstance(value, str):
101
101
  value: str = str(value)
102
102
 
103
- if config.workflow_id_simple_mode:
103
+ if config.gen_id_simple_mode:
104
104
  return hash_str(f"{(value if sensitive else value.lower())}", n=10) + (
105
105
  f"{datetime.now(tz=config.tz):%Y%m%d%H%M%S%f}" if unique else ""
106
106
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.17
3
+ Version: 0.0.18
4
4
  Summary: Lightweight workflow orchestration with less dependencies
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -24,6 +24,7 @@ Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
25
  Requires-Dist: ddeutil >=0.4.3
26
26
  Requires-Dist: ddeutil-io[toml,yaml] >=0.2.3
27
+ Requires-Dist: pydantic ==2.9.2
27
28
  Requires-Dist: python-dotenv ==1.0.1
28
29
  Requires-Dist: typer <1.0.0,==0.12.5
29
30
  Requires-Dist: schedule <2.0.0,==1.2.2
@@ -194,7 +195,7 @@ and do not raise any error to you.
194
195
  | `WORKFLOW_CORE_JOB_RAISE_ERROR` | Core | true | A flag that all job raise JobException from job strategy execution. | |
195
196
  | `WORKFLOW_CORE_MAX_NUM_POKING` | Core | 4 | . | |
196
197
  | `WORKFLOW_CORE_MAX_JOB_PARALLEL` | Core | 2 | The maximum job number that able to run parallel in workflow executor. | |
197
- | `WORKFLOW_CORE_WORKFLOW_ID_SIMPLE_MODE` | Core | true | . | |
198
+ | `WORKFLOW_CORE_GENERATE_ID_SIMPLE_MODE` | Core | true | A flog that enable generating ID with `md5` algorithm. | |
198
199
  | `WORKFLOW_LOG_DEBUG_MODE` | Log | true | A flag that enable logging with debug level mode. | |
199
200
  | `WORKFLOW_LOG_ENABLE_WRITE` | Log | true | A flag that enable logging object saving log to its destination. | |
200
201
  | `WORKFLOW_APP_MAX_PROCESS` | Schedule | 2 | The maximum process worker number that run in scheduler app module. | |
@@ -0,0 +1,21 @@
1
+ ddeutil/workflow/__about__.py,sha256=b5h9QJ6GhQ-EDPZTcMYoeJZy8blWgeG9xjpFBHrVLPg,28
2
+ ddeutil/workflow/__cron.py,sha256=ZiuV4ASkXvAyFJYxEb9PKiAFNYnUt4AJozu_kH3pI4U,25777
3
+ ddeutil/workflow/__init__.py,sha256=HA0tjGBXJItNPsAqvhnFUXU0fP0K6iMMfMtJ37tRwcw,1385
4
+ ddeutil/workflow/__types.py,sha256=yizLXzjQpBt_WPaof2pIyncitJvYeksw4Q1zYJeuCLA,3707
5
+ ddeutil/workflow/api.py,sha256=vUT2RVS9sF3hvY-IrzAEnahxwq4ZFYP0G3xfctHbNsw,4701
6
+ ddeutil/workflow/cli.py,sha256=baHhvtI8snbHYHeThoX401Cd6SMB2boyyCbCtTrIl3E,3278
7
+ ddeutil/workflow/conf.py,sha256=4j7m2blvCPlz_me4SBHf_exViUK3ZLLBCwldPztHJKo,15390
8
+ ddeutil/workflow/exceptions.py,sha256=Uf1-Tn8rAzj0aiVHSqo4fBqO80W0za7UFZgKv24E-tg,706
9
+ ddeutil/workflow/job.py,sha256=kSllDDiSnDpyFnIT9-Sum6OHQ16Pn5h2t5_-XljHbgk,23979
10
+ ddeutil/workflow/on.py,sha256=rneZB5HyFWTBWriGef999bovA3glQIK6LTgC996q9Gc,7334
11
+ ddeutil/workflow/repeat.py,sha256=s0azh-f5JQeow7kpxM8GKlqgAmKL7oU6St3L4Ggx4cY,4925
12
+ ddeutil/workflow/route.py,sha256=JALwOH6xKu5rnII7DgA1Lbp_E5ehCoBbOW_eKqB_Olk,6753
13
+ ddeutil/workflow/scheduler.py,sha256=baCYbv5f8HiQgV36fUvkkUpSiIRhrznuwKefsgKjHv4,47546
14
+ ddeutil/workflow/stage.py,sha256=6Ng3RiCSrnQ-FUsRRcuG2ClMD6ifiQlgyBFi6tohfxI,25455
15
+ ddeutil/workflow/utils.py,sha256=ouuQ3mqjKVzuchCcvVelo8Hh8c6UJ4_lHPqejcxNDRA,25147
16
+ ddeutil_workflow-0.0.18.dist-info/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
17
+ ddeutil_workflow-0.0.18.dist-info/METADATA,sha256=VLZchB_AWG5kMf7RYFTyKI3zkxWdj7k942f_XZpBxyQ,13606
18
+ ddeutil_workflow-0.0.18.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
19
+ ddeutil_workflow-0.0.18.dist-info/entry_points.txt,sha256=0BVOgO3LdUdXVZ-CiHHDKxzEk2c8J30jEwHeKn2YCWI,62
20
+ ddeutil_workflow-0.0.18.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
21
+ ddeutil_workflow-0.0.18.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- ddeutil/workflow/__about__.py,sha256=z3f1GAF3VbZK1m4FWAXXMsWplP_jSe-X-wVlshvlDWU,28
2
- ddeutil/workflow/__cron.py,sha256=ZiuV4ASkXvAyFJYxEb9PKiAFNYnUt4AJozu_kH3pI4U,25777
3
- ddeutil/workflow/__init__.py,sha256=RNKME4FPMAjqtrBR-IBwQVEKeoY5yBAiHYcZw0k9cI4,729
4
- ddeutil/workflow/__types.py,sha256=yizLXzjQpBt_WPaof2pIyncitJvYeksw4Q1zYJeuCLA,3707
5
- ddeutil/workflow/api.py,sha256=vUT2RVS9sF3hvY-IrzAEnahxwq4ZFYP0G3xfctHbNsw,4701
6
- ddeutil/workflow/cli.py,sha256=baHhvtI8snbHYHeThoX401Cd6SMB2boyyCbCtTrIl3E,3278
7
- ddeutil/workflow/conf.py,sha256=SV4GMtjUc-Bor9BPi0yOtTIsiZ0FImsoRbuJysUIE9w,15395
8
- ddeutil/workflow/exceptions.py,sha256=Uf1-Tn8rAzj0aiVHSqo4fBqO80W0za7UFZgKv24E-tg,706
9
- ddeutil/workflow/job.py,sha256=dW9NXR_bttDGLwelVi7qXXlLd96KX-TKG8xnHejA6u0,24041
10
- ddeutil/workflow/on.py,sha256=rneZB5HyFWTBWriGef999bovA3glQIK6LTgC996q9Gc,7334
11
- ddeutil/workflow/repeat.py,sha256=9uKku5uMcQgzY5fWyaJMwJ0wPFX0oTwmu7vXKdgB_ec,4923
12
- ddeutil/workflow/route.py,sha256=JALwOH6xKu5rnII7DgA1Lbp_E5ehCoBbOW_eKqB_Olk,6753
13
- ddeutil/workflow/scheduler.py,sha256=Oa6bZpphjlGp0mXdBuLMk1m6G-dezaBNQxQX-SB3WJ0,47032
14
- ddeutil/workflow/stage.py,sha256=fMv_oFkoqpfoewzPUMdl3-BQcrJ8SE53cF7es8yGxfs,25525
15
- ddeutil/workflow/utils.py,sha256=lpnqGGd_Rw7eZo2wDbZ-NZNItBooFooPjwM4_40Csh8,25152
16
- ddeutil_workflow-0.0.17.dist-info/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
17
- ddeutil_workflow-0.0.17.dist-info/METADATA,sha256=btmCr-yjy4gzhnZppfXjANfPH-3tKUJFGon2aOMUK30,13574
18
- ddeutil_workflow-0.0.17.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
19
- ddeutil_workflow-0.0.17.dist-info/entry_points.txt,sha256=0BVOgO3LdUdXVZ-CiHHDKxzEk2c8J30jEwHeKn2YCWI,62
20
- ddeutil_workflow-0.0.17.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
21
- ddeutil_workflow-0.0.17.dist-info/RECORD,,