- Fix documentation: 0.9.2 requires `deserialize` of types to explicitly

deal with the potential to receive ``colander.null``.
This commit is contained in:
Chris McDonough
2011-03-29 15:59:41 -04:00
parent 2e269fc706
commit ed5b611199
5 changed files with 114 additions and 71 deletions

View File

@@ -1,6 +1,12 @@
Changes
=======
Next release
------------
- Fix documentation: 0.9.2 requires ``deserialize`` of types to explicitly
deal with the potential to receive ``colander.null``.
0.9.2 (2011-03-28)
------------------

View File

@@ -87,13 +87,12 @@ of our definitions, a ``Person`` represents:
Schema Node Objects
~~~~~~~~~~~~~~~~~~~
A schema is composed of one or more *schema node* objects, each
typically of the class :class:`colander.SchemaNode`, usually in a
nested arrangement. Each schema node object has a required *type*, an
optional deserialization *validator*, an optional *default*, an
optional *missing*, an optional *title*, an optional *description*, an
optional *widget*, and a slightly less optional *name*. It also
accepts *arbitrary* keyword arguments, which are attached directly as
A schema is composed of one or more *schema node* objects, each typically of
the class :class:`colander.SchemaNode`, usually in a nested arrangement.
Each schema node object has a required *type*, an optional deserialization
*validator*, an optional *default*, an optional *missing*, an optional
*title*, an optional *description*, and a slightly less optional *name*. It
also accepts *arbitrary* keyword arguments, which are attached directly as
attributes to the node instance.
The *type* of a schema node indicates its data type (such as
@@ -126,19 +125,20 @@ of the *name*.
The *description* of a schema node is metadata about a schema node
that can be used by higher-level systems. By default, it is empty.
The *widget* of a schema node is a concept used only by higher level
systems (such as form systems). By default it is ``None``. It won't
be discussed any further in the Colander documentation; it will
instead be explained in the context of the documentation of systems
which make use of it.
Any other keyword arguments to a schema node constructor will be
attached to the node unmolested (e.g. when ``foo=1`` is passed, the
resulting schema node will have an attribute named ``foo`` with the
value ``1``).
.. note:: Abitrary keyword arguments are allowed to a schema node
constructor in Colander 0.9+. Prior version disallow them.
.. note:: You may see some higher-level systems (such as Deform) pass a
``widget`` argument to a SchemaNode constructor. Such systems make use of
the fact that a SchemaNode can be passed arbitrary keyword arguments for
extension purposes. ``widget`` and other keyword arguments not enumerated
here but which are passed during schema node construction by someone
constructing a schema for a particular purpose are not used internally by
Colander; they are instead only meaningful to higher-level systems which
consume Colander schemas. Abitrary keyword arguments are allowed to a
schema node constructor in Colander 0.9+. Prior version disallow them.
The name of a schema node that is introduced as a class-level
attribute of a :class:`colander.MappingSchema`,
@@ -193,7 +193,7 @@ Earlier we defined a schema:
class Friend(colander.TupleSchema):
rank = colander.SchemaNode(colander.Int(),
validator=colander.Range(0, 9999))
validator=colander.Range(0, 9999))
name = colander.SchemaNode(colander.String())
class Phone(colander.MappingSchema):

View File

@@ -27,13 +27,15 @@ scope.
What Is Schema Binding?
-----------------------
- Values passed to a SchemaNode (e.g. ``description``, ``missing``,
etc.) may be an instance of the ``colander.deferred`` class.
Instances of the ``colander.deferred`` class are callables which
accept two positional arguments: a ``node`` and a ``kw`` dictionary.
- Any values passed as a keyword argument to a SchemaNode
(e.g. ``description``, ``missing``, etc.) may be an instance of the
``colander.deferred`` class. Instances of the ``colander.deferred`` class
are callables which accept two positional arguments: a ``node`` and a
``kw`` dictionary.
- When a schema node is bound, it is cloned, and any
``colander.deferred`` values it has as attributes will be resolved.
- When a schema node is bound, it is cloned, and any ``colander.deferred``
values it has as attributes will be resolved by invoking the callable
represented by the deferred value.
- A ``colander.deferred`` value is a callable that accepts two
positional arguments: the schema node being bound and a set of

