Add Set schematype (backported from deform.Set) #71

Merged
merged 3 commits into from Nov 21, 2012
View
@@ -11,6 +11,8 @@ Bug Fixes
Features
~~~~~~~~
+- Add ``colander.Set`` type, ported from ``deform.Set``
+
- Add Python 3.3 to tox configuration and use newer tox testing regime
(setup.py dev).
View
@@ -107,4 +107,4 @@ Contributors
- Atsushi Odagiri, 2012/02/04
- Daniel Nouri, 2012/03/28
- Gary van der Merwe, 2012/04/05
-- Domen Kožar, 2012-04-16
+- Domen Kožar, 2012/04/16
View
@@ -733,6 +733,37 @@ def get_value(self, node, appstruct, path):
return appstruct[index]
+class Set(SchemaType):
+ """ A type representing a non-overlapping set of items.
+ Deserializes an iterable to a ``set`` object.
+
+ If the :attr:`colander.null` value is passed to the serialize
+ method of this class, the :attr:`colander.null` value will be
+ returned.
+
+ .. versionadded: 0.9.9.1
+
+ """
+
+ def serialize(self, node, appstruct):
+ if appstruct is null:
+ return null
+
+ return appstruct
+
+ def deserialize(self, node, cstruct):
+ if cstruct is null:
+ return null
+
+ if not is_nonstr_iter(cstruct):
+ raise Invalid(
+ node,
+ _('${cstruct} is not iterable', mapping={'cstruct': cstruct})
+ )
+
+ return set(cstruct)
+
+
class SequenceItems(list):
"""
List marker subclass for use by Sequence.cstruct_children, which indicates
@@ -949,6 +949,58 @@ def test_cstruct_children_justright(self):
result = typ.cstruct_children(node1, ['one', 'two'])
self.assertEqual(result, ['one', 'two'])
+
+class TestSet(unittest.TestCase):
+ def _makeOne(self, **kw):
+ from colander import Set
+ return Set(**kw)
+
+ def test_serialize(self):
+ typ = self._makeOne()
+ node = DummySchemaNode(typ)
+ provided = []
+ result = typ.serialize(node, provided)
+ self.assertTrue(result is provided)
+
+ def test_serialize_null(self):
+ from colander import null
+ typ = self._makeOne()
+ node = DummySchemaNode(typ)
+ result = typ.serialize(node, null)
+ self.assertTrue(result is null)
+
+ def test_deserialize_no_iter(self):
+ typ = self._makeOne()
+ node = DummySchemaNode(typ)
+ e = invalid_exc(typ.deserialize, node, 1)
+ self.assertEqual(e.msg, '${cstruct} is not iterable')
+
+ def test_deserialize_str_no_iter(self):
+ typ = self._makeOne()
+ node = DummySchemaNode(typ)
+ e = invalid_exc(typ.deserialize, node, "foo")
+ self.assertEqual(e.msg, '${cstruct} is not iterable')
+
+ def test_deserialize_null(self):
+ from colander import null
+ typ = self._makeOne()
+ node = DummySchemaNode(typ)
+ result = typ.deserialize(node, null)
+ self.assertEqual(result, null)
+
+ def test_deserialize_valid(self):
+ typ = self._makeOne()
+ node = DummySchemaNode(typ)
+ result = typ.deserialize(node, ('a',))
+ self.assertEqual(result, set(('a',)))
+
+ def test_deserialize_empty_set(self):
+ import colander
+ typ = self._makeOne()
+ node = DummySchemaNode(typ)
+ result = typ.deserialize(node, set())
+ self.assertEqual(result, set())
+
class TestSequence(unittest.TestCase):
def _makeOne(self, **kw):
from colander import Sequence
@@ -74,6 +74,8 @@ Types
.. autoclass:: Tuple
+ .. autoclass:: Set
+
.. autoclass:: Sequence
.. autoclass:: Seq