From 966f116d190dcf82ba2f825a9af7bd03cd786fc7 Mon Sep 17 00:00:00 2001 From: Joe Dallago Date: Tue, 19 Mar 2013 18:13:22 +0000 Subject: [PATCH] Trying to create an api for allowing a list of validators, deferred or not. --- colander/__init__.py | 31 ++++++++++++++++++++++++------- colander/tests/test_colander.py | 27 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/colander/__init__.py b/colander/__init__.py index e035c303..4b251dbe 100644 --- a/colander/__init__.py +++ b/colander/__init__.py @@ -1813,10 +1813,10 @@ def deserialize(self, cstruct=null): appstruct = self.typ.deserialize(self, cstruct) if self.preparer is not None: - # if the preparer is a function, call a single preparer + # if the preparer is a callable, call a single preparer if hasattr(self.preparer, '__call__'): appstruct = self.preparer(appstruct) - # if the preparer is a list, call each separate preparer + # if the preparer is an iterable, call each separate preparer elif is_nonstr_iter(self.preparer): for preparer in self.preparer: appstruct = preparer(appstruct) @@ -1831,8 +1831,14 @@ def deserialize(self, cstruct=null): return appstruct if self.validator is not None: - if not isinstance(self.validator, deferred): # unbound - self.validator(self, appstruct) + # if the validator is not an iterable, turn it into one + if not is_nonstr_iter(self.validator): + self.validator = [self.validator] + + for validator in self.validator: + if not isinstance(validator, deferred): + validator(self, appstruct) + return appstruct def add(self, node): @@ -1888,12 +1894,23 @@ def _bind(self, kw): names = dir(self) for k in names: v = getattr(self, k) - if isinstance(v, deferred): - v = v(self, kw) - setattr(self, k, v) + self._find_deferreds(kw, k, v) if getattr(self, 'after_bind', None): self.after_bind(self, kw) + def _find_deferreds(self, kw, k, v): + if isinstance(v, deferred): + v = v(self, kw) + setattr(self, k, v) + elif is_nonstr_iter(v): + new_v = [] + for item in v: + if isinstance(v, deferred): + new_v.append(item(self, kw)) + else: + new_v.append(item) + setattr(self, k, new_v) + def cstruct_children(self, cstruct): """ Will call the node's type's ``cstruct_children`` method with this node as a first argument, and ``cstruct`` as a second argument.""" diff --git a/colander/tests/test_colander.py b/colander/tests/test_colander.py index 127dce4a..8daa103b 100644 --- a/colander/tests/test_colander.py +++ b/colander/tests/test_colander.py @@ -2242,6 +2242,28 @@ def test_deserialize_with_validator(self): e = invalid_exc(node.deserialize, 1) self.assertEqual(e.msg, 'Wrong') + def test_deserialize_deferred_validator(self): + typ = DummyType() + node = self._makeOne(typ, validator=DummyDeferredValidator) + node = node.bind(message='good') + e = invalid_exc(node.deserialize, 1) + self.assertEqual(e.msg, 'good') + + def test_deserialize_all_with_deferred_validator(self): + typ = DummyType() + node = self._makeOne(typ, validator=[DummyDeferredValidator]) + node = node.bind(message='good') + e = invalid_exc(node.deserialize, 1) + self.assertEqual(e.msg, 'good') + + def test_deserialize_all_with_deferred_and_normal_validator(self): + typ = DummyType() + validator = DummyValidator(msg='Wrong') + node = self._makeOne(typ, validator=[DummyDeferredValidator, validator]) + node = node.bind(message='good') + e = invalid_exc(node.deserialize, 1) + self.assertEqual(e.msg, 'good') + def test_deserialize_value_is_null_no_missing(self): from colander import null from colander import Invalid @@ -3363,6 +3385,11 @@ def __call__(self, node, value): self.children and e.children.extend(self.children) raise e +from colander import deferred +@deferred +def DummyDeferredValidator(node, kw): + return DummyValidator(kw.get('message', 'Deferred')) + class Uncooperative(object): def __str__(self): raise ValueError('I wont cooperate')