ddeutil-workflow 0.0.80__tar.gz → 0.0.81__tar.gz
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-0.0.80/src/ddeutil_workflow.egg-info → ddeutil_workflow-0.0.81}/PKG-INFO +1 -1
- ddeutil_workflow-0.0.81/src/ddeutil/workflow/__about__.py +1 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/cli.py +39 -29
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/conf.py +21 -21
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/errors.py +3 -2
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/reusables.py +16 -17
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/utils.py +0 -11
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/workflow.py +9 -11
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81/src/ddeutil_workflow.egg-info}/PKG-INFO +1 -1
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_audits.py +4 -1
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_conf.py +58 -26
- ddeutil_workflow-0.0.80/src/ddeutil/workflow/__about__.py +0 -1
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/LICENSE +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/README.md +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/pyproject.toml +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/setup.cfg +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/__cron.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/__init__.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/__main__.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/__types.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/__init__.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/log_conf.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/routes/__init__.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/routes/job.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/routes/logs.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/routes/workflows.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/audits.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/event.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/job.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/params.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/__init__.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/__init__.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/aws.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/az.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/container.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/gcs.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/result.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/stages.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/traces.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/SOURCES.txt +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/requires.txt +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test__cron.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test__regex.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_cli.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_errors.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_event.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_job.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_job_exec.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_job_exec_strategy.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_params.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_result.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_reusables_call_tag.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_reusables_func_model.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_reusables_template.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_reusables_template_filter.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_strategy.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_traces.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_utils.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_workflow.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_workflow_exec.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_workflow_exec_job.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_workflow_release.py +0 -0
- {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_workflow_rerun.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__version__: str = "0.0.81"
|
@@ -54,29 +54,30 @@ def init() -> None:
|
|
54
54
|
dedent(
|
55
55
|
"""
|
56
56
|
# Example workflow template.
|
57
|
-
wf-example:
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
57
|
+
name: wf-example:
|
58
|
+
type: Workflow
|
59
|
+
desc: |
|
60
|
+
An example workflow template that provide the demo of workflow.
|
61
|
+
params:
|
62
|
+
name:
|
63
|
+
type: str
|
64
|
+
default: "World"
|
65
|
+
jobs:
|
66
|
+
first-job:
|
67
|
+
stages:
|
68
|
+
|
69
|
+
- name: "Hello Stage"
|
70
|
+
echo: "Start say hi to the console"
|
71
|
+
|
72
|
+
- name: "Call tasks"
|
73
|
+
uses: tasks/say-hello-func@example
|
74
|
+
with:
|
75
|
+
name: ${{ params.name }}
|
76
|
+
|
77
|
+
second-job:
|
78
|
+
|
79
|
+
- name: "Hello Env"
|
80
|
+
echo: "Start say hi with ${ WORKFLOW_DEMO_HELLO }"
|
80
81
|
"""
|
81
82
|
).lstrip("\n")
|
82
83
|
)
|
@@ -89,12 +90,20 @@ def init() -> None:
|
|
89
90
|
dummy_tasks_path.write_text(
|
90
91
|
dedent(
|
91
92
|
"""
|
93
|
+
from typing import Any, Optional
|
94
|
+
|
92
95
|
from ddeutil.workflow import Result, tag
|
93
96
|
|
94
97
|
@tag(name="example", alias="say-hello-func")
|
95
|
-
def hello_world_task(name: str, rs: Result) -> dict[str, str]:
|
98
|
+
def hello_world_task(name: str, rs: Result, extras: Optional[dict[str, Any]] = None) -> dict[str, str]:
|
96
99
|
\"\"\"Logging hello task function\"\"\"
|
97
|
-
|
100
|
+
_extras = extras or {}
|
101
|
+
# NOTE: I will use custom newline logging if you pass `||`.
|
102
|
+
rs.trace.info(
|
103
|
+
f"Hello, {name}||"
|
104
|
+
f"> running ID: {rs.run_id}"
|
105
|
+
f"> extras: {_extras}"
|
106
|
+
)
|
98
107
|
return {"name": name}
|
99
108
|
"""
|
100
109
|
).lstrip("\n")
|
@@ -106,18 +115,19 @@ def init() -> None:
|
|
106
115
|
dotenv_file = Path(".env")
|
107
116
|
mode: str = "a" if dotenv_file.exists() else "w"
|
108
117
|
with dotenv_file.open(mode=mode) as f:
|
109
|
-
f.write("\n# Workflow
|
118
|
+
f.write("\n# Workflow Environment Variables\n")
|
110
119
|
f.write(
|
111
120
|
"WORKFLOW_DEMO_HELLO=foo\n"
|
112
121
|
"WORKFLOW_CORE_DEBUG_MODE=true\n"
|
113
122
|
"WORKFLOW_LOG_TIMEZONE=Asia/Bangkok\n"
|
114
|
-
"
|
123
|
+
'WORKFLOW_LOG_TRACE_HANDLERS=\'[{"type": "console"}]\'\n'
|
124
|
+
'WORKFLOW_LOG_AUDIT_CONF=\'{"type": "file", "path": "./audits"}\''
|
115
125
|
"WORKFLOW_LOG_AUDIT_ENABLE_WRITE=true\n"
|
116
126
|
)
|
117
127
|
|
118
128
|
typer.echo("Starter command:")
|
119
129
|
typer.echo(
|
120
|
-
"
|
130
|
+
">>> `source .env && workflow-cli workflows execute --name=wf-example`"
|
121
131
|
)
|
122
132
|
|
123
133
|
|
@@ -163,7 +173,7 @@ def api(
|
|
163
173
|
debug: Annotated[bool, typer.Option(help="A debug mode flag")] = True,
|
164
174
|
workers: Annotated[int, typer.Option(help="A worker number")] = None,
|
165
175
|
reload: Annotated[bool, typer.Option(help="A reload flag")] = False,
|
166
|
-
):
|
176
|
+
) -> None:
|
167
177
|
"""
|
168
178
|
Provision API application from the FastAPI.
|
169
179
|
"""
|
@@ -22,19 +22,6 @@ Functions:
|
|
22
22
|
pass_env: Process environment variable substitution
|
23
23
|
api_config: Get API-specific configuration settings
|
24
24
|
|
25
|
-
Example:
|
26
|
-
```python
|
27
|
-
from ddeutil.workflow.conf import Config, YamlParser
|
28
|
-
|
29
|
-
# Load workflow configuration
|
30
|
-
parser = YamlParser("my-workflow")
|
31
|
-
workflow_config = parser.data
|
32
|
-
|
33
|
-
# Access dynamic configuration
|
34
|
-
from ddeutil.workflow.conf import dynamic
|
35
|
-
log_level = dynamic("log_level", default="INFO")
|
36
|
-
```
|
37
|
-
|
38
25
|
Note:
|
39
26
|
Configuration files support environment variable substitution using
|
40
27
|
${VAR_NAME} syntax and provide extensive validation capabilities.
|
@@ -155,7 +142,7 @@ class Config: # pragma: no cov
|
|
155
142
|
)
|
156
143
|
|
157
144
|
@property
|
158
|
-
def audit_conf(self) -> str:
|
145
|
+
def audit_conf(self) -> dict[str, Any]:
|
159
146
|
return json.loads(
|
160
147
|
env("LOG_AUDIT_URL", '{"type": "file", "path": "./audits"}')
|
161
148
|
)
|
@@ -288,9 +275,12 @@ class YamlParser:
|
|
288
275
|
continue
|
289
276
|
|
290
277
|
if data := cls.filter_yaml(file, name=name):
|
278
|
+
|
279
|
+
# NOTE: Start adding file metadata.
|
291
280
|
file_stat: os.stat_result = file.lstat()
|
292
281
|
data["created_at"] = file_stat.st_ctime
|
293
282
|
data["updated_at"] = file_stat.st_mtime
|
283
|
+
|
294
284
|
if not obj_type:
|
295
285
|
all_data.append((file_stat.st_mtime, data))
|
296
286
|
elif (t := data.get("type")) and t == obj_type:
|
@@ -324,9 +314,8 @@ class YamlParser:
|
|
324
314
|
extras: (DictData) An extra parameter that use to override core
|
325
315
|
config values.
|
326
316
|
ignore_filename: (str) An ignore filename. Default is
|
327
|
-
|
328
|
-
tags
|
329
|
-
A list of tag that want to filter.
|
317
|
+
``.confignore`` filename.
|
318
|
+
tags (list[str]): A list of tag that want to filter.
|
330
319
|
|
331
320
|
:rtype: Iterator[tuple[str, DictData]]
|
332
321
|
"""
|
@@ -365,6 +354,8 @@ class YamlParser:
|
|
365
354
|
continue
|
366
355
|
|
367
356
|
if (t := data.get("type")) and t == obj_type:
|
357
|
+
|
358
|
+
# NOTE: Start adding file metadata.
|
368
359
|
file_stat: os.stat_result = file.lstat()
|
369
360
|
data["created_at"] = file_stat.st_ctime
|
370
361
|
data["updated_at"] = file_stat.st_mtime
|
@@ -372,6 +363,7 @@ class YamlParser:
|
|
372
363
|
file.lstat().st_mtime,
|
373
364
|
data,
|
374
365
|
)
|
366
|
+
|
375
367
|
if key in all_data:
|
376
368
|
all_data[key].append(marking)
|
377
369
|
else:
|
@@ -405,15 +397,23 @@ class YamlParser:
|
|
405
397
|
def filter_yaml(cls, file: Path, name: Optional[str] = None) -> DictData:
|
406
398
|
"""Read a YAML file context from an input file path and specific name.
|
407
399
|
|
408
|
-
:
|
409
|
-
|
400
|
+
Args:
|
401
|
+
file (Path): A file path that want to extract YAML context.
|
402
|
+
name (str): A key name that search on a YAML context.
|
410
403
|
|
411
|
-
:
|
404
|
+
Returns:
|
405
|
+
DictData: A data that read from this file if it is YAML format.
|
412
406
|
"""
|
413
407
|
if any(file.suffix.endswith(s) for s in (".yml", ".yaml")):
|
414
408
|
values: DictData = YamlFlResolve(file).read()
|
415
409
|
if values is not None:
|
416
|
-
|
410
|
+
if name:
|
411
|
+
if "name" in values and values.get("name") == name:
|
412
|
+
return values
|
413
|
+
return (
|
414
|
+
values[name] | {"name": name} if name in values else {}
|
415
|
+
)
|
416
|
+
return values
|
417
417
|
return {}
|
418
418
|
|
419
419
|
@cached_property
|
@@ -135,11 +135,12 @@ class BaseError(Exception):
|
|
135
135
|
|
136
136
|
Example:
|
137
137
|
>>> error = BaseError("Something failed", refs="stage-1")
|
138
|
-
|
138
|
+
|
139
|
+
Simple format
|
139
140
|
>>> error.to_dict()
|
140
141
|
>>> # Returns: {"name": "BaseError", "message": "Something failed"}
|
141
142
|
|
142
|
-
|
143
|
+
With reference mapping
|
143
144
|
>>> error.to_dict(with_refs=True)
|
144
145
|
>>> # Returns: {"stage-1": {"name": "BaseError", "message": "Something failed"}}
|
145
146
|
```
|
@@ -25,22 +25,20 @@ Functions:
|
|
25
25
|
create_model_from_caller: Generate Pydantic models from function signatures
|
26
26
|
|
27
27
|
Example:
|
28
|
-
|
29
|
-
from ddeutil.workflow.reusables import tag
|
30
|
-
|
31
|
-
@tag("data-processing", alias="process-csv")
|
32
|
-
def process_csv_file(input_path: str, output_path: str) -> dict:
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
# output_path: "/data/output.csv"
|
43
|
-
```
|
28
|
+
|
29
|
+
>>> from ddeutil.workflow.reusables import tag
|
30
|
+
>>>
|
31
|
+
>>> @tag("data-processing", alias="process-csv")
|
32
|
+
>>> def process_csv_file(input_path: str, output_path: str) -> dict:
|
33
|
+
>>> return {"status": "completed", "rows_processed": 1000}
|
34
|
+
|
35
|
+
>>> # Use in workflow YAML:
|
36
|
+
>>> # stages:
|
37
|
+
>>> # - name: "Process data"
|
38
|
+
>>> # uses: "data-processing/process-csv@latest"
|
39
|
+
>>> # args:
|
40
|
+
>>> # input_path: "/data/input.csv"
|
41
|
+
>>> # output_path: "/data/output.csv"
|
44
42
|
|
45
43
|
Note:
|
46
44
|
The registry system supports versioning and aliasing for better function
|
@@ -64,6 +62,7 @@ from typing import (
|
|
64
62
|
Protocol,
|
65
63
|
TypeVar,
|
66
64
|
Union,
|
65
|
+
cast,
|
67
66
|
get_type_hints,
|
68
67
|
)
|
69
68
|
|
@@ -201,7 +200,7 @@ def get_args_const(
|
|
201
200
|
f"Post-filter: {expr} does not valid because it raise syntax error."
|
202
201
|
) from None
|
203
202
|
|
204
|
-
body: list[Expr] = mod.body
|
203
|
+
body: list[Expr] = cast(list[Expr], mod.body)
|
205
204
|
if len(body) > 1:
|
206
205
|
raise UtilError(
|
207
206
|
"Post-filter function should be only one calling per workflow."
|
@@ -28,17 +28,6 @@ Functions:
|
|
28
28
|
cut_id: Cut running ID to specified length
|
29
29
|
dump_all: Serialize nested BaseModel objects to dictionaries
|
30
30
|
obj_name: Get object name or class name
|
31
|
-
|
32
|
-
Example:
|
33
|
-
```python
|
34
|
-
from ddeutil.workflow.utils import gen_id, get_dt_now
|
35
|
-
|
36
|
-
# Generate unique ID
|
37
|
-
run_id = gen_id("workflow")
|
38
|
-
|
39
|
-
# Get current datetime
|
40
|
-
now = get_dt_now()
|
41
|
-
```
|
42
31
|
"""
|
43
32
|
from __future__ import annotations
|
44
33
|
|
@@ -198,17 +198,15 @@ class Workflow(BaseModel):
|
|
198
198
|
FileNotFoundError: If workflow configuration file not found
|
199
199
|
|
200
200
|
Example:
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
)
|
211
|
-
```
|
201
|
+
>>> # Load from default config path
|
202
|
+
>>> workflow = Workflow.from_conf('data-pipeline')
|
203
|
+
|
204
|
+
>>> # Load with custom path and extras
|
205
|
+
>>> workflow = Workflow.from_conf(
|
206
|
+
... 'data-pipeline',
|
207
|
+
... path=Path('./custom-configs'),
|
208
|
+
... extras={'env': 'prod'}
|
209
|
+
... )
|
212
210
|
"""
|
213
211
|
load: YamlParser = YamlParser(name, path=path, extras=extras, obj=cls)
|
214
212
|
data: DictData = copy.deepcopy(load.data)
|
@@ -47,7 +47,10 @@ def test_base_audit():
|
|
47
47
|
},
|
48
48
|
}
|
49
49
|
)
|
50
|
-
|
50
|
+
assert log.model_dump() == {
|
51
|
+
"type": "test",
|
52
|
+
"extras": {"foo": "bar", "datetime": datetime(2024, 1, 1, 1, 15)},
|
53
|
+
}
|
51
54
|
|
52
55
|
|
53
56
|
@mock.patch.object(Config, "enable_write_audit", False)
|
@@ -122,12 +122,14 @@ def test_load_file(target_path: Path):
|
|
122
122
|
|
123
123
|
load = YamlParser("test_load_file", extras={"conf_paths": [target_path]})
|
124
124
|
assert exclude_created_and_updated(load.data) == {
|
125
|
+
"name": "test_load_file",
|
125
126
|
"type": "Workflow",
|
126
127
|
"desc": "Test multi config path",
|
127
128
|
"env": "${WORKFLOW_LOG_TIMEZONE}",
|
128
129
|
}
|
129
130
|
assert pass_env(load.data["env"]) == "Asia/Bangkok"
|
130
131
|
assert exclude_created_and_updated(pass_env(load.data)) == {
|
132
|
+
"name": "test_load_file",
|
131
133
|
"type": "Workflow",
|
132
134
|
"desc": "Test multi config path",
|
133
135
|
"env": "Asia/Bangkok",
|
@@ -137,6 +139,7 @@ def test_load_file(target_path: Path):
|
|
137
139
|
"test_load_file", extras={"conf_paths": [target_path]}, obj="Workflow"
|
138
140
|
)
|
139
141
|
assert exclude_created_and_updated(load.data) == {
|
142
|
+
"name": "test_load_file",
|
140
143
|
"type": "Workflow",
|
141
144
|
"desc": "Test multi config path",
|
142
145
|
"env": "${WORKFLOW_LOG_TIMEZONE}",
|
@@ -153,6 +156,56 @@ def test_load_file(target_path: Path):
|
|
153
156
|
_ = load.type
|
154
157
|
|
155
158
|
|
159
|
+
@pytest.fixture(scope="function")
|
160
|
+
def mock_workflow_with_name_key(test_path):
|
161
|
+
target_p = test_path / "test_read_file_with_name_key"
|
162
|
+
target_p.mkdir(exist_ok=True)
|
163
|
+
|
164
|
+
with (target_p / "wf_1.yaml").open(mode="w") as f:
|
165
|
+
yaml.dump(
|
166
|
+
{
|
167
|
+
"name": "wf_1",
|
168
|
+
"type": "Workflow",
|
169
|
+
"value": 1,
|
170
|
+
"tags": [
|
171
|
+
1,
|
172
|
+
],
|
173
|
+
},
|
174
|
+
f,
|
175
|
+
)
|
176
|
+
|
177
|
+
with (target_p / "wf_2.yaml").open(mode="w") as f:
|
178
|
+
yaml.dump(
|
179
|
+
{
|
180
|
+
"name": "wf_2",
|
181
|
+
"type": "Workflow",
|
182
|
+
"value": 1,
|
183
|
+
"tags": [
|
184
|
+
1,
|
185
|
+
],
|
186
|
+
},
|
187
|
+
f,
|
188
|
+
)
|
189
|
+
|
190
|
+
yield target_p
|
191
|
+
|
192
|
+
shutil.rmtree(target_p)
|
193
|
+
|
194
|
+
|
195
|
+
def test_load_file_with_name_key(mock_workflow_with_name_key):
|
196
|
+
assert exclude_created_and_updated(
|
197
|
+
YamlParser(
|
198
|
+
"wf_1", extras={"conf_paths": [mock_workflow_with_name_key]}
|
199
|
+
).data
|
200
|
+
) == {"name": "wf_1", "tags": [1], "type": "Workflow", "value": 1}
|
201
|
+
|
202
|
+
with pytest.raises(ValueError):
|
203
|
+
YamlParser(
|
204
|
+
"wf_not_exists",
|
205
|
+
extras={"conf_paths": [mock_workflow_with_name_key]},
|
206
|
+
)
|
207
|
+
|
208
|
+
|
156
209
|
def test_load_file_filter(mock_conf: Path):
|
157
210
|
assert (
|
158
211
|
"wf_1",
|
@@ -197,10 +250,10 @@ def test_load_file_filter(mock_conf: Path):
|
|
197
250
|
|
198
251
|
assert exclude_created_and_updated(
|
199
252
|
YamlParser.find("wf_1", path=mock_conf)
|
200
|
-
) == {"tags": [1], "type": "Workflow", "value": 1}
|
253
|
+
) == {"name": "wf_1", "tags": [1], "type": "Workflow", "value": 1}
|
201
254
|
assert exclude_created_and_updated(
|
202
255
|
YamlParser.find("wf_2", path=mock_conf)
|
203
|
-
) == {"tags": [2], "type": "Workflow", "value": 2}
|
256
|
+
) == {"name": "wf_2", "tags": [2], "type": "Workflow", "value": 2}
|
204
257
|
assert (
|
205
258
|
exclude_created_and_updated(
|
206
259
|
YamlParser.find("wf_3", path=mock_conf, obj="Workflow")
|
@@ -209,7 +262,7 @@ def test_load_file_filter(mock_conf: Path):
|
|
209
262
|
)
|
210
263
|
assert exclude_created_and_updated(
|
211
264
|
YamlParser.find("wf_4", path=mock_conf, obj="Custom")
|
212
|
-
) == {"type": "Custom", "value": 4}
|
265
|
+
) == {"name": "wf_4", "type": "Custom", "value": 4}
|
213
266
|
|
214
267
|
with pytest.raises(TypeError):
|
215
268
|
list(YamlParser.finds("Custom", paths={"path": mock_conf}, tags=[1]))
|
@@ -285,6 +338,7 @@ def test_load_file_finds(target_path: Path):
|
|
285
338
|
|
286
339
|
load = YamlParser.find("test_load_file", path=target_path, obj="Workflow")
|
287
340
|
assert exclude_created_and_updated(load) == {
|
341
|
+
"name": "test_load_file",
|
288
342
|
"type": "Workflow",
|
289
343
|
"data": "foo",
|
290
344
|
}
|
@@ -292,6 +346,7 @@ def test_load_file_finds(target_path: Path):
|
|
292
346
|
# NOTE: Load with the same name, but it set different type.
|
293
347
|
load = YamlParser.find("test_load_file", path=target_path, obj="Config")
|
294
348
|
assert exclude_created_and_updated(load) == {
|
349
|
+
"name": "test_load_file",
|
295
350
|
"type": "Config",
|
296
351
|
"data": "bar",
|
297
352
|
}
|
@@ -351,26 +406,3 @@ def test_dynamic():
|
|
351
406
|
|
352
407
|
conf = dynamic("trace_handlers", extras={})
|
353
408
|
assert conf == [{"type": "console"}]
|
354
|
-
|
355
|
-
|
356
|
-
def test_parse_url():
|
357
|
-
from urllib.parse import ParseResult, urlparse
|
358
|
-
|
359
|
-
url: ParseResult = urlparse("./logs")
|
360
|
-
assert url == ParseResult(
|
361
|
-
scheme="", netloc="", path="./logs", params="", query="", fragment=""
|
362
|
-
)
|
363
|
-
assert url.scheme == ""
|
364
|
-
assert url.path == "./logs"
|
365
|
-
|
366
|
-
url: ParseResult = urlparse("file:///./logs")
|
367
|
-
assert url.scheme == "file"
|
368
|
-
assert url.path == "/./logs"
|
369
|
-
|
370
|
-
url: ParseResult = urlparse("sqlite:///home/warehouse/sqlite.db")
|
371
|
-
assert url.scheme == "sqlite"
|
372
|
-
assert url.path == "/home/warehouse/sqlite.db"
|
373
|
-
|
374
|
-
url: ParseResult = urlparse("file:./data.db")
|
375
|
-
assert url.scheme == "file"
|
376
|
-
assert url.path == "./data.db"
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__: str = "0.0.80"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/routes/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/routes/workflows.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/__init__.py
RENAMED
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/aws.py
RENAMED
File without changes
|
{ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/az.py
RENAMED
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/gcs.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/SOURCES.txt
RENAMED
File without changes
|
File without changes
|
{ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/entry_points.txt
RENAMED
File without changes
|
{ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/requires.txt
RENAMED
File without changes
|
{ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/top_level.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|