ddeutil-workflow 0.0.58__py3-none-any.whl → 0.0.60__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.
@@ -7,6 +7,7 @@
7
7
  """Reusables module that keep any templating functions."""
8
8
  from __future__ import annotations
9
9
 
10
+ import copy
10
11
  import inspect
11
12
  import logging
12
13
  from ast import Call, Constant, Expr, Module, Name, parse
@@ -35,7 +36,7 @@ logger = logging.getLogger("ddeutil.workflow")
35
36
  logging.getLogger("asyncio").setLevel(logging.INFO)
36
37
 
37
38
 
38
- FILTERS: dict[str, callable] = { # pragma: no cov
39
+ FILTERS: dict[str, Callable] = { # pragma: no cov
39
40
  "abs": abs,
40
41
  "str": str,
41
42
  "int": int,
@@ -258,9 +259,9 @@ def str2template(
258
259
  value: str,
259
260
  params: DictData,
260
261
  *,
261
- filters: dict[str, FilterRegistry] | None = None,
262
+ filters: Optional[dict[str, FilterRegistry]] = None,
262
263
  registers: Optional[list[str]] = None,
263
- ) -> str:
264
+ ) -> Optional[str]:
264
265
  """(Sub-function) Pass param to template string that can search by
265
266
  ``RE_CALLER`` regular expression.
266
267
 
@@ -326,7 +327,7 @@ def str2template(
326
327
  def param2template(
327
328
  value: T,
328
329
  params: DictData,
329
- filters: dict[str, FilterRegistry] | None = None,
330
+ filters: Optional[dict[str, FilterRegistry]] = None,
330
331
  *,
331
332
  extras: Optional[DictData] = None,
332
333
  ) -> T:
@@ -381,7 +382,7 @@ def datetime_format(value: datetime, fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
381
382
 
382
383
 
383
384
  @custom_filter("coalesce") # pragma: no cov
384
- def coalesce(value: T | None, default: Any) -> T:
385
+ def coalesce(value: Optional[T], default: Any) -> T:
385
386
  """Coalesce with default value if the main value is None."""
386
387
  return default if value is None else value
387
388
 
@@ -450,11 +451,10 @@ def make_registry(
450
451
  :rtype: dict[str, Registry]
451
452
  """
452
453
  rs: dict[str, Registry] = {}
453
- regis_calls: list[str] = dynamic(
454
- "registry_caller", f=registries
455
- ) # pragma: no cov
454
+ regis_calls: list[str] = copy.deepcopy(
455
+ dynamic("registry_caller", f=registries)
456
+ )
456
457
  regis_calls.extend(["ddeutil.vendors"])
457
-
458
458
  for module in regis_calls:
459
459
  # NOTE: try to sequential import task functions
460
460
  try:
@@ -335,9 +335,9 @@ class Schedule(BaseModel):
335
335
  def pending(
336
336
  self,
337
337
  *,
338
- stop: datetime | None = None,
338
+ stop: Optional[datetime] = None,
339
339
  audit: type[Audit] | None = None,
340
- parent_run_id: str | None = None,
340
+ parent_run_id: Optional[str] = None,
341
341
  ) -> Result: # pragma: no cov
342
342
  """Pending this schedule tasks with the schedule package.
343
343
 
@@ -384,7 +384,7 @@ DecoratorCancelJob = Callable[[ReturnResultOrCancel], ReturnResultOrCancel]
384
384
 
385
385
  def catch_exceptions(
386
386
  cancel_on_failure: bool = False,
387
- parent_run_id: str | None = None,
387
+ parent_run_id: Optional[str] = None,
388
388
  ) -> DecoratorCancelJob:
389
389
  """Catch exception error from scheduler job that running with schedule
390
390
  package and return CancelJob if this function raise an error.
@@ -440,7 +440,7 @@ def schedule_task(
440
440
  threads: ReleaseThreads,
441
441
  audit: type[Audit],
442
442
  *,
443
- parent_run_id: str | None = None,
443
+ parent_run_id: Optional[str] = None,
444
444
  extras: Optional[DictData] = None,
445
445
  ) -> ResultOrCancel:
446
446
  """Schedule task function that generate thread of workflow task release
@@ -558,7 +558,7 @@ def schedule_task(
558
558
 
559
559
  def monitor(
560
560
  threads: ReleaseThreads,
561
- parent_run_id: str | None = None,
561
+ parent_run_id: Optional[str] = None,
562
562
  ) -> None: # pragma: no cov
563
563
  """Monitoring function that running every five minute for track long-running
564
564
  thread instance from the schedule_control function that run every minute.
@@ -685,11 +685,11 @@ def scheduler_pending(
685
685
 
686
686
  def schedule_control(
687
687
  schedules: list[str],
688
- stop: datetime | None = None,
688
+ stop: Optional[datetime] = None,
689
689
  *,
690
690
  extras: DictData | None = None,
691
691
  audit: type[Audit] | None = None,
692
- parent_run_id: str | None = None,
692
+ parent_run_id: Optional[str] = None,
693
693
  ) -> Result: # pragma: no cov
694
694
  """Scheduler control function that run the chuck of schedules every minute
695
695
  and this function release monitoring thread for tracking undead thread in
@@ -744,7 +744,7 @@ def schedule_control(
744
744
 
745
745
 
746
746
  def schedule_runner(
747
- stop: datetime | None = None,
747
+ stop: Optional[datetime] = None,
748
748
  *,
749
749
  max_process: int | None = None,
750
750
  extras: DictData | None = None,
@@ -58,7 +58,7 @@ from pydantic import BaseModel, Field
58
58
  from pydantic.functional_validators import model_validator
59
59
  from typing_extensions import Self
60
60
 
61
- from .__types import DictData, DictStr, StrOrInt, TupleStr
61
+ from .__types import DictData, DictStr, StrOrInt, StrOrNone, TupleStr
62
62
  from .conf import dynamic
63
63
  from .exceptions import StageException, to_dict
64
64
  from .result import CANCEL, FAILED, SUCCESS, WAIT, Result, Status
@@ -87,7 +87,7 @@ class BaseStage(BaseModel, ABC):
87
87
  default_factory=dict,
88
88
  description="An extra parameter that override core config values.",
89
89
  )
90
- id: Optional[str] = Field(
90
+ id: StrOrNone = Field(
91
91
  default=None,
92
92
  description=(
93
93
  "A stage ID that use to keep execution output or getting by job "
@@ -97,7 +97,7 @@ class BaseStage(BaseModel, ABC):
97
97
  name: str = Field(
98
98
  description="A stage name that want to logging when start execution.",
99
99
  )
100
- condition: Optional[str] = Field(
100
+ condition: StrOrNone = Field(
101
101
  default=None,
102
102
  description=(
103
103
  "A stage condition statement to allow stage executable. This field "
@@ -141,8 +141,8 @@ class BaseStage(BaseModel, ABC):
141
141
  self,
142
142
  params: DictData,
143
143
  *,
144
- result: Result | None = None,
145
- event: Event | None = None,
144
+ result: Optional[Result] = None,
145
+ event: Optional[Event] = None,
146
146
  ) -> Result:
147
147
  """Execute abstraction method that action something by sub-model class.
148
148
  This is important method that make this class is able to be the stage.
@@ -162,12 +162,12 @@ class BaseStage(BaseModel, ABC):
162
162
  self,
163
163
  params: DictData,
164
164
  *,
165
- run_id: str | None = None,
166
- parent_run_id: str | None = None,
167
- result: Result | None = None,
168
- event: Event | None = None,
169
- raise_error: bool | None = None,
170
- ) -> Result | DictData:
165
+ run_id: StrOrNone = None,
166
+ parent_run_id: StrOrNone = None,
167
+ result: Optional[Result] = None,
168
+ event: Optional[Event] = None,
169
+ raise_error: Optional[bool] = None,
170
+ ) -> Union[Result, DictData]:
171
171
  """Handler stage execution result from the stage `execute` method.
172
172
 
173
173
  This stage exception handler still use ok-error concept, but it
@@ -376,8 +376,8 @@ class BaseAsyncStage(BaseStage):
376
376
  self,
377
377
  params: DictData,
378
378
  *,
379
- result: Result | None = None,
380
- event: Event | None = None,
379
+ result: Optional[Result] = None,
380
+ event: Optional[Event] = None,
381
381
  ) -> Result:
382
382
  raise NotImplementedError(
383
383
  "Async Stage should implement `execute` method."
@@ -388,8 +388,8 @@ class BaseAsyncStage(BaseStage):
388
388
  self,
389
389
  params: DictData,
390
390
  *,
391
- result: Result | None = None,
392
- event: Event | None = None,
391
+ result: Optional[Result] = None,
392
+ event: Optional[Event] = None,
393
393
  ) -> Result:
394
394
  """Async execution method for this Empty stage that only logging out to
