Permalink
Browse files

Add support for callables in 'default'/'missing' serialization/deseri…

…alization.

Add tests.
Change docs inside SchemaNode.__init__.
  • Loading branch information...
1 parent ccd865d commit f68a8d44dcd1ec27f5020786ebc7f45a5875754c @stefanofontanelli committed Aug 13, 2012
Showing with 50 additions and 5 deletions.
  1. +12 −4 colander/__init__.py
  2. +38 −1 colander/tests/test_colander.py
View
@@ -1,6 +1,7 @@
import datetime
import decimal
import time
+import inspect
import itertools
import pprint
import re
@@ -1444,12 +1445,14 @@ class SchemaNode(object):
- ``default``: The default serialization value for this node.
Default: :attr:`colander.null`.
+ It can also be any python callable: it will be called during serialization.
- - ``missing``: The default deserialization value for this node. If it is
+ - ``missing``: The default deserialization value for this node. If it is
not provided, the missing value of this node will be the special marker
value :attr:`colander.required`, indicating that it is considered
'required'. When ``missing`` is :attr:`colander.required`, the
``required`` computed attribute will be ``True``.
+ It can also be any python callable: it will be called during deserialization.
- ``preparer``: Optional preparer for this node. It should be
an object that implements the
@@ -1540,9 +1543,12 @@ def serialize(self, appstruct=null):
If an ``appstruct`` argument is not explicitly provided, it
defaults to :attr:`colander.null`.
"""
- if appstruct is null:
+ if appstruct is null and\
+ not isinstance(self.default, deferred) and callable(self.default):
+ appstruct = self.default()
+ elif appstruct is null:
appstruct = self.default
- if isinstance(appstruct, deferred): # unbound schema with deferreds
+ if isinstance(appstruct, deferred): # unbound schema with deferreds
appstruct = null
cstruct = self.typ.serialize(self, appstruct)
return cstruct
@@ -1606,9 +1612,11 @@ def deserialize(self, cstruct=null):
if appstruct is null:
appstruct = self.missing
+ if not isinstance(appstruct, deferred) and callable(appstruct):
+ appstruct = appstruct()
if appstruct is required:
raise Invalid(self, _('Required'))
- if isinstance(appstruct, deferred): # unbound schema with deferreds
+ if isinstance(appstruct, deferred): # unbound schema with deferreds
raise Invalid(self, _('Required'))
# We never deserialize or validate the missing value
return appstruct
@@ -2174,7 +2174,44 @@ class FnordSchema(colander.Schema):
)
schema = FnordSchema()
self.assertEqual(schema['fnord[]'].name, 'fnord[]')
-
+
+ def test_deserialize_missing_callable(self):
+ import datetime
+ typ = DummyType()
+ node = self._makeOne(typ)
+ node.missing = datetime.datetime.now
+ data = node.deserialize()
+ self.assertEqual(isinstance(data, datetime.datetime), True)
+
+ def test_deserialize_wrong_missing_callable(self):
+
+ def f(a):
+ return {}
+
+ typ = DummyType()
+ node = self._makeOne(typ)
+ node.missing = f
+ self.assertRaises(TypeError, node.deserialize)
+
+ def test_serialize_default_callable(self):
+ import datetime
+ typ = DummyType()
+ node = self._makeOne(typ)
+ node.default = datetime.datetime.now
+ data = node.serialize()
+ self.assertEqual(isinstance(data, datetime.datetime), True)
+
+ def test_serialize_wrong_default_callable(self):
+
+ def f(a):
+ return {}
+
+ typ = DummyType()
+ node = self._makeOne(typ)
+ node.default = f
+ self.assertRaises(TypeError, node.serialize)
+
+
class TestDeferred(unittest.TestCase):
def _makeOne(self, wrapped):
from colander import deferred

0 comments on commit f68a8d4

Please sign in to comment.