replace schema_order with insert_before. closes #58
This commit is contained in:
11
CHANGES.txt
11
CHANGES.txt
@@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@@ -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,),
|
||||
|
@@ -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):
|
||||
|
@@ -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
|
||||
------------------------------
|
||||
|
Reference in New Issue
Block a user