This repository has been archived on 2025-09-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Alicja Cięciwa cb8886666c login page
2020-10-27 12:57:58 +01:00

163 lines
4.0 KiB
Python

try:
import cerberus
except ImportError:
cerberus = None
class ValidationError(ValueError):
def __init__(self, value, validator):
super(ValidationError, self).__init__(value)
self.validator = validator
self.value = value
def __str__(self):
return '{}\n{}'.format(
self.value,
'\n'.join(
'{}: {}'.format(k, e)
for k, errors in self.validator.errors.items()
for e in errors
)
)
VALIDATORS = {}
def validate(cls, data):
if not cerberus: # Skip validation if Cerberus is not available.
return
schema = cls.__SCHEMA__
key = id(schema)
try:
v = VALIDATORS[key]
except KeyError:
v = VALIDATORS[key] = cerberus.Validator(schema, allow_unknown=True)
if v.validate(data, normalize=False):
return
raise ValidationError(data, v)
class DataView(object):
"""A "view" to a data.
Validates the input mapping on creation. A subclass is expected to
provide a `__SCHEMA__` class attribute specifying a validator schema.
"""
def __init__(self, data):
self.validate(data)
self._data = data
def __repr__(self):
return "{0}({1!r})".format(type(self).__name__, self._data)
def __eq__(self, other):
if not isinstance(other, type(self)):
raise TypeError(
"cannot compare {0!r} with {1!r}".format(
type(self).__name__, type(other).__name__
)
)
return self._data == other._data
def __getitem__(self, key):
return self._data[key]
def __setitem__(self, key, value):
self._data[key] = value
def __delitem__(self, key):
del self._data[key]
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
@classmethod
def validate(cls, data):
return validate(cls, data)
class DataViewCollection(DataView):
"""A homogeneous collection of data views.
Subclasses are expected to assign a class attribute `item_class` to specify
the type of items it contains. This class will be used to coerce return
values when accessed. The item class should conform to the `DataView`
protocol.
You should not instantiate an instance from this class, but from one of its
subclasses instead.
"""
item_class = None
def __repr__(self):
return "{0}({1!r})".format(type(self).__name__, self._data)
def __len__(self):
return len(self._data)
def __getitem__(self, key):
return self.item_class(self._data[key])
def __setitem__(self, key, value):
if isinstance(value, DataView):
value = value._data
self._data[key] = value
def __delitem__(self, key):
del self._data[key]
class DataViewMapping(DataViewCollection):
"""A mapping of data views.
The keys are primitive values, while values are instances of `item_class`.
"""
@classmethod
def validate(cls, data):
for d in data.values():
cls.item_class.validate(d)
def __iter__(self):
return iter(self._data)
def keys(self):
return self._data.keys()
def values(self):
return [self[k] for k in self._data]
def items(self):
return [(k, self[k]) for k in self._data]
class DataViewSequence(DataViewCollection):
"""A sequence of data views.
Each entry is an instance of `item_class`.
"""
@classmethod
def validate(cls, data):
for d in data:
cls.item_class.validate(d)
def __iter__(self):
return (self.item_class(d) for d in self._data)
def __getitem__(self, key):
if isinstance(key, slice):
return type(self)(self._data[key])
return super(DataViewSequence, self).__getitem__(key)
def append(self, value):
if isinstance(value, DataView):
value = value._data
self._data.append(value)