Add set_value method.

This commit is contained in:
Chris Rossi
2011-07-27 08:20:57 -04:00
parent 164644793a
commit 38d953a9e1
2 changed files with 114 additions and 0 deletions

View File

@@ -350,6 +350,9 @@ class SchemaType(object):
assert paths == [name], "paths should be [name] for leaf nodes."
return fstruct[name]
def set_value(self, node, appstruct, path, value):
raise AssertionError("Can't call 'set_value' on a leaf node.")
class Mapping(SchemaType):
""" A type which represents a mapping of names to nodes.
@@ -487,6 +490,16 @@ class Mapping(SchemaType):
def unflatten(self, node, paths, fstruct):
return _unflatten_mapping(node, paths, fstruct)
def set_value(self, node, appstruct, path, value):
if '.' in path:
next_name, rest = path.split('.', 1)
next_node = node[next_name]
next_appstruct = appstruct[next_name]
appstruct[next_name] = next_node.typ.set_value(
next_node, next_appstruct, rest, value)
else:
appstruct[path] = value
return appstruct
class Positional(object):
"""
@@ -587,6 +600,25 @@ class Tuple(Positional, SchemaType):
appstruct.append(mapstruct[subnode.name])
return tuple(appstruct)
def set_value(self, node, appstruct, path, value):
appstruct = list(appstruct)
if '.' in path:
next_name, rest = path.split('.', 1)
else:
next_name, rest = path, None
for index, next_node in enumerate(node.children):
if next_node.name == next_name:
break
else:
raise KeyError(next_name)
if rest is not None:
next_appstruct = appstruct[index]
appstruct[index] = next_node.typ.set_value(
next_node, next_appstruct, rest, value)
else:
appstruct[index] = value
return tuple(appstruct)
class Sequence(Positional, SchemaType):
"""
A type which represents a variable-length sequence of nodes,
@@ -737,6 +769,18 @@ class Sequence(Positional, SchemaType):
get_child, rewrite_subpath)
return [mapstruct[str(index)] for index in xrange(len(mapstruct))]
def set_value(self, node, appstruct, path, value):
if '.' in path:
next_name, rest = path.split('.', 1)
index = int(next_name)
next_node = node.children[0]
next_appstruct = appstruct[index]
appstruct[index] = next_node.typ.set_value(
next_node, next_appstruct, rest, value)
else:
index = int(path)
appstruct[index] = value
return appstruct
Seq = Sequence
@@ -1461,6 +1505,11 @@ class SchemaNode(object):
paths = sorted(fstruct.keys())
return self.typ.unflatten(self, paths, fstruct)
def set_value(self, appstruct, dotted_name, value):
""" Uses the schema to set a value in an appstruct from a dotted_name
path. """
self.typ.set_value(self, appstruct, dotted_name, value)
def deserialize(self, cstruct=null):
""" Deserialize the :term:`cstruct` into an :term:`appstruct` based
on the schema, run this :term:`appstruct` through the

View File

@@ -342,6 +342,11 @@ class TestSchemaType(unittest.TestCase):
result = typ.unflatten(node, ['node'], {'node': 'appstruct'})
self.assertEqual(result, 'appstruct')
def test_set_value(self):
typ = self._makeOne()
self.assertRaises(
AssertionError, typ.set_value, None, None, None, None)
class TestMapping(unittest.TestCase):
def _makeOne(self, *arg, **kw):
from colander import Mapping
@@ -534,6 +539,14 @@ class TestMapping(unittest.TestCase):
self.assertEqual(result, {
'one': {'a': 1, 'b': 2}, 'two': {'c': 3, 'd': 4}})
def test_set_value(self):
typ = self._makeOne()
node1 = DummySchemaNode(typ, name='node1')
node2 = DummySchemaNode(typ, name='node2')
node1.children = [node2]
appstruct = {'node2': {'foo': 'foo', 'baz': 'baz'}}
typ.set_value(node1, appstruct, 'node2.foo', 'bar')
self.assertEqual(appstruct, {'node2': {'foo': 'bar', 'baz': 'baz'}})
class TestTuple(unittest.TestCase):
def _makeOne(self):
@@ -690,6 +703,35 @@ class TestTuple(unittest.TestCase):
{'node': (1, 2), 'node.a': 1, 'node.b': 2})
self.assertEqual(result, (1, 2))
def test_set_value(self):
typ = self._makeOne()
node = DummySchemaNode(typ, name='node')
node.children = [
DummySchemaNode(typ, name='foo'),
DummySchemaNode(typ, name='bar')
]
node['foo'].children = [
DummySchemaNode(None, name='a'),
DummySchemaNode(None, name='b'),
]
node['bar'].children = [
DummySchemaNode(None, name='c'),
DummySchemaNode(None, name='d'),
]
appstruct = ((1, 2), (3, 4))
result = typ.set_value(node, appstruct, 'bar.c', 34)
self.assertEqual(result, ((1, 2), (34, 4)))
def test_set_value_bad_path(self):
typ = self._makeOne()
node = DummySchemaNode(typ, name='node')
node.children = [
DummySchemaNode(None, name='foo'),
DummySchemaNode(None, name='bar')
]
self.assertRaises(
KeyError, typ.set_value, node, (1, 2), 'foobar', 34)
class TestSequence(unittest.TestCase):
def _makeOne(self, **kw):
from colander import Sequence
@@ -821,6 +863,16 @@ class TestSequence(unittest.TestCase):
{'node.0': 'a', 'node.1': 'b'})
self.assertEqual(result, ['a', 'b'])
def test_setvalue(self):
typ = self._makeOne()
node1 = DummySchemaNode(typ, name='seq1')
node2 = DummySchemaNode(typ, name='seq2')
node1.children = [node2]
node2.children = DummySchemaNode(None, name='items')
appstruct = [[1, 2], [3, 4]]
typ.set_value(node1, appstruct, '1.0', 34)
self.assertEqual(appstruct, [[1, 2], [34, 4]])
class TestString(unittest.TestCase):
def _makeOne(self, encoding=None):
@@ -2110,6 +2162,19 @@ class TestFunctional(object):
schema.unflatten(schema.flatten(appstruct)),
appstruct)
def test_set_value(self):
import colander
appstruct = {
'int':10,
'ob':colander.tests,
'seq':[(1, 's'),(2, 's'), (3, 's'), (4, 's')],
'seq2':[{'key':1, 'key2':2}, {'key':3, 'key2':4}],
'tup':(1, 's'),
}
schema = self._makeSchema()
schema.set_value(appstruct, 'seq2.1.key', 6)
self.assertEqual(appstruct['seq2'][1], {'key':6, 'key2':4})
def test_invalid_asdict(self):
expected = {
'schema.int': '20 is greater than maximum value 10',