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.
Files changed (66) hide show
  1. {ddeutil_workflow-0.0.80/src/ddeutil_workflow.egg-info → ddeutil_workflow-0.0.81}/PKG-INFO +1 -1
  2. ddeutil_workflow-0.0.81/src/ddeutil/workflow/__about__.py +1 -0
  3. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/cli.py +39 -29
  4. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/conf.py +21 -21
  5. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/errors.py +3 -2
  6. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/reusables.py +16 -17
  7. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/utils.py +0 -11
  8. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/workflow.py +9 -11
  9. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81/src/ddeutil_workflow.egg-info}/PKG-INFO +1 -1
  10. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_audits.py +4 -1
  11. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_conf.py +58 -26
  12. ddeutil_workflow-0.0.80/src/ddeutil/workflow/__about__.py +0 -1
  13. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/LICENSE +0 -0
  14. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/README.md +0 -0
  15. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/pyproject.toml +0 -0
  16. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/setup.cfg +0 -0
  17. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/__cron.py +0 -0
  18. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/__init__.py +0 -0
  19. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/__main__.py +0 -0
  20. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/__types.py +0 -0
  21. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/__init__.py +0 -0
  22. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/log_conf.py +0 -0
  23. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/routes/__init__.py +0 -0
  24. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/routes/job.py +0 -0
  25. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/routes/logs.py +0 -0
  26. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/api/routes/workflows.py +0 -0
  27. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/audits.py +0 -0
  28. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/event.py +0 -0
  29. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/job.py +0 -0
  30. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/params.py +0 -0
  31. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/__init__.py +0 -0
  32. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/__init__.py +0 -0
  33. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/aws.py +0 -0
  34. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/az.py +0 -0
  35. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/container.py +0 -0
  36. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/plugins/providers/gcs.py +0 -0
  37. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/result.py +0 -0
  38. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/stages.py +0 -0
  39. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil/workflow/traces.py +0 -0
  40. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/SOURCES.txt +0 -0
  41. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
  42. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/entry_points.txt +0 -0
  43. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/requires.txt +0 -0
  44. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
  45. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test__cron.py +0 -0
  46. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test__regex.py +0 -0
  47. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_cli.py +0 -0
  48. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_errors.py +0 -0
  49. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_event.py +0 -0
  50. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_job.py +0 -0
  51. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_job_exec.py +0 -0
  52. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_job_exec_strategy.py +0 -0
  53. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_params.py +0 -0
  54. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_result.py +0 -0
  55. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_reusables_call_tag.py +0 -0
  56. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_reusables_func_model.py +0 -0
  57. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_reusables_template.py +0 -0
  58. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_reusables_template_filter.py +0 -0
  59. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_strategy.py +0 -0
  60. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_traces.py +0 -0
  61. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_utils.py +0 -0
  62. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_workflow.py +0 -0
  63. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_workflow_exec.py +0 -0
  64. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_workflow_exec_job.py +0 -0
  65. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_workflow_release.py +0 -0
  66. {ddeutil_workflow-0.0.80 → ddeutil_workflow-0.0.81}/tests/test_workflow_rerun.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.80
3
+ Version: 0.0.81
4
4
  Summary: Lightweight workflow orchestration with YAML template
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -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
- 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
- second-job:
77
-
78
- - name: "Hello Env"
79
- echo: "Start say hi with ${ WORKFLOW_DEMO_HELLO }"
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
- rs.trace.info(f"Hello, {name}")
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 env vars\n")
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
- "WORKFLOW_LOG_TRACE_ENABLE_WRITE=false\n"
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
- "> `source .env && workflow-cli workflows execute --name=wf-example`"
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
- ``.confignore`` filename.
328
- tags: (list[str])
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
- :param file: (Path) A file path that want to extract YAML context.
409
- :param name: (str) A key name that search on a YAML context.
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
- :rtype: DictData
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
- return values.get(name, {}) if name else values
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
- >>> # Simple format
138
+
139
+ Simple format
139
140
  >>> error.to_dict()
140
141
  >>> # Returns: {"name": "BaseError", "message": "Something failed"}
141
142
 
142
- >>> # With reference mapping
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
- ```python
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
- # Custom processing logic
34
- return {"status": "completed", "rows_processed": 1000}
35
-
36
- # Use in workflow YAML:
37
- # stages:
38
- # - name: "Process data"
39
- # uses: "data-processing/process-csv@latest"
40
- # args:
41
- # input_path: "/data/input.csv"
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
- ```python
202
- # Load from default config path
203
- workflow = Workflow.from_conf('data-pipeline')
204
-
205
- # Load with custom path and extras
206
- workflow = Workflow.from_conf(
207
- 'data-pipeline',
208
- path=Path('./custom-configs'),
209
- extras={'environment': 'production'}
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.80
3
+ Version: 0.0.81
4
4
  Summary: Lightweight workflow orchestration with YAML template
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -47,7 +47,10 @@ def test_base_audit():
47
47
  },
48
48
  }
49
49
  )
50
- print(log.model_dump())
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"