ddeutil-workflow 0.0.13__py3-none-any.whl → 0.0.15__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.
ddeutil/workflow/utils.py CHANGED
@@ -13,6 +13,7 @@ import time
13
13
  from abc import ABC, abstractmethod
14
14
  from ast import Call, Constant, Expr, Module, Name, parse
15
15
  from collections.abc import Iterator
16
+ from dataclasses import field
16
17
  from datetime import date, datetime
17
18
  from functools import cached_property, wraps
18
19
  from hashlib import md5
@@ -32,19 +33,22 @@ except ImportError:
32
33
  from ddeutil.core import getdot, hasdot, hash_str, import_string, lazy, str2bool
33
34
  from ddeutil.io import PathData, PathSearch, YamlFlResolve, search_env_replace
34
35
  from ddeutil.io.models.lineage import dt_now
35
- from pydantic import BaseModel, ConfigDict, Field, PrivateAttr
36
+ from pydantic import BaseModel, ConfigDict, Field
37
+ from pydantic.dataclasses import dataclass
36
38
  from pydantic.functional_serializers import field_serializer
37
39
  from pydantic.functional_validators import model_validator
38
40
  from typing_extensions import Self
39
41
 
40
42
  from .__types import DictData, Matrix, Re
43
+ from .conf import config
41
44
  from .exceptions import ParamValueException, UtilException
42
45
 
43
- logger = logging.getLogger("ddeutil.workflow")
44
46
  P = ParamSpec("P")
45
47
  AnyModel = TypeVar("AnyModel", bound=BaseModel)
46
48
  AnyModelType = type[AnyModel]
47
49
 
50
+ logger = logging.getLogger("ddeutil.workflow")
51
+
48
52
 
49
53
  def get_diff_sec(dt: datetime, tz: ZoneInfo | None = None) -> int:
50
54
  """Return second value that come from diff of an input datetime and the
@@ -110,7 +114,7 @@ class ConfParams(BaseModel):
110
114
  )
111
115
 
112
116
 
113
- def config() -> ConfParams:
117
+ def load_config() -> ConfParams:
114
118
  """Load Config data from ``workflows-conf.yaml`` file.
115
119
 
116
120
  Configuration Docs:
@@ -158,7 +162,7 @@ class SimLoad:
158
162
  :param externals: An external parameters
159
163
 
160
164
  Noted:
161
- ---
165
+
162
166
  The config data should have ``type`` key for modeling validation that
163
167
  make this loader know what is config should to do pass to.
164
168
 
@@ -202,6 +206,12 @@ class SimLoad:
202
206
  """Find all data that match with object type in config path. This class
203
207
  method can use include and exclude list of identity name for filter and
204
208
  adds-on.
209
+
210
+ :param obj:
211
+ :param params:
212
+ :param include:
213
+ :param exclude:
214
+ :rtype: Iterator[tuple[str, DictData]]
205
215
  """
206
216
  exclude: list[str] = exclude or []
207
217
  for file in PathSearch(params.engine.paths.conf).files:
@@ -248,11 +258,11 @@ class Loader(SimLoad):
248
258
  ) -> DictData:
249
259
  """Override the find class method from the Simple Loader object."""
250
260
  return super().finds(
251
- obj=obj, params=config(), include=include, exclude=exclude
261
+ obj=obj, params=load_config(), include=include, exclude=exclude
252
262
  )
253
263
 
254
264
  def __init__(self, name: str, externals: DictData) -> None:
255
- super().__init__(name, config(), externals)
265
+ super().__init__(name, load_config(), externals)
256
266
 
257
267
 