View File

@@ -9,18 +9,19 @@ a new :term:`validator`.
Defining a New Type
-------------------
A new type is a class with two methods:: ``serialize`` and
``deserialize``. ``serialize`` converts a Python data structure (an
:term:`appstruct`) into a serialization (a :term:`cstruct`).
``deserialize`` converts a serialized value (a :term:`cstruct`) into a
Python data structure (a :term:`appstruct`).
A new type is a class with two methods:: ``serialize`` and ``deserialize``.
``serialize`` converts a Python data structure (an :term:`appstruct`) into a
serialization (a :term:`cstruct`). ``deserialize`` converts a serialized
value (a :term:`cstruct`) into a Python data structure (a :term:`appstruct`).
Here's a type which implements boolean serialization and
deserialization. It serializes a boolean to the string ``true`` or
``false`` or the special :attr:`colander.null` sentinel; it then
deserializes a string (presumably ``true`` or ``false``, but allows
some wiggle room for ``t``, ``on``, ``yes``, ``y``, and ``1``) to a
boolean value.
An Example
~~~~~~~~~~
Here's a type which implements boolean serialization and deserialization. It
serializes a boolean to the string ``true`` or ``false`` or the special
:attr:`colander.null` sentinel; it then deserializes a string (presumably
``true`` or ``false``, but allows some wiggle room for ``t``, ``on``,
``yes``, ``y``, and ``1``) to a boolean value.
.. code-block:: python
:linenos:
@@ -36,6 +37,8 @@ boolean value.
return appstruct and 'true' or 'false'
def deserialize(self, node, cstruct):
if cstruct is null:
return null
if not isinstance(cstruct, basestring):
raise Invalid(node, '%r is not a string' % cstruct)
value = cstruct.lower()
@@ -43,13 +46,6 @@ boolean value.
return True
return False
Note that the ``deserialize`` method of a type does not need to
explicitly deserialize the :attr:`colander.null` value.
Deserialization of the null value is dealt with at a higher level
(with the :meth:`colander.SchemaNode.deserialize` method); a type will
never receive an :attr:`colander.null` value as a ``cstruct`` argument
to its ``deserialize`` method.
Here's how you would use the resulting class as part of a schema:
.. code-block:: python
@@ -61,34 +57,61 @@ Here's how you would use the resulting class as part of a schema:
interested = colander.SchemaNode(Boolean())
The above schema has a member named ``interested`` which will now be
serialized and deserialized as a boolean, according to the logic
defined in the ``Boolean`` type class.
serialized and deserialized as a boolean, according to the logic defined in
the ``Boolean`` type class.
Note that the only two real constraints of a type class are:
Implementing Type Classes
~~~~~~~~~~~~~~~~~~~~~~~~~
- it must deal specially with the value :attr:`colander.null` within
``serialize``, translating it to a type-specific null value.
The constraints of a type class implementation are:
- its ``serialize`` method must be able to make sense of a value
generated by its ``deserialize`` method and vice versa, except that
the ``deserialize`` method needn't deal with the
:attr:`colander.null` value specially even if the ``serialize``
method returns it.
- It must have both a ``serialize`` and ``deserialize`` method.
- it must deal specially with the value :attr:`colander.null` within both
``serialize`` and ``deserialize``.
- its ``serialize`` method must be able to make sense of a value generated by
its ``deserialize`` method and vice versa.
The ``serialize`` method of a type accepts two values: ``node``, and
``appstruct``. ``node`` will be the schema node associated with this
type. It is used when the type must raise a :exc:`colander.Invalid`
error, which expects a schema node as its first constructor argument.
``appstruct`` will be the :term:`appstruct` value that needs to be
serialized.
``appstruct``. ``node`` will be the schema node associated with this type.
The node is used when the type must raise a :exc:`colander.Invalid` error,
which expects a schema node as its first constructor argument. ``appstruct``
will be the :term:`appstruct` value that needs to be serialized.
The deserialize and method of a type accept two values: ``node``, and
``cstruct``. ``node`` will be the schema node associated with this
type. It is used when the type must raise a :exc:`colander.Invalid`
error, which expects a schema node as its first constructor argument.
``cstruct`` will be the :term:`cstruct` value that needs to be
deserialized.
``cstruct``. ``node`` will be the schema node associated with this type.
The node is used when the type must raise a :exc:`colander.Invalid` error,
which expects a schema node as its first constructor argument. ``cstruct``
will be the :term:`cstruct` value that needs to be deserialized.
Null Values
~~~~~~~~~~~
The framework requires that both the ``serialize`` method and the
``deserialize`` method of a type explicitly deal with the potential to
receive a :attr:`colander.null` value. :attr:`colander.null` will be sent to
the type during serialization and deserialization in circumstances where a
value has not been provided by the data structure being serialized or
deserialized. In the common case, when the ``serialize`` or ``deserialize``
method of type receives the :attr:`colander.null` value, it should just
return :attr:`colander.null` to its caller.
A type might also choose to return :attr:`colander.null` if the value it
receives is *logically* (but not literally) null. For example,
:class:`colander.String` type converts the empty string to ``colander.null``
within its ``deserialize`` method.
.. code-block:: python
:linenos:
def deserialize(self, node, cstruct):
if not cstruct:
return null
Type Constructors
~~~~~~~~~~~~~~~~~
A type class does not need to implement a constructor (``__init__``),
but it isn't prevented from doing so if it needs to accept arguments;
Colander itself doesn't construct any types, only users of Colander

