import pyparsing as pp
[docs]
class Result:
"""When data are parsed, some (repeated) options can yield
special keys, results in agregating"""
[docs]
def result(self):
return self.value
[docs]
class Key:
"""A base class for items, that have to be treated in a special way"""
[docs]
@staticmethod
def NONE(x):
return x
""" The regular items has no special key """
[docs]
def __init__(self, key):
self.key = key
[docs]
def get(self, too):
if self.key in too:
out = too[self.key]
if out.__class__ is not self.ResultClass:
raise pp.ParseException(f"Conflicting values for item {self.key}")
return out
out = self.ResultClass()
too[self.key] = out
too.process.append(self.key)
return out
[docs]
class IgnoredKey(Key):
"""This key is totaly and silently ignored. Use it
for the keys, that should be written, but not read.
"""
[docs]
def add(self, too, val):
return
[docs]
class ValidateKey(Key):
"""This key is totaly and silently ignored. Use it
for the keys, that should be written, but not read.
"""
[docs]
def add(self, too, val):
too.checks.append(val)
[docs]
class SubKey(Key):
"""A base class for items that have subkeys"""
[docs]
def __init__(self, key, sub):
self.key = key
self.sub = self.convert(sub)
[docs]
def convert(self, sub):
return int(sub)
[docs]
class DictKey(SubKey):
[docs]
class ResultClass(Result):
def __init__(self):
self.value = {}
[docs]
def add(self, too, value):
out = self.get(too)
if self.sub in out.value:
raise pp.ParseException(f"Duplicate key {self.sub} in {self.key}")
out.value[self.sub] = value
[docs]
class DefDictKey(DictKey):
[docs]
def convert(self, sub):
return sub if sub == "def" else int(sub)
[docs]
class ArrayKey(SubKey):
[docs]
class ResultClass(Result):
NOT_SET = object()
def __init__(self):
self.value = []
[docs]
def result(self):
return self.value
[docs]
def add(self, too, val):
out = self.get(too)
ln = len(out.value)
i = self.sub - 1
if ln < i:
out.value += [out.NOT_SET] * (i - ln + 1)
if ln == i:
out.value.append(val)
else:
if out.value[i] is not out.NOT_SET:
raise pp.ParseException(f"Duplicate key {self.sub} in {self.key}")
out.value[i] = val
[docs]
class RepeatedKey(Key):
[docs]
class ResultClass(Result):
def __init__(self):
self.value = []
[docs]
def add(self, too, val):
self.get(too).value.append(val)
[docs]
class Values(dict):
"""Result of dict_from_parsed: dictionary with list of checks on the parsed values."""
[docs]
def __init__(self):
super().__init__()
self.checks = []
self.process = []
[docs]
def to_dict(self):
return {i: j.to_dict() if isinstance(j, Values) else j for i, j in self.items()}
[docs]
def dict_from_parsed(values):
"""Create a dictionary from the arguments.
From duplicate arguments create numpy arrays.
Moreover, if there is key of type (a,b), it will be transformed to subdictionary.
Such a keys do not allow duplicates.
>>> dict_from_parsed([("x", "y"), ((DictKey("a", 1)), 1), ((DictKey("a", 3)), 2)])
{'x': 'y', 'a': {1: 1, 3: 2}}
>>> dict_from_parsed([("x", 1), ("x", "2")]) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
pyparsing.exceptions.ParseException: There are non-unique keys: x
>>> dict_from_parsed([(RepeatedKey("x"), 1), (RepeatedKey("x"), 2)])
{'x': [1, 2]}
"""
out = Values()
duplicates = set()
errors = []
def add(key, value):
if isinstance(key, Key):
key.add(out, value)
elif key in out:
duplicates.add(k)
else:
out[key] = value
for k, v in values:
try:
add(k, v)
except Exception as e:
errors.append(e)
for key in out.process:
out[key] = out[key].result()
for i in out.checks:
try:
i(out)
except Exception as e:
errors.append(e)
if duplicates:
duplicates = ", ".join((i.upper() for i in duplicates))
errors.append(pp.ParseException(f"There are duplicate items named {duplicates}"))
if errors:
if len(errors) == 1:
raise errors[0]
errors = "\n".join((str(e) for e in errors))
raise pp.ParseException(f"There are errors in data: {errors}")
return out