- Get rid of `pserialize in favor of making serialize` always

partially serialize.

- Get rid of ``pdeserialize``: it existed only for symmetry.  We'll
  add something like it back later if we need it.
This commit is contained in:
Chris McDonough
2010-04-03 20:16:31 +00:00
parent a3eb5928f9
commit 62f6252cb2
4 changed files with 59 additions and 208 deletions

View File

@@ -6,6 +6,12 @@ Next release
- Fix bug in serialization of non-Unicode values in the ``String`` class.
- Get rid of ``pserialize`` in favor of making ``serialize`` always
partially serialize.
- Get rid of ``pdeserialize``: it existed only for symmetry. We'll
add something like it back later if we need it.
0.5.1 (2010-04-02)
------------------

View File

@@ -189,15 +189,7 @@ class OneOf(object):
raise Invalid(node, '"%s" is not one of %s' % (
value, ', '.join(['%s' % x for x in self.values])))
class Type(object):
""" Abstract base class for types (only for convenience) """
def pserialize(self, node, value):
return self.serialize(node, value)
def pdeserialize(self, node, value):
return self.deserialize(node, value)
class Mapping(Type):
class Mapping(object):
""" A type which represents a mapping of names to nodes.
The subnodes of the :class:`colander.SchemaNode` that wraps
@@ -209,9 +201,9 @@ class Mapping(Type):
control the behavior after construction.
unknown
``unknown`` controls the behavior of this type when an unknown
key is encountered in the value passed to the ``serialize`` or
``deserialize`` methods of this instance. The potential
``unknown`` controls the behavior of this type when an
unknown key is encountered in the value passed to the
``deserialize`` method of this instance. The potential
values of ``unknown`` are:
- ``ignore`` means that keys that are not present in the schema
@@ -222,30 +214,23 @@ class Mapping(Type):
raised when unknown keys are present during deserialization.
- ``preserve`` will preserve the 'raw' unknown keys and values
in the returned data structure.
in the returned data structure during deserialization.
Default: ``ignore``.
Note that the ``pserialize`` and ``pdeserialize`` methods of
this type will override this behavior, behaving as if
``unknown`` is ``ignore`` for the duration of those method calls.
partial
``partial`` controls the behavior of this type when a
schema-expected key is missing from the value passed to the
``serialize`` and ``deserialize`` methods of this instance.
During serialization and deserialization, when ``partial`` is
``False``, a :exc:`colander.Invalid` exception will be raised
if the mapping value does not contain a key specified by the
schema node related to this mapping type. When ``partial`` is
``True``, no exception is raised and a partial mapping will
be serialized/deserialized.
``deserialize`` method of this instance.
During deserialization, when ``partial`` is ``False``, a
:exc:`colander.Invalid` exception will be raised if the
mapping value does not contain a key specified by the schema
node related to this mapping type. When ``partial`` is
``True``, no exception is raised and a partial mapping will be
deserialized.
Default: ``False``.
Note that the ``pserialize`` and ``pdeserialize`` methods of
this type will override this behavior, behaving as if
``partial`` is ``True`` for the duration of those method calls.
"""
def __init__(self, unknown='ignore', partial=False):
@@ -322,7 +307,8 @@ class Mapping(Type):
return subnode.serialize(subval)
def default_callback(subnode):
return subnode.serialize(subnode.default)
return self._impl(node, value, callback, default_callback)
return self._impl(node, value, callback, default_callback,
unknown='ignore', partial=True)
def deserialize(self, node, value):
def callback(subnode, subval):
@@ -331,24 +317,6 @@ class Mapping(Type):
return subnode.default
return self._impl(node, value, callback, default_callback)
def pserialize(self, node, value):
def callback(subnode, subval):
return subnode.pserialize(subval)
def default_callback(subnode):
return subnode.serialize(subnode.default)
return self._impl(
node, value, callback, default_callback, unknown='ignore',
partial=True)
def pdeserialize(self, node, value):
def callback(subnode, subval):
return subnode.pdeserialize(subval)
def default_callback(subnode):
return subnode.default
return self._impl(
node, value, callback, default_callback, unknown='ignore',
partial=True)
class Positional(object):
"""
Marker abstract base class meaning 'this type has children which
@@ -357,7 +325,7 @@ class Positional(object):
creating a dictionary representation of an error tree.
"""
class Tuple(Type, Positional):
class Tuple(Positional):
""" A type which represents a fixed-length sequence of nodes.
The subnodes of the :class:`colander.SchemaNode` that wraps
@@ -411,7 +379,7 @@ class Tuple(Type, Positional):
return subnode.serialize(subval)
return self._impl(node, value, callback)
class Sequence(Type, Positional):
class Sequence(Positional):
"""
A type which represents a variable-length sequence of nodes,
all of which must be of the same type.
@@ -519,7 +487,7 @@ Seq = Sequence
default_encoding = 'utf-8'
class String(Type):
class String(object):
""" A type representing a Unicode string.
This type constructor accepts a single argument ``encoding``,
@@ -568,7 +536,7 @@ class String(Type):
Str = String
class Integer(Type):
class Integer(object):
""" A type representing an integer.
The subnodes of the :class:`colander.SchemaNode` that wraps
@@ -592,7 +560,7 @@ class Integer(Type):
Int = Integer
class Float(Type):
class Float(object):
""" A type representing a float.
The subnodes of the :class:`colander.SchemaNode` that wraps
@@ -616,7 +584,7 @@ class Float(Type):
Int = Integer
class Boolean(Type):
class Boolean(object):
""" A type representing a boolean object.
During deserialization, a value in the set (``false``, ``0``) will
@@ -649,7 +617,7 @@ class Boolean(Type):
Bool = Boolean
class GlobalObject(Type):
class GlobalObject(object):
""" A type representing an importable Python object. This type
serializes 'global' Python objects (objects which can be imported)
to dotted Python names.
@@ -756,7 +724,7 @@ class GlobalObject(Type):
except AttributeError:
raise Invalid(node, '%r has no __name__' % value)
class DateTime(Type):
class DateTime(object):
""" A type representing a Python ``datetime.datetime`` object.
This type serializes python ``datetime.datetime`` objects to a
@@ -822,7 +790,7 @@ class DateTime(Type):
'exc':e})
return result
class Date(Type):
class Date(object):
""" A type representing a Python ``datetime.date`` object.
This type serializes python ``datetime.date`` objects to a
@@ -958,16 +926,6 @@ class SchemaNode(object):
this node."""
return self.typ.serialize(self, value)
def pserialize(self, value):
""" Partially serialize the value based on the schema
represented by this node. """
return self.typ.pserialize(self, value)
def pdeserialize(self, value):
""" Partially deserialize the value based on the schema
represented by this node. """
return self.typ.pdeserialize(self, value)
def add(self, node):
""" Add a subnode to this node. """
self.children.append(node)

