ddeutil-workflow 0.0.77__py3-none-any.whl → 0.0.79__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.
@@ -77,7 +77,15 @@ from pathlib import Path
77
77
  from subprocess import CompletedProcess
78
78
  from textwrap import dedent
79
79
  from threading import Event
80
- from typing import Annotated, Any, Optional, TypeVar, Union, get_type_hints
80
+ from typing import (
81
+ Annotated,
82
+ Any,
83
+ Callable,
84
+ Optional,
85
+ TypeVar,
86
+ Union,
87
+ get_type_hints,
88
+ )
81
89
 
82
90
  from ddeutil.core import str2list
83
91
  from pydantic import BaseModel, Field, ValidationError
@@ -106,7 +114,7 @@ from .reusables import (
106
114
  not_in_template,
107
115
  param2template,
108
116
  )
109
- from .traces import Trace, get_trace
117
+ from .traces import TraceManager, get_trace
110
118
  from .utils import (
111
119
  delay,
112
120
  dump_all,
@@ -177,7 +185,7 @@ class BaseStage(BaseModel, ABC):
177
185
  "A stage description that use to logging when start execution."
178
186
  ),
179
187
  )
180
- condition: StrOrNone = Field(
188
+ condition: Optional[Union[str, bool]] = Field(
181
189
  default=None,
182
190
  description=(
183
191
  "A stage condition statement to allow stage executable. This field "
@@ -296,7 +304,7 @@ class BaseStage(BaseModel, ABC):
296
304
  parent_run_id: str = run_id
297
305
  run_id: str = run_id or gen_id(self.iden, unique=True)
298
306
  context: DictData = {"status": WAIT}
299
- trace: Trace = get_trace(
307
+ trace: TraceManager = get_trace(
300
308
  run_id, parent_run_id=parent_run_id, extras=self.extras
301
309
  )
302
310
  try:
@@ -513,6 +521,9 @@ class BaseStage(BaseModel, ABC):
513
521
  if not self.condition:
514
522
  return False
515
523
 
524
+ if isinstance(self.condition, bool):
525
+ return self.condition
526
+
516
527
  try:
517
528
  # WARNING: The eval build-in function is very dangerous. So, it
518
529
  # should use the `re` module to validate eval-string before
@@ -623,7 +634,7 @@ class BaseAsyncStage(BaseStage, ABC):
623
634
  parent_run_id: StrOrNone = run_id
624
635
  run_id: str = run_id or gen_id(self.iden, unique=True)
625
636
  context: DictData = {}
626
- trace: Trace = get_trace(
637
+ trace: TraceManager = get_trace(
627
638
  run_id, parent_run_id=parent_run_id, extras=self.extras
628
639
  )
629
640
  try:
@@ -766,7 +777,7 @@ class BaseRetryStage(BaseAsyncStage, ABC): # pragma: no cov
766
777
  current_retry: int = 0
767
778
  exception: Exception
768
779
  catch(context, status=WAIT)
769
- trace: Trace = get_trace(
780
+ trace: TraceManager = get_trace(
770
781
  run_id, parent_run_id=parent_run_id, extras=self.extras
771
782
  )
772
783
 
@@ -839,7 +850,7 @@ class BaseRetryStage(BaseAsyncStage, ABC): # pragma: no cov
839
850
  current_retry: int = 0
840
851
  exception: Exception
841
852
  catch(context, status=WAIT)
842
- trace: Trace = get_trace(
853
+ trace: TraceManager = get_trace(
843
854
  run_id, parent_run_id=parent_run_id, extras=self.extras
844
855
  )
845
856
 
@@ -971,7 +982,7 @@ class EmptyStage(BaseAsyncStage):
971
982
  Returns:
972
983
  Result: The execution result with status and context data.
973
984
  """
974
- trace: Trace = get_trace(
985
+ trace: TraceManager = get_trace(
975
986
  run_id, parent_run_id=parent_run_id, extras=self.extras
976
987
  )
977
988
  message: str = (
@@ -1024,7 +1035,7 @@ class EmptyStage(BaseAsyncStage):
1024
1035
  Returns:
1025
1036
  Result: The execution result with status and context data.
1026
1037
  """
1027
- trace: Trace = get_trace(
1038
+ trace: TraceManager = get_trace(
1028
1039
  run_id, parent_run_id=parent_run_id, extras=self.extras
1029
1040
  )
1030
1041
  message: str = (
@@ -1189,7 +1200,7 @@ class BashStage(BaseRetryStage):
1189
1200
  Returns:
1190
1201
  Result: The execution result with status and context data.
1191
1202
  """
1192
- trace: Trace = get_trace(
1203
+ trace: TraceManager = get_trace(
1193
1204
  run_id, parent_run_id=parent_run_id, extras=self.extras
1194
1205
  )
1195
1206
  bash: str = param2template(
@@ -1253,7 +1264,7 @@ class BashStage(BaseRetryStage):
1253
1264
  Returns:
1254
1265
  Result: The execution result with status and context data.
1255
1266
  """
1256
- trace: Trace = get_trace(
1267
+ trace: TraceManager = get_trace(
1257
1268
  run_id, parent_run_id=parent_run_id, extras=self.extras
1258
1269
  )
1259
1270
  bash: str = param2template(
@@ -1394,7 +1405,7 @@ class PyStage(BaseRetryStage):
1394
1405
  Returns:
1395
1406
  Result: The execution result with status and context data.
1396
1407
  """
1397
- trace: Trace = get_trace(
1408
+ trace: TraceManager = get_trace(
1398
1409
  run_id, parent_run_id=parent_run_id, extras=self.extras
1399
1410
  )
1400
1411
  trace.info("[STAGE]: Prepare `globals` and `locals` variables.")
@@ -1475,7 +1486,7 @@ class PyStage(BaseRetryStage):
1475
1486
  Returns:
1476
1487
  Result: The execution result with status and context data.
1477
1488
  """
1478
- trace: Trace = get_trace(
1489
+ trace: TraceManager = get_trace(
1479
1490
  run_id, parent_run_id=parent_run_id, extras=self.extras
1480
1491
  )
1481
1492
  await trace.ainfo("[STAGE]: Prepare `globals` and `locals` variables.")
@@ -1580,15 +1591,23 @@ class CallStage(BaseRetryStage):
1580
1591
 
1581
1592
  :rtype: Any
1582
1593
  """
1583
- if isinstance(value, dict):
1584
- if any(k in value for k in ("result", "extras")):
1585
- raise ValueError(
1586
- "The argument on workflow template for the caller stage "
1587
- "should not pass `result` and `extras`. They are special "
1588
- "arguments."
1589
- )
1594
+ if isinstance(value, dict) and any(
1595
+ k in value for k in ("result", "extras")
1596
+ ):
1597
+ raise ValueError(
1598
+ "The argument on workflow template for the caller stage "
1599
+ "should not pass `result` and `extras`. They are special "
1600
+ "arguments."
1601
+ )
1590
1602
  return value
1591
1603
 
1604
+ def get_caller(self, params: DictData) -> Callable[[], TagFunc]:
1605
+ """Get the lazy TagFuc object from registry."""
1606
+ return extract_call(
1607
+ param2template(self.uses, params, extras=self.extras),
1608
+ registries=self.extras.get("registry_caller"),
1609
+ )
1610
+
1592
1611
  def process(
1593
1612
  self,
1594
1613
  params: DictData,
@@ -1612,14 +1631,10 @@ class CallStage(BaseRetryStage):
1612
1631
  Returns:
1613
1632
  Result: The execution result with status and context data.
1614
1633
  """
1615
- trace: Trace = get_trace(
1634
+ trace: TraceManager = get_trace(
1616
1635
  run_id, parent_run_id=parent_run_id, extras=self.extras
1617
1636
  )
1618
- call_func: TagFunc = extract_call(
1619
- param2template(self.uses, params, extras=self.extras),
1620
- registries=self.extras.get("registry_caller"),
1621
- )()
1622
-
1637
+ call_func: TagFunc = self.get_caller(params=params)()
1623
1638
  trace.info(f"[STAGE]: Caller Func: '{call_func.name}@{call_func.tag}'")
1624
1639
 
1625
1640
  # VALIDATE: check input task caller parameters that exists before
@@ -1677,7 +1692,7 @@ class CallStage(BaseRetryStage):
1677
1692
  )
1678
1693
 
1679
1694
  args: DictData = self.validate_model_args(
1680
- call_func, args, run_id, parent_run_id
1695
+ call_func, args, run_id, parent_run_id, extras=self.extras
1681
1696
  )
1682
1697
  if inspect.iscoroutinefunction(call_func):
1683
1698
  loop = asyncio.get_event_loop()
@@ -1735,14 +1750,10 @@ class CallStage(BaseRetryStage):
1735
1750
  Returns:
1736
1751
  Result: The execution result with status and context data.
1737
1752
  """
1738
- trace: Trace = get_trace(
1753
+ trace: TraceManager = get_trace(
1739
1754
  run_id, parent_run_id=parent_run_id, extras=self.extras
1740
1755
  )
1741
- call_func: TagFunc = extract_call(
1742
- param2template(self.uses, params, extras=self.extras),
1743
- registries=self.extras.get("registry_caller"),
1744
- )()
1745
-
1756
+ call_func: TagFunc = self.get_caller(params=params)()
1746
1757
  await trace.ainfo(
1747
1758
  f"[STAGE]: Caller Func: '{call_func.name}@{call_func.tag}'"
1748
1759
  )
@@ -1795,8 +1806,13 @@ class CallStage(BaseRetryStage):
1795
1806
  if "extras" not in sig.parameters and not has_keyword:
1796
1807
  args.pop("extras")
1797
1808
 
1809
+ if event and event.is_set():
1810
+ raise StageCancelError(
1811
+ "Execution was canceled from the event before start parallel."
1812
+ )
1813
+
1798
1814
  args: DictData = self.validate_model_args(
1799
- call_func, args, run_id, parent_run_id
1815
+ call_func, args, run_id, parent_run_id, extras=self.extras
1800
1816
  )
1801
1817
  if inspect.iscoroutinefunction(call_func):
1802
1818
  rs: DictOrModel = await call_func(
@@ -1829,12 +1845,13 @@ class CallStage(BaseRetryStage):
1829
1845
  extras=self.extras,
1830
1846
  )
1831
1847
 
1848
+ @staticmethod
1832
1849
  def validate_model_args(
1833
- self,
1834
1850
  func: TagFunc,
1835
1851
  args: DictData,
1836
1852
  run_id: str,
1837
1853
  parent_run_id: Optional[str] = None,
1854
+ extras: Optional[DictData] = None,
1838
1855
  ) -> DictData:
1839
1856
  """Validate an input arguments before passing to the caller function.
1840
1857
 
@@ -1846,18 +1863,18 @@ class CallStage(BaseRetryStage):
1846
1863
  :rtype: DictData
1847
1864
  """
1848
1865
  try:
1849
- model_instance: BaseModel = create_model_from_caller(
1850
- func
1851
- ).model_validate(args)
1852
- override: DictData = dict(model_instance)
1866
+ override: DictData = dict(
1867
+ create_model_from_caller(func).model_validate(args)
1868
+ )
1853
1869
  args.update(override)
1870
+
1854
1871
  type_hints: dict[str, Any] = get_type_hints(func)
1855
1872
  for arg in type_hints:
1856
1873
 
1857
1874
  if arg == "return":
1858
1875
  continue
1859
1876
 
1860
- if arg.removeprefix("_") in args:
1877
+ if arg.startswith("_") and arg.removeprefix("_") in args:
1861
1878
  args[arg] = args.pop(arg.removeprefix("_"))
1862
1879
  continue
1863
1880
 
@@ -1867,8 +1884,8 @@ class CallStage(BaseRetryStage):
1867
1884
  "Validate argument from the caller function raise invalid type."
1868
1885
  ) from e
1869
1886
  except TypeError as e:
1870
- trace: Trace = get_trace(
1871
- run_id, parent_run_id=parent_run_id, extras=self.extras
1887
+ trace: TraceManager = get_trace(
1888
+ run_id, parent_run_id=parent_run_id, extras=extras
1872
1889
  )
1873
1890
  trace.warning(
1874
1891
  f"[STAGE]: Get type hint raise TypeError: {e}, so, it skip "
@@ -1992,7 +2009,7 @@ class TriggerStage(BaseNestedStage):
1992
2009
  """
1993
2010
  from .workflow import Workflow
1994
2011
 
1995
- trace: Trace = get_trace(
2012
+ trace: TraceManager = get_trace(
1996
2013
  run_id, parent_run_id=parent_run_id, extras=self.extras
1997
2014
  )
1998
2015
  _trigger: str = param2template(self.trigger, params, extras=self.extras)
@@ -2097,7 +2114,7 @@ class ParallelStage(BaseNestedStage):
2097
2114
 
2098
2115
  :rtype: tuple[Status, DictData]
2099
2116
  """
2100
- trace: Trace = get_trace(
2117
+ trace: TraceManager = get_trace(
2101
2118
  run_id, parent_run_id=parent_run_id, extras=self.extras
2102
2119
  )
2103
2120
  trace.debug(f"[STAGE]: Execute Branch: {branch!r}")
@@ -2227,7 +2244,7 @@ class ParallelStage(BaseNestedStage):
2227
2244
  Returns:
2228
2245
  Result: The execution result with status and context data.
2229
2246
  """
2230
- trace: Trace = get_trace(
2247
+ trace: TraceManager = get_trace(
2231
2248
  run_id, parent_run_id=parent_run_id, extras=self.extras
2232
2249
  )
2233
2250
  event: Event = event or Event()
@@ -2295,7 +2312,13 @@ class ForEachStage(BaseNestedStage):
2295
2312
  ... }
2296
2313
  """
2297
2314
 
2298
- foreach: Union[list[str], list[int], str] = Field(
2315
+ foreach: Union[
2316
+ list[str],
2317
+ list[int],
2318
+ str,
2319
+ dict[str, Any],
2320
+ dict[int, Any],
2321
+ ] = Field(
2299
2322
  description=(
2300
2323
  "A items for passing to stages via ${{ item }} template parameter."
2301
2324
  ),
@@ -2360,7 +2383,7 @@ class ForEachStage(BaseNestedStage):
2360
2383
 
2361
2384
  :rtype: tuple[Status, Result]
2362
2385
  """
2363
- trace: Trace = get_trace(
2386
+ trace: TraceManager = get_trace(
2364
2387
  run_id, parent_run_id=parent_run_id, extras=self.extras
2365
2388
  )
2366
2389
  trace.debug(f"[STAGE]: Execute Item: {item!r}")
@@ -2497,11 +2520,11 @@ class ForEachStage(BaseNestedStage):
2497
2520
  Returns:
2498
2521
  Result: The execution result with status and context data.
2499
2522
  """
2500
- trace: Trace = get_trace(
2523
+ trace: TraceManager = get_trace(
2501
2524
  run_id, parent_run_id=parent_run_id, extras=self.extras
2502
2525
  )
2503
2526
  event: Event = event or Event()
2504
- foreach: Union[list[str], list[int]] = pass_env(
2527
+ foreach: Union[list[str], list[int], str] = pass_env(
2505
2528
  param2template(self.foreach, params, extras=self.extras)
2506
2529
  )
2507
2530
 
@@ -2516,9 +2539,10 @@ class ForEachStage(BaseNestedStage):
2516
2539
  ) from e
2517
2540
 
2518
2541
  # [VALIDATE]: Type of the foreach should be `list` type.
2519
- elif not isinstance(foreach, list):
2542
+ elif isinstance(foreach, dict):
2520
2543
  raise TypeError(
2521
- f"Does not support foreach: {foreach!r} ({type(foreach)})"
2544
+ f"Does not support dict foreach: {foreach!r} ({type(foreach)}) "
2545
+ f"yet."
2522
2546
  )
2523
2547
  # [Validate]: Value in the foreach item should not be duplicate when the
2524
2548
  # `use_index_as_key` field did not set.
@@ -2681,7 +2705,7 @@ class UntilStage(BaseNestedStage):
2681
2705
  :rtype: tuple[Status, DictData, T]
2682
2706
  :return: Return a pair of Result and changed item.
2683
2707
  """
2684
- trace: Trace = get_trace(
2708
+ trace: TraceManager = get_trace(
2685
2709
  run_id, parent_run_id=parent_run_id, extras=self.extras
2686
2710
  )
2687
2711
  trace.debug(f"[STAGE]: Execute Loop: {loop} (Item {item!r})")
@@ -2823,7 +2847,7 @@ class UntilStage(BaseNestedStage):
2823
2847
  Returns:
2824
2848
  Result: The execution result with status and context data.
2825
2849
  """
2826
- trace: Trace = get_trace(
2850
+ trace: TraceManager = get_trace(
2827
2851
  run_id, parent_run_id=parent_run_id, extras=self.extras
2828
2852
  )
2829
2853
  event: Event = event or Event()
@@ -2975,7 +2999,7 @@ class CaseStage(BaseNestedStage):
2975
2999
 
2976
3000
  :rtype: DictData
2977
3001
  """
2978
- trace: Trace = get_trace(
3002
+ trace: TraceManager = get_trace(
2979
3003
  run_id, parent_run_id=parent_run_id, extras=self.extras
2980
3004
  )
2981
3005
  trace.debug(f"[STAGE]: Execute Case: {case!r}")
@@ -3056,7 +3080,7 @@ class CaseStage(BaseNestedStage):
3056
3080
  Returns:
3057
3081
  Result: The execution result with status and context data.
3058
3082
  """
3059
- trace: Trace = get_trace(
3083
+ trace: TraceManager = get_trace(
3060
3084
  run_id, parent_run_id=parent_run_id, extras=self.extras
3061
3085
  )
3062
3086
 
@@ -3154,7 +3178,7 @@ class RaiseStage(BaseAsyncStage):
3154
3178
  Returns:
3155
3179
  Result: The execution result with status and context data.
3156
3180
  """
3157
- trace: Trace = get_trace(
3181
+ trace: TraceManager = get_trace(
3158
3182
  run_id, parent_run_id=parent_run_id, extras=self.extras
3159
3183
  )
3160
3184
  message: str = param2template(self.message, params, extras=self.extras)
@@ -3185,7 +3209,7 @@ class RaiseStage(BaseAsyncStage):
3185
3209
  Returns:
3186
3210
  Result: The execution result with status and context data.
3187
3211
  """
3188
- trace: Trace = get_trace(
3212
+ trace: TraceManager = get_trace(
3189
3213
  run_id, parent_run_id=parent_run_id, extras=self.extras
3190
3214
  )
3191
3215
  message: str = param2template(self.message, params, extras=self.extras)
@@ -3266,7 +3290,7 @@ class DockerStage(BaseStage): # pragma: no cov
3266
3290
  "by `pip install docker` first."
3267
3291
  ) from None
3268
3292
 
3269
- trace: Trace = get_trace(
3293
+ trace: TraceManager = get_trace(
3270
3294
  run_id, parent_run_id=parent_run_id, extras=self.extras
3271
3295
  )
3272
3296
  client = DockerClient(
@@ -3366,7 +3390,7 @@ class DockerStage(BaseStage): # pragma: no cov
3366
3390
  Returns:
3367
3391
  Result: The execution result with status and context data.
3368
3392
  """
3369
- trace: Trace = get_trace(
3393
+ trace: TraceManager = get_trace(
3370
3394
  run_id, parent_run_id=parent_run_id, extras=self.extras
3371
3395
  )
3372
3396
  trace.info(f"[STAGE]: Docker: {self.image}:{self.tag}")
@@ -3476,7 +3500,7 @@ class VirtualPyStage(PyStage): # pragma: no cov
3476
3500
  Returns:
3477
3501
  Result: The execution result with status and context data.
3478
3502
  """
3479
- trace: Trace = get_trace(
3503
+ trace: TraceManager = get_trace(
3480
3504
  run_id, parent_run_id=parent_run_id, extras=self.extras
3481
3505
  )
3482
3506
  run: str = param2template(dedent(self.run), params, extras=self.extras)