summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lesana/types.py46
-rw-r--r--setup.py1
-rw-r--r--tests/test_types.py54
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.
diff --git a/setup.py b/setup.py
index ffc79b9..b016fb3 100644
--- a/setup.py
+++ b/setup.py
@@ -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()