- 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 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) 0.9.2 (2011-03-28)
------------------ ------------------

View File

@@ -87,13 +87,12 @@ of our definitions, a ``Person`` represents:
Schema Node Objects Schema Node Objects
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
A schema is composed of one or more *schema node* objects, each A schema is composed of one or more *schema node* objects, each typically of
typically of the class :class:`colander.SchemaNode`, usually in a the class :class:`colander.SchemaNode`, usually in a nested arrangement.
nested arrangement. Each schema node object has a required *type*, an Each schema node object has a required *type*, an optional deserialization
optional deserialization *validator*, an optional *default*, an *validator*, an optional *default*, an optional *missing*, an optional
optional *missing*, an optional *title*, an optional *description*, an *title*, an optional *description*, and a slightly less optional *name*. It
optional *widget*, and a slightly less optional *name*. It also also accepts *arbitrary* keyword arguments, which are attached directly as
accepts *arbitrary* keyword arguments, which are attached directly as
attributes to the node instance. attributes to the node instance.
The *type* of a schema node indicates its data type (such as 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 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. 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 Any other keyword arguments to a schema node constructor will be
attached to the node unmolested (e.g. when ``foo=1`` is passed, the attached to the node unmolested (e.g. when ``foo=1`` is passed, the
resulting schema node will have an attribute named ``foo`` with the resulting schema node will have an attribute named ``foo`` with the
value ``1``). value ``1``).
.. note:: Abitrary keyword arguments are allowed to a schema node .. note:: You may see some higher-level systems (such as Deform) pass a
constructor in Colander 0.9+. Prior version disallow them. ``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 The name of a schema node that is introduced as a class-level
attribute of a :class:`colander.MappingSchema`, attribute of a :class:`colander.MappingSchema`,

View File

@@ -27,13 +27,15 @@ scope.
What Is Schema Binding? What Is Schema Binding?
----------------------- -----------------------
- Values passed to a SchemaNode (e.g. ``description``, ``missing``, - Any values passed as a keyword argument to a SchemaNode
etc.) may be an instance of the ``colander.deferred`` class. (e.g. ``description``, ``missing``, etc.) may be an instance of the
Instances of the ``colander.deferred`` class are callables which ``colander.deferred`` class. Instances of the ``colander.deferred`` class
accept two positional arguments: a ``node`` and a ``kw`` dictionary. are callables which accept two positional arguments: a ``node`` and a
``kw`` dictionary.
- When a schema node is bound, it is cloned, and any - When a schema node is bound, it is cloned, and any ``colander.deferred``
``colander.deferred`` values it has as attributes will be resolved. 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 - A ``colander.deferred`` value is a callable that accepts two
positional arguments: the schema node being bound and a set of 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 Defining a New Type
------------------- -------------------
A new type is a class with two methods:: ``serialize`` and A new type is a class with two methods:: ``serialize`` and ``deserialize``.
``deserialize``. ``serialize`` converts a Python data structure (an ``serialize`` converts a Python data structure (an :term:`appstruct`) into a
:term:`appstruct`) into a serialization (a :term:`cstruct`). serialization (a :term:`cstruct`). ``deserialize`` converts a serialized
``deserialize`` converts a serialized value (a :term:`cstruct`) into a value (a :term:`cstruct`) into a Python data structure (a :term:`appstruct`).
Python data structure (a :term:`appstruct`).
Here's a type which implements boolean serialization and An Example
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 Here's a type which implements boolean serialization and deserialization. It
some wiggle room for ``t``, ``on``, ``yes``, ``y``, and ``1``) to a serializes a boolean to the string ``true`` or ``false`` or the special
boolean value. :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 .. code-block:: python
:linenos: :linenos:
@@ -36,6 +37,8 @@ boolean value.
return appstruct and 'true' or 'false' return appstruct and 'true' or 'false'
def deserialize(self, node, cstruct): def deserialize(self, node, cstruct):
if cstruct is null:
return null
if not isinstance(cstruct, basestring): if not isinstance(cstruct, basestring):
raise Invalid(node, '%r is not a string' % cstruct) raise Invalid(node, '%r is not a string' % cstruct)
value = cstruct.lower() value = cstruct.lower()
@@ -43,13 +46,6 @@ boolean value.
return True return True
return False 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: Here's how you would use the resulting class as part of a schema:
.. code-block:: python .. code-block:: python
@@ -61,33 +57,60 @@ Here's how you would use the resulting class as part of a schema:
interested = colander.SchemaNode(Boolean()) interested = colander.SchemaNode(Boolean())
The above schema has a member named ``interested`` which will now be The above schema has a member named ``interested`` which will now be
serialized and deserialized as a boolean, according to the logic serialized and deserialized as a boolean, according to the logic defined in
defined in the ``Boolean`` type class. 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 The constraints of a type class implementation are:
``serialize``, translating it to a type-specific null value.
- its ``serialize`` method must be able to make sense of a value - It must have both a ``serialize`` and ``deserialize`` method.
generated by its ``deserialize`` method and vice versa, except that
the ``deserialize`` method needn't deal with the - it must deal specially with the value :attr:`colander.null` within both
:attr:`colander.null` value specially even if the ``serialize`` ``serialize`` and ``deserialize``.
method returns it.
- 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 The ``serialize`` method of a type accepts two values: ``node``, and
``appstruct``. ``node`` will be the schema node associated with this ``appstruct``. ``node`` will be the schema node associated with this type.
type. It is used when the type must raise a :exc:`colander.Invalid` The node is used when the type must raise a :exc:`colander.Invalid` error,
error, which expects a schema node as its first constructor argument. which expects a schema node as its first constructor argument. ``appstruct``
``appstruct`` will be the :term:`appstruct` value that needs to be will be the :term:`appstruct` value that needs to be serialized.
serialized.
The deserialize and method of a type accept two values: ``node``, and The deserialize and method of a type accept two values: ``node``, and
``cstruct``. ``node`` will be the schema node associated with this ``cstruct``. ``node`` will be the schema node associated with this type.
type. It is used when the type must raise a :exc:`colander.Invalid` The node is used when the type must raise a :exc:`colander.Invalid` error,
error, which expects a schema node as its first constructor argument. which expects a schema node as its first constructor argument. ``cstruct``
``cstruct`` will be the :term:`cstruct` value that needs to be will be the :term:`cstruct` value that needs to be deserialized.
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__``), 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; but it isn't prevented from doing so if it needs to accept arguments;

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 to is missing, and if possible, the value of the ``missing`` attribute
of the corresponding node should be used instead. of the corresponding node should be used instead.
Note that :attr:`colander.null` has no relationship to the built-in Note that :attr:`colander.null` has no relationship to the built-in Python
Python ``None`` value. ``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: .. _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 :meth:`colander.SchemaNode.deserialize` method of a particular node in
a schema, the node will take the following steps: a schema, the node will take the following steps:
- If the schema node has an explicit ``missing`` attribute (the node's - The *type* object's ``deserialize`` method will be called with the null
constructor was supplied with an explicit ``missing`` argument), the value to allow the type to convert the null value to a type-specific
``missing`` value will be returned. Note that when this happens, default. The resulting "appstruct" is used instead of the value passed
the ``missing`` value is not validated by any schema node validator: directly to :meth:`colander.SchemaNode.deserialize` in subsequent
it is simply returned. operations. Most types, when they receive the ``null`` value will simply
return it, however.
- If the schema node does *not* have an explicitly provided - If the appstruct value computed by the type's ``deserialize`` method is
``missing`` attribute (the node's constructor was not supplied with ``colander.null`` and the schema node has an explicit ``missing`` attribute
an explicit ``missing`` value), a :exc:`colander.Invalid` exception (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. will be raised with a message indicating that the field is required.
.. note:: There are differences between serialization and .. 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 .. note:: Note that ``None`` can be used for the ``missing`` schema
node value as required, as in the above example. It's no different 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. 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 The :attr:`colander.null` value is also the default, so it needn't be