ddeutil-workflow 0.0.74__py3-none-any.whl → 0.0.75__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/__about__.py +1 -1
- ddeutil/workflow/__cron.py +6 -4
- ddeutil/workflow/conf.py +10 -10
- ddeutil/workflow/params.py +6 -4
- ddeutil/workflow/result.py +3 -3
- ddeutil/workflow/traces.py +6 -4
- ddeutil/workflow/utils.py +9 -22
- ddeutil/workflow/workflow.py +14 -9
- {ddeutil_workflow-0.0.74.dist-info → ddeutil_workflow-0.0.75.dist-info}/METADATA +3 -3
- {ddeutil_workflow-0.0.74.dist-info → ddeutil_workflow-0.0.75.dist-info}/RECORD +14 -14
- {ddeutil_workflow-0.0.74.dist-info → ddeutil_workflow-0.0.75.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.74.dist-info → ddeutil_workflow-0.0.75.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.74.dist-info → ddeutil_workflow-0.0.75.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.74.dist-info → ddeutil_workflow-0.0.75.dist-info}/top_level.txt +0 -0
ddeutil/workflow/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__: str = "0.0.
|
1
|
+
__version__: str = "0.0.75"
|
ddeutil/workflow/__cron.py
CHANGED
@@ -793,10 +793,11 @@ class CronRunner:
|
|
793
793
|
"Invalid type of `tz` parameter, it should be str or "
|
794
794
|
"ZoneInfo instance."
|
795
795
|
)
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
796
|
+
else:
|
797
|
+
try:
|
798
|
+
self.tz = ZoneInfo(tz)
|
799
|
+
except ZoneInfoNotFoundError as err:
|
800
|
+
raise ValueError(f"Invalid timezone: {tz}") from err
|
800
801
|
|
801
802
|
# NOTE: Prepare date
|
802
803
|
if date:
|
@@ -807,6 +808,7 @@ class CronRunner:
|
|
807
808
|
if tz is not None:
|
808
809
|
self.date: datetime = date.astimezone(self.tz)
|
809
810
|
else:
|
811
|
+
self.tz = date.tzinfo
|
810
812
|
self.date: datetime = date
|
811
813
|
else:
|
812
814
|
self.date: datetime = datetime.now(tz=self.tz)
|
ddeutil/workflow/conf.py
CHANGED
@@ -89,16 +89,6 @@ class Config: # pragma: no cov
|
|
89
89
|
"""
|
90
90
|
return Path(env("CORE_CONF_PATH", "./conf"))
|
91
91
|
|
92
|
-
@property
|
93
|
-
def tz(self) -> ZoneInfo:
|
94
|
-
"""Timezone value that return with the `ZoneInfo` object and use for all
|
95
|
-
datetime object in this workflow engine.
|
96
|
-
|
97
|
-
Returns:
|
98
|
-
ZoneInfo: The timezone configuration for the workflow engine.
|
99
|
-
"""
|
100
|
-
return ZoneInfo(env("CORE_TIMEZONE", "UTC"))
|
101
|
-
|
102
92
|
@property
|
103
93
|
def generate_id_simple_mode(self) -> bool:
|
104
94
|
"""Flag for generate running ID with simple mode. That does not use
|
@@ -143,6 +133,16 @@ class Config: # pragma: no cov
|
|
143
133
|
"""
|
144
134
|
return str2bool(env("LOG_DEBUG_MODE", "true"))
|
145
135
|
|
136
|
+
@property
|
137
|
+
def log_tz(self) -> ZoneInfo:
|
138
|
+
"""Timezone value that return with the `ZoneInfo` object and use for all
|
139
|
+
datetime object in this workflow engine.
|
140
|
+
|
141
|
+
Returns:
|
142
|
+
ZoneInfo: The timezone configuration for the workflow engine.
|
143
|
+
"""
|
144
|
+
return ZoneInfo(env("LOG_TIMEZONE", "UTC"))
|
145
|
+
|
146
146
|
@property
|
147
147
|
def log_format(self) -> str:
|
148
148
|
return env(
|
ddeutil/workflow/params.py
CHANGED
@@ -52,7 +52,7 @@ from pydantic import BaseModel, Field
|
|
52
52
|
|
53
53
|
from .__types import StrOrInt
|
54
54
|
from .errors import ParamError
|
55
|
-
from .utils import get_d_now, get_dt_now
|
55
|
+
from .utils import UTC, get_d_now, get_dt_now
|
56
56
|
|
57
57
|
T = TypeVar("T")
|
58
58
|
|
@@ -169,16 +169,18 @@ class DatetimeParam(DefaultParam):
|
|
169
169
|
return self.default
|
170
170
|
|
171
171
|
if isinstance(value, datetime):
|
172
|
-
|
172
|
+
if value.tzinfo is None:
|
173
|
+
return value.replace(tzinfo=UTC)
|
174
|
+
return value.astimezone(UTC)
|
173
175
|
elif isinstance(value, date):
|
174
|
-
return datetime(value.year, value.month, value.day)
|
176
|
+
return datetime(value.year, value.month, value.day, tzinfo=UTC)
|
175
177
|
elif not isinstance(value, str):
|
176
178
|
raise ParamError(
|
177
179
|
f"Value that want to convert to datetime does not support for "
|
178
180
|
f"type: {type(value)}"
|
179
181
|
)
|
180
182
|
try:
|
181
|
-
return datetime.fromisoformat(value)
|
183
|
+
return datetime.fromisoformat(value).replace(tzinfo=UTC)
|
182
184
|
except ValueError:
|
183
185
|
raise ParamError(
|
184
186
|
f"Invalid the ISO format string for datetime: {value!r}"
|
ddeutil/workflow/result.py
CHANGED
@@ -43,7 +43,7 @@ from . import (
|
|
43
43
|
from .__types import DictData
|
44
44
|
from .audits import Trace, get_trace
|
45
45
|
from .errors import ResultError
|
46
|
-
from .utils import default_gen_id,
|
46
|
+
from .utils import default_gen_id, get_dt_now
|
47
47
|
|
48
48
|
|
49
49
|
class Status(str, Enum):
|
@@ -190,7 +190,7 @@ class Result:
|
|
190
190
|
info: DictData = field(default_factory=dict)
|
191
191
|
run_id: Optional[str] = field(default_factory=default_gen_id)
|
192
192
|
parent_run_id: Optional[str] = field(default=None, compare=False)
|
193
|
-
ts: datetime = field(default_factory=
|
193
|
+
ts: datetime = field(default_factory=get_dt_now, compare=False)
|
194
194
|
trace: Optional[Trace] = field(default=None, compare=False, repr=False)
|
195
195
|
extras: DictData = field(default_factory=dict, compare=False, repr=False)
|
196
196
|
|
@@ -266,7 +266,7 @@ class Result:
|
|
266
266
|
|
267
267
|
:rtype: float
|
268
268
|
"""
|
269
|
-
return (
|
269
|
+
return (get_dt_now() - self.ts).total_seconds()
|
270
270
|
|
271
271
|
|
272
272
|
def catch(
|
ddeutil/workflow/traces.py
CHANGED
@@ -167,7 +167,9 @@ class TraceMeta(BaseModel): # pragma: no cov
|
|
167
167
|
|
168
168
|
mode: Literal["stdout", "stderr"] = Field(description="A meta mode.")
|
169
169
|
level: str = Field(description="A log level.")
|
170
|
-
datetime: str = Field(
|
170
|
+
datetime: str = Field(
|
171
|
+
description="A datetime string with the specific config format."
|
172
|
+
)
|
171
173
|
process: int = Field(description="A process ID.")
|
172
174
|
thread: int = Field(description="A thread ID.")
|
173
175
|
message: str = Field(description="A message log.")
|
@@ -234,9 +236,9 @@ class TraceMeta(BaseModel): # pragma: no cov
|
|
234
236
|
mode=mode,
|
235
237
|
level=level,
|
236
238
|
datetime=(
|
237
|
-
get_dt_now(
|
238
|
-
|
239
|
-
)
|
239
|
+
get_dt_now()
|
240
|
+
.astimezone(dynamic("log_tz", extras=extras))
|
241
|
+
.strftime(dynamic("log_datetime_format", extras=extras))
|
240
242
|
),
|
241
243
|
process=os.getpid(),
|
242
244
|
thread=get_ident(),
|
ddeutil/workflow/utils.py
CHANGED
@@ -109,45 +109,32 @@ def replace_sec(dt: datetime) -> datetime:
|
|
109
109
|
|
110
110
|
|
111
111
|
def clear_tz(dt: datetime) -> datetime:
|
112
|
-
"""Replace timezone info on an input datetime object to
|
113
|
-
return dt.replace(tzinfo=
|
112
|
+
"""Replace timezone info on an input datetime object to UTC."""
|
113
|
+
return dt.replace(tzinfo=UTC)
|
114
114
|
|
115
115
|
|
116
|
-
def get_dt_now(
|
116
|
+
def get_dt_now(offset: float = 0.0) -> datetime:
|
117
117
|
"""Return the current datetime object.
|
118
118
|
|
119
|
-
:param tz: A ZoneInfo object for replace timezone of return datetime object.
|
120
119
|
:param offset: An offset second value.
|
121
120
|
|
122
121
|
:rtype: datetime
|
123
122
|
:return: The current datetime object that use an input timezone or UTC.
|
124
123
|
"""
|
125
|
-
return datetime.now(
|
124
|
+
return datetime.now().replace(tzinfo=UTC) - timedelta(seconds=offset)
|
126
125
|
|
127
126
|
|
128
|
-
def
|
129
|
-
"""Get current datetime with no timezone.
|
130
|
-
|
131
|
-
Returns the current datetime object using the None timezone.
|
132
|
-
|
133
|
-
Returns:
|
134
|
-
datetime: Current datetime with no timezone
|
135
|
-
"""
|
136
|
-
return get_dt_now(tz=None)
|
137
|
-
|
138
|
-
|
139
|
-
def get_d_now(
|
140
|
-
tz: Optional[ZoneInfo] = None, offset: float = 0.0
|
141
|
-
) -> date: # pragma: no cov
|
127
|
+
def get_d_now(offset: float = 0.0) -> date: # pragma: no cov
|
142
128
|
"""Return the current date object.
|
143
129
|
|
144
|
-
:param tz: A ZoneInfo object for replace timezone of return date object.
|
145
130
|
:param offset: An offset second value.
|
146
131
|
|
147
132
|
:rtype: date
|
148
133
|
:return: The current date object that use an input timezone or UTC.
|
149
134
|
"""
|
150
|
-
return (
|
135
|
+
return (
|
136
|
+
datetime.now().replace(tzinfo=UTC) - timedelta(seconds=offset)
|
137
|
+
).date()
|
151
138
|
|
152
139
|
|
153
140
|
def get_diff_sec(dt: datetime, offset: float = 0.0) -> int:
|
@@ -240,7 +227,7 @@ def gen_id(
|
|
240
227
|
if not isinstance(value, str):
|
241
228
|
value: str = str(value)
|
242
229
|
|
243
|
-
dt: datetime = datetime.now(tz=
|
230
|
+
dt: datetime = datetime.now(tz=UTC)
|
244
231
|
if dynamic("generate_id_simple_mode", f=simple_mode, extras=extras):
|
245
232
|
return (f"{dt:%Y%m%d%H%M%S%f}T" if unique else "") + hash_str(
|
246
233
|
f"{(value if sensitive else value.lower())}", n=10
|
ddeutil/workflow/workflow.py
CHANGED
@@ -36,7 +36,6 @@ from queue import Queue
|
|
36
36
|
from textwrap import dedent
|
37
37
|
from threading import Event as ThreadEvent
|
38
38
|
from typing import Any, Optional, Union
|
39
|
-
from zoneinfo import ZoneInfo
|
40
39
|
|
41
40
|
from pydantic import BaseModel, Field
|
42
41
|
from pydantic.functional_validators import field_validator, model_validator
|
@@ -64,8 +63,9 @@ from .result import (
|
|
64
63
|
from .reusables import has_template, param2template
|
65
64
|
from .traces import Trace, get_trace
|
66
65
|
from .utils import (
|
66
|
+
UTC,
|
67
67
|
gen_id,
|
68
|
-
|
68
|
+
get_dt_now,
|
69
69
|
replace_sec,
|
70
70
|
)
|
71
71
|
|
@@ -153,14 +153,14 @@ class Workflow(BaseModel):
|
|
153
153
|
description="A mapping of job ID and job model that already loaded.",
|
154
154
|
)
|
155
155
|
created_at: datetime = Field(
|
156
|
-
default_factory=
|
156
|
+
default_factory=get_dt_now,
|
157
157
|
description=(
|
158
158
|
"A created datetime of this workflow template when loading from "
|
159
159
|
"file."
|
160
160
|
),
|
161
161
|
)
|
162
162
|
updated_dt: datetime = Field(
|
163
|
-
default_factory=
|
163
|
+
default_factory=get_dt_now,
|
164
164
|
description=(
|
165
165
|
"A updated datetime of this workflow template when loading from "
|
166
166
|
"file."
|
@@ -369,12 +369,15 @@ class Workflow(BaseModel):
|
|
369
369
|
Returns:
|
370
370
|
datetime: The validated release datetime.
|
371
371
|
"""
|
372
|
-
|
372
|
+
if dt.tzinfo is None:
|
373
|
+
dt = dt.replace(tzinfo=UTC)
|
374
|
+
|
375
|
+
release: datetime = replace_sec(dt.astimezone(UTC))
|
373
376
|
if not self.on:
|
374
377
|
return release
|
375
378
|
|
376
379
|
for on in self.on.schedule:
|
377
|
-
if release == on.cronjob.schedule(release).next:
|
380
|
+
if release == on.cronjob.schedule(release, tz=UTC).next:
|
378
381
|
return release
|
379
382
|
raise WorkflowError(
|
380
383
|
"Release datetime does not support for this workflow"
|
@@ -385,8 +388,8 @@ class Workflow(BaseModel):
|
|
385
388
|
release: datetime,
|
386
389
|
params: DictData,
|
387
390
|
*,
|
388
|
-
release_type: ReleaseType = NORMAL,
|
389
391
|
run_id: Optional[str] = None,
|
392
|
+
release_type: ReleaseType = NORMAL,
|
390
393
|
audit: type[Audit] = None,
|
391
394
|
override_log_name: Optional[str] = None,
|
392
395
|
timeout: int = 600,
|
@@ -420,25 +423,27 @@ class Workflow(BaseModel):
|
|
420
423
|
:rtype: Result
|
421
424
|
"""
|
422
425
|
name: str = override_log_name or self.name
|
426
|
+
|
427
|
+
# NOTE: Generate the parent running ID with not None value.
|
423
428
|
if run_id:
|
424
429
|
parent_run_id: str = run_id
|
425
430
|
run_id: str = gen_id(name, unique=True)
|
426
431
|
else:
|
427
432
|
run_id: str = gen_id(name, unique=True)
|
428
433
|
parent_run_id: str = run_id
|
434
|
+
|
429
435
|
context: DictData = {}
|
430
436
|
trace: Trace = get_trace(
|
431
437
|
run_id, parent_run_id=parent_run_id, extras=self.extras
|
432
438
|
)
|
433
439
|
release: datetime = self.validate_release(dt=release)
|
434
440
|
trace.info(f"[RELEASE]: Start {name!r} : {release:%Y-%m-%d %H:%M:%S}")
|
435
|
-
tz: ZoneInfo = dynamic("tz", extras=self.extras)
|
436
441
|
values: DictData = param2template(
|
437
442
|
params,
|
438
443
|
params={
|
439
444
|
"release": {
|
440
445
|
"logical_date": release,
|
441
|
-
"execute_date":
|
446
|
+
"execute_date": get_dt_now(),
|
442
447
|
"run_id": run_id,
|
443
448
|
}
|
444
449
|
},
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ddeutil-workflow
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.75
|
4
4
|
Summary: Lightweight workflow orchestration with YAML template
|
5
5
|
Author-email: ddeutils <korawich.anu@gmail.com>
|
6
6
|
License: MIT
|
@@ -68,7 +68,7 @@ by a `.yaml` template.
|
|
68
68
|
3. All parallel tasks inside workflow core engine use **Multi-Threading** pool
|
69
69
|
(Python 3.13 unlock GIL 🐍🔓)
|
70
70
|
4. Recommend to pass a **Secret Value** with environment variable in YAML template 🔐
|
71
|
-
5. Any datatime value convert to **
|
71
|
+
5. Any datatime value convert to **UTC Timezone** 🌐
|
72
72
|
|
73
73
|
---
|
74
74
|
|
@@ -288,10 +288,10 @@ it will use default value and do not raise any error to you.
|
|
288
288
|
| **REGISTRY_CALLER** | CORE | `.` | List of importable string for the call stage. |
|
289
289
|
| **REGISTRY_FILTER** | CORE | `ddeutil.workflow.templates` | List of importable string for the filter template. |
|
290
290
|
| **CONF_PATH** | CORE | `./conf` | The config path that keep all template `.yaml` files. |
|
291
|
-
| **TIMEZONE** | CORE | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. |
|
292
291
|
| **STAGE_DEFAULT_ID** | CORE | `false` | A flag that enable default stage ID that use for catch an execution output. |
|
293
292
|
| **GENERATE_ID_SIMPLE_MODE** | CORE | `true` | A flog that enable generating ID with `md5` algorithm. |
|
294
293
|
| **DEBUG_MODE** | LOG | `true` | A flag that enable logging with debug level mode. |
|
294
|
+
| **TIMEZONE** | LOG | `Asia/Bangkok` | A Timezone string value that will pass to `ZoneInfo` object. |
|
295
295
|
| **FORMAT** | LOG | `%(asctime)s.%(msecs)03d (%(name)-10s, %(process)-5d,%(thread)-5d) [%(levelname)-7s] %(message)-120s (%(filename)s:%(lineno)s)` | A trace message console format. |
|
296
296
|
| **FORMAT_FILE** | LOG | `{datetime} ({process:5d}, {thread:5d}) {message:120s} ({filename}:{lineno})` | A trace message format that use to write to target pointer. |
|
297
297
|
| **DATETIME_FORMAT** | LOG | `%Y-%m-%d %H:%M:%S` | A datetime format of the trace log. |
|
@@ -1,30 +1,30 @@
|
|
1
|
-
ddeutil/workflow/__about__.py,sha256=
|
2
|
-
ddeutil/workflow/__cron.py,sha256=
|
1
|
+
ddeutil/workflow/__about__.py,sha256=iaM2-rdtvV7sEgUAaF8bPdKBNJ3_zhFdOi5A1NC-s3U,28
|
2
|
+
ddeutil/workflow/__cron.py,sha256=0_40rdL3QEChMcikrLQfQPb67YfHmbYr_aPCp6NM7Cw,28912
|
3
3
|
ddeutil/workflow/__init__.py,sha256=kjKFdRNOh19IiLdmSFPKesU8BqSijEWHRZ8_fKGXFUk,3276
|
4
4
|
ddeutil/workflow/__main__.py,sha256=Qd-f8z2Q2vpiEP2x6PBFsJrpACWDVxFKQk820MhFmHo,59
|
5
5
|
ddeutil/workflow/__types.py,sha256=tA2vsr6mzTSzbWB1sb62c5GgxODlfVRz6FvgLNJtQao,4788
|
6
6
|
ddeutil/workflow/audits.py,sha256=PZ0dDBBANpXoLHlDrmlEoIXr0iYK9PCw4-9tPJX9UXE,12528
|
7
7
|
ddeutil/workflow/cli.py,sha256=V42C3lmtzRUiIjgMjLlr7v600PX0A3Aqbp8ccUSgQew,5244
|
8
|
-
ddeutil/workflow/conf.py,sha256=
|
8
|
+
ddeutil/workflow/conf.py,sha256=UCw6v2GFD3tA2LRbp7vLifXniey0P5Ef0U9eBPknrWk,16267
|
9
9
|
ddeutil/workflow/errors.py,sha256=n10YjXptY4iiY57FFVq22aedlUojpMuB5cM7aQ0KNpo,5522
|
10
10
|
ddeutil/workflow/event.py,sha256=siChcBhsu4ejzW1fK0tjHPXQVaSUCSxPYDgDrh6duwo,13676
|
11
11
|
ddeutil/workflow/job.py,sha256=oCjxG1Zz2D9Yc2SuZth3k1geIQ-AJ3oLs_38g5NMXLE,43994
|
12
|
-
ddeutil/workflow/params.py,sha256=
|
13
|
-
ddeutil/workflow/result.py,sha256=
|
12
|
+
ddeutil/workflow/params.py,sha256=Cyz142OcvENIZrM7Efc2xuGPmmFBhROifP5ojoaCezg,13658
|
13
|
+
ddeutil/workflow/result.py,sha256=LBzBdVlqXsG4vgHQadb_bfuxY32yQ-dlBf-HmnX-dBk,9135
|
14
14
|
ddeutil/workflow/reusables.py,sha256=q_OA-oifCGIhW_5j6hTZXZk7FBOmDt0xVrtNnscJfNg,23294
|
15
15
|
ddeutil/workflow/stages.py,sha256=2Rz9NGVOftIqiYaSgasHDS8MvgvLp3jtAjBHEwdtx7U,121837
|
16
|
-
ddeutil/workflow/traces.py,sha256=
|
17
|
-
ddeutil/workflow/utils.py,sha256=
|
18
|
-
ddeutil/workflow/workflow.py,sha256=
|
16
|
+
ddeutil/workflow/traces.py,sha256=KFGeIFk1ocDWsaNkjXJk3LQHvq8dRfIf3hZsTuCpF1Y,28334
|
17
|
+
ddeutil/workflow/utils.py,sha256=EXhIuWzOJHvlcoAdyvuDUomGtMTIB59HxOLpj2VJ1bI,10857
|
18
|
+
ddeutil/workflow/workflow.py,sha256=izGz5teb0unlKtnpklvcdWFHK1af60IJVeSbUhMdPtA,41206
|
19
19
|
ddeutil/workflow/api/__init__.py,sha256=5DzYL3ngceoRshh5HYCSVWChqNJSiP01E1bEd8XxPi0,4799
|
20
20
|
ddeutil/workflow/api/log_conf.py,sha256=WfS3udDLSyrP-C80lWOvxxmhd_XWKvQPkwDqKblcH3E,1834
|
21
21
|
ddeutil/workflow/api/routes/__init__.py,sha256=JRaJZB0D6mgR17MbZo8yLtdYDtD62AA8MdKlFqhG84M,420
|
22
22
|
ddeutil/workflow/api/routes/job.py,sha256=-lbZ_hS9pEdSy6zeke5qrXEgdNxtQ2w9in7cHuM2Jzs,2536
|
23
23
|
ddeutil/workflow/api/routes/logs.py,sha256=RiZ62eQVMWArPHE3lpan955U4DdLLkethlvSMlwF7Mg,5312
|
24
24
|
ddeutil/workflow/api/routes/workflows.py,sha256=1Mqx4Hft4uJglgJI-Wcw-JzkhomFYZrtP0DnQDBkAFQ,4410
|
25
|
-
ddeutil_workflow-0.0.
|
26
|
-
ddeutil_workflow-0.0.
|
27
|
-
ddeutil_workflow-0.0.
|
28
|
-
ddeutil_workflow-0.0.
|
29
|
-
ddeutil_workflow-0.0.
|
30
|
-
ddeutil_workflow-0.0.
|
25
|
+
ddeutil_workflow-0.0.75.dist-info/licenses/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
|
26
|
+
ddeutil_workflow-0.0.75.dist-info/METADATA,sha256=sJGNuv_qmZziEljC1T-9tCCOAGT3ovafMkAdRg-BvPo,15781
|
27
|
+
ddeutil_workflow-0.0.75.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
28
|
+
ddeutil_workflow-0.0.75.dist-info/entry_points.txt,sha256=qDTpPSauL0ciO6T4iSVt8bJeYrVEkkoEEw_RlGx6Kgk,63
|
29
|
+
ddeutil_workflow-0.0.75.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
|
30
|
+
ddeutil_workflow-0.0.75.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|