diff options
| -rw-r--r-- | lesana/types.py | 46 | ||||
| -rw-r--r-- | setup.py | 1 | ||||
| -rw-r--r-- | tests/test_types.py | 54 | 
3 files changed, 101 insertions, 0 deletions
| diff --git a/lesana/types.py b/lesana/types.py index db6b661..4d3910c 100644 --- a/lesana/types.py +++ b/lesana/types.py @@ -4,8 +4,11 @@ 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 dateutil.parser +  class LesanaType:      """ @@ -100,6 +103,49 @@ class LesanaDecimal(LesanaType):          return decimal.Decimal(0) +class LesanaTimestamp(LesanaType): +    """ +    A datetime +    """ +    name = "timestamp" + +    def load(self, data): +        if not data: +            return data +        if isinstance(data, datetime.datetime) or \ +                isinstance(data, datetime.date): +            return data +        try: +            return dateutil.parser.parse(data) +        except dateutil.parser.ParserError: +            raise LesanaValueError( +                "Invalid value for datetime field: {}".format(data) +            ) + +    def empty(self): +        return None + + +class LesanaBoolean(LesanaType): +    """ +    A boolean value +    """ +    name = 'boolean' + +    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) +            ) + +    def empty(self): +        return None + +  class LesanaValueError(ValueError):      """      Raised in case of validation errors. @@ -20,6 +20,7 @@ setup(          # 'xapian >= 1.4',          'ruamel.yaml',          'jinja2', +        'dateutil',      ],      python_requires='>=3',      # Metadata diff --git a/tests/test_types.py b/tests/test_types.py index 7aff73f..cc2ff3b 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -1,3 +1,4 @@ +import datetime  import decimal  import unittest @@ -11,6 +12,16 @@ class testTypes(unittest.TestCase):      def tearDown(self):          pass +    def test_base(self): +        checker = types.LesanaType() + +        # The base class does not implement empty nor load +        with self.assertRaises(NotImplementedError): +            checker.empty() + +        with self.assertRaises(NotImplementedError): +            checker.load("") +      def test_string(self):          checker = types.LesanaString() @@ -98,6 +109,49 @@ class testTypes(unittest.TestCase):          v = checker.load(None)          self.assertEqual(v, None) +    def test_timestamp(self): +        checker = types.LesanaTimestamp() + +        v = checker.empty() +        self.assertEqual(v, None) + +        now = datetime.datetime.now() +        v = checker.load(now) +        self.assertEqual(v, now) + +        today = datetime.date.today() +        v = checker.load(today) +        self.assertEqual(v, today) + +        v = checker.load("2020-01-01") +        self.assertEqual(v, datetime.datetime(2020, 1, 1)) + +        v = checker.load("2020-01-01 10:00") +        self.assertEqual(v, datetime.datetime(2020, 1, 1, 10, 0)) + +        for d in ("today", "2020-13-01"): +            with self.assertRaises(types.LesanaValueError): +                checker.load(d) + +        v = checker.load(None) +        self.assertEqual(v, None) + +    def test_boolean(self): +        checker = types.LesanaBoolean() + +        v = checker.empty() +        self.assertEqual(v, None) + +        v = checker.load(True) +        self.assertEqual(v, True) + +        for d in ("maybe", "yes", "no"): +            with self.assertRaises(types.LesanaValueError): +                checker.load(d) + +        v = checker.load(None) +        self.assertEqual(v, None) +  if __name__ == '__main__':      unittest.main() | 