395
395
  stdout.
@@ -411,11 +411,11 @@ class BaseAsyncStage(BaseStage):
411
411
  self,
412
412
  params: DictData,
413
413
  *,
414
- run_id: str | None = None,
415
- parent_run_id: str | None = None,
416
- result: Result | None = None,
417
- event: Event | None = None,
418
- raise_error: bool | None = None,
414
+ run_id: StrOrNone = None,
415
+ parent_run_id: StrOrNone = None,
416
+ result: Optional[Result] = None,
417
+ event: Optional[Event] = None,
418
+ raise_error: Optional[bool] = None,
419
419
  ) -> Result:
420
420
  """Async Handler stage execution result from the stage `execute` method.
421
421
 
@@ -469,7 +469,7 @@ class EmptyStage(BaseAsyncStage):
469
469
  ... }
470
470
  """
471
471
 
472
- echo: Optional[str] = Field(
472
+ echo: StrOrNone = Field(
473
473
  default=None,
474
474
  description="A message that want to show on the stdout.",
475
475
  )
@@ -487,8 +487,8 @@ class EmptyStage(BaseAsyncStage):
487
487
  self,
488
488
  params: DictData,
489
489
  *,
490
- result: Result | None = None,
491
- event: Event | None = None,
490
+ result: Optional[Result] = None,
491
+ event: Optional[Event] = None,
492
492
  ) -> Result:
