opentf-toolkit-nightly 0.62.0.dev1283__py3-none-any.whl → 0.62.0.dev1286__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/config.py CHANGED
@@ -25,14 +25,14 @@ from logging.config import dictConfig
25
25
  import yaml
26
26
 
27
27
  from .exceptions import ConfigError
28
- from .schemas import validate_schema, SERVICECONFIG
28
+ from .schemas import read_and_validate, SERVICECONFIG
29
29
 
30
30
 
31
31
  ########################################################################
32
32
 
33
33
  NOTIFICATION_LOGGER_EXCLUSIONS = 'eventbus'
34
34
 
35
- DEFAULT_CONTEXT = {
35
+ DEFAULT_CONTEXT: Dict[str, Any] = {
36
36
  'host': '127.0.0.1',
37
37
  'port': 443,
38
38
  'ssl_context': 'adhoc',
@@ -46,6 +46,21 @@ DEBUG_LEVELS = {'CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET'}
46
46
 
47
47
 
48
48
  def make_argparser(description: str, configfile: str) -> argparse.ArgumentParser:
49
+ """Make an argument parser.
50
+
51
+ The configured argument parser includes definitions for all commons
52
+ command-line parameters. It can be extended with additional
53
+ parameters.
54
+
55
+ # Required parameters
56
+
57
+ - description: a string, the parser description
58
+ - configfile: a string, the default configuration file name
59
+
60
+ # Returned value
61
+
62
+ An argument parser, configured.
63
+ """
49
64
  parser = argparse.ArgumentParser(description=description)
50
65
  parser.add_argument('--descriptor', help='alternate descriptor file')
51
66
  parser.add_argument(
@@ -98,7 +113,34 @@ def make_argparser(description: str, configfile: str) -> argparse.ArgumentParser
98
113
 
99
114
 
100
115
  def configure_logging(name: str, debug_level: str) -> None:
101
- logging_conf = {
116
+ """Configure logging.
117
+
118
+ The logging configuration is driven by the `debug_level` parameter.
119
+
120
+ A `wsgi` handler is defined, that will log messages to the WSGI
121
+ stream.
122
+
123
+ You can use the `OPENTF_LOGGING_REDIRECT` environment variable to
124
+ redirect the logs to a specific stream.
125
+
126
+ If `name` is not in `NOTIFICATION_LOGGER_EXCLUSIONS`, an `eventbus`
127
+ handler is added. It will post log messages to the event bus as
128
+ `Notification` events.
129
+
130
+ The configured format is:
131
+
132
+ `[%(asctime)s] %(levelname)s in {name}: %(message)s`
133
+
134
+ # Required parameters
135
+
136
+ - name: a string, the service name
137
+ - debug_level: a string, the log level
138
+
139
+ # Returned value
140
+
141
+ None.
142
+ """
143
+ logging_conf: Dict[str, Any] = {
102
144
  'version': 1,
103
145
  'formatters': {
104
146
  'default': {
@@ -126,41 +168,57 @@ def configure_logging(name: str, debug_level: str) -> None:
126
168
  dictConfig(logging_conf)
127
169
 
128
170
 
129
- def _read_configfile(
130
- argsconfig: Optional[str], configfile: str
131
- ) -> Tuple[str, Dict[str, Any]]:
132
- try:
133
- filename = argsconfig or configfile
134
- with open(filename, 'r', encoding='utf-8') as cnf:
135
- config = yaml.safe_load(cnf)
136
- if not isinstance(config, dict):
137
- raise ValueError('Config file is not an object.')
138
- return filename, config
139
- except Exception as err:
140
- raise ConfigError(f'Could not get configfile "{filename}", aborting: {err}.')
141
-
142
-
143
171
  def read_config(
144
- argsconfig: Optional[str],
145
- argscontext: Optional[str],
172
+ altconfig: Optional[str],
173
+ altcontext: Optional[str],
146
174
  configfile: str,
147
- defaultcontext,
148
- schema,
175
+ defaultcontext: Optional[Dict[str, Any]],
176
+ schema: Optional[str],
149
177
  ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
150
- if argsconfig is None and not os.path.isfile(configfile):
151
- if argscontext:
178
+ """Read service configuration.
179
+
180
+ If not None, the `alt` parameters are used to override the default
181
+ ones.
182
+
183
+ # Required parameters
184
+
185
+ - altconfig: a string (the configuration file location) or None
186
+ - altcontext: a string (the context name) or None
187
+ - configfile: a string, the configuration file name
188
+ - defaultcontext: a dictionary (the default context) or None
189
+ - schema: a string (the schema that validates the configuration)
190
+ or None
191
+
192
+ If `altcontext` is None, returns the current context as specified
193
+ in the service configuration.
194
+
195
+ If `schema` is None, uses SERVICECONFIG as the default schema.
196
+
197
+ # Returned value
198
+
199
+ A pair of dictionaries. The first item is the context, the second
200
+ item is the complete configuration.
201
+
202
+ # Raised exceptions
203
+
204
+ A _ConfigError_ exception is raised if the configuration file cannot
205
+ be read or if the configuration file is invalid.
206
+ """
207
+ if altconfig is None and not os.path.isfile(configfile):
208
+ if altcontext:
152
209
  raise ConfigError(
153
210
  'Cannot specify a context when using default configuration.'
154
211
  )
155
212
  context = defaultcontext or DEFAULT_CONTEXT
156
213
  config = {}
157
214
  else:
158
- configfile, config = _read_configfile(argsconfig, configfile)
159
- valid, extra = validate_schema(schema or SERVICECONFIG, config)
160
- if not valid:
161
- raise ConfigError(f'Config file "{configfile}" is invalid: {extra}.')
215
+ configfile = altconfig or configfile
216
+ try:
217
+ config = read_and_validate(configfile, schema or SERVICECONFIG)
218
+ except (ValueError, OSError) as err:
219
+ raise ConfigError(f'Could not read configfile "{configfile}": {err}.')
162
220
 
163
- context_name = argscontext or config['current-context']
221
+ context_name = altcontext or config['current-context']
164
222
  try:
165
223
  context = get_named(context_name, config['contexts'])['context']
166
224
  except ValueError as err:
@@ -169,11 +227,32 @@ def read_config(
169
227
 
170
228
 
171
229
  def read_descriptor(
172
- argsdescriptor: Optional[str], descriptor: Any
230
+ altdescriptor: Optional[str], descriptor: Optional[str]
173
231
  ) -> Tuple[str, List[Dict[str, Any]]]:
232
+ """Read service descriptor.
233
+
234
+ If `altdescriptor` is None, the descriptor is read from the module
235
+ where the function is called. If `descriptor` is None, the default
236
+ descriptor name is `service.yaml`.
237
+
238
+ # Required parameter
239
+
240
+ - altdescriptor: a string, the descriptor file location or None
241
+ - descriptor: a string, the descriptor file name or None
242
+
243
+ # Returned value
244
+
245
+ A pair (filename, manifests) where filename is the descriptor file
246
+ name and manifests is a list of dictionaries.
247
+
248
+ # Raised exceptions
249
+
250
+ A _ConfigError_ exception is raised if the descriptor file cannot
251
+ be read.
252
+ """
174
253
  try:
175
- if argsdescriptor:
176
- filename = argsdescriptor
254
+ if altdescriptor:
255
+ filename = altdescriptor
177
256
  else:
178
257
  for frame in inspect.stack():
179
258
  if frame.frame.f_code.co_name == '<module>':
@@ -187,12 +266,17 @@ def read_descriptor(
187
266
  with open(filename, 'r', encoding='utf-8') as definition:
188
267
  manifests = list(yaml.safe_load_all(definition))
189
268
  return filename, manifests
269
+ except ConfigError:
270
+ raise
190
271
  except Exception as err:
191
272
  raise ConfigError(f'Could not get descriptor "{filename}", aborting: {err}.')
192
273
 
193
274
 
194
275
  def get_named(name: str, entries: List[Dict[str, Any]]) -> Dict[str, Any]:
195
- """Get an entry from a list of entries.
276
+ """Get an entry from a list of dictionaries.
277
+
278
+ Matching entries are those with a 'name' entry equal to the
279
+ requested name.
196
280
 
197
281
  # Required parameters
198
282
 
@@ -219,7 +303,7 @@ def get_named(name: str, entries: List[Dict[str, Any]]) -> Dict[str, Any]:
219
303
  def get_debug_level(name: str) -> str:
220
304
  """Get service log level.
221
305
 
222
- Driven by environment variables. If `{service name}_DEBUG_LEVEL` is
306
+ Driven by environment variables. If `{name}_DEBUG_LEVEL` is
223
307
  defined, this value is used. If not, if `DEBUG_LEVEL` is set, then
224
308
  it is used. Otherwise, returns `INFO`.
225
309
 
opentf/commons/schemas.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) Henix, Henix.fr
1
+ # Copyright (c) 2023 Henix, Henix.fr
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -14,13 +14,14 @@
14
14
 
15
15
  """Helpers for the OpenTestFactory schemas."""
16
16
 
17
- from typing import Any, Dict, Tuple
17
+ from typing import Any, Dict, Optional, Tuple
18
18
 
19
19
  import json
20
20
  import logging
21
21
  import os
22
22
 
23
23
  from jsonschema import Draft201909Validator, ValidationError
24
+ from yaml import safe_load
24
25
 
25
26
  import opentf.schemas
26
27
 
@@ -68,8 +69,8 @@ INSIGHT_COLLECTOR = 'opentestfactory.org/v1alpha1/InsightCollector'
68
69
  ########################################################################
69
70
  # JSON Schema Helpers
70
71
 
71
- _schemas = {}
72
- _validators = {}
72
+ _schemas: Dict[str, Dict[str, Any]] = {}
73
+ _validators: Dict[str, Draft201909Validator] = {}
73
74
 
74
75
  SCHEMAS_ROOT_DIRECTORY = list(opentf.schemas.__path__)[0]
75
76
 
@@ -99,19 +100,21 @@ def get_schema(name: str) -> Dict[str, Any]:
99
100
  ) as schema:
100
101
  _schemas[name] = json.loads(schema.read())
101
102
  except Exception as err:
102
- logging.error('Could not read schema %s: %s', name, err)
103
+ logging.error('Could not read schema "%s": %s', name, err)
103
104
  raise
104
105
  return _schemas[name]
105
106
 
106
107
 
107
- def _validator(schema: str):
108
+ def _validator(schema: str) -> Draft201909Validator:
108
109
  if schema not in _validators:
109
110
  _validators[schema] = Draft201909Validator(get_schema(schema))
110
111
  return _validators[schema]
111
112
 
112
113
 
113
- def validate_schema(schema, instance) -> Tuple[bool, Any]:
114
- """Return True if instance validates schema.
114
+ def validate_schema(
115
+ schema: str, instance: Dict[str, Any]
116
+ ) -> Tuple[bool, Optional[str]]:
117
+ """Return (True, None) if instance validates schema.
115
118
 
116
119
  # Required parameters
117
120
 
@@ -122,14 +125,40 @@ def validate_schema(schema, instance) -> Tuple[bool, Any]:
122
125
 
123
126
  A (bool, Optional[str]) pair. If `instance` is a valid instance of
124
127
  `schema`, returns `(True, None)`. If not, returns `(False, error)`.
125
-
126
- # Raised exceptions
127
-
128
- If an error occurs while reading the schema, the initial exception
129
- is logged and raised again.
130
128
  """
131
129
  try:
132
130
  _validator(schema).validate(instance=instance)
133
131
  except ValidationError as err:
134
- return False, err
132
+ return False, str(err)
135
133
  return True, None
134
+
135
+
136
+ def read_and_validate(filename: str, schema: str) -> Dict[str, Any]:
137
+ """Read and validate a configuration file.
138
+
139
+ # Required parameters
140
+
141
+ - filename: a string, the configuration file name
142
+ - schema: a string, the schema to validate the configuration
143
+
144
+ # Returned value
145
+
146
+ A dictionary, the configuration.
147
+
148
+ # Raised exceptions
149
+
150
+ An _OSError_ exception is raised if the configuration file cannot
151
+ be read.
152
+
153
+ A _ValueError_ exception is raised if the configuration file is
154
+ invalid.
155
+ """
156
+ with open(filename, 'r', encoding='utf-8') as cnf:
157
+ config = safe_load(cnf)
158
+
159
+ if not isinstance(config, dict):
160
+ raise ValueError('Config file is not an object.')
161
+ valid, extra = validate_schema(schema or SERVICECONFIG, config)
162
+ if not valid:
163
+ raise ValueError(f'Config file "{filename}" is invalid: {extra}.')
164
+ return config
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: opentf-toolkit-nightly
3
- Version: 0.62.0.dev1283
3
+ Version: 0.62.0.dev1286
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,10 @@
1
1
  opentf/commons/__init__.py,sha256=thcqhAp--rYDw-JIBIkgpaLu1kEk3pyPJz8brx7f7vU,22599
2
2
  opentf/commons/auth.py,sha256=gXRp_0Tf3bfd65F4QiQmh6C6vR9y3ugag_0DSvozJFk,15898
3
- opentf/commons/config.py,sha256=_8WzSaeB0yjGRa8mTQ69OASyUSbKZbwNOiKpMp2jFwI,7842
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
6
  opentf/commons/pubsub.py,sha256=M0bvajR9raUP-xe5mfRjdrweZyHQw1_Qsy56gS-Sck4,7676
7
- opentf/commons/schemas.py,sha256=cjyPXRhRIPQauCdy9zGRaOkqdVcklz47JnljTTLNpq8,4254
7
+ opentf/commons/schemas.py,sha256=u1TdoGFNWoslWV7foBqXPgkGTQtRZWtXTr_PEd_TsFY,5189
8
8
  opentf/commons/selectors.py,sha256=R7y1E_5yP1zsz40eNy1buk464SaKVH5dCcRZnEYkdpU,7136
9
9
  opentf/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  opentf/schemas/abac.opentestfactory.org/v1alpha1/Policy.json,sha256=JXsfNAPSEYggeyaDutSQBeG38o4Bmcr70dPLWWeqIh8,2105
@@ -58,8 +58,8 @@ opentf/scripts/startup.py,sha256=vOGxl7xBcp1-_LsAKiOmeOqFl2iT81A2XRrXBLUrNi4,227
58
58
  opentf/toolkit/__init__.py,sha256=mYeJPZ92ulbTBItqEsZgF4nnuRh6G19QPY3Jxc92ifc,23028
59
59
  opentf/toolkit/channels.py,sha256=whLfPVT5PksVlprmoeb2ktaZ3KEhqyryUCVWBJq7PeY,24308
60
60
  opentf/toolkit/core.py,sha256=fqnGgaYnuVcd4fyeNIwpc0QtyUo7jsKeVgdkBfY3iqo,9443
61
- opentf_toolkit_nightly-0.62.0.dev1283.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
62
- opentf_toolkit_nightly-0.62.0.dev1283.dist-info/METADATA,sha256=higuBX2Pumx06AjFYWQWbInDjMYdHyt0ftbhZJmmkls,2192
63
- opentf_toolkit_nightly-0.62.0.dev1283.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
64
- opentf_toolkit_nightly-0.62.0.dev1283.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
65
- opentf_toolkit_nightly-0.62.0.dev1283.dist-info/RECORD,,
61
+ opentf_toolkit_nightly-0.62.0.dev1286.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
62
+ opentf_toolkit_nightly-0.62.0.dev1286.dist-info/METADATA,sha256=_LVp3XnOgkbwM-JnwBA7EiqniPxVpA6bfZGfhdBZW_o,2192
63
+ opentf_toolkit_nightly-0.62.0.dev1286.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
64
+ opentf_toolkit_nightly-0.62.0.dev1286.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
65
+ opentf_toolkit_nightly-0.62.0.dev1286.dist-info/RECORD,,