replace schema_order with insert_before. closes #58

This commit is contained in:
Chris McDonough
2012-08-31 01:56:15 -04:00
parent 2fed561c71
commit 9962e0375d
4 changed files with 51 additions and 27 deletions

View File

@@ -16,19 +16,18 @@ Features
- Add an ``insert`` method to SchemaNode objects.
- Add an ``insert_before`` method to SchemaNode objects.
- Better class-based mapping schema inheritance model.
* A node declared in a subclass of a mapping schema superclass now
overrides any node with the same name inherited from any superclass.
Previously, it just repeated and didn't override.
* A ``schema_order`` attribute may be passed to SchemaNode constructor.
This is an integer which defines the position in a parent node's child
ordering.
* An ``insert_before`` keyword argument may be passed to a SchemaNode
constructor. This is an string naming a node in a superclass which this
node should be placed before to influence schema ordering.
* ``colander.FIRST`` and ``colander.LAST`` constants are available for
passing in as ``schema_order``.
Backwards Incompatibilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -1696,6 +1696,15 @@ class SchemaNode(object):
a SchemaNode."""
self.children.insert(index, node)
def add_before(self, name, node):
""" Insert a subnode into the position before the node named ``name``
"""
for pos, sub in enumerate(self.children[:]):
if sub.name == name:
self.insert(pos, node)
return
raise KeyError('No such node named %s' % name)
def get(self, name, default=None):
""" Return the subnode associated with ``name`` or ``default`` if no
such node exists."""
@@ -1804,12 +1813,12 @@ def _Schema__new__(cls, *args, **kw):
typ = cls.schema_type()
node.__init__(typ, *args, **kw)
for n in cls.nodes:
order = getattr(n, 'schema_order', None)
insert_before = getattr(n, 'insert_before', None)
exists = node.get(n.name, _marker) is not _marker
# use exists for microspeed; we could just call __setitem__
# exclusively, but it does an enumeration that's unnecessary in the
# common (nonexisting) case (.add is faster)
if order is None:
if insert_before is None:
if exists:
node[n.name] = n
else:
@@ -1817,7 +1826,7 @@ def _Schema__new__(cls, *args, **kw):
else:
if exists:
del node[n.name]
node.insert(order, n)
node.add_before(insert_before, n)
return node
Schema = _SchemaMeta('Schema', (object,),

View File

@@ -2276,7 +2276,7 @@ class TestMappingSchemaInheritance(unittest.TestCase):
)
iwannacomefirst1_node = colander.SchemaNode(
colander.String(),
schema_order=colander.FIRST
insert_before='rank',
)
another_node = colander.SchemaNode(
colander.String(),
@@ -2289,7 +2289,7 @@ class TestMappingSchemaInheritance(unittest.TestCase):
)
serial2_node = colander.SchemaNode(
colander.Bool(),
schema_order=colander.LAST,
insert_before='name',
)
class Friend(colander.Schema):
@@ -2311,9 +2311,9 @@ class TestMappingSchemaInheritance(unittest.TestCase):
[
iwannacomefirst2_node,
rank_node,
serial2_node,
name_node,
another_node,
serial2_node
]
)
@@ -2354,7 +2354,20 @@ class TestMappingSchemaInheritance(unittest.TestCase):
inst.children,
[a2_node, b3_node, c2_node, d3_node]
)
def test_insert_before_failure(self):
import colander
a_node = colander.SchemaNode(
colander.Int(),
)
b_node = colander.SchemaNode(
colander.Int(),
insert_before='c'
)
class One(colander.Schema):
a = a_node
b = b_node
self.assertRaises(KeyError, One)
class TestDeferred(unittest.TestCase):
def _makeOne(self, wrapped):

View File

@@ -537,7 +537,7 @@ One class-based schema can be inherited from another. For example:
class SpecialFriend(Friend):
iwannacomefirst = colander.SchemaNode(
colander.String(),
schema_order=colander.FIRST
insert_before='rank',
)
another = colander.SchemaNode(
colander.String(),
@@ -549,7 +549,7 @@ One class-based schema can be inherited from another. For example:
)
friend = SuperSpecialFriend()
pprint.print([(x, x.typ) for x in friend.children])
pprint.pprint([(x, x.typ) for x in friend.children])
Here's what's printed when the above is run:
@@ -619,22 +619,25 @@ The behavior of subclassing one mapping schema using another is as follows:
* A node declared in a subclass of a mapping schema overrides any node with
the same name inherited from any superclass. The node remains at the child
order of the superclass node unless the subclass node defines a
``schema_order``.
order of the superclass node unless the subclass node defines an
``insert_before`` value.
* A node declared in a subclass of a mapping schema with a name that doesn't
override any node in a superclass will be placed *after* all nodes defined
in all superclasses unless the subclass node defines a ``schema_order``.
You can think of it like this: nodes added in subclasses will *follow*
nodes added in superclasses.
in all superclasses unless the subclass node defines an ``insert_before``
value. You can think of it like this: nodes added in subclasses will
*follow* nodes added in superclasses.
A ``schema_order`` attribute may be passed to the SchemaNode constructor of
mapping schema child nodes. This is an integer which influences the position
in its mapping schema's child ordering. ``colander.FIRST`` and
``colander.LAST`` constants are available for passing in as ``schema_order``.
The former tries to place the node in the very first position. The latter,
the very last. If ``schema_order`` is any other integer, the system will
attempt to place the node at the integer position in the ordering.
An ``insert_before`` keyword argument may be passed to the SchemaNode
constructor of mapping schema child nodes. This is a string which influences
the node's position in its mapping schema. The node will be inserted into
the mapping schema before the node named by ``insert_before``. An
``insert_before`` value must match the name of a schema node in a superclass
or it must match the name of a schema node already defined in the class; it
cannot name a schema node in a subclass, and it cannot name a schema node in
the same class that hasn't already been defined. If an ``insert_before`` is
provided that doesn't match any existing node name, a :exc:`KeyError` is
raised.
Defining A Schema Imperatively
------------------------------