493
493
  """Execution method for the Empty stage that do only logging out to
494
494
  stdout.
@@ -528,8 +528,8 @@ class EmptyStage(BaseAsyncStage):
528
528
  self,
529
529
  params: DictData,
530
530
  *,
531
- result: Result | None = None,
532
- event: Event | None = None,
531
+ result: Optional[Result] = None,
532
+ event: Optional[Event] = None,
533
533
  ) -> Result:
534
534
  """Async execution method for this Empty stage that only logging out to
535
535
  stdout.
@@ -598,14 +598,14 @@ class BashStage(BaseAsyncStage):
598
598
  )
599
599
 
600
600
  @contextlib.asynccontextmanager
601
- async def acreate_sh_file(
602
- self, bash: str, env: DictStr, run_id: str | None = None
601
+ async def async_create_sh_file(
602
+ self, bash: str, env: DictStr, run_id: StrOrNone = None
603
603
  ) -> AsyncIterator[TupleStr]:
604
604
  """Async create and write `.sh` file with the `aiofiles` package.
605
605
 
606
606
  :param bash: (str) A bash statement.
607
607
  :param env: (DictStr) An environment variable that set before run bash.
608
- :param run_id: (str | None) A running stage ID that use for writing sh
608
+ :param run_id: (StrOrNone) A running stage ID that use for writing sh
609
609
  file instead generate by UUID4.
610
610
 
611
611
  :rtype: AsyncIterator[TupleStr]
@@ -635,14 +635,14 @@ class BashStage(BaseAsyncStage):
635
635
 
636
636
  @contextlib.contextmanager
637
637
  def create_sh_file(
638
- self, bash: str, env: DictStr, run_id: str | None = None
638
+ self, bash: str, env: DictStr, run_id: StrOrNone = None
639
639
  ) -> Iterator[TupleStr]:
640
640
  """Create and write the `.sh` file before giving this file name to
641
641
  context. After that, it will auto delete this file automatic.
642
642
 
643
643
  :param bash: (str) A bash statement.
644
644
  :param env: (DictStr) An environment variable that set before run bash.
645
- :param run_id: (str | None) A running stage ID that use for writing sh
645
+ :param run_id: (StrOrNone) A running stage ID that use for writing sh
646
646
  file instead generate by UUID4.
647
647
 
648
648
  :rtype: Iterator[TupleStr]
@@ -673,8 +673,8 @@ class BashStage(BaseAsyncStage):
673
673
  self,
674
674
  params: DictData,
675
675
  *,
676
- result: Result | None = None,
677
- event: Event | None = None,
676
+ result: Optional[Result] = None,
677
+ event: Optional[Event] = None,
678
678
  ) -> Result:
