Permalink
Browse files

Add Set schematype

  • Loading branch information...
1 parent e2ef91c commit 73382436bf5f1da2ef00fdedb6379df8df4b1749 @domenkozar domenkozar committed Nov 10, 2012
Showing with 85 additions and 1 deletion.
  1. +2 −0 CHANGES.txt
  2. +1 −1 CONTRIBUTORS.txt
  3. +34 −0 colander/__init__.py
  4. +46 −0 colander/tests/test_colander.py
  5. +2 −0 docs/api.rst
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,40 @@ 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 hasattr(cstruct, '__iter__'):
@mcdonc
mcdonc Nov 10, 2012 Member

This should probably be checked using "colander.compat.is_nonstr_iter"

+ raise Invalid(
+ node,
+ _('${cstruct} is not iterable', mapping={'cstruct': cstruct})
+ )
+
+ cstruct = set(cstruct)
+ if not cstruct:
@mcdonc
mcdonc Nov 10, 2012 Member

Although this may be the way it was coded in deform, and other schema types return null when the cstruct evals boolean false, I think this thing should probably return the empty set rather than null (as an empty set is a valid set, and isn't necessarily, null, it's just empty).

@domenkozar
domenkozar Nov 10, 2012 Member

The plan was to keep consistency with all the other types, but I agree - empty set is a valid use case.

+ return null
+ return cstruct
+
+
class SequenceItems(list):
"""
List marker subclass for use by Sequence.cstruct_children, which indicates
@@ -949,6 +949,52 @@ 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_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, colander.null)
+
class TestSequence(unittest.TestCase):
def _makeOne(self, **kw):
from colander import Sequence
View
@@ -74,6 +74,8 @@ Types
.. autoclass:: Tuple
+ .. autoclass:: Set
+
.. autoclass:: Sequence
.. autoclass:: Seq

0 comments on commit 7338243

Please sign in to comment.