opentf-toolkit-nightly 0.62.0.dev1302__py3-none-any.whl → 0.62.0.dev1311__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.
- opentf/commons/__init__.py +86 -0
- opentf/toolkit/__init__.py +24 -59
- {opentf_toolkit_nightly-0.62.0.dev1302.dist-info → opentf_toolkit_nightly-0.62.0.dev1311.dist-info}/METADATA +1 -1
- {opentf_toolkit_nightly-0.62.0.dev1302.dist-info → opentf_toolkit_nightly-0.62.0.dev1311.dist-info}/RECORD +7 -7
- {opentf_toolkit_nightly-0.62.0.dev1302.dist-info → opentf_toolkit_nightly-0.62.0.dev1311.dist-info}/LICENSE +0 -0
- {opentf_toolkit_nightly-0.62.0.dev1302.dist-info → opentf_toolkit_nightly-0.62.0.dev1311.dist-info}/WHEEL +0 -0
- {opentf_toolkit_nightly-0.62.0.dev1302.dist-info → opentf_toolkit_nightly-0.62.0.dev1311.dist-info}/top_level.txt +0 -0
opentf/commons/__init__.py
CHANGED
|
@@ -790,3 +790,89 @@ def validate_pipeline(
|
|
|
790
790
|
return True, [list(items) for items in toposort(jobs)]
|
|
791
791
|
except CircularDependencyError as err:
|
|
792
792
|
return False, str(err)
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
def _normalize_inputs(inputs: Dict[str, Any]) -> None:
|
|
796
|
+
"""Normalize inputs.
|
|
797
|
+
|
|
798
|
+
The 'normalized' form for inputs is with `-` separators, not `_`.
|
|
799
|
+
|
|
800
|
+
Non-normalized inputs are removed from the dictionary.
|
|
801
|
+
|
|
802
|
+
# Raised exceptions
|
|
803
|
+
|
|
804
|
+
A _ValueError_ exception is raised if an input is provided twice, in
|
|
805
|
+
a normalized as well as a non-normalized form.
|
|
806
|
+
"""
|
|
807
|
+
for key in inputs.copy():
|
|
808
|
+
if '_' in key:
|
|
809
|
+
normalized = key.replace('_', '-')
|
|
810
|
+
if normalized in inputs:
|
|
811
|
+
raise ValueError(
|
|
812
|
+
f'Both "{key}" and "{normalized}" specified in inputs.'
|
|
813
|
+
)
|
|
814
|
+
inputs[normalized] = inputs.pop(key)
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
def _set_default(inputs: Dict[str, Any], key: str, definition: Dict[str, Any]) -> None:
|
|
818
|
+
if (default := definition.get('default')) is not None:
|
|
819
|
+
inputs[key] = default
|
|
820
|
+
elif type_ := definition.get('type'):
|
|
821
|
+
if type_ == 'string':
|
|
822
|
+
inputs[key] = ''
|
|
823
|
+
elif type_ == 'number':
|
|
824
|
+
inputs[key] = 0
|
|
825
|
+
elif type_ == 'boolean':
|
|
826
|
+
inputs[key] = False
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
def validate_inputs(
|
|
830
|
+
declaration: Dict[str, Dict[str, Any]],
|
|
831
|
+
inputs: Dict[str, Any],
|
|
832
|
+
additional_inputs: bool = False,
|
|
833
|
+
) -> None:
|
|
834
|
+
"""Validate inputs.
|
|
835
|
+
|
|
836
|
+
Default values are filled in `inputs` as appropriate.
|
|
837
|
+
|
|
838
|
+
Input names are normalized to use hyphens instead of underscores.
|
|
839
|
+
|
|
840
|
+
Non-normalized inputs are removed from the dictionary.
|
|
841
|
+
|
|
842
|
+
# Required parameters
|
|
843
|
+
|
|
844
|
+
- declaration: a dictionary
|
|
845
|
+
- inputs: a dictionary
|
|
846
|
+
|
|
847
|
+
# Optional parameters
|
|
848
|
+
|
|
849
|
+
- additional_inputs: a boolean (False by default)
|
|
850
|
+
|
|
851
|
+
# Raised exceptions
|
|
852
|
+
|
|
853
|
+
A _ValueError_ exception is raised if inputs do not match
|
|
854
|
+
declaration.
|
|
855
|
+
"""
|
|
856
|
+
for key in declaration:
|
|
857
|
+
if key.startswith('{'): # Skip template entries
|
|
858
|
+
break
|
|
859
|
+
else:
|
|
860
|
+
_normalize_inputs(inputs)
|
|
861
|
+
|
|
862
|
+
for key, definition in declaration.items():
|
|
863
|
+
if key.startswith('{'):
|
|
864
|
+
continue
|
|
865
|
+
if key not in inputs:
|
|
866
|
+
if definition.get('required', False):
|
|
867
|
+
raise ValueError(f'Mandatory input "{key}" not provided.')
|
|
868
|
+
_set_default(inputs, key, definition)
|
|
869
|
+
|
|
870
|
+
if additional_inputs:
|
|
871
|
+
return
|
|
872
|
+
|
|
873
|
+
for key in inputs:
|
|
874
|
+
if key not in declaration and key.replace('_', '-') not in declaration:
|
|
875
|
+
allowed = ', '.join(sorted([f'"{k}"' for k in declaration.keys()]))
|
|
876
|
+
raise ValueError(
|
|
877
|
+
f'Unexpected input "{key}" found. Allowed inputs: {allowed}.'
|
|
878
|
+
)
|
opentf/toolkit/__init__.py
CHANGED
|
@@ -39,9 +39,10 @@ from opentf.commons import (
|
|
|
39
39
|
GENERATORCOMMAND,
|
|
40
40
|
SERVICECONFIG,
|
|
41
41
|
CHANNEL_HOOKS,
|
|
42
|
-
validate_schema,
|
|
43
|
-
make_status_response,
|
|
44
42
|
make_dispatchqueue,
|
|
43
|
+
make_status_response,
|
|
44
|
+
validate_inputs,
|
|
45
|
+
validate_schema,
|
|
45
46
|
)
|
|
46
47
|
from opentf.toolkit import core
|
|
47
48
|
|
|
@@ -72,28 +73,6 @@ def _one_and_only_one(*args) -> bool:
|
|
|
72
73
|
return len([arg for arg in args if arg is not None]) == 1
|
|
73
74
|
|
|
74
75
|
|
|
75
|
-
def _normalize_inputs(inputs: Dict[str, Any]) -> None:
|
|
76
|
-
"""Normalize inputs.
|
|
77
|
-
|
|
78
|
-
The 'normalized' form for inputs is with `-` separators, not `_`.
|
|
79
|
-
|
|
80
|
-
Non-normalized inputs are removed from the dictionary.
|
|
81
|
-
|
|
82
|
-
# Raised exceptions
|
|
83
|
-
|
|
84
|
-
A _core.ExecutionError_ is raised if an input is provided twice, in
|
|
85
|
-
a normalized as well as a non-normalized form.
|
|
86
|
-
"""
|
|
87
|
-
for key in inputs.copy():
|
|
88
|
-
if '_' in key:
|
|
89
|
-
normalized = key.replace('_', '-')
|
|
90
|
-
if normalized in inputs:
|
|
91
|
-
raise core.ExecutionError(
|
|
92
|
-
f"Both '{key}' and '{normalized}' specified in inputs."
|
|
93
|
-
)
|
|
94
|
-
inputs[normalized] = inputs.pop(key)
|
|
95
|
-
|
|
96
|
-
|
|
97
76
|
def _get_pcv(
|
|
98
77
|
labels: Dict[str, str], default: Optional[str] = None
|
|
99
78
|
) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
@@ -127,7 +106,10 @@ def _ensure_inputs_match(
|
|
|
127
106
|
"""Check inputs.
|
|
128
107
|
|
|
129
108
|
Normalize inputs, fills missing optional inputs with their default
|
|
130
|
-
values
|
|
109
|
+
values.
|
|
110
|
+
|
|
111
|
+
If one of the inputs is a template (name starting with '{'), no
|
|
112
|
+
normalization is performed.
|
|
131
113
|
|
|
132
114
|
# Raised exceptions
|
|
133
115
|
|
|
@@ -139,27 +121,10 @@ def _ensure_inputs_match(
|
|
|
139
121
|
return
|
|
140
122
|
|
|
141
123
|
declaration, additional_inputs = entry
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
_normalize_inputs(inputs)
|
|
147
|
-
for key, definition in declaration.items():
|
|
148
|
-
if key not in inputs:
|
|
149
|
-
if definition.get('required'):
|
|
150
|
-
raise core.ExecutionError(f"Mandatory input '{key}' not provided.")
|
|
151
|
-
if (default := definition.get('default')) is not None:
|
|
152
|
-
inputs[key] = default
|
|
153
|
-
|
|
154
|
-
if additional_inputs:
|
|
155
|
-
return
|
|
156
|
-
|
|
157
|
-
for key in inputs:
|
|
158
|
-
if key not in declaration and key.replace('_', '-') not in declaration:
|
|
159
|
-
allowed = ', '.join(sorted([f"'{k}'" for k in declaration.keys()]))
|
|
160
|
-
raise core.ExecutionError(
|
|
161
|
-
f"Unexpected input '{key}' found in function step. Allowed inputs: {allowed}."
|
|
162
|
-
)
|
|
124
|
+
try:
|
|
125
|
+
validate_inputs(declaration, inputs, additional_inputs)
|
|
126
|
+
except ValueError as err:
|
|
127
|
+
raise core.ExecutionError(str(err))
|
|
163
128
|
|
|
164
129
|
|
|
165
130
|
def _get_target(
|
|
@@ -256,15 +221,15 @@ def _read_hooks_definition(
|
|
|
256
221
|
hooks = yaml.safe_load(src)
|
|
257
222
|
if not isinstance(hooks, dict) or not 'hooks' in hooks:
|
|
258
223
|
plugin.logger.error(
|
|
259
|
-
|
|
224
|
+
'Hooks definition file "%s" needs a "hooks" entry, ignoring.', hooksfile
|
|
260
225
|
)
|
|
261
226
|
config['hooks'] = [invalid]
|
|
262
227
|
return
|
|
263
228
|
|
|
264
229
|
if config.get('hooks'):
|
|
265
|
-
plugin.logger.info(
|
|
230
|
+
plugin.logger.info('Replacing hooks definition using "%s".', hooksfile)
|
|
266
231
|
else:
|
|
267
|
-
plugin.logger.info(
|
|
232
|
+
plugin.logger.info('Reading hooks definition from "%s".', hooksfile)
|
|
268
233
|
|
|
269
234
|
config['hooks'] = hooks['hooks']
|
|
270
235
|
valid, extra = validate_schema(schema, config)
|
|
@@ -272,11 +237,11 @@ def _read_hooks_definition(
|
|
|
272
237
|
return
|
|
273
238
|
|
|
274
239
|
plugin.logger.error(
|
|
275
|
-
|
|
240
|
+
'Error while verifying "%s" hooks definition: %s.', hooksfile, extra
|
|
276
241
|
)
|
|
277
242
|
except Exception as err:
|
|
278
243
|
plugin.logger.error(
|
|
279
|
-
|
|
244
|
+
'Error while reading "%s" hooks definition: %s.', hooksfile, err
|
|
280
245
|
)
|
|
281
246
|
|
|
282
247
|
config['hooks'] = [invalid]
|
|
@@ -351,7 +316,7 @@ def _run_handlers(plugin: Flask, file, handlers) -> None:
|
|
|
351
316
|
handler(plugin, file, *args, **kwargs)
|
|
352
317
|
except Exception as err:
|
|
353
318
|
plugin.logger.error(
|
|
354
|
-
|
|
319
|
+
'Handler "%s" for file "%s" failed: %s. Ignoring.', handler, file, err
|
|
355
320
|
)
|
|
356
321
|
|
|
357
322
|
|
|
@@ -421,7 +386,7 @@ def watch_file(plugin: Flask, path: str, handler, *args, **kwargs) -> None:
|
|
|
421
386
|
if need_init:
|
|
422
387
|
plugin.config[WATCHEDFILES_KEY] = defaultdict(list)
|
|
423
388
|
plugin.config[WATCHEDFILES_EVENT_KEY] = threading.Event()
|
|
424
|
-
plugin.logger.debug(
|
|
389
|
+
plugin.logger.debug('Adding configuration watcher for "%s".', path)
|
|
425
390
|
plugin.config[WATCHEDFILES_KEY][path].append((handler, args, kwargs))
|
|
426
391
|
if need_init:
|
|
427
392
|
_start_watchdog(plugin)
|
|
@@ -503,14 +468,14 @@ def _subscribe(
|
|
|
503
468
|
}
|
|
504
469
|
except KeyError as err:
|
|
505
470
|
plugin.logger.error(
|
|
506
|
-
|
|
471
|
+
'Invalid descriptor "outputs" section, could not find key %s in: %s.',
|
|
507
472
|
err,
|
|
508
473
|
manifest.get('outputs', {}),
|
|
509
474
|
)
|
|
510
475
|
sys.exit(2)
|
|
511
476
|
except Exception as err:
|
|
512
477
|
plugin.logger.error(
|
|
513
|
-
|
|
478
|
+
'Invalid descriptor "outputs" section, got %s while parsing outputs.', err
|
|
514
479
|
)
|
|
515
480
|
sys.exit(2)
|
|
516
481
|
return subscribe(kind=kind, target='inbox', app=plugin, labels=labels)
|
|
@@ -546,7 +511,7 @@ def run_plugin(plugin: Flask) -> None:
|
|
|
546
511
|
)
|
|
547
512
|
else:
|
|
548
513
|
plugin.logger.warning(
|
|
549
|
-
|
|
514
|
+
'At least one of "category", "categoryPrefix" required, ignoring.'
|
|
550
515
|
)
|
|
551
516
|
elif context[KIND_KEY] == EXECUTIONCOMMAND:
|
|
552
517
|
context[SUBSCRIPTION_KEY].append(
|
|
@@ -663,15 +628,15 @@ def make_plugin(
|
|
|
663
628
|
|
|
664
629
|
if not _one_and_only_one(channel, generator, provider, providers, publisher):
|
|
665
630
|
raise ValueError(
|
|
666
|
-
|
|
631
|
+
'One and only one of "channel", "generator", "provider", "providers", or "publisher" is required.'
|
|
667
632
|
)
|
|
668
633
|
if not (descriptor is None or isinstance(descriptor, (dict, list))):
|
|
669
634
|
raise ValueError(
|
|
670
|
-
"
|
|
635
|
+
'"descriptor", if specified, must be a dictionary or a list of dictionaries.'
|
|
671
636
|
)
|
|
672
637
|
if channel and (not isinstance(args, list) or len(args) != 1):
|
|
673
638
|
raise ValueError(
|
|
674
|
-
"
|
|
639
|
+
'"args" is required for channel plugins and must be a list of one element.'
|
|
675
640
|
)
|
|
676
641
|
|
|
677
642
|
kind = (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: opentf-toolkit-nightly
|
|
3
|
-
Version: 0.62.0.
|
|
3
|
+
Version: 0.62.0.dev1311
|
|
4
4
|
Summary: OpenTestFactory Orchestrator Toolkit
|
|
5
5
|
Home-page: https://gitlab.com/henixdevelopment/open-source/opentestfactory/python-toolkit
|
|
6
6
|
Author: Martin Lafaix
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
opentf/commons/__init__.py,sha256=
|
|
1
|
+
opentf/commons/__init__.py,sha256=HvqRbKN7QbHrL4eSNp3q7hdf1OEo4-qvTzwVH_nx5js,26712
|
|
2
2
|
opentf/commons/auth.py,sha256=gXRp_0Tf3bfd65F4QiQmh6C6vR9y3ugag_0DSvozJFk,15898
|
|
3
3
|
opentf/commons/config.py,sha256=RVSSdQhMle4oCo_z_AR2EQ4U6sUjSxw-qVBtjKuJVfo,10219
|
|
4
4
|
opentf/commons/exceptions.py,sha256=7dhUXO8iyAbqVwlUKxZhgRzGqVcb7LkG39hFlm-VxIA,2407
|
|
@@ -55,11 +55,11 @@ opentf/schemas/opentestfactory.org/v1beta1/Workflow.json,sha256=QZ8mM9PhzsI9gTmw
|
|
|
55
55
|
opentf/schemas/opentestfactory.org/v1beta2/ServiceConfig.json,sha256=rEvK2YWL5lG94_qYgR_GnLWNsaQhaQ-2kuZdWJr5NnY,3517
|
|
56
56
|
opentf/scripts/launch_java_service.sh,sha256=S0jAaCuv2sZy0Gf2NGBuPX-eD531rcM-b0fNyhmzSjw,2423
|
|
57
57
|
opentf/scripts/startup.py,sha256=AcVXU2auPvqMb_6OpGzkVqrpgYV6vz7x_Rnv8YbAEkk,23114
|
|
58
|
-
opentf/toolkit/__init__.py,sha256=
|
|
58
|
+
opentf/toolkit/__init__.py,sha256=xh0XggCuR4jumiYDeMGaklktMmV8_9HAb6K5z1oJzdU,22327
|
|
59
59
|
opentf/toolkit/channels.py,sha256=6qKSsAgq_oJpuDRiKqVUz-EAjdfikcCG3SFAGmKZdhQ,25551
|
|
60
60
|
opentf/toolkit/core.py,sha256=fqnGgaYnuVcd4fyeNIwpc0QtyUo7jsKeVgdkBfY3iqo,9443
|
|
61
|
-
opentf_toolkit_nightly-0.62.0.
|
|
62
|
-
opentf_toolkit_nightly-0.62.0.
|
|
63
|
-
opentf_toolkit_nightly-0.62.0.
|
|
64
|
-
opentf_toolkit_nightly-0.62.0.
|
|
65
|
-
opentf_toolkit_nightly-0.62.0.
|
|
61
|
+
opentf_toolkit_nightly-0.62.0.dev1311.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
62
|
+
opentf_toolkit_nightly-0.62.0.dev1311.dist-info/METADATA,sha256=i_WxoxnBB2QmspW3dzimmGitEDI4dn008lIyif90GiE,2192
|
|
63
|
+
opentf_toolkit_nightly-0.62.0.dev1311.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
|
|
64
|
+
opentf_toolkit_nightly-0.62.0.dev1311.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
|
|
65
|
+
opentf_toolkit_nightly-0.62.0.dev1311.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|