679
679
  """Execute bash statement with the Python build-in `subprocess` package.
680
680
  It will catch result from the `subprocess.run` returning output like
@@ -730,8 +730,8 @@ class BashStage(BaseAsyncStage):
730
730
  self,
731
731
  params: DictData,
732
732
  *,
733
- result: Result | None = None,
734
- event: Event | None = None,
733
+ result: Optional[Result] = None,
734
+ event: Optional[Event] = None,
735
735
  ) -> Result:
736
736
  """Async execution method for this Bash stage that only logging out to
737
737
  stdout.
@@ -752,7 +752,7 @@ class BashStage(BaseAsyncStage):
752
752
  dedent(self.bash.strip("\n")), params, extras=self.extras
753
753
  )
754
754
 
755
- async with self.acreate_sh_file(
755
+ async with self.async_create_sh_file(
756
756
  bash=bash,
757
757
  env=param2template(self.env, params, extras=self.extras),
758
758
  run_id=result.run_id,
@@ -858,8 +858,8 @@ class PyStage(BaseAsyncStage):
858
858
  self,
859
859
  params: DictData,
860
860
  *,
861
- result: Result | None = None,
862
- event: Event | None = None,
861
+ result: Optional[Result] = None,
862
+ event: Optional[Event] = None,
863
863
  ) -> Result:
864
864
  """Execute the Python statement that pass all globals and input params
865
865
  to globals argument on `exec` build-in function.
@@ -916,8 +916,8 @@ class PyStage(BaseAsyncStage):
916
916
  self,
917
917
  params: DictData,
918
918
  *,
919
- result: Result | None = None,
920
- event: Event | None = None,
919
+ result: Optional[Result] = None,
920
+ event: Optional[Event] = None,
921
921
  ) -> Result:
922
922
  """Async execution method for this Bash stage that only logging out to
923
923
  stdout.
@@ -1018,8 +1018,8 @@ class CallStage(BaseAsyncStage):
1018
1018
  self,
1019
1019
  params: DictData,
1020
1020
  *,
1021
- result: Result | None = None,
1022
- event: Event | None = None,
1021
+ result: Optional[Result] = None,
1022
+ event: Optional[Event] = None,
1023
1023
  ) -> Result:
1024
1024
  """Execute this caller function with its argument parameter.
1025
1025
 
@@ -1108,8 +1108,8 @@ class CallStage(BaseAsyncStage):
1108
1108
  self,
1109
1109
  params: DictData,
1110
1110
  *,
1111
- result: Result | None = None,
1112
- event: Event | None = None,
1111
+ result: Optional[Result] = None,
1112
+ event: Optional[Event] = None,
1113
1113
  ) -> Result:
1114
1114
  """Async execution method for this Bash stage that only logging out to
1115
1115
  stdout.
@@ -1266,8 +1266,8 @@ class TriggerStage(BaseStage):
1266
1266
  self,
1267
1267
  params: DictData,
1268
1268
  *,
1269
- result: Result | None = None,
1270
- event: Event | None = None,
1269
+ result: Optional[Result] = None,
1270
+ event: Optional[Event] = None,
1271
1271
  ) -> Result:
