opentf-toolkit-nightly 0.62.0.dev1326__py3-none-any.whl → 0.62.0.dev1330__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 +5 -5
- opentf/commons/meta.py +100 -0
- opentf/commons/schemas.py +8 -9
- opentf/toolkit/__init__.py +32 -28
- {opentf_toolkit_nightly-0.62.0.dev1326.dist-info → opentf_toolkit_nightly-0.62.0.dev1330.dist-info}/METADATA +1 -1
- {opentf_toolkit_nightly-0.62.0.dev1326.dist-info → opentf_toolkit_nightly-0.62.0.dev1330.dist-info}/RECORD +9 -8
- {opentf_toolkit_nightly-0.62.0.dev1326.dist-info → opentf_toolkit_nightly-0.62.0.dev1330.dist-info}/WHEEL +1 -1
- {opentf_toolkit_nightly-0.62.0.dev1326.dist-info → opentf_toolkit_nightly-0.62.0.dev1330.dist-info}/licenses/LICENSE +0 -0
- {opentf_toolkit_nightly-0.62.0.dev1326.dist-info → opentf_toolkit_nightly-0.62.0.dev1330.dist-info}/top_level.txt +0 -0
opentf/commons/__init__.py
CHANGED
|
@@ -524,11 +524,11 @@ def get_context_parameter(
|
|
|
524
524
|
|
|
525
525
|
def _maybe_validate(v):
|
|
526
526
|
newv, reason = validator(name, v) if validator else (v, None)
|
|
527
|
-
lhs = f'
|
|
527
|
+
lhs = f'{spec["descriptiveName"]} ({name})' if spec else name
|
|
528
528
|
if newv != v:
|
|
529
|
-
app.logger.info(f'{lhs} {newv} (was defined as {v}, but {reason})')
|
|
529
|
+
app.logger.info(f' {lhs}: {newv} (was defined as {v}, but {reason})')
|
|
530
530
|
else:
|
|
531
|
-
app.logger.info(f'{lhs} {newv}')
|
|
531
|
+
app.logger.info(f' {lhs}: {newv}')
|
|
532
532
|
return newv
|
|
533
533
|
|
|
534
534
|
def _fatal(msg: str) -> NoReturn:
|
|
@@ -869,10 +869,10 @@ def validate_inputs(
|
|
|
869
869
|
raise ValueError(f'Mandatory input "{key}" not provided.')
|
|
870
870
|
_set_default(inputs, key, definition)
|
|
871
871
|
if definition.get('type') == 'choice':
|
|
872
|
-
if inputs.get(key) not in definition['options']:
|
|
872
|
+
if (val := inputs.get(key)) not in definition['options']:
|
|
873
873
|
allowed = '", "'.join(sorted(definition['options']))
|
|
874
874
|
raise ValueError(
|
|
875
|
-
f'Invalid value "{
|
|
875
|
+
f'Invalid value "{val}" for input "{key}". Allowed values: "{allowed}".'
|
|
876
876
|
)
|
|
877
877
|
|
|
878
878
|
if additional_inputs:
|
opentf/commons/meta.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Copyright (c) 2025 Henix, Henix.fr
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Toolkit metadata helpers."""
|
|
16
|
+
|
|
17
|
+
from typing import Dict, Iterable, Optional, Tuple
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
########################################################################
|
|
21
|
+
## Constants
|
|
22
|
+
|
|
23
|
+
CATEGORY_LABEL = 'opentestfactory.org/category'
|
|
24
|
+
CATEGORYPREFIX_LABEL = 'opentestfactory.org/categoryPrefix'
|
|
25
|
+
CATEGORYVERSION_LABEL = 'opentestfactory.org/categoryVersion'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
########################################################################
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def set_category_labels(labels: Dict[str, str], category: str) -> None:
|
|
32
|
+
"""Set category labels.
|
|
33
|
+
|
|
34
|
+
`labels` is updated.
|
|
35
|
+
|
|
36
|
+
# Required parameters
|
|
37
|
+
|
|
38
|
+
- labels: a dictionary of labels, possibly empty
|
|
39
|
+
- category: a string
|
|
40
|
+
"""
|
|
41
|
+
category_prefix = category_version = '_'
|
|
42
|
+
if '/' in category:
|
|
43
|
+
category_prefix, category = category.split('/')
|
|
44
|
+
if '@' in category:
|
|
45
|
+
category, category_version = category.split('@')
|
|
46
|
+
labels[CATEGORY_LABEL] = category
|
|
47
|
+
labels[CATEGORYPREFIX_LABEL] = category_prefix
|
|
48
|
+
labels[CATEGORYVERSION_LABEL] = category_version
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def maybe_set_category_labels(
|
|
52
|
+
labels: Dict[str, str],
|
|
53
|
+
prefix: Optional[str],
|
|
54
|
+
cat: Optional[str],
|
|
55
|
+
version: Optional[str],
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Set category labels if not None.
|
|
58
|
+
|
|
59
|
+
`labels` is updated if at least one parameter is not None.
|
|
60
|
+
"""
|
|
61
|
+
if cat is not None:
|
|
62
|
+
labels[CATEGORY_LABEL] = cat
|
|
63
|
+
if prefix is not None:
|
|
64
|
+
labels[CATEGORYPREFIX_LABEL] = prefix
|
|
65
|
+
if version is not None:
|
|
66
|
+
labels[CATEGORYVERSION_LABEL] = version
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def read_category_labels(
|
|
70
|
+
labels: Dict[str, str], default: Optional[str] = None
|
|
71
|
+
) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
72
|
+
"""Get categorty items from labels."""
|
|
73
|
+
prefix = labels.get(CATEGORYPREFIX_LABEL, default)
|
|
74
|
+
category = labels.get(CATEGORY_LABEL, default)
|
|
75
|
+
version = labels.get(CATEGORYVERSION_LABEL, default) or None
|
|
76
|
+
return prefix, category, version
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def event_match(labels: Dict[str, str], event: Dict[str, str]) -> bool:
|
|
80
|
+
"""Return True if event matches labels."""
|
|
81
|
+
|
|
82
|
+
def _match(what: str) -> bool:
|
|
83
|
+
return event[what] in ('_', labels.get(f'opentestfactory.org/{what}'))
|
|
84
|
+
|
|
85
|
+
if not ('category' in event or 'categoryPrefix' in event):
|
|
86
|
+
return False
|
|
87
|
+
if 'category' in event and 'categoryPrefix' in event:
|
|
88
|
+
match = _match('category') and _match('categoryPrefix')
|
|
89
|
+
elif 'category' in event:
|
|
90
|
+
match = _match('category')
|
|
91
|
+
else:
|
|
92
|
+
match = _match('categoryPrefix')
|
|
93
|
+
if match and 'categoryVersion' in event:
|
|
94
|
+
match = _match('categoryVersion')
|
|
95
|
+
return match
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def match_any(labels: Dict[str, str], events: Iterable[Dict[str, str]]) -> bool:
|
|
99
|
+
"""Check if any event matches category labels."""
|
|
100
|
+
return any(event_match(labels, event) for event in events)
|
opentf/commons/schemas.py
CHANGED
|
@@ -134,31 +134,30 @@ def validate_schema(
|
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
def read_and_validate(filename: str, schema: str) -> Dict[str, Any]:
|
|
137
|
-
"""Read and validate a
|
|
137
|
+
"""Read and validate a JSON or YAML file.
|
|
138
138
|
|
|
139
139
|
# Required parameters
|
|
140
140
|
|
|
141
|
-
- filename: a string, the
|
|
142
|
-
- schema: a string, the schema to validate the
|
|
141
|
+
- filename: a string, the file name
|
|
142
|
+
- schema: a string, the schema to validate the file content
|
|
143
143
|
|
|
144
144
|
# Returned value
|
|
145
145
|
|
|
146
|
-
A dictionary, the
|
|
146
|
+
A dictionary, the definition.
|
|
147
147
|
|
|
148
148
|
# Raised exceptions
|
|
149
149
|
|
|
150
|
-
An _OSError_ exception is raised if the
|
|
151
|
-
be read.
|
|
150
|
+
An _OSError_ exception is raised if the file cannot be read.
|
|
152
151
|
|
|
153
|
-
A _ValueError_ exception is raised if the
|
|
152
|
+
A _ValueError_ exception is raised if the JSON or YAML file is
|
|
154
153
|
invalid.
|
|
155
154
|
"""
|
|
156
155
|
with open(filename, 'r', encoding='utf-8') as cnf:
|
|
157
156
|
config = safe_load(cnf)
|
|
158
157
|
|
|
159
158
|
if not isinstance(config, dict):
|
|
160
|
-
raise ValueError('
|
|
159
|
+
raise ValueError('File is not a JSON object.')
|
|
161
160
|
valid, extra = validate_schema(schema or SERVICECONFIG, config)
|
|
162
161
|
if not valid:
|
|
163
|
-
raise ValueError(f'
|
|
162
|
+
raise ValueError(f'Invalid content: {extra}.')
|
|
164
163
|
return config
|
opentf/toolkit/__init__.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
"""A toolkit for creating OpenTestFactory plugins."""
|
|
16
16
|
|
|
17
|
-
from typing import Any, Callable, Dict, Optional
|
|
17
|
+
from typing import Any, Callable, Dict, Optional
|
|
18
18
|
|
|
19
19
|
import os
|
|
20
20
|
import threading
|
|
@@ -44,6 +44,7 @@ from opentf.commons import (
|
|
|
44
44
|
validate_inputs,
|
|
45
45
|
validate_schema,
|
|
46
46
|
)
|
|
47
|
+
from opentf.commons.meta import read_category_labels, maybe_set_category_labels
|
|
47
48
|
from opentf.toolkit import core
|
|
48
49
|
|
|
49
50
|
|
|
@@ -73,19 +74,9 @@ def _one_and_only_one(*args) -> bool:
|
|
|
73
74
|
return len([arg for arg in args if arg is not None]) == 1
|
|
74
75
|
|
|
75
76
|
|
|
76
|
-
def _get_pcv(
|
|
77
|
-
labels: Dict[str, str], default: Optional[str] = None
|
|
78
|
-
) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
79
|
-
"""Extract prefix, category, version from labels."""
|
|
80
|
-
prefix = labels.get('opentestfactory.org/categoryPrefix', default)
|
|
81
|
-
category = labels.get('opentestfactory.org/category', default)
|
|
82
|
-
version = labels.get('opentestfactory.org/categoryVersion', default) or None
|
|
83
|
-
return prefix, category, version
|
|
84
|
-
|
|
85
|
-
|
|
86
77
|
def _maybe_get_item(cache: Dict[Any, Any], labels: Dict[str, str]) -> Optional[Any]:
|
|
87
78
|
"""Get most relevant item from cache if it exists."""
|
|
88
|
-
prefix, category, version =
|
|
79
|
+
prefix, category, version = read_category_labels(labels)
|
|
89
80
|
|
|
90
81
|
for keys in (
|
|
91
82
|
(prefix, category, version),
|
|
@@ -132,16 +123,21 @@ def _get_target(
|
|
|
132
123
|
) -> Optional[Handler]:
|
|
133
124
|
"""Find target for labels.
|
|
134
125
|
|
|
135
|
-
|
|
136
|
-
|
|
126
|
+
- `prefix/category[@vn]` is more specific than `category[@vn]`
|
|
127
|
+
- `category@vn` is more specific than `category`
|
|
128
|
+
- `category[@vn]` is more specific than `prefix`
|
|
137
129
|
|
|
138
|
-
|
|
130
|
+
# Required parameters
|
|
139
131
|
|
|
140
|
-
|
|
132
|
+
- labels: a dictionary, the labels
|
|
133
|
+
- providers: a dictionary, the providers
|
|
141
134
|
|
|
142
|
-
|
|
135
|
+
# Returned value
|
|
136
|
+
|
|
137
|
+
Finds the most specific provider. Returns None if no provider
|
|
138
|
+
matches.
|
|
143
139
|
"""
|
|
144
|
-
prefix, category, version =
|
|
140
|
+
prefix, category, version = read_category_labels(labels)
|
|
145
141
|
|
|
146
142
|
for template in (f'{prefix}/{category}', category):
|
|
147
143
|
if version:
|
|
@@ -174,6 +170,19 @@ INVALID_HOOKS_DEFINITION_TEMPLATE = {
|
|
|
174
170
|
|
|
175
171
|
|
|
176
172
|
def _maybe_add_hook_watcher(plugin: Flask, schema: str) -> None:
|
|
173
|
+
"""Add hook watcher if needed.
|
|
174
|
+
|
|
175
|
+
If the `{name}_{type}_HOOKS` environment variable is set, the plugin
|
|
176
|
+
will watch the file and update its hooks accordingly.
|
|
177
|
+
|
|
178
|
+
If the hooks definition is invalid, a default hook is defined
|
|
179
|
+
instead, informing workflows of the issue.
|
|
180
|
+
|
|
181
|
+
# Required parameters
|
|
182
|
+
|
|
183
|
+
- plugin: a Flask object
|
|
184
|
+
- schema: a string, the schema name
|
|
185
|
+
"""
|
|
177
186
|
if plugin.config['CONTEXT'][KIND_KEY] == EXECUTIONCOMMAND:
|
|
178
187
|
type_ = 'CHANNEL'
|
|
179
188
|
else:
|
|
@@ -264,7 +273,7 @@ def _dispatch_providercommand(
|
|
|
264
273
|
plugin.logger.debug(
|
|
265
274
|
'Calling provider function %s (%s/%s@%s).',
|
|
266
275
|
handler.__name__,
|
|
267
|
-
*
|
|
276
|
+
*read_category_labels(labels, default='_'),
|
|
268
277
|
)
|
|
269
278
|
inputs: Dict[str, Any] = body['step'].get('with', {})
|
|
270
279
|
_ensure_inputs_match(plugin, labels, inputs)
|
|
@@ -293,7 +302,7 @@ def _dispatch_generatorcommand(
|
|
|
293
302
|
plugin.logger.debug(
|
|
294
303
|
'Calling generator %s (%s/%s@%s).',
|
|
295
304
|
handler.__name__,
|
|
296
|
-
*
|
|
305
|
+
*read_category_labels(labels, default='_'),
|
|
297
306
|
)
|
|
298
307
|
inputs: Dict[str, Any] = body.get('with', {})
|
|
299
308
|
_ensure_inputs_match(plugin, labels, inputs)
|
|
@@ -330,12 +339,12 @@ def _watchdog(plugin: Flask, polling_delay: int) -> None:
|
|
|
330
339
|
try:
|
|
331
340
|
current_modified_time = os.stat(file).st_mtime
|
|
332
341
|
except OSError as err:
|
|
333
|
-
plugin.logger.debug(
|
|
342
|
+
plugin.logger.debug('Could not stat file "%s": %s.', file, err)
|
|
334
343
|
current_modified_time = 0
|
|
335
344
|
if current_modified_time == files_stat[file] and not first:
|
|
336
345
|
continue
|
|
337
346
|
if files_stat[file] != current_modified_time and not first:
|
|
338
|
-
plugin.logger.debug(
|
|
347
|
+
plugin.logger.debug('Watched file "%s" has changed.', file)
|
|
339
348
|
files_stat[file] = current_modified_time
|
|
340
349
|
_run_handlers(plugin, file, list(files_handlers[file]))
|
|
341
350
|
first = False
|
|
@@ -451,12 +460,7 @@ def _subscribe(
|
|
|
451
460
|
context = plugin.config['CONTEXT']
|
|
452
461
|
kind = context[KIND_KEY]
|
|
453
462
|
labels = {}
|
|
454
|
-
|
|
455
|
-
labels['opentestfactory.org/category'] = cat
|
|
456
|
-
if cat_prefix is not None:
|
|
457
|
-
labels['opentestfactory.org/categoryPrefix'] = cat_prefix
|
|
458
|
-
if cat_version is not None:
|
|
459
|
-
labels['opentestfactory.org/categoryVersion'] = cat_version
|
|
463
|
+
maybe_set_category_labels(labels, cat_prefix, cat, cat_version)
|
|
460
464
|
context[INPUTS_KEY][(cat_prefix, cat, cat_version)] = (
|
|
461
465
|
manifest.get('inputs', {}),
|
|
462
466
|
manifest.get('additionalInputs'),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opentf-toolkit-nightly
|
|
3
|
-
Version: 0.62.0.
|
|
3
|
+
Version: 0.62.0.dev1330
|
|
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,10 +1,11 @@
|
|
|
1
|
-
opentf/commons/__init__.py,sha256=
|
|
1
|
+
opentf/commons/__init__.py,sha256=zyXR1ivlO_PZipQzRA2156qPMA0z7SpeFOOalQ7EG0Y,27075
|
|
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
|
|
5
5
|
opentf/commons/expressions.py,sha256=jM_YKXVOFhvOE2aE2IuacuvxhIsOYTFs2oQkpcbWR6g,19645
|
|
6
|
+
opentf/commons/meta.py,sha256=ygSO3mE2d-Ux62abzK1wYk86noT4R5Tumd90nyZo0MU,3322
|
|
6
7
|
opentf/commons/pubsub.py,sha256=M0bvajR9raUP-xe5mfRjdrweZyHQw1_Qsy56gS-Sck4,7676
|
|
7
|
-
opentf/commons/schemas.py,sha256=
|
|
8
|
+
opentf/commons/schemas.py,sha256=MbUt4XLiFKdyL6g-gKwgnKVUs7f5CXrY89vuqT0ehRM,5128
|
|
8
9
|
opentf/commons/selectors.py,sha256=2mmnvfZ13KizBQLsIvHXPU0Qtf6hkIvJpYdejNRszUs,7203
|
|
9
10
|
opentf/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
11
|
opentf/schemas/abac.opentestfactory.org/v1alpha1/Policy.json,sha256=JXsfNAPSEYggeyaDutSQBeG38o4Bmcr70dPLWWeqIh8,2105
|
|
@@ -55,11 +56,11 @@ opentf/schemas/opentestfactory.org/v1beta1/Workflow.json,sha256=QZ8mM9PhzsI9gTmw
|
|
|
55
56
|
opentf/schemas/opentestfactory.org/v1beta2/ServiceConfig.json,sha256=rEvK2YWL5lG94_qYgR_GnLWNsaQhaQ-2kuZdWJr5NnY,3517
|
|
56
57
|
opentf/scripts/launch_java_service.sh,sha256=S0jAaCuv2sZy0Gf2NGBuPX-eD531rcM-b0fNyhmzSjw,2423
|
|
57
58
|
opentf/scripts/startup.py,sha256=AcVXU2auPvqMb_6OpGzkVqrpgYV6vz7x_Rnv8YbAEkk,23114
|
|
58
|
-
opentf/toolkit/__init__.py,sha256=
|
|
59
|
+
opentf/toolkit/__init__.py,sha256=7MGAfKb5V9ckzWE8ozfj0PcC7g0a-0VfdyrClkWTk38,22319
|
|
59
60
|
opentf/toolkit/channels.py,sha256=6qKSsAgq_oJpuDRiKqVUz-EAjdfikcCG3SFAGmKZdhQ,25551
|
|
60
61
|
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.
|
|
62
|
+
opentf_toolkit_nightly-0.62.0.dev1330.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
63
|
+
opentf_toolkit_nightly-0.62.0.dev1330.dist-info/METADATA,sha256=IN0fv6k0RErVnPzp5ZrrjdNSvDoyP2vKQIODmDm_dVc,2214
|
|
64
|
+
opentf_toolkit_nightly-0.62.0.dev1330.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
|
65
|
+
opentf_toolkit_nightly-0.62.0.dev1330.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
|
|
66
|
+
opentf_toolkit_nightly-0.62.0.dev1330.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|