ddeutil-workflow 0.0.77__py3-none-any.whl → 0.0.79__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/__init__.py +1 -5
- ddeutil/workflow/api/routes/job.py +2 -2
- ddeutil/workflow/audits.py +554 -112
- ddeutil/workflow/cli.py +25 -3
- ddeutil/workflow/conf.py +16 -28
- ddeutil/workflow/errors.py +13 -15
- ddeutil/workflow/event.py +37 -41
- ddeutil/workflow/job.py +161 -92
- ddeutil/workflow/params.py +172 -58
- ddeutil/workflow/plugins/__init__.py +0 -0
- ddeutil/workflow/plugins/providers/__init__.py +0 -0
- ddeutil/workflow/plugins/providers/aws.py +908 -0
- ddeutil/workflow/plugins/providers/az.py +1003 -0
- ddeutil/workflow/plugins/providers/container.py +703 -0
- ddeutil/workflow/plugins/providers/gcs.py +826 -0
- ddeutil/workflow/result.py +35 -37
- ddeutil/workflow/reusables.py +153 -96
- ddeutil/workflow/stages.py +84 -60
- ddeutil/workflow/traces.py +1660 -521
- ddeutil/workflow/utils.py +111 -69
- ddeutil/workflow/workflow.py +74 -47
- {ddeutil_workflow-0.0.77.dist-info → ddeutil_workflow-0.0.79.dist-info}/METADATA +52 -20
- ddeutil_workflow-0.0.79.dist-info/RECORD +36 -0
- ddeutil_workflow-0.0.77.dist-info/RECORD +0 -30
- {ddeutil_workflow-0.0.77.dist-info → ddeutil_workflow-0.0.79.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.77.dist-info → ddeutil_workflow-0.0.79.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.77.dist-info → ddeutil_workflow-0.0.79.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.77.dist-info → ddeutil_workflow-0.0.79.dist-info}/top_level.txt +0 -0
ddeutil/workflow/result.py
CHANGED
@@ -20,7 +20,6 @@ Functions:
|
|
20
20
|
from __future__ import annotations
|
21
21
|
|
22
22
|
from dataclasses import field
|
23
|
-
from datetime import datetime
|
24
23
|
from enum import Enum
|
25
24
|
from typing import Optional, Union
|
26
25
|
|
@@ -40,9 +39,9 @@ from . import (
|
|
40
39
|
WorkflowError,
|
41
40
|
)
|
42
41
|
from .__types import DictData
|
43
|
-
from .audits import
|
42
|
+
from .audits import TraceManager, get_trace
|
44
43
|
from .errors import ResultError
|
45
|
-
from .utils import default_gen_id
|
44
|
+
from .utils import default_gen_id
|
46
45
|
|
47
46
|
|
48
47
|
class Status(str, Enum):
|
@@ -105,8 +104,9 @@ def validate_statuses(statuses: list[Status]) -> Status:
|
|
105
104
|
"""Determine final status from multiple status values.
|
106
105
|
|
107
106
|
Applies workflow logic to determine the overall status based on a collection
|
108
|
-
of individual status values. Follows priority order:
|
109
|
-
|
107
|
+
of individual status values. Follows priority order:
|
108
|
+
|
109
|
+
CANCEL > FAILED > WAIT > individual status consistency.
|
110
110
|
|
111
111
|
Args:
|
112
112
|
statuses: List of status values to evaluate
|
@@ -132,7 +132,7 @@ def validate_statuses(statuses: list[Status]) -> Status:
|
|
132
132
|
for status in (SUCCESS, SKIP):
|
133
133
|
if all(s == status for s in statuses):
|
134
134
|
return status
|
135
|
-
return
|
135
|
+
return SUCCESS
|
136
136
|
|
137
137
|
|
138
138
|
def get_status_from_error(
|
@@ -166,10 +166,6 @@ def get_status_from_error(
|
|
166
166
|
return FAILED
|
167
167
|
|
168
168
|
|
169
|
-
def default_context() -> DictData:
|
170
|
-
return {"status": WAIT}
|
171
|
-
|
172
|
-
|
173
169
|
@dataclass(
|
174
170
|
config=ConfigDict(arbitrary_types_allowed=True, use_enum_values=True),
|
175
171
|
)
|
@@ -186,14 +182,15 @@ class Result:
|
|
186
182
|
field that keep dict value change its ID when update new value to it.
|
187
183
|
"""
|
188
184
|
|
185
|
+
extras: DictData = field(default_factory=dict, compare=False, repr=False)
|
189
186
|
status: Status = field(default=WAIT)
|
190
|
-
context: DictData = field(
|
187
|
+
context: Optional[DictData] = field(default=None)
|
191
188
|
info: DictData = field(default_factory=dict)
|
192
|
-
run_id:
|
189
|
+
run_id: str = field(default_factory=default_gen_id)
|
193
190
|
parent_run_id: Optional[str] = field(default=None)
|
194
|
-
|
195
|
-
|
196
|
-
|
191
|
+
trace: Optional[TraceManager] = field(
|
192
|
+
default=None, compare=False, repr=False
|
193
|
+
)
|
197
194
|
|
198
195
|
@model_validator(mode="after")
|
199
196
|
def __prepare_trace(self) -> Self:
|
@@ -202,25 +199,23 @@ class Result:
|
|
202
199
|
:rtype: Self
|
203
200
|
"""
|
204
201
|
if self.trace is None: # pragma: no cov
|
205
|
-
self.trace:
|
202
|
+
self.trace: TraceManager = get_trace(
|
206
203
|
self.run_id,
|
207
204
|
parent_run_id=self.parent_run_id,
|
208
205
|
extras=self.extras,
|
209
206
|
)
|
210
|
-
return self
|
211
|
-
|
212
|
-
def set_parent_run_id(self, running_id: str) -> Self:
|
213
|
-
"""Set a parent running ID.
|
214
207
|
|
215
|
-
|
208
|
+
return self
|
216
209
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
210
|
+
@classmethod
|
211
|
+
def from_trace(cls, trace: TraceManager):
|
212
|
+
"""Construct the result model from trace for clean code objective."""
|
213
|
+
return cls(
|
214
|
+
run_id=trace.run_id,
|
215
|
+
parent_run_id=trace.parent_run_id,
|
216
|
+
extras=trace.extras,
|
217
|
+
trace=trace,
|
222
218
|
)
|
223
|
-
return self
|
224
219
|
|
225
220
|
def catch(
|
226
221
|
self,
|
@@ -237,7 +232,11 @@ class Result:
|
|
237
232
|
|
238
233
|
:rtype: Self
|
239
234
|
"""
|
240
|
-
self.__dict__["context"]
|
235
|
+
if self.__dict__["context"] is None:
|
236
|
+
self.__dict__["context"] = context
|
237
|
+
else:
|
238
|
+
self.__dict__["context"].update(context or {})
|
239
|
+
|
241
240
|
self.__dict__["status"] = (
|
242
241
|
Status(status) if isinstance(status, int) else status
|
243
242
|
)
|
@@ -262,13 +261,6 @@ class Result:
|
|
262
261
|
self.__dict__["info"].update(data)
|
263
262
|
return self
|
264
263
|
|
265
|
-
def alive_time(self) -> float: # pragma: no cov
|
266
|
-
"""Return total seconds that this object use since it was created.
|
267
|
-
|
268
|
-
:rtype: float
|
269
|
-
"""
|
270
|
-
return (get_dt_now() - self.ts).total_seconds()
|
271
|
-
|
272
264
|
|
273
265
|
def catch(
|
274
266
|
context: DictData,
|
@@ -276,7 +268,13 @@ def catch(
|
|
276
268
|
updated: DictData | None = None,
|
277
269
|
**kwargs,
|
278
270
|
) -> DictData:
|
279
|
-
"""Catch updated context to the current context.
|
271
|
+
"""Catch updated context to the current context.
|
272
|
+
|
273
|
+
Args:
|
274
|
+
context: A context data that want to be the current context.
|
275
|
+
status: A status enum object.
|
276
|
+
updated: A updated data that will update to the current context.
|
277
|
+
"""
|
280
278
|
context.update(updated or {})
|
281
279
|
context["status"] = Status(status) if isinstance(status, int) else status
|
282
280
|
|
@@ -289,7 +287,7 @@ def catch(
|
|
289
287
|
context[k].update(kwargs[k])
|
290
288
|
# NOTE: Exclude the `info` key for update information data.
|
291
289
|
elif k == "info":
|
292
|
-
context
|
290
|
+
context.update({"info": kwargs["info"]})
|
293
291
|
else:
|
294
292
|
raise ResultError(f"The key {k!r} does not exists on context data.")
|
295
293
|
return context
|
ddeutil/workflow/reusables.py
CHANGED
@@ -119,12 +119,15 @@ FilterRegistry = Union[FilterFunc, Callable[[...], Any]]
|
|
119
119
|
|
120
120
|
|
121
121
|
def custom_filter(name: str) -> Callable[P, FilterFunc]:
|
122
|
-
"""Custom filter decorator function that
|
123
|
-
for making filter registries variable.
|
122
|
+
"""Custom filter decorator function that sets function attributes.
|
124
123
|
|
125
|
-
|
124
|
+
This decorator sets the `filter` attribute for making filter registries variable.
|
126
125
|
|
127
|
-
:
|
126
|
+
Args:
|
127
|
+
name: A filter name for different use-cases of a function.
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
Callable[P, FilterFunc]: Decorated function with filter attributes.
|
128
131
|
"""
|
129
132
|
|
130
133
|
def func_internal(func: Callable[[...], Any]) -> FilterFunc:
|
@@ -144,11 +147,13 @@ def custom_filter(name: str) -> Callable[P, FilterFunc]:
|
|
144
147
|
def make_filter_registry(
|
145
148
|
registers: Optional[list[str]] = None,
|
146
149
|
) -> dict[str, FilterRegistry]:
|
147
|
-
"""Return registries of all functions that
|
150
|
+
"""Return registries of all functions that can be called with tasks.
|
148
151
|
|
149
|
-
:
|
152
|
+
Args:
|
153
|
+
registers: Optional override list of registers.
|
150
154
|
|
151
|
-
:
|
155
|
+
Returns:
|
156
|
+
dict[str, FilterRegistry]: Dictionary mapping filter names to functions.
|
152
157
|
"""
|
153
158
|
rs: dict[str, FilterRegistry] = {}
|
154
159
|
for module in dynamic("registry_filter", f=registers):
|
@@ -180,9 +185,14 @@ def get_args_const(
|
|
180
185
|
) -> tuple[str, list[Constant], dict[str, Constant]]:
|
181
186
|
"""Get arguments and keyword-arguments from function calling string.
|
182
187
|
|
183
|
-
:
|
188
|
+
Args:
|
189
|
+
expr: A string expression representing a function call.
|
190
|
+
|
191
|
+
Returns:
|
192
|
+
tuple[str, list[Constant], dict[str, Constant]]: Function name, args, and kwargs.
|
184
193
|
|
185
|
-
:
|
194
|
+
Raises:
|
195
|
+
UtilError: If the expression has syntax errors or invalid format.
|
186
196
|
"""
|
187
197
|
try:
|
188
198
|
mod: Module = parse(expr)
|
@@ -223,12 +233,18 @@ def get_args_from_filter(
|
|
223
233
|
filters: dict[str, FilterRegistry],
|
224
234
|
) -> tuple[str, FilterRegistry, list[Any], dict[Any, Any]]: # pragma: no cov
|
225
235
|
"""Get arguments and keyword-arguments from filter function calling string.
|
226
|
-
and validate it with the filter functions mapping dict.
|
227
236
|
|
228
|
-
|
229
|
-
|
237
|
+
This function validates the filter function call with the filter functions mapping dict.
|
238
|
+
|
239
|
+
Args:
|
240
|
+
ft: Filter function calling string.
|
241
|
+
filters: A mapping of filter registry.
|
230
242
|
|
231
|
-
:
|
243
|
+
Returns:
|
244
|
+
tuple[str, FilterRegistry, list[Any], dict[Any, Any]]: Function name, function, args, and kwargs.
|
245
|
+
|
246
|
+
Raises:
|
247
|
+
UtilError: If the filter function is not supported or has invalid arguments.
|
232
248
|
"""
|
233
249
|
func_name, _args, _kwargs = get_args_const(ft)
|
234
250
|
args: list[Any] = [arg.value for arg in _args]
|
@@ -250,14 +266,18 @@ def map_post_filter(
|
|
250
266
|
post_filter: list[str],
|
251
267
|
filters: dict[str, FilterRegistry],
|
252
268
|
) -> T:
|
253
|
-
"""
|
254
|
-
|
269
|
+
"""Map post-filter functions to value with sequence of filter function names.
|
270
|
+
|
271
|
+
Args:
|
272
|
+
value: A value to map with filter functions.
|
273
|
+
post_filter: A list of post-filter function names.
|
274
|
+
filters: A mapping of filter registry.
|
255
275
|
|
256
|
-
:
|
257
|
-
|
258
|
-
:param filters: A mapping of filter registry.
|
276
|
+
Returns:
|
277
|
+
T: The value after applying all post-filter functions.
|
259
278
|
|
260
|
-
:
|
279
|
+
Raises:
|
280
|
+
UtilError: If a post-filter function fails or is incompatible with the value.
|
261
281
|
"""
|
262
282
|
for ft in post_filter:
|
263
283
|
func_name, f_func, args, kwargs = get_args_from_filter(ft, filters)
|
@@ -278,13 +298,15 @@ def map_post_filter(
|
|
278
298
|
|
279
299
|
|
280
300
|
def not_in_template(value: Any, *, not_in: str = "matrix.") -> bool:
|
281
|
-
"""Check value should not pass template with not_in value prefix.
|
301
|
+
"""Check if value should not pass template with not_in value prefix.
|
282
302
|
|
283
|
-
:
|
284
|
-
|
285
|
-
|
303
|
+
Args:
|
304
|
+
value: A value to check for parameter template prefix.
|
305
|
+
not_in: The not-in string to use in the `.startswith` function.
|
306
|
+
Default is `matrix.`.
|
286
307
|
|
287
|
-
:
|
308
|
+
Returns:
|
309
|
+
bool: True if value should not pass template, False otherwise.
|
288
310
|
"""
|
289
311
|
if isinstance(value, dict):
|
290
312
|
return any(not_in_template(value[k], not_in=not_in) for k in value)
|
@@ -299,11 +321,13 @@ def not_in_template(value: Any, *, not_in: str = "matrix.") -> bool:
|
|
299
321
|
|
300
322
|
|
301
323
|
def has_template(value: Any) -> bool:
|
302
|
-
"""Check value
|
324
|
+
"""Check if value includes templating string.
|
303
325
|
|
304
|
-
:
|
326
|
+
Args:
|
327
|
+
value: A value to check for parameter template.
|
305
328
|
|
306
|
-
:
|
329
|
+
Returns:
|
330
|
+
bool: True if value contains template variables, False otherwise.
|
307
331
|
"""
|
308
332
|
if isinstance(value, dict):
|
309
333
|
return any(has_template(value[k]) for k in value)
|
@@ -322,21 +346,24 @@ def str2template(
|
|
322
346
|
filters: Optional[dict[str, FilterRegistry]] = None,
|
323
347
|
registers: Optional[list[str]] = None,
|
324
348
|
) -> Optional[str]:
|
325
|
-
"""
|
326
|
-
|
349
|
+
"""Pass parameters to template string using RE_CALLER regular expression.
|
350
|
+
|
351
|
+
This is a sub-function that processes template strings. The getter value
|
352
|
+
that maps a template should have typing support aligned with workflow
|
353
|
+
parameter types: `str`, `int`, `datetime`, and `list`.
|
327
354
|
|
328
|
-
|
329
|
-
|
330
|
-
|
355
|
+
Args:
|
356
|
+
value: A string value to map with parameters.
|
357
|
+
params: Parameter values to get with matched regular expression.
|
358
|
+
context: Optional context data.
|
359
|
+
filters: Optional mapping of filter registry.
|
360
|
+
registers: Optional override list of registers.
|
331
361
|
|
332
|
-
:
|
333
|
-
|
334
|
-
regular expression.
|
335
|
-
:param context: (DictData)
|
336
|
-
:param filters: (dict[str, FilterRegistry]) A mapping of filter registry.
|
337
|
-
:param registers: (Optional[list[str]]) Override list of register.
|
362
|
+
Returns:
|
363
|
+
Optional[str]: The processed template string or None if value is "None".
|
338
364
|
|
339
|
-
:
|
365
|
+
Raises:
|
366
|
+
UtilError: If parameters cannot be retrieved or template processing fails.
|
340
367
|
"""
|
341
368
|
filters: dict[str, FilterRegistry] = filters or make_filter_registry(
|
342
369
|
registers=registers
|
@@ -395,19 +422,17 @@ def param2template(
|
|
395
422
|
*,
|
396
423
|
extras: Optional[DictData] = None,
|
397
424
|
) -> Any:
|
398
|
-
"""Pass
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
:
|
408
|
-
|
409
|
-
:rtype: Any
|
410
|
-
:returns: An any getter value from the params input.
|
425
|
+
"""Pass parameters to template string using RE_CALLER regular expression.
|
426
|
+
|
427
|
+
Args:
|
428
|
+
value: A value to map with parameters.
|
429
|
+
params: Parameter values to get with matched regular expression.
|
430
|
+
context: Optional context data.
|
431
|
+
filters: Optional filter mapping for use with `map_post_filter` function.
|
432
|
+
extras: Optional override extras.
|
433
|
+
|
434
|
+
Returns:
|
435
|
+
Any: The processed value with template variables replaced.
|
411
436
|
"""
|
412
437
|
registers: Optional[list[str]] = (
|
413
438
|
extras.get("registry_filter") if extras else None
|
@@ -437,19 +462,21 @@ def param2template(
|
|
437
462
|
|
438
463
|
@custom_filter("fmt") # pragma: no cov
|
439
464
|
def datetime_format(value: datetime, fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
|
440
|
-
"""Format datetime object to string with the format.
|
465
|
+
"""Format datetime object to string with the specified format.
|
441
466
|
|
442
467
|
Examples:
|
443
|
-
|
444
468
|
>>> "${{ start-date | fmt('%Y%m%d') }}"
|
445
469
|
>>> "${{ start-date | fmt }}"
|
446
470
|
|
447
|
-
:
|
448
|
-
value.
|
449
|
-
|
450
|
-
method.
|
471
|
+
Args:
|
472
|
+
value: A datetime value to format to string.
|
473
|
+
fmt: A format string pattern to pass to the `dt.strftime` method.
|
451
474
|
|
452
|
-
:
|
475
|
+
Returns:
|
476
|
+
str: The formatted datetime string.
|
477
|
+
|
478
|
+
Raises:
|
479
|
+
UtilError: If the input value is not a datetime object.
|
453
480
|
"""
|
454
481
|
if isinstance(value, datetime):
|
455
482
|
return value.strftime(fmt)
|
@@ -463,12 +490,14 @@ def coalesce(value: Optional[T], default: Any) -> T:
|
|
463
490
|
"""Coalesce with default value if the main value is None.
|
464
491
|
|
465
492
|
Examples:
|
466
|
-
|
467
493
|
>>> "${{ value | coalesce('foo') }}"
|
468
494
|
|
469
|
-
:
|
470
|
-
|
471
|
-
value
|
495
|
+
Args:
|
496
|
+
value: A value to check for null.
|
497
|
+
default: A default value to return if the input value is null.
|
498
|
+
|
499
|
+
Returns:
|
500
|
+
T: The original value if not None, otherwise the default value.
|
472
501
|
"""
|
473
502
|
return default if value is None else value
|
474
503
|
|
@@ -477,13 +506,22 @@ def coalesce(value: Optional[T], default: Any) -> T:
|
|
477
506
|
def get_item(
|
478
507
|
value: DictData, key: Union[str, int], default: Optional[Any] = None
|
479
508
|
) -> Any:
|
480
|
-
"""Get a value with
|
509
|
+
"""Get a value with a specific key.
|
481
510
|
|
482
511
|
Examples:
|
483
|
-
|
484
512
|
>>> "${{ value | getitem('key') }}"
|
485
513
|
>>> "${{ value | getitem('key', 'default') }}"
|
486
514
|
|
515
|
+
Args:
|
516
|
+
value: A dictionary to get the value from.
|
517
|
+
key: The key to look up in the dictionary.
|
518
|
+
default: Optional default value if key is not found.
|
519
|
+
|
520
|
+
Returns:
|
521
|
+
Any: The value associated with the key, or the default value.
|
522
|
+
|
523
|
+
Raises:
|
524
|
+
UtilError: If the value is not a dictionary.
|
487
525
|
"""
|
488
526
|
if not isinstance(value, dict):
|
489
527
|
raise UtilError(
|
@@ -495,12 +533,20 @@ def get_item(
|
|
495
533
|
|
496
534
|
@custom_filter("getindex") # pragma: no cov
|
497
535
|
def get_index(value: list[Any], index: int) -> Any:
|
498
|
-
"""Get a value with
|
536
|
+
"""Get a value with a specific index.
|
499
537
|
|
500
538
|
Examples:
|
501
|
-
|
502
539
|
>>> "${{ value | getindex(1) }}"
|
503
540
|
|
541
|
+
Args:
|
542
|
+
value: A list to get the value from.
|
543
|
+
index: The index to access in the list.
|
544
|
+
|
545
|
+
Returns:
|
546
|
+
Any: The value at the specified index.
|
547
|
+
|
548
|
+
Raises:
|
549
|
+
UtilError: If the value is not a list or if the index is out of range.
|
504
550
|
"""
|
505
551
|
if not isinstance(value, list):
|
506
552
|
raise UtilError(
|
@@ -534,16 +580,18 @@ def tag(
|
|
534
580
|
name: Optional[str] = None,
|
535
581
|
alias: Optional[str] = None,
|
536
582
|
) -> DecoratorTagFunc: # pragma: no cov
|
537
|
-
"""Tag decorator function that
|
538
|
-
for making registries variable.
|
583
|
+
"""Tag decorator function that sets function attributes for registry.
|
539
584
|
|
540
|
-
|
541
|
-
It will use 'latest' if this tag name does not set.
|
542
|
-
:param: alias: (str) A alias function name that keeping in registries.
|
543
|
-
If this value does not supply, it will use original function name
|
544
|
-
from `__name__` argument.
|
585
|
+
This decorator sets the `tag` and `name` attributes for making registries variable.
|
545
586
|
|
546
|
-
:
|
587
|
+
Args:
|
588
|
+
name: A tag name for different use-cases of a function.
|
589
|
+
Uses 'latest' if not set.
|
590
|
+
alias: An alias function name to keep in registries.
|
591
|
+
Uses original function name from `__name__` if not supplied.
|
592
|
+
|
593
|
+
Returns:
|
594
|
+
DecoratorTagFunc: Decorated function with tag attributes.
|
547
595
|
"""
|
548
596
|
|
549
597
|
def func_internal(func: Callable[[...], Any]) -> ReturnTagFunc:
|
@@ -574,12 +622,17 @@ def make_registry(
|
|
574
622
|
*,
|
575
623
|
registries: Optional[list[str]] = None,
|
576
624
|
) -> dict[str, Registry]:
|
577
|
-
"""Return registries of all functions that
|
625
|
+
"""Return registries of all functions that can be called with tasks.
|
626
|
+
|
627
|
+
Args:
|
628
|
+
submodule: A module prefix to import registry from.
|
629
|
+
registries: Optional list of registries.
|
578
630
|
|
579
|
-
:
|
580
|
-
|
631
|
+
Returns:
|
632
|
+
dict[str, Registry]: Dictionary mapping function names to their registries.
|
581
633
|
|
582
|
-
:
|
634
|
+
Raises:
|
635
|
+
ValueError: If a tag already exists for a function name.
|
583
636
|
"""
|
584
637
|
rs: dict[str, Registry] = {}
|
585
638
|
regis_calls: list[str] = copy.deepcopy(
|
@@ -599,7 +652,7 @@ def make_registry(
|
|
599
652
|
if not (
|
600
653
|
hasattr(func, "tag")
|
601
654
|
and hasattr(func, "name")
|
602
|
-
and str(getattr(func, "mark", "
|
655
|
+
and str(getattr(func, "mark", "NOTSET")) == "tag"
|
603
656
|
): # pragma: no cov
|
604
657
|
continue
|
605
658
|
|
@@ -617,6 +670,7 @@ def make_registry(
|
|
617
670
|
f"{module}.{submodule}, you should change this tag name or "
|
618
671
|
f"change it func name."
|
619
672
|
)
|
673
|
+
|
620
674
|
rs[func.name][func.tag] = lazy(f"{module}.{submodule}.{fstr}")
|
621
675
|
|
622
676
|
return rs
|
@@ -638,13 +692,9 @@ def extract_call(
|
|
638
692
|
*,
|
639
693
|
registries: Optional[list[str]] = None,
|
640
694
|
) -> Callable[[], TagFunc]:
|
641
|
-
"""Extract
|
642
|
-
does run it at runtime.
|
695
|
+
"""Extract call function from string value to call partial function at runtime.
|
643
696
|
|
644
|
-
|
645
|
-
:param registries: (Optional[list[str]]) A list of registry.
|
646
|
-
|
647
|
-
The format of call value should contain 3 regular expression groups
|
697
|
+
The format of call value should contain 3 regular expression groups
|
648
698
|
which match with the below config format:
|
649
699
|
|
650
700
|
>>> "^(?P<path>[^/@]+)/(?P<func>[^@]+)@(?P<tag>.+)$"
|
@@ -655,12 +705,16 @@ def extract_call(
|
|
655
705
|
>>> extract_call("tasks/return-type-not-valid@raise")
|
656
706
|
...
|
657
707
|
|
658
|
-
:
|
659
|
-
|
660
|
-
|
661
|
-
|
708
|
+
Args:
|
709
|
+
call: A call value that matches the Task regex.
|
710
|
+
registries: Optional list of registries.
|
711
|
+
|
712
|
+
Returns:
|
713
|
+
Callable[[], TagFunc]: A callable function that can be executed.
|
662
714
|
|
663
|
-
:
|
715
|
+
Raises:
|
716
|
+
ValueError: If the call does not match the regex format.
|
717
|
+
NotImplementedError: If the function or tag does not exist in the registry.
|
664
718
|
"""
|
665
719
|
if not (found := Re.RE_TASK_FMT.search(call)):
|
666
720
|
raise ValueError(
|
@@ -696,17 +750,20 @@ class BaseCallerArgs(BaseModel): # pragma: no cov
|
|
696
750
|
|
697
751
|
|
698
752
|
def create_model_from_caller(func: Callable) -> BaseModel: # pragma: no cov
|
699
|
-
"""Create model from the caller function.
|
700
|
-
|
701
|
-
|
753
|
+
"""Create model from the caller function.
|
754
|
+
|
755
|
+
This function is used to validate the caller function argument type hints
|
756
|
+
that are valid with the args field.
|
702
757
|
|
703
758
|
Reference:
|
704
759
|
- https://github.com/lmmx/pydantic-function-models
|
705
760
|
- https://docs.pydantic.dev/1.10/usage/models/#dynamic-model-creation
|
706
761
|
|
707
|
-
:
|
762
|
+
Args:
|
763
|
+
func: A caller function to create a model from.
|
708
764
|
|
709
|
-
:
|
765
|
+
Returns:
|
766
|
+
BaseModel: A Pydantic model created from the function signature.
|
710
767
|
"""
|
711
768
|
sig: inspect.Signature = inspect.signature(func)
|
712
769
|
type_hints: dict[str, Any] = get_type_hints(func)
|