1272
1272
  """Trigger another workflow execution. It will wait the trigger
1273
1273
  workflow running complete before catching its result.
@@ -1294,11 +1294,12 @@ class TriggerStage(BaseStage):
1294
1294
  extras=self.extras | {"stage_raise_error": True},
1295
1295
  ).execute(
1296
1296
  params=param2template(self.params, params, extras=self.extras),
1297
- parent_run_id=result.run_id,
1297
+ run_id=None,
1298
+ parent_run_id=result.parent_run_id,
1298
1299
  event=event,
1299
1300
  )
1300
1301
  if rs.status == FAILED:
1301
- err_msg: str | None = (
1302
+ err_msg: StrOrNone = (
1302
1303
  f" with:\n{msg}"
1303
1304
  if (msg := rs.context.get("errors", {}).get("message"))
1304
1305
  else "."
@@ -1319,8 +1320,8 @@ class BaseNestedStage(BaseStage):
1319
1320
  self,
1320
1321
  params: DictData,
1321
1322
  *,
1322
- result: Result | None = None,
1323
- event: Event | None = None,
1323
+ result: Optional[Result] = None,
1324
+ event: Optional[Event] = None,
1324
1325
  ) -> Result:
1325
1326
  """Execute abstraction method that action something by sub-model class.
1326
1327
  This is important method that make this class is able to be the nested
@@ -1390,7 +1391,7 @@ class ParallelStage(BaseNestedStage):
1390
1391
  params: DictData,
1391
1392
  result: Result,
1392
1393
  *,
1393
- event: Event | None = None,
1394
+ event: Optional[Event] = None,
1394
1395
  ) -> Result:
1395
1396
  """Execute all stage with specific branch ID.
1396
1397
 
@@ -1487,8 +1488,8 @@ class ParallelStage(BaseNestedStage):
1487
1488
  self,
1488
1489
  params: DictData,
1489
1490
  *,
1490
- result: Result | None = None,
1491
- event: Event | None = None,
1491
+ result: Optional[Result] = None,
1492
+ event: Optional[Event] = None,
1492
1493
  ) -> Result:
1493
1494
  """Execute parallel each branch via multi-threading pool.
1494
1495
 
@@ -1526,7 +1527,7 @@ class ParallelStage(BaseNestedStage):
1526
1527
  context: DictData = {}
1527
1528
  status: Status = SUCCESS
1528
1529
 
1529
- futures: list[Future] = (
1530
+ futures: list[Future] = [
1530
1531
  executor.submit(
1531
1532
  self.execute_branch,
1532
1533
  branch=branch,
@@ -1535,7 +1536,7 @@ class ParallelStage(BaseNestedStage):
1535
1536
  event=event,
1536
1537
  )
1537
1538
  for branch in self.parallel
1538
- )
1539
+ ]
1539
1540
 
1540
1541
  for future in as_completed(futures):
1541
1542
  try:
@@ -1605,7 +1606,7 @@ class ForEachStage(BaseNestedStage):
1605
1606
  params: DictData,
1606
1607
  result: Result,
1607
1608
  *,
1608
- event: Event | None = None,
1609
+ event: Optional[Event] = None,
1609
1610
  ) -> Result:
1610
1611
  """Execute all nested stage that set on this stage with specific foreach
1611
1612
  item parameter.
@@ -1709,8 +1710,8 @@ class ForEachStage(BaseNestedStage):
1709
1710
  self,
1710
1711
  params: DictData,
1711
1712
  *,
1712
- result: Result | None = None,
1713
- event: Event | None = None,
1713
+ result: Optional[Result] = None,
1714
+ event: Optional[Event] = None,
1714
1715
  ) -> Result:
1715
1716
  """Execute the stages that pass each item form the foreach field.
1716
1717
 
@@ -1775,7 +1776,7 @@ class ForEachStage(BaseNestedStage):
1775
1776
  status: Status = SUCCESS
1776
1777
 
1777
1778
  done, not_done = wait(futures, return_when=FIRST_EXCEPTION)
1778
- if len(done) != len(futures):
1779
+ if len(list(done)) != len(futures):
1779
1780
  result.trace.warning(
1780
1781
  "[STAGE]: Set event for stop pending for-each stage."
1781
1782
  )
