Made raise_for_structure provide better feedback on errors
The function now takes an argument corresponding to the key that is being worked on. Also made the handling of dict validation more explicit about keys and the reasoning around why it does what it does.
This commit is contained in:
parent
8de039a3f3
commit
11917608bb
@ -37,10 +37,10 @@ canonical_manifest = {
|
||||
'description': str,
|
||||
'created': lambda v: (isinstance(v, int)
|
||||
or v is None
|
||||
or f"Invalid type {type(v)}, int"),
|
||||
or f"{type(v)}, expected int"),
|
||||
'duration': lambda v: (isinstance(v, float)
|
||||
or v is None
|
||||
or f"Invalid type {type(v)}, float"),
|
||||
or f"{type(v)}, expected float"),
|
||||
'presenters': [str],
|
||||
'courses': [{'designation': str,
|
||||
'semester': str}],
|
||||
@ -55,7 +55,7 @@ canonical_manifest = {
|
||||
}
|
||||
|
||||
|
||||
def raise_for_structure(data, structure):
|
||||
def raise_for_structure(data, structure, key=''):
|
||||
"""
|
||||
Validate the structure of a json-like object (data) against an expected
|
||||
format (structure), throwing an exception on invalid structure.
|
||||
@ -67,39 +67,44 @@ def raise_for_structure(data, structure):
|
||||
# Base case, validate that data type matches structure type
|
||||
if not isinstance(data, structure):
|
||||
raise ValueError(
|
||||
f"Invalid type {type(data)} for {data}, expected {structure}")
|
||||
f"Invalid type for {key}: {type(data)}, expected {structure}. "
|
||||
f"Value was {data}.")
|
||||
elif isinstance(structure, FunctionType):
|
||||
# The expected type is complicated and defined by function. Call it.
|
||||
result = structure(data)
|
||||
if result != True:
|
||||
raise ValueError(result)
|
||||
raise ValueError(f"Invalid type for {key}: {result}. "
|
||||
f"Value was {data}.")
|
||||
elif isinstance(structure, list):
|
||||
# List case, validate that data is list and recurse over items
|
||||
if not isinstance(data, list):
|
||||
raise ValueError(
|
||||
f"Invalid type {type(data)} for {data}, expected list")
|
||||
f"Invalid type for {key}: {type(data)}, expected list. "
|
||||
f"Value was {data}.")
|
||||
for c in data:
|
||||
raise_for_structure(c, structure[0])
|
||||
raise_for_structure(c, structure[0], f"{key} listitem")
|
||||
elif isinstance(structure, dict):
|
||||
# Dict case, validate that data is dict
|
||||
# and recurse over keys and values
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(
|
||||
f"Invalid type {type(data)} for {data}, expected dict")
|
||||
for k in data:
|
||||
if k not in structure:
|
||||
f"Invalid type for {key}: {type(data)}, expected dict. "
|
||||
f"Value was {data}.")
|
||||
for k in data.keys():
|
||||
if k not in structure.keys():
|
||||
# Maybe the key in structure is a type?
|
||||
# In that case there must only be one key in the structure.
|
||||
str_key = list(structure.keys())[0]
|
||||
if (len(structure.keys()) == 1
|
||||
and isinstance(str_key, type)):
|
||||
# Check that all keys in that structure are the right type
|
||||
raise_for_structure(k, str_key)
|
||||
raise_for_structure(k, str_key, f"{key} dictkey")
|
||||
# Keep checking the values as normal
|
||||
raise_for_structure(data[k], structure[str_key])
|
||||
raise_for_structure(data[k], structure[str_key], k)
|
||||
else:
|
||||
raise KeyError(f"Unexpected key {k}")
|
||||
else:
|
||||
raise_for_structure(data[k], structure[k])
|
||||
raise_for_structure(data[k], structure[k], k)
|
||||
return True
|
||||
|
||||
def get_section(config, name):
|
||||
|
Loading…
x
Reference in New Issue
Block a user