View File

@@ -211,23 +211,6 @@ class TestOneOf(unittest.TestCase):
e = invalid_exc(validator, None, None)
self.assertEqual(e.msg, '"None" is not one of 1, 2')
class TestType(unittest.TestCase):
def _makeOne(self):
from colander import Type
return Type()
def test_pserialize(self):
typ = self._makeOne()
typ.serialize = lambda *arg: arg
result = typ.pserialize(None, None)
self.assertEqual(result, (None, None))
def test_pdeserialize(self):
typ = self._makeOne()
typ.deserialize = lambda *arg: arg
result = typ.pdeserialize(None, None)
self.assertEqual(result, (None, None))
class TestMapping(unittest.TestCase):
def _makeOne(self, *arg, **kw):
from colander import Mapping
@@ -339,101 +322,35 @@ class TestMapping(unittest.TestCase):
result = typ.serialize(node, {'a':1})
self.assertEqual(result, {'a':1})
def test_serialize_unknown_raise(self):
node = DummySchemaNode(None)
node.children = [DummySchemaNode(None, name='a')]
typ = self._makeOne('raise')
e = invalid_exc(typ.serialize, node, {'a':1, 'b':2})
self.assertEqual(e.msg, "Unrecognized keys in mapping: {'b': 2}")
def test_serialize_unknown_preserve(self):
node = DummySchemaNode(None)
node.children = [DummySchemaNode(None, name='a')]
typ = self._makeOne(unknown='preserve')
result = typ.serialize(node, {'a':1, 'b':2})
self.assertEqual(result, {'a':1, 'b':2})
def test_serialize_subnodes_raise(self):
node = DummySchemaNode(None)
node.children = [
DummySchemaNode(None, name='a', exc='Wrong 2'),
DummySchemaNode(None, name='b', exc='Wrong 2'),
]
typ = self._makeOne()
e = invalid_exc(typ.serialize, node, {'a':1, 'b':2})
self.assertEqual(e.msg, None)
self.assertEqual(len(e.children), 2)
def test_serialize_subnode_missing_default(self):
node = DummySchemaNode(None)
node.children = [
DummySchemaNode(None, name='a'),
DummySchemaNode(None, name='b', default='abc'),
]
typ = self._makeOne()
result = typ.serialize(node, {'a':1})
self.assertEqual(result, {'a':1, 'b':'abc'})
def test_serialize_subnode_missing_nodefault(self):
node = DummySchemaNode(None)
node.children = [
DummySchemaNode(None, name='a'),
DummySchemaNode(None, name='b'),
]
typ = self._makeOne()
e = invalid_exc(typ.serialize, node, {'a':1})
self.assertEqual(e.children[0].msg, '"b" is required but missing')
def test_serialize_subnode_partial(self):
node = DummySchemaNode(None)
node.children = [
DummySchemaNode(None, name='a'),
DummySchemaNode(None, name='b'),
]
typ = self._makeOne(partial=True)
typ = self._makeOne()
result = typ.serialize(node, {'a':1})
self.assertEqual(result, {'a':1})
def test_pserialize(self):
node = DummySchemaNode(None)
node.children = [
DummySchemaNode(None, name='a'),
DummySchemaNode(None, name='b'),
]
typ = self._makeOne()
result = typ.pserialize(node, {'a':1})
self.assertEqual(result, {'a':1})
def test_pserialize_using_default(self):
def test_serialize_partial_with_default(self):
node = DummySchemaNode(None)
node.children = [
DummySchemaNode(None, name='a'),
DummySchemaNode(None, name='b', default='abc'),
]
typ = self._makeOne()
result = typ.pserialize(node, {'a':1})
result = typ.serialize(node, {'a':1})
self.assertEqual(result, {'a':1, 'b':'abc'})
def test_pdeserialize(self):
def test_serialize_with_unknown(self):
node = DummySchemaNode(None)
node.children = [
DummySchemaNode(None, name='a'),
DummySchemaNode(None, name='b'),
]
typ = self._makeOne()
result = typ.pdeserialize(node, {'a':1})
result = typ.serialize(node, {'a':1, 'b':2})
self.assertEqual(result, {'a':1})
def test_pdeserialize_using_default(self):
node = DummySchemaNode(None)
node.children = [
DummySchemaNode(None, name='a'),
DummySchemaNode(None, name='b', default='abc'),
]
typ = self._makeOne()
result = typ.pdeserialize(node, {'a':1})
self.assertEqual(result, {'a':1, 'b':'abc'})
class TestTuple(unittest.TestCase):
def _makeOne(self):
from colander import Tuple
@@ -1207,24 +1124,12 @@ class TestSchemaNode(unittest.TestCase):
e = invalid_exc(node.deserialize, 1)
self.assertEqual(e.msg, 'Wrong')
def test_pdeserialize(self):
typ = DummyType()
node = self._makeOne(typ)
result = node.pdeserialize(1)
self.assertEqual(result, 1)
def test_serialize(self):
typ = DummyType()
node = self._makeOne(typ)
result = node.serialize(1)
self.assertEqual(result, 1)
def test_pserialize(self):
typ = DummyType()
node = self._makeOne(typ)
result = node.pserialize(1)
self.assertEqual(result, 1)
def test_add(self):
node = self._makeOne(None)
node.add(1)
@@ -1471,9 +1376,6 @@ class DummySchemaNode(object):
raise Invalid(self, self.exc)
return val
pserialize = serialize
pdeserialize = deserialize
class DummyValidator(object):
def __init__(self, msg=None):
self.msg = msg
@@ -1496,9 +1398,3 @@ class DummyType(object):
def deserialize(self, node, value):
return value
def pserialize(self, node, value):
return value
def pdeserialize(self, node, value):
return value