@@ -1795,7 +1796,7 @@ class ForEachStage(BaseNestedStage):
1795
1796
  result.trace.debug(
1796
1797
  f"[STAGE]: ... Foreach-Stage set failed event{nd}"
1797
1798
  )
1798
- done: list[Future] = as_completed(futures)
1799
+ done: Iterator[Future] = as_completed(futures)
1799
1800
 
1800
1801
  for future in done:
1801
1802
  try:
@@ -1826,7 +1827,10 @@ class UntilStage(BaseNestedStage):
1826
1827
  ... "stages": [
1827
1828
  ... {
1828
1829
  ... "name": "Start increase item value.",
1829
- ... "run": "item = ${{ item }}\\nitem += 1\\n"
1830
+ ... "run": (
1831
+ ... "item = ${{ item }}\\n"
1832
+ ... "item += 1\\n"
1833
+ ... )
1830
1834
  ... },
1831
1835
  ... ],
1832
1836
  ... }
@@ -1862,7 +1866,7 @@ class UntilStage(BaseNestedStage):
1862
1866
  loop: int,
1863
1867
  params: DictData,
1864
1868
  result: Result,
1865
- event: Event | None = None,
1869
+ event: Optional[Event] = None,
1866
1870
  ) -> tuple[Result, T]:
1867
1871
  """Execute all stage with specific loop and item.
1868
1872
 
@@ -1975,8 +1979,8 @@ class UntilStage(BaseNestedStage):
1975
1979
  self,
1976
1980
  params: DictData,
1977
1981
  *,
1978
- result: Result | None = None,
1979
- event: Event | None = None,
1982
+ result: Optional[Result] = None,
1983
+ event: Optional[Event] = None,
1980
1984
  ) -> Result:
1981
1985
  """Execute until loop with checking until condition.
1982
1986
 
@@ -2113,7 +2117,7 @@ class CaseStage(BaseNestedStage):
2113
2117
  params: DictData,
2114
2118
  result: Result,
2115
2119
  *,
2116
- event: Event | None = None,
2120
+ event: Optional[Event] = None,
2117
2121
  ) -> Result:
2118
2122
  """Execute case.
2119
2123
 
@@ -2198,8 +2202,8 @@ class CaseStage(BaseNestedStage):
2198
2202
  self,
2199
2203
  params: DictData,
2200
2204
  *,
2201
- result: Result | None = None,
2202
- event: Event | None = None,
2205
+ result: Optional[Result] = None,
2206
+ event: Optional[Event] = None,
2203
2207
  ) -> Result:
2204
2208
  """Execute case-match condition that pass to the case field.
2205
2209
 
@@ -2215,9 +2219,7 @@ class CaseStage(BaseNestedStage):
2215
2219
  extras=self.extras,
2216
2220
  )
2217
2221
 
2218
- _case: Optional[str] = param2template(
2219
- self.case, params, extras=self.extras
2220
- )
2222
+ _case: StrOrNone = param2template(self.case, params, extras=self.extras)
2221
2223
 
2222
2224
  result.trace.info(f"[STAGE]: Execute Case-Stage: {_case!r}.")
2223
2225
  _else: Optional[Match] = None
@@ -2291,8 +2293,8 @@ class RaiseStage(BaseAsyncStage):
2291
2293
  self,
2292
2294
  params: DictData,
2293
2295
  *,
2294
- result: Result | None = None,
2295
- event: Event | None = None,
2296
+ result: Optional[Result] = None,
2297
+ event: Optional[Event] = None,
2296
2298
  ) -> Result:
2297
2299
  """Raise the StageException object with the message field execution.
2298
2300
 
@@ -2313,8 +2315,8 @@ class RaiseStage(BaseAsyncStage):
2313
2315
  self,
2314
2316
  params: DictData,
2315
2317
  *,
2316
- result: Result | None = None,
2317
- event: Event | None = None,
2318
+ result: Optional[Result] = None,
2319
+ event: Optional[Event] = None,
2318
2320
  ) -> Result:
2319
2321
  """Async execution method for this Empty stage that only logging out to
2320
2322
  stdout.