View File

@@ -17,8 +17,11 @@ that the :term:`cstruct` value corresponding to the node it's passed
to is missing, and if possible, the value of the ``missing`` attribute
of the corresponding node should be used instead.
Note that :attr:`colander.null` has no relationship to the built-in
Python ``None`` value.
Note that :attr:`colander.null` has no relationship to the built-in Python
``None`` value. ``colander.null`` is used instead of ``None`` because
``None`` is a potentially valid value for some serializations and
deserializations, and using it as a sentinel would prevent ``None`` from
being used in this way.
.. _serializing_null:
@@ -206,15 +209,24 @@ When a :attr:`colander.null` sentinel marker is passed to the
:meth:`colander.SchemaNode.deserialize` method of a particular node in
a schema, the node will take the following steps:
- If the schema node has an explicit ``missing`` attribute (the node's
constructor was supplied with an explicit ``missing`` argument), the
``missing`` value will be returned. Note that when this happens,
the ``missing`` value is not validated by any schema node validator:
it is simply returned.
- The *type* object's ``deserialize`` method will be called with the null
value to allow the type to convert the null value to a type-specific
default. The resulting "appstruct" is used instead of the value passed
directly to :meth:`colander.SchemaNode.deserialize` in subsequent
operations. Most types, when they receive the ``null`` value will simply
return it, however.
- If the schema node does *not* have an explicitly provided
``missing`` attribute (the node's constructor was not supplied with
an explicit ``missing`` value), a :exc:`colander.Invalid` exception
- If the appstruct value computed by the type's ``deserialize`` method is
``colander.null`` and the schema node has an explicit ``missing`` attribute
(the node's constructor was supplied with an explicit ``missing``
argument), the ``missing`` value will be returned. Note that when this
happens, the ``missing`` value is not validated by any schema node
validator: it is simply returned.
- If the appstruct value computed by the type's ``deserialize`` method is
``colander.null`` and the schema node does *not* have an explicitly
provided ``missing`` attribute (the node's constructor was not supplied
with an explicit ``missing`` value), a :exc:`colander.Invalid` exception
will be raised with a message indicating that the field is required.
.. note:: There are differences between serialization and
@@ -255,7 +267,7 @@ an ``age`` key of :attr:`colander.null`, the ``missing`` value of
.. note:: Note that ``None`` can be used for the ``missing`` schema
node value as required, as in the above example. It's no different
than any other value used as ``missing``. or ``colander.nuil`` can
than any other value used as ``missing``. The empty string can
also be used as the ``missing`` value if that is helpful.
The :attr:`colander.null` value is also the default, so it needn't be