- Add a `pos argument to the colander.Invalid.add` method.

- Add a ``__setitem__`` method to the ``colander.Invalid`` class.
This commit is contained in:
Chris McDonough
2010-04-01 20:08:50 +00:00
parent 6653b21259
commit 507ac4b31c
3 changed files with 67 additions and 9 deletions

View File

@@ -10,6 +10,10 @@ Next release
- Allow ``colander.Date`` and ``colander.DateTime`` invalid error
messages to be customized.
- Add a ``pos`` argument to the ``colander.Invalid.add`` method.
- Add a ``__setitem__`` method to the ``colander.Invalid`` class.
0.5 (2010-03-31)
----------------

View File

@@ -27,12 +27,52 @@ class Invalid(Exception):
self.msg = msg
self.children = []
def add(self, exc):
def add(self, exc, pos=None):
""" Add a child exception; ``exc`` must be an instance of
:class:`colander.Invalid`"""
:class:`colander.Invalid` or a subclass.
``pos`` is a value important for accurate error reporting. If
it is provided, it must be an integer representing the
position of ``exc`` relative to all other subexceptions of
this exception node. For example, if the exception being
added is about the third child of the exception which is
``self``, ``pos`` might be passed as ``3``.
If ``pos`` is provided, it will be assigned to the ``pos``
attribute of the provided ``exc`` object.
The ``parent`` attribute of the provided ``exc`` will be set
as a reference to ``self``.
"""
exc.parent = self
if pos is not None:
exc.pos = pos
self.children.append(exc)
def __setitem__(self, name, msg):
""" Add a subexception related to a child node with the
message ``msg``. ``name`` must be present in the names of the
set of child nodes of this exception's node; if this is not so,
a ``KeyError`` is raised.
For example, if the exception upon which ``__setitem__`` is
called has a node attribute with children, and that node
attribute has children that have the names ``name`` and
``title``, you may successfully call ``__setitem__('name',
'Bad name')`` or ``__setitem__('title', 'Bad title')``. But
calling ``__setitem__('wrong', 'whoops')`` will result in a
KeyError.
This method is typically only useful if the ``node`` it wraps
is a schema node representing a mapping.
"""
for num, child in enumerate(self.node.children):
if child.name == name:
exc = Invalid(child, msg)
self.add(exc, num)
return
raise KeyError(name)
def paths(self):
""" Return all paths through the exception graph """
def traverse(node, stack):
@@ -205,8 +245,7 @@ class Mapping(object):
except Invalid, e:
if error is None:
error = Invalid(node)
e.pos = num
error.add(e)
error.add(e, num)
if self.unknown_keys == 'raise':
if value:
@@ -280,8 +319,7 @@ class Tuple(Positional):
except Invalid, e:
if error is None:
error = Invalid(node)
e.pos = num
error.add(e)
error.add(e, num)
if error is not None:
raise error
@@ -339,8 +377,7 @@ class Sequence(Positional):
except Invalid, e:
if error is None:
error = Invalid(node)
e.pos = num
error.add(e)
error.add(e, num)
if error is not None:
raise error

View File

@@ -99,7 +99,24 @@ class TestInvalid(unittest.TestCase):
result,
"{'node1.node2.3': 'exc1; exc2; exc3', 'node1.node4': 'exc1; exc4'}"
)
def test___setitem__fails(self):
node = DummySchemaNode(None)
exc = self._makeOne(node, 'msg')
self.assertRaises(KeyError, exc.__setitem__, 'notfound', 'msg')
def test___setitem__succeeds(self):
node = DummySchemaNode(None)
child = DummySchemaNode(None)
child.name = 'found'
node.children = [child]
exc = self._makeOne(node, 'msg')
exc['found'] = 'msg2'
self.assertEqual(len(exc.children), 1)
childexc = exc.children[0]
self.assertEqual(childexc.pos, 0)
self.assertEqual(childexc.node.name, 'found')
class TestAll(unittest.TestCase):
def _makeOne(self, validators):
from colander import All