Add Time type.

This commit is contained in:
Wichert Akkerman
2011-06-22 10:51:56 +02:00
parent e22af6e332
commit 98b2e91ebf
4 changed files with 163 additions and 0 deletions

View File

@@ -4,6 +4,8 @@ Changes
Next release
------------
- Add ``Time`` type.
- Add Dutch translation.
- Fix documentation: 0.9.2 requires ``deserialize`` of types to explicitly

View File

@@ -1204,6 +1204,86 @@ class Date(SchemaType):
)
return result
class Time(SchemaType):
""" A type representing a Python ``datetime.time`` object.
This type serializes python ``datetime.time`` objects to a
`ISO8601 <http://en.wikipedia.org/wiki/ISO_8601>`_ string format.
The format includes the date only.
The constructor accepts no arguments.
You can adjust the error message reported by this class by
changing its ``err_template`` attribute in a subclass on an
instance of this class. By default, the ``err_template``
attribute is the string ``Invalid date``. This string is used as
the interpolation subject of a dictionary composed of ``val`` and
``err``. ``val`` and ``err`` are the unvalidatable value and the
exception caused trying to convert the value, respectively. These
may be used in an overridden err_template as ``${val}`` and
``${err}`` respectively as necessary, e.g. ``_('${val} cannot be
parsed as an iso8601 date: ${err}')``.
For convenience, this type is also willing to coerce
``datetime.datetime`` objects to a time-only ISO string
representation during serialization. It does so by stripping off
any date information, converting the ``datetime.datetime`` into a
time before serializing.
Likewise, for convenience, this type is also willing to coerce ISO
representations that contain time info into a ``datetime.time``
object during deserialization. It does so by throwing away any
date information related to the serialized value during
deserialization.
If the :attr:`colander.null` value is passed to the serialize
method of this class, the :attr:`colander.null` value will be
returned.
The subnodes of the :class:`colander.SchemaNode` that wraps
this type are ignored.
"""
err_template = _('Invalid time')
def serialize(self, node, appstruct):
if appstruct is null:
return null
if isinstance(appstruct, datetime.datetime):
appstruct = appstruct.time()
if not isinstance(appstruct, datetime.time):
raise Invalid(node,
_('"${val}" is not a time object',
mapping={'val':appstruct})
)
return appstruct.isoformat().split('.')[0]
def deserialize(self, node, cstruct):
if not cstruct:
return null
try:
result = iso8601.parse_date(cstruct)
result = result.time()
except (iso8601.ParseError, TypeError):
try:
result = iso8601.parse_date('1970-01-01 %s' % cstruct)
result = result.time()
except (iso8601.ParseError, TypeError):
try:
parts = map(int, cstruct.split(':'))
if len(parts) > 3:
raise ValueError('Too many digits')
result = datetime.date(*parts[:3])
except Exception, e:
raise Invalid(node,
_(self.err_template,
mapping={'val':cstruct, 'err':e})
)
return result
class SchemaNode(object):
"""
Fundamental building block of schemas.

View File

@@ -1424,6 +1424,85 @@ class TestDate(unittest.TestCase):
result = typ.deserialize(node, iso)
self.assertEqual(result.isoformat(), dt.date().isoformat())
class TestTime(unittest.TestCase):
def _makeOne(self, *arg, **kw):
from colander import Time
return Time(*arg, **kw)
def _dt(self):
import datetime
return datetime.datetime(2010, 4, 26, 10, 48)
def _now(self):
import datetime
return datetime.datetime.now().time()
def test_serialize_null(self):
import colander
val = colander.null
node = DummySchemaNode(None)
typ = self._makeOne()
result = typ.serialize(node, val)
self.assertEqual(result, colander.null)
def test_serialize_with_garbage(self):
typ = self._makeOne()
node = DummySchemaNode(None)
e = invalid_exc(typ.serialize, node, 'garbage')
self.assertEqual(e.msg.interpolate(), '"garbage" is not a time object')
def test_serialize_with_time(self):
typ = self._makeOne()
time = self._now()
node = DummySchemaNode(None)
result = typ.serialize(node, time)
expected = time.isoformat().split('.')[0]
self.assertEqual(result, expected)
def test_serialize_with_datetime(self):
typ = self._makeOne()
dt = self._dt()
node = DummySchemaNode(None)
result = typ.serialize(node, dt)
expected = dt.time().isoformat().split('.')[0]
self.assertEqual(result, expected)
def test_deserialize_invalid_ParseError(self):
node = DummySchemaNode(None)
typ = self._makeOne()
e = invalid_exc(typ.deserialize, node, 'garbage')
self.failUnless('Invalid' in e.msg)
def test_deserialize_null(self):
import colander
node = DummySchemaNode(None)
typ = self._makeOne()
result = typ.deserialize(node, colander.null)
self.assertEqual(result, colander.null)
def test_deserialize_empty(self):
import colander
node = DummySchemaNode(None)
typ = self._makeOne()
result = typ.deserialize(node, '')
self.assertEqual(result, colander.null)
def test_deserialize_success_time(self):
import datetime
typ = self._makeOne()
node = DummySchemaNode(None)
result = typ.deserialize(node, '10:12:13')
self.assertEqual(result, datetime.time(10, 12, 13))
def test_deserialize_success_datetime(self):
dt = self._dt()
typ = self._makeOne()
iso = dt.isoformat()
node = DummySchemaNode(None)
result = typ.deserialize(node, iso)
self.assertEqual(result.isoformat(),
dt.time().isoformat().split('.')[0])
class TestSchemaNode(unittest.TestCase):
def _makeOne(self, *arg, **kw):
from colander import SchemaNode

View File

@@ -98,6 +98,8 @@ Types
.. autoclass:: Date
.. autoclass:: Time
Schema-Related
~~~~~~~~~~~~~~