View File

@@ -345,10 +345,10 @@ Serialization
Serializing a data structure is obviously the inverse operation from
deserializing a data structure. The ``serialize`` method of a schema
performs serialization of application data. If you pass the
``serialize`` method data that can be understood by the schema types
in the schema you're calling it against, you will be returned a data
structure of serialized values.
performs serialization of application data (aka an ``appstruct``). If
you pass the ``serialize`` method data that can be understood by the
schema types in the schema you're calling it against, you will be
returned a data structure of serialized values.
For example, given the following schema:
@@ -362,8 +362,7 @@ For example, given the following schema:
age = colander.SchemaNode(colander.Int(),
validator=colander.Range(0, 200))
If we try to serialize partial data using the ``serialize`` method of
the schema:
We can serialize a matching data structure:
.. code-block:: python
:linenos:
@@ -375,15 +374,18 @@ the schema:
The value for ``deserialized`` above will be ``{'age':'20',
'name':'Bob'}`` (note the integer has become a string).
Note that validation of values happens during serialization, just as
it does during deserialization.
Serialization and deserialization are not completely symmetric,
however. Although schema-driven data conversion happens during
serialization, and defaults are injected as necessary, the default
:mod:`colander` types are defined in such a way that the validation of
values and structural validation does *not* happen as it does during
deserialization. For example, the ``required`` argument of a schema
is typically ignored, none of the validators associated with the
schema or any of is nodes is invoked.
Schema nodes also define a ``pserialize`` method, which can be used to
"partially" serialize data. This is most useful when you want to
serialize a data structure where some of the values are missing.
For example, if we try to serialize partial data using the
``serialize`` method of the schema we defined above:
This usually means you may "partially" serialize a data structure
where some of the values are missing. If we try to serialize partial
data using the ``serialize`` method of the schema:
.. code-block:: python
:linenos:
@@ -392,25 +394,14 @@ For example, if we try to serialize partial data using the
schema = Person()
deserialized = schema.serialize(data)
When we attempt to invoke ``serialize``, an :exc:`colander.Invalid`
error will be raised, because we did not include the ``name``
attribute in our data.
The value for ``deserialized`` above will be ``{'age':'20'}`` (note
the integer has become a string). Above, even though we did not
include the ``name`` attribute in the data we fed to ``serialize``, an
error is *not* raised.
To serialize with data representing only a part of the schema, use the
``pserialize`` method:
.. code-block:: python
:linenos:
data = {'age':20}
schema = Person()
deserialized = schema.pserialize(data)
No error is raised, and the value for ``deserialized`` above will be
``{'age':'20'}`` (note the integer has become a string).
A ``pdeserialize`` method also exists, which is a mirror image of
``pserialize`` for deserialization.
The corollary: it is the responsibility of the developer to ensure he
serializes "the right" data; :mod:`colander` will not raise an error
when asked to serialize something that is partially nonsense.
Defining A Schema Imperatively
------------------------------