Source code for lesana.types

"""
Type checkers for lesana fields.

Warning: this part of the code is still in flux and it may change
significantly in a future release.
"""
import datetime
import decimal
import logging

import dateutil.parser


[docs]class LesanaType: """ Base class for lesana field types. """ def __init__(self, field, types): self.field = field
[docs] def load(self, data): raise NotImplementedError
[docs] def empty(self): raise NotImplementedError
[docs]class LesanaString(LesanaType): """ A string of unicode text """ name = 'string'
[docs] def load(self, data): if not data: return data return str(data)
[docs] def empty(self): return ""
[docs]class LesanaText(LesanaString): """ A longer block of unicode text """ name = 'text'
[docs]class LesanaInt(LesanaType): """ An integer number """ name = "integer"
[docs] def load(self, data): if not data: return data try: return int(data) except ValueError: raise LesanaValueError( "Invalid value for integer field: {}".format(data) )
[docs] def empty(self): return 0
[docs]class LesanaFloat(LesanaType): """ A floating point number """ name = "float"
[docs] def load(self, data): if not data: return data try: return float(data) except ValueError: raise LesanaValueError( "Invalid value for float field: {}".format(data) )
[docs] def empty(self): return 0.0
[docs]class LesanaDecimal(LesanaType): """ A floating point number """ name = "decimal"
[docs] def load(self, data): if not data: return data try: return decimal.Decimal(data) except decimal.InvalidOperation: raise LesanaValueError( "Invalid value for decimal field: {}".format(data) )
[docs] def empty(self): return decimal.Decimal(0)
[docs]class LesanaTimestamp(LesanaType): """ A unix timestamp, assumed to be UTC """ name = "timestamp"
[docs] def load(self, data): if not data: return data if isinstance(data, datetime.datetime): return data try: return datetime.datetime.fromtimestamp( int(data), datetime.timezone.utc, ) except (TypeError, ValueError): raise LesanaValueError( "Invalid value for timestamp field: {}".format(data) )
[docs] def empty(self): return None
[docs]class LesanaDatetime(LesanaType): """ A datetime """ name = "datetime"
[docs] def load(self, data): if not data: return data if isinstance(data, datetime.datetime): return data if isinstance(data, datetime.date): return datetime.datetime(data.year, data.month, data.day) try: return dateutil.parser.parse(data) except dateutil.parser.ParserError: raise LesanaValueError( "Invalid value for datetime field: {}".format(data) )
[docs] def empty(self): return None
[docs]class LesanaDate(LesanaType): """ A date """ name = "date"
[docs] def load(self, data): if not data: return data if isinstance(data, datetime.date): return data try: return dateutil.parser.parse(data) except dateutil.parser.ParserError: raise LesanaValueError( "Invalid value for date field: {}".format(data) )
[docs] def empty(self): return None
[docs]class LesanaBoolean(LesanaType): """ A boolean value """ name = 'boolean'
[docs] def load(self, data): if not data: return data if isinstance(data, bool): return data else: raise LesanaValueError( "Invalid value for boolean field: {}".format(data) )
[docs] def empty(self): return None
[docs]class LesanaFile(LesanaString): """ A path to a local file. Relative paths are assumed to be relative to the base lesana directory (i.e. where .lesana lives) """ name = 'file'
[docs]class LesanaURL(LesanaString): """ An URL """ name = 'url'
[docs]class LesanaYAML(LesanaType): """ Free YAML contents (no structure is enforced) """ name = 'yaml'
[docs] def load(self, data): return data
[docs] def empty(self): return None
[docs]class LesanaList(LesanaType): """ A list of other values """ name = 'list' def __init__(self, field, types): super().__init__(field, types) try: self.sub_type = types[field['list']](field, types) except KeyError: logging.warning( "Unknown field type %s in field %s", field['type'], field['name'], ) self.sub_type = types['yaml'](field, types)
[docs] def load(self, data): if data is None: # empty for this type means an empty list return [] try: return [self.sub_type.load(x) for x in data] except TypeError: raise LesanaValueError( "Invalid value for list field: {}".format(data) )
[docs] def empty(self): return []
[docs]class LesanaValueError(ValueError): """ Raised in case of validation errors. """