258
268
  def gen_id(
@@ -275,15 +285,14 @@ def gen_id(
275
285
  if not isinstance(value, str):
276
286
  value: str = str(value)
277
287
 
278
- tz: ZoneInfo = ZoneInfo(os.getenv("WORKFLOW_CORE_TIMEZONE", "UTC"))
279
288
  if str2bool(os.getenv("WORKFLOW_CORE_PIPELINE_ID_SIMPLE", "true")):
280
289
  return hash_str(f"{(value if sensitive else value.lower())}", n=10) + (
281
- f"{datetime.now(tz=tz):%Y%m%d%H%M%S%f}" if unique else ""
290
+ f"{datetime.now(tz=config.tz):%Y%m%d%H%M%S%f}" if unique else ""
282
291
  )
283
292
  return md5(
284
293
  (
285
294
  f"{(value if sensitive else value.lower())}"
286
- + (f"{datetime.now(tz=tz):%Y%m%d%H%M%S%f}" if unique else "")
295
+ + (f"{datetime.now(tz=config.tz):%Y%m%d%H%M%S%f}" if unique else "")
287
296
  ).encode()
288
297
  ).hexdigest()
289
298
 
@@ -314,19 +323,24 @@ class TagFunc(Protocol):
314
323
  name: str
315
324
  tag: str
316
325
 
317
- def __call__(self, *args, **kwargs): ...
326
+ def __call__(self, *args, **kwargs): ... # pragma: no cove
327
+
328
+
329
+ ReturnTagFunc = Callable[P, TagFunc]
330
+ DecoratorTagFunc = Callable[[Callable[[...], Any]], ReturnTagFunc]
318
331
 
319
332
 
320
- def tag(name: str, alias: str | None = None):
333
+ def tag(name: str, alias: str | None = None) -> DecoratorTagFunc:
321
334
  """Tag decorator function that set function attributes, ``tag`` and ``name``
322
335
  for making registries variable.
323
336
 
324
- :param: name: A tag value for make different use-case of a function.
337
+ :param: name: A tag name for make different use-case of a function.
325
338
  :param: alias: A alias function name that keeping in registries. If this
326
339
  value does not supply, it will use original function name from __name__.
340
+ :rtype: Callable[P, TagFunc]
327
341
  """
328
342
 
329
- def func_internal(func: Callable[[...], Any]) -> TagFunc:
343
+ def func_internal(func: Callable[[...], Any]) -> ReturnTagFunc:
330
344
  func.tag = name
331
345
  func.name = alias or func.__name__.replace("_", "-")
332
346
 
@@ -350,7 +364,7 @@ def make_registry(submodule: str) -> dict[str, Registry]:
350
364
  :rtype: dict[str, Registry]
351
365
  """
352
366
  rs: dict[str, Registry] = {}
353
- for module in config().engine.registry:
367
+ for module in load_config().engine.registry:
354
368
  # NOTE: try to sequential import task functions
355
369
  try:
356
370
  importer = import_module(f"{module}.{submodule}")
@@ -394,6 +408,10 @@ class BaseParam(BaseModel, ABC):
394
408
 
395
409
  @field_serializer("type")
396
410
  def __serializer_type(self, value: str) -> str:
411
+ """Serialize the value of the type field.
412
+
413
+ :rtype: str
414
+ """
397
415
  return value
398
416
 
399
417
 
@@ -515,29 +533,50 @@ Param = Union[
515
533
  ]
516
534
 
517
535
 
518
- class Result(BaseModel):
519
- """Result Pydantic Model for passing parameter and receiving output from
520
- the workflow execution.
536
+ @dataclass
537
+ class Result:
538
+ """Result Pydantic Model for passing and receiving data context from any
539
+ module execution process like stage execution, job execution, or workflow
540
+ execution.
541
+
542
+ For comparison property, this result will use ``status``, ``context``,
543
+ and ``_run_id`` fields to comparing with other result instance.
521
544
  """
522
545
 
523
- status: int = Field(default=2)
524
- context: DictData = Field(default_factory=dict)
546
+ status: int = field(default=2)
547
+ context: DictData = field(default_factory=dict)
548
+ start_at: datetime = field(default_factory=dt_now, compare=False)
549
+ end_at: Optional[datetime] = field(default=None, compare=False)
525
550
 
526
551
  # NOTE: Ignore this field to compare another result model with __eq__.
527
- _parent_run_id: Optional[str] = PrivateAttr(default=None)
528
- _run_id: Optional[str] = PrivateAttr(default=None)
552
+ _run_id: Optional[str] = field(default=None)
553
+ _parent_run_id: Optional[str] = field(default=None, compare=False)
529
554
 
530
555
  @model_validator(mode="after")
531
- def __prepare_run_id(self):
532
- if self._run_id is None:
533
- self._run_id = gen_id("manual", unique=True)
556
+ def __prepare_run_id(self) -> Self:
557
+ """Prepare running ID which use default ID if it initialize at the first
558
+ time
559
+
560
+ :rtype: Self
561
+ """
562
+ self._run_id = gen_id("manual", unique=True)
534
563
  return self
535
564
 
536
565
  def set_run_id(self, running_id: str) -> Self:
566
+ """Set a running ID.
567
+
568
+ :param running_id: A running ID that want to update on this model.
569
+ :rtype: Self
570
+ """
537
571
  self._run_id = running_id
538
572
  return self
539
573
 
540
574
  def set_parent_run_id(self, running_id: str) -> Self:
575
+ """Set a parent running ID.
576
+
577
+ :param running_id: A running ID that want to update on this model.
578
+ :rtype: Self
579
+ """
541
580
  self._parent_run_id = running_id
542
581
  return self
543
582
 
@@ -549,33 +588,55 @@ class Result(BaseModel):
549
588
  def run_id(self):
550
589
  return self._run_id
551
590
 
552
- def receive(self, result: Result) -> Result:
591
+ def catch(self, status: int, context: DictData) -> Self:
592
+ """Catch the status and context to current data."""
593
+ self.__dict__["status"] = status
594
+ self.__dict__["context"].update(context)
595
+ return self
596
+
597
+ def receive(self, result: Result) -> Self:
598
+ """Receive context from another result object.
599
+
600
+ :rtype: Self
601
+ """
553
602
  self.__dict__["status"] = result.status
554
603
  self.__dict__["context"].update(result.context)
604
+
605
+ # NOTE: Update running ID from an incoming result.
555
606
  self._parent_run_id = result.parent_run_id
556
607
  self._run_id = result.run_id
557
608
  return self
558
609
 
559
- def receive_jobs(self, result: Result) -> Result:
610
+ def receive_jobs(self, result: Result) -> Self:
611
+ """Receive context from another result object that use on the workflow
612
+ execution which create a ``jobs`` keys on the context if it do not
613
+ exist.
614
+
615
+ :rtype: Self
616
+ """
560
617
  self.__dict__["status"] = result.status
561
618
 
562
619
  # NOTE: Check the context has jobs key.
563
620
  if "jobs" not in self.__dict__["context"]:
564
621
  self.__dict__["context"]["jobs"] = {}
565
-
566
622
  self.__dict__["context"]["jobs"].update(result.context)
623
+
624
+ # NOTE: Update running ID from an incoming result.
567
625
  self._parent_run_id = result.parent_run_id
568
626
  self._run_id = result.run_id
569
627
  return self
570
628
 
571
629
 
572
- def make_exec(path: str | Path):
573
- """Change mode of file to be executable file."""
630
+ def make_exec(path: str | Path) -> None: # pragma: no cov
631
+ """Change mode of file to be executable file.
632
+
633
+ :param path: A file path that want to make executable permission.
634
+ """
574
635
  f: Path = Path(path) if isinstance(path, str) else path
575
636
  f.chmod(f.stat().st_mode | stat.S_IEXEC)
576
637
 
577
638
 
578
- FILTERS: dict[str, callable] = {
639
+ FILTERS: dict[str, callable] = { # pragma: no cov
579
640
  "abs": abs,
580
641
  "str": str,
581
642
  "int": int,
@@ -590,17 +651,18 @@ class FilterFunc(Protocol):
590
651
 
591
652
  name: str
592
653
 
593
- def __call__(self, *args, **kwargs): ...
654
+ def __call__(self, *args, **kwargs): ... # pragma: no cov
594
655
 
595
656
 
596
- def custom_filter(name: str) -> Callable[P, TagFunc]:
657
+ def custom_filter(name: str) -> Callable[P, FilterFunc]:
597
658
  """Custom filter decorator function that set function attributes, ``filter``
598
659
  for making filter registries variable.
599
660
 
600
661
  :param: name: A filter name for make different use-case of a function.
662
+ :rtype: Callable[P, FilterFunc]
601
663
  """
602
664
 
603
- def func_internal(func: Callable[[...], Any]) -> TagFunc:
665
+ def func_internal(func: Callable[[...], Any]) -> FilterFunc:
604
666
  func.filter = name
605
667
 
606
668
  @wraps(func)
@@ -622,7 +684,7 @@ def make_filter_registry() -> dict[str, FilterRegistry]:
622
684
  :rtype: dict[str, Registry]
623
685
  """
624
686
  rs: dict[str, Registry] = {}
625
- for module in config().engine.registry_filter:
687
+ for module in load_config().engine.registry_filter:
626
688
  # NOTE: try to sequential import task functions
627
689
  try:
628
690
  importer = import_module(module)
@@ -644,7 +706,10 @@ def make_filter_registry() -> dict[str, FilterRegistry]:
644
706
  def get_args_const(
645
707
  expr: str,
646
708
  ) -> tuple[str, list[Constant], dict[str, Constant]]:
647
- """Get arguments and keyword-arguments from function calling string."""
709
+ """Get arguments and keyword-arguments from function calling string.
710
+
711
+ :rtype: tuple[str, list[Constant], dict[str, Constant]]
712
+ """
648
713
  try:
649
714
  mod: Module = parse(expr)
650
715
  except SyntaxError:
@@ -678,6 +743,7 @@ def get_args_const(
678
743
 
679
744
  @custom_filter("fmt")
680
745
  def datetime_format(value: datetime, fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
746
+ """Format datetime object to string with the format."""
681
747
  if isinstance(value, datetime):
682
748
  return value.strftime(fmt)
683
749
  raise UtilException(
@@ -699,8 +765,8 @@ def map_post_filter(
699
765
  """
700
766
  for _filter in post_filter:
701
767
  func_name, _args, _kwargs = get_args_const(_filter)
702
- args = [arg.value for arg in _args]
703
- kwargs = {k: v.value for k, v in _kwargs.items()}
768
+ args: list = [arg.value for arg in _args]
769
+ kwargs: dict = {k: v.value for k, v in _kwargs.items()}
704
770
 
705
771
  if func_name not in filters:
706
772
  raise UtilException(
@@ -741,8 +807,8 @@ def not_in_template(value: Any, *, not_in: str = "matrix.") -> bool:
741
807
  elif not isinstance(value, str):
742
808
  return False
743
809
  return any(
744
- (not found.group("caller").strip().startswith(not_in))
745
- for found in Re.RE_CALLER.finditer(value.strip())
810
+ (not found.caller.strip().startswith(not_in))
811
+ for found in Re.finditer_caller(value.strip())
746
812
  )
747
813
 
748
814
 
@@ -783,18 +849,16 @@ def str2template(
783
849
 
784
850
  # NOTE: remove space before and after this string value.
785
851
  value: str = value.strip()
786
- for found in Re.RE_CALLER.finditer(value):
852
+ for found in Re.finditer_caller(value):
787
853
  # NOTE:
788
854
  # Get caller and filter values that setting inside;
789
855
  #
790
856
  # ... ``${{ <caller-value> [ | <filter-value>] ... }}``
791
857
  #
792
- caller: str = found.group("caller")
858
+ caller: str = found.caller
793
859
  pfilter: list[str] = [
794
860
  i.strip()
795
- for i in (
796
- found.group("post_filters").strip().removeprefix("|").split("|")
797
- )
861
+ for i in (found.post_filters.strip().removeprefix("|").split("|"))
798
862
  if i != ""
799
863
  ]
800
864
  if not hasdot(caller, params):
@@ -807,7 +871,7 @@ def str2template(
807
871
  # If type of getter caller is not string type and it does not use to
808
872
  # concat other string value, it will return origin value from the
809
873
  # ``getdot`` function.
810
- if value.replace(found.group(0), "", 1) == "":
874
+ if value.replace(found.full, "", 1) == "":
811
875
  return map_post_filter(getter, pfilter, filters=filters)
812
876
 
813
877
  # NOTE: map post-filter function.
@@ -815,7 +879,7 @@ def str2template(
815
879
  if not isinstance(getter, str):
816
880
  getter: str = str(getter)
817
881
 
818
- value: str = value.replace(found.group(0), getter, 1)
882
+ value: str = value.replace(found.full, getter, 1)
819
883
 
820
884
  return search_env_replace(value)
821
885
 
@@ -845,8 +909,12 @@ def param2template(
845
909
 
846
910
 
847
911
  def filter_func(value: Any) -> Any:
848
- """Filter own created function out of any value with replace it to its
849
- function name. If it is built-in function, it does not have any changing.
912
+ """Filter out an own created function of any value of mapping context by
913
+ replacing it to its function name. If it is built-in function, it does not
914
+ have any changing.
915
+
916
+ :param value: A value context data that want to filter out function value.
917
+ :type: The same type of an input ``value``.
850
918
  """
851
919
  if isinstance(value, dict):
852
920
  return {k: filter_func(value[k]) for k in value}
@@ -869,14 +937,20 @@ def dash2underscore(
869
937
  *,
870
938
  fixed: str | None = None,
871
939
  ) -> DictData:
872
- """Change key name that has dash to underscore."""
940
+ """Change key name that has dash to underscore.
941
+
942
+ :rtype: DictData
943
+ """
873
944
  if key in values:
874
945
  values[(fixed or key.replace("-", "_"))] = values.pop(key)
875
946
  return values
876
947
 
877
948
 
878
949
  def cross_product(matrix: Matrix) -> Iterator[DictData]:
879
- """Iterator of products value from matrix."""
950
+ """Iterator of products value from matrix.
951
+
952
+ :rtype: Iterator[DictData]
953
+ """
880
954
  yield from (
881
955
  {_k: _v for e in mapped for _k, _v in e.items()}
882
956
  for mapped in product(
@@ -897,7 +971,7 @@ def batch(iterable: Iterator[Any], n: int) -> Iterator[Any]:
897
971
  """
898
972
  if n < 1:
899
973
  raise ValueError("n must be at least one")
900
- it = iter(iterable)
974
+ it: Iterator[Any] = iter(iterable)
901
975
  while True:
902
976
  chunk_it = islice(it, n)
903
977
  try:
@@ -905,3 +979,7 @@ def batch(iterable: Iterator[Any], n: int) -> Iterator[Any]:
905
979
  except StopIteration:
906
980
  return
907
981
  yield chain((first_el,), chunk_it)
982
+
983
+
984
+ def queue2str(queue: list[datetime]) -> Iterator[str]: # pragma: no cov
985
+ return (f"{q:%Y-%m-%d %H:%M:%S}" for q in queue)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.13
3
+ Version: 0.0.15
4
4
  Summary: Lightweight workflow orchestration with less dependencies
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -22,12 +22,13 @@ 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-io >=0.1.12
25
+ Requires-Dist: ddeutil >=0.4.0
26
+ Requires-Dist: ddeutil-io >=0.1.13
26
27
  Requires-Dist: python-dotenv ==1.0.1
27
28
  Requires-Dist: typer <1.0.0,==0.12.5
28
29
  Requires-Dist: schedule <2.0.0,==1.2.2
29
30
  Provides-Extra: api
30
- Requires-Dist: fastapi <1.0.0,>=0.114.1 ; extra == 'api'
31
+ Requires-Dist: fastapi <1.0.0,>=0.115.0 ; extra == 'api'
31
32
 
32
33
  # Workflow
33
34
 
@@ -62,7 +63,7 @@ configuration. It called **Metadata Driven Data Workflow**.
62
63
 
63
64
  > [!NOTE]
64
65
  > _Disclaimer_: I inspire the dynamic statement from the [**GitHub Action**](https://github.com/features/actions)
65
- > `.yml` files and all of config file from several data orchestration framework
66
+ > with `.yml` files and all of config file from several data orchestration framework
66
67
  > tools from my experience on Data Engineer. :grimacing:
67
68
  >
68
69
  > Other workflow that I interest on them and pick some interested feature to this
@@ -92,6 +93,7 @@ this package with application add-ons, you should add `app` in installation;
92
93
  > | ddeutil-workflow:python3.10 | `3.10` | :x: |
93
94
  > | ddeutil-workflow:python3.11 | `3.11` | :x: |
94
95
  > | ddeutil-workflow:python3.12 | `3.12` | :x: |
96
+ > | ddeutil-workflow:python3.12 | `3.13` | :x: |
95
97
 
96
98
  ## :beers: Usage
97
99
 
@@ -0,0 +1,22 @@
1
+ ddeutil/workflow/__about__.py,sha256=w_vBOopUg1crMbDyfdE0LgsxsncnhGYp0D39LSSnSVI,28
2
+ ddeutil/workflow/__init__.py,sha256=-DIy8SGFsD7_wqp-V-K8v8jTxacmqrcyj_SFx1WS6qg,687
3
+ ddeutil/workflow/__types.py,sha256=WWugALcayRiP0IQO-eBWK767_XxK7KGlY7SuVgyaJnk,3196
4
+ ddeutil/workflow/api.py,sha256=cwju_qhY6m0kLtaoa77QLglC9tl7RjjZ4UnJYV3SlQQ,4810
5
+ ddeutil/workflow/cli.py,sha256=Ikcq526WeIl-737-v55T0PwAZ2pNiZFxlN0Y-DjhDbQ,3374
6
+ ddeutil/workflow/conf.py,sha256=D0g7rHXilpGwOD36QwVd9I5kEwqsAUA0Z3tAINS2Pws,1287
7
+ ddeutil/workflow/cron.py,sha256=naWefHc3EnVo41Yf1zQeXOzF27YlTlnfj0XnQ6_HO-U,25514
8
+ ddeutil/workflow/exceptions.py,sha256=Uf1-Tn8rAzj0aiVHSqo4fBqO80W0za7UFZgKv24E-tg,706
9
+ ddeutil/workflow/job.py,sha256=9H_2C0ikD5y6jLVdIBj8de4CdSpS632XOfqYVhM4bHI,21582
10
+ ddeutil/workflow/log.py,sha256=Ev-Szi0KC_MmbFY4g4BWv6tUSmcLKWKZ03ZInmYPmgU,6490
11
+ ddeutil/workflow/on.py,sha256=vsZG19mNoztDSB_ObD_4ZWPKgHYpBDJMWw97ZiTavNE,7237
12
+ ddeutil/workflow/repeat.py,sha256=e3dekPTlMlxCCizfBYsZ8dD8Juy4rtfqDZJU3Iky2oA,5011
13
+ ddeutil/workflow/route.py,sha256=ABEk-WlVo9XGFc7zCPbckX33URCNH7woQFU1keX_8PQ,6970
14
+ ddeutil/workflow/scheduler.py,sha256=12Dd5CVphOVKjUwoiB8dCHt4WpYRPG3dSOt-pR6NNxc,46167
15
+ ddeutil/workflow/stage.py,sha256=Avz1Mbb8WAP6kFn0bnN0p14-EnQ_AzdKr435JRxjkao,23844
16
+ ddeutil/workflow/utils.py,sha256=XUD5hoygAyxi4xo1spTacoDNGKN2TlRob_o8qfCj4Pc,30993
17
+ ddeutil_workflow-0.0.15.dist-info/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
18
+ ddeutil_workflow-0.0.15.dist-info/METADATA,sha256=6JvY9y-cT3WnirRva45NS582Iz7ZuXJZpsiCtN57OoA,11653
19
+ ddeutil_workflow-0.0.15.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
20
+ ddeutil_workflow-0.0.15.dist-info/entry_points.txt,sha256=0BVOgO3LdUdXVZ-CiHHDKxzEk2c8J30jEwHeKn2YCWI,62
21
+ ddeutil_workflow-0.0.15.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
22
+ ddeutil_workflow-0.0.15.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.0.0)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,21 +0,0 @@
1
- ddeutil/workflow/__about__.py,sha256=StSv8QbtF16HmqqJ8TfZlgbD1BgLyYHcubwplM-eSto,28
2
- ddeutil/workflow/__init__.py,sha256=aEQiEWwTPGhfwpzzdb99xXaHchi5ABWUHl2iLIyT18E,664
3
- ddeutil/workflow/__types.py,sha256=SYMoxbENQX8uPsiCZkjtpHAqqHOh8rUrarAFicAJd0E,1773
4
- ddeutil/workflow/api.py,sha256=xVP8eGu1nnR8HM0ULTwxs9TV9tsxCOjZ68cAffw2f3o,4802
5
- ddeutil/workflow/cli.py,sha256=Ikcq526WeIl-737-v55T0PwAZ2pNiZFxlN0Y-DjhDbQ,3374
6
- ddeutil/workflow/cron.py,sha256=uhp3E5pl_tX_H88bsDujcwdhZmOE53csyV-ouPpPdK8,25321
7
- ddeutil/workflow/exceptions.py,sha256=Uf1-Tn8rAzj0aiVHSqo4fBqO80W0za7UFZgKv24E-tg,706
8
- ddeutil/workflow/job.py,sha256=iwiDUGgnId6QFkzqLZuiWFYUNfY-qYJebaGwhFnMKH8,20633
9
- ddeutil/workflow/log.py,sha256=bZyyqf3oNBB8oRf8RI0YvII7wHHoj4wC-nmW_pQjQ1c,6036
10
- ddeutil/workflow/on.py,sha256=vsZG19mNoztDSB_ObD_4ZWPKgHYpBDJMWw97ZiTavNE,7237
11
- ddeutil/workflow/repeat.py,sha256=e3dekPTlMlxCCizfBYsZ8dD8Juy4rtfqDZJU3Iky2oA,5011
12
- ddeutil/workflow/route.py,sha256=ABEk-WlVo9XGFc7zCPbckX33URCNH7woQFU1keX_8PQ,6970
13
- ddeutil/workflow/scheduler.py,sha256=fe9NGobU8zN95C0FY2PB7eYI9tzyvyh-_K7vcUFFBO8,41674
14
- ddeutil/workflow/stage.py,sha256=rGFdLLYj6eo8aqSRr4lkBBdah4KIzCzKefJeg0hk0O8,22289
15
- ddeutil/workflow/utils.py,sha256=TbqgPkDDYBpqCZ7HV2TU3AH1_Mv-zfrJdwVL-l2SPUo,28559
16
- ddeutil_workflow-0.0.13.dist-info/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
17
- ddeutil_workflow-0.0.13.dist-info/METADATA,sha256=HuSRkM94JcefbkiCR6_3khXeUiAsb0FMirS3d7qWGHk,11556
18
- ddeutil_workflow-0.0.13.dist-info/WHEEL,sha256=5Mi1sN9lKoFv_gxcPtisEVrJZihrm_beibeg5R6xb4I,91
19
- ddeutil_workflow-0.0.13.dist-info/entry_points.txt,sha256=0BVOgO3LdUdXVZ-CiHHDKxzEk2c8J30jEwHeKn2YCWI,62
20
- ddeutil_workflow-0.0.13.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
21
- ddeutil_workflow-0.0.13.dist-info/RECORD,,