Skip to content

Commit

Permalink
Merge pull request #101 from cjw296/instantiate2
Browse files Browse the repository at this point in the history
implement, document and test the 'instantiate' decorator
  • Loading branch information
cguardia committed Apr 17, 2013
2 parents 183a9d4 + 5d12249 commit e83cebb
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGES.txt
Expand Up @@ -6,6 +6,9 @@ Features

- Add ``colander.ContainsOnly`` and ``colander.url`` validators.

- Add ``colander.instantiate`` to help define schemas containing
mappings and sequences more succinctly.

1.0a1 (2013-01-10)
------------------

Expand Down
15 changes: 15 additions & 0 deletions colander/__init__.py
Expand Up @@ -2089,3 +2089,18 @@ def rewrite_subpath(subpath):
appstruct[curname] = subnode.typ.unflatten(
subnode, subpaths, subfstruct)
return appstruct

class instantiate(object):
"""
A decorator which can be used to instantiate :class:`SchemaNode`
elements inline within a class definition.
All parameters passed to the decorator and passed along to the
:class:`SchemaNode` during instantiation.
"""

def __init__(self,*args,**kw):
self.args,self.kw = args,kw

def __call__(self,class_):
return class_(*self.args,**self.kw)
36 changes: 36 additions & 0 deletions colander/tests/test_colander.py
Expand Up @@ -3337,6 +3337,42 @@ class MainSchema(colander.MappingSchema):
schema = MainSchema()
return schema

class TestDeclarativeWithInstantiate(unittest.TestCase, TestFunctional):

def _makeSchema(self, name='schema'):

import colander

# an unlikely usage, but goos to test passing
# parameters to instantiation works
@colander.instantiate(name=name)
class schema(colander.MappingSchema):
int = colander.SchemaNode(colander.Int(),
validator=colander.Range(0, 10))
ob = colander.SchemaNode(colander.GlobalObject(package=colander))
@colander.instantiate()
class seq(colander.SequenceSchema):

@colander.instantiate()
class tup(colander.TupleSchema):
tupint = colander.SchemaNode(colander.Int())
tupstring = colander.SchemaNode(colander.String())

@colander.instantiate()
class tup(colander.TupleSchema):
tupint = colander.SchemaNode(colander.Int())
tupstring = colander.SchemaNode(colander.String())

@colander.instantiate()
class seq2(colander.SequenceSchema):

@colander.instantiate()
class mapping(colander.MappingSchema):
key = colander.SchemaNode(colander.Int())
key2 = colander.SchemaNode(colander.Int())

return schema

class Test_null(unittest.TestCase):
def test___nonzero__(self):
from colander import null
Expand Down
2 changes: 2 additions & 0 deletions docs/api.rst
Expand Up @@ -132,6 +132,8 @@ Schema-Related

.. autoclass:: deferred

.. autoclass:: instantiate

.. attribute:: null

Represents a null value in colander-related operations.
Expand Down
57 changes: 57 additions & 0 deletions docs/basics.rst
Expand Up @@ -982,6 +982,63 @@ indeed be present in the child list of the ``schema`` instance
``title`` attribute will be ``Some Schema`` (``schema.title`` will return
``Some Schema``).
Defining A Schema Declaratively
-------------------------------
Previously, we defined the schema in such a way that the individual
sequences and mappings within the schema could be re-used in different
schemas. If all nodes within a schema are only likely to be used in that
schema, then the schema definition can be made more succinct using the
:class:`~colander.instantiate` class decorator as shown below:
.. code-block:: python
:linenos:
import colander
class Person(colander.MappingSchema):
name = colander.SchemaNode(colander.String())
age = colander.SchemaNode(colander.Int(),
validator=colander.Range(0, 200))
@colander.instantiate()
class friends(colander.SequenceSchema):
@colander.instantiate()
class friend(colander.TupleSchema):
rank = colander.SchemaNode(colander.Int(),
validator=colander.Range(0, 9999))
name = colander.SchemaNode(colander.String())
@colander.instantiate()
class phones(colander.SequenceSchema):
@colander.instantiate()
class phone(colander.MappingSchema):
location = colander.SchemaNode(colander.String(),
validator=colander.OneOf(['home', 'work']))
number = colander.SchemaNode(colander.String())
If you need to pass parameters when using this style of schema
definition, such as a ``missing`` value to a :class:`SchemaNode`
during instantiation, you can pass these as parameters to
:class:`~colander.instantiate`.
For example, if we wanted to limit the number of friends a person can
have, and cater for people who have no friends, we could adjust the
schema as shown below:
.. code-block:: python
:linenos:
class Person(colander.MappingSchema):
@colander.instantiate(missing=(),
validator=colander.Length(max=5))
class friends(colander.SequenceSchema):
@colander.instantiate()
class friend(colander.TupleSchema):
name = colander.SchemaNode(colander.String())
Defining A Schema Imperatively
------------------------------
Expand Down

0 comments on commit e83cebb

Please sign in to comment.