@@ -2367,7 +2369,7 @@ class DockerStage(BaseStage): # pragma: no cov
2367
2369
  env: DictData = Field(
2368
2370
  default_factory=dict,
2369
2371
  description=(
2370
- "An environment variable that want pass to Docker container.",
2372
+ "An environment variable that want pass to Docker container."
2371
2373
  ),
2372
2374
  )
2373
2375
  volume: DictData = Field(
@@ -2385,7 +2387,7 @@ class DockerStage(BaseStage): # pragma: no cov
2385
2387
  self,
2386
2388
  params: DictData,
2387
2389
  result: Result,
2388
- event: Event | None = None,
2390
+ event: Optional[Event] = None,
2389
2391
  ) -> Result:
2390
2392
  """Execute Docker container task.
2391
2393
 
@@ -2396,8 +2398,14 @@ class DockerStage(BaseStage): # pragma: no cov
2396
2398
 
2397
2399
  :rtype: Result
2398
2400
  """
2399
- from docker import DockerClient
2400
- from docker.errors import ContainerError
2401
+ try:
2402
+ from docker import DockerClient
2403
+ from docker.errors import ContainerError
2404
+ except ImportError:
2405
+ raise ImportError(
2406
+ "Docker stage need the docker package, you should install it "
2407
+ "by `pip install docker` first."
2408
+ ) from None
2401
2409
 
2402
2410
  client = DockerClient(
2403
2411
  base_url="unix://var/run/docker.sock", version="auto"
@@ -2459,7 +2467,7 @@ class DockerStage(BaseStage): # pragma: no cov
2459
2467
  exit_status,
2460
2468
  None,
2461
2469
  f"{self.image}:{self.tag}",
2462
- out,
2470
+ out.decode("utf-8"),
2463
2471
  )
2464
2472
  output_file: Path = Path(f".docker.{result.run_id}.logs/outputs.json")
2465
2473
  if not output_file.exists():
@@ -2473,8 +2481,8 @@ class DockerStage(BaseStage): # pragma: no cov
2473
2481
  self,
2474
2482
  params: DictData,
2475
2483
  *,
2476
- result: Result | None = None,
2477
- event: Event | None = None,
2484
+ result: Optional[Result] = None,
2485
+ event: Optional[Event] = None,
2478
2486
  ) -> Result:
2479
2487
  """Execute the Docker image via Python API.
2480
2488
 
@@ -2518,7 +2526,7 @@ class VirtualPyStage(PyStage): # pragma: no cov
2518
2526
  py: str,
2519
2527
  values: DictData,
2520
2528
  deps: list[str],
2521
- run_id: str | None = None,
2529
+ run_id: StrOrNone = None,
2522
2530
  ) -> Iterator[str]:
2523
2531
  """Create the .py file with an input Python string statement.
2524
2532
 
@@ -2526,7 +2534,7 @@ class VirtualPyStage(PyStage): # pragma: no cov
2526
2534
  :param values: A variable that want to set before running this
2527
2535
  :param deps: An additional Python dependencies that want install before
2528
2536
  run this python stage.
2529
- :param run_id: (str | None) A running ID of this stage execution.
2537
+ :param run_id: (StrOrNone) A running ID of this stage execution.
2530
2538
  """
2531
2539
  run_id: str = run_id or uuid.uuid4()
2532
2540
  f_name: str = f"{run_id}.py"
@@ -2565,8 +2573,8 @@ class VirtualPyStage(PyStage): # pragma: no cov
2565
2573
  self,
2566
2574
  params: DictData,
2567
2575
  *,
2568
- result: Result | None = None,
2569
- event: Event | None = None,
2576
+ result: Optional[Result] = None,
2577
+ event: Optional[Event] = None,
2570
2578
  ) -> Result:
2571
2579
  """Execute the Python statement via Python virtual environment.
2572
2580
 
@@ -2644,5 +2652,8 @@ Stage = Annotated[
2644
2652
  RaiseStage,
2645
2653
  EmptyStage,
2646
2654
  ],
2647
- Field(union_mode="smart"),
2655
+ Field(
2656
+ union_mode="smart",
2657
+ description="A stage models that already implemented on this package.",
2658
+ ),
2648
2659
  ] # pragma: no cov