Skip to content

Commit f0097fd

Browse files
authored
Add the 'error' data type to warn users about missing plugins (#2579)
1 parent 9145d14 commit f0097fd

File tree

2 files changed

+61
-8
lines changed

2 files changed

+61
-8
lines changed

docs/dev/validation.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,37 @@ You can also specify additional hints and help messages with an attribute:
161161
| **_help** | Help displayed when a value does not match the data type.<br>Overrides the built-in help message |
162162
| **_hint** | Additional hint(s) displayed together with an error message.<br>Use this attribute to explain what could be wrong |
163163

164+
(validation-error-type)=
165+
### Error Data Type
166+
167+
Sometimes you want to display a very specific error message when encountering an invalid attribute. For example, the **config.inline** validation test attribute only works when the **[files](plugin-files)** plugin is enabled. You can use the **error** data type to catch such scenarios, for example:
168+
169+
```
170+
_v_entry: # Validation entry
171+
config:
172+
inline:
173+
type: error
174+
_err_msg: Enable "files" plugin to use the "inline" configuration template
175+
_alt_types: [ str ]
176+
```
177+
178+
You can also use the **error** data type as a **_subtype** definition when an attribute can have a dictionary value only under very specific conditions. For example, the node **config** attribute is usually a list but could be a dictionary if the lab topology uses the **files** plugin:
179+
180+
```
181+
node:
182+
config:
183+
type: dict
184+
_subtype:
185+
type: error
186+
_err_msg: Enable "files" plugin to use the inline node/group configuration template(s)
187+
_alt_types: [ list ]
188+
```
189+
190+
The **error** data type can have two additional attributes:
191+
192+
* **_err_msg**: The text to display as a hint to the user
193+
* **_err_hint**: A hint from the **defaults.hints** dictionary to display as an explanation of the error
194+
164195
(validation-definition-examples)=
165196
### Data Type Definition Examples
166197

netsim/data/validate.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,23 @@ def validate_dictionary(
182182
# Option #1: Validate dictionary where every value is another type
183183
#
184184
if '_subtype' in data_type:
185+
subtype = data_type._subtype
185186
for k,v in data.items(): # Iterate over all dictionary values
186-
if isinstance(data_type._subtype,str) and data_type._subtype in attributes:
187+
if isinstance(subtype,Box) and subtype.get('type',None) == 'error':
188+
log.error(
189+
f"Incorrect {data_name} attribute '{k}' in {parent_path}",
190+
category=log.IncorrectAttr,
191+
module=module,
192+
hint=subtype.get('_err_hint',None),
193+
more_hints=subtype.get('_err_msg',None))
194+
OK = False
195+
elif isinstance(data_type._subtype,str) and data_type._subtype in attributes:
187196
OK = validate_attributes( # User defined data type, do full recursive validation including namespaces and modules
188197
data=v, # Call main validation routines with as many parameters as we can supply
189198
topology=topology,
190199
data_path=f'{parent_path}.{k}',
191-
data_name=f'{data_type._subtype}',
192-
attr_list=[ data_type._subtype ],
200+
data_name=f'{subtype}',
201+
attr_list=[ subtype ],
193202
modules=enabled_modules,
194203
module=module,
195204
module_source=module_source,
@@ -198,7 +207,7 @@ def validate_dictionary(
198207
OK = validate_item( # ... call the simpler item validation routine
199208
parent=data,
200209
key=k,
201-
data_type=data_type._subtype,
210+
data_type=subtype,
202211
parent_path=parent_path,
203212
data_name=data_name,
204213
module=module,
@@ -217,17 +226,30 @@ def validate_dictionary(
217226
return return_value # ... there's nothing further to validate, return what we accumulated so far
218227

219228
for k in list(data.keys()): # Iterate over the elements
220-
if not k in data_type._keys: # ... and report elements with invalid name
229+
key_type = data_type._keys.get(k,None)
230+
is_error = isinstance(key_type,Box) and key_type.get('type',None) == 'error'
231+
if not k in data_type._keys or is_error: # ... and report elements with invalid name
232+
#
233+
# We can get here because the dictionary key is not a valid attribute according to our schema
234+
# or because we stumbled upon an attribute with an explicit 'error' type
235+
#
236+
# In the latter case, we'll try to grab _err_hint and _err_msg attributes and use them
237+
# (when present) in the error message for further clarification. Please note that we're
238+
# using a different set of type attributes, to allow a plugin to replace the 'error' type
239+
# with something useful without inheriting the hints from the 'error' type
240+
#
221241
log.error(
222242
f"Incorrect {data_name} attribute '{k}' in {parent_path}",
223-
log.IncorrectAttr,
224-
module)
243+
category=log.IncorrectAttr,
244+
module=module,
245+
hint=key_type.get('_err_hint',None) if is_error else None,
246+
more_hints=key_type.get('_err_msg',None) if is_error else None)
225247
return_value = False
226248
else: # For valid elements, validate them
227249
validate_item(
228250
parent=data,
229251
key=k,
230-
data_type=data_type._keys[k],
252+
data_type=key_type,
231253
parent_path=parent_path,
232254
data_name=data_name,
233255
module=module,

0 commit comments

Comments
 (0)