replace schema_order with insert_before. closes #58
This commit is contained in:
11
CHANGES.txt
11
CHANGES.txt
@@ -16,18 +16,17 @@ Features
|
|||||||
|
|
||||||
- Add an ``insert`` method to SchemaNode objects.
|
- Add an ``insert`` method to SchemaNode objects.
|
||||||
|
|
||||||
|
- Add an ``insert_before`` method to SchemaNode objects.
|
||||||
|
|
||||||
- Better class-based mapping schema inheritance model.
|
- Better class-based mapping schema inheritance model.
|
||||||
|
|
||||||
* A node declared in a subclass of a mapping schema superclass now
|
* A node declared in a subclass of a mapping schema superclass now
|
||||||
overrides any node with the same name inherited from any superclass.
|
overrides any node with the same name inherited from any superclass.
|
||||||
Previously, it just repeated and didn't override.
|
Previously, it just repeated and didn't override.
|
||||||
|
|
||||||
* A ``schema_order`` attribute may be passed to SchemaNode constructor.
|
* An ``insert_before`` keyword argument may be passed to a SchemaNode
|
||||||
This is an integer which defines the position in a parent node's child
|
constructor. This is an string naming a node in a superclass which this
|
||||||
ordering.
|
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
|
Backwards Incompatibilities
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@@ -1696,6 +1696,15 @@ class SchemaNode(object):
|
|||||||
a SchemaNode."""
|
a SchemaNode."""
|
||||||
self.children.insert(index, node)
|
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):
|
def get(self, name, default=None):
|
||||||
""" Return the subnode associated with ``name`` or ``default`` if no
|
""" Return the subnode associated with ``name`` or ``default`` if no
|
||||||
such node exists."""
|
such node exists."""
|
||||||
@@ -1804,12 +1813,12 @@ def _Schema__new__(cls, *args, **kw):
|
|||||||
typ = cls.schema_type()
|
typ = cls.schema_type()
|
||||||
node.__init__(typ, *args, **kw)
|
node.__init__(typ, *args, **kw)
|
||||||
for n in cls.nodes:
|
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
|
exists = node.get(n.name, _marker) is not _marker
|
||||||
# use exists for microspeed; we could just call __setitem__
|
# use exists for microspeed; we could just call __setitem__
|
||||||
# exclusively, but it does an enumeration that's unnecessary in the
|
# exclusively, but it does an enumeration that's unnecessary in the
|
||||||
# common (nonexisting) case (.add is faster)
|
# common (nonexisting) case (.add is faster)
|
||||||
if order is None:
|
if insert_before is None:
|
||||||
if exists:
|
if exists:
|
||||||
node[n.name] = n
|
node[n.name] = n
|
||||||
else:
|
else:
|
||||||
@@ -1817,7 +1826,7 @@ def _Schema__new__(cls, *args, **kw):
|
|||||||
else:
|
else:
|
||||||
if exists:
|
if exists:
|
||||||
del node[n.name]
|
del node[n.name]
|
||||||
node.insert(order, n)
|
node.add_before(insert_before, n)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
Schema = _SchemaMeta('Schema', (object,),
|
Schema = _SchemaMeta('Schema', (object,),
|
||||||
|
@@ -2276,7 +2276,7 @@ class TestMappingSchemaInheritance(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
iwannacomefirst1_node = colander.SchemaNode(
|
iwannacomefirst1_node = colander.SchemaNode(
|
||||||
colander.String(),
|
colander.String(),
|
||||||
schema_order=colander.FIRST
|
insert_before='rank',
|
||||||
)
|
)
|
||||||
another_node = colander.SchemaNode(
|
another_node = colander.SchemaNode(
|
||||||
colander.String(),
|
colander.String(),
|
||||||
@@ -2289,7 +2289,7 @@ class TestMappingSchemaInheritance(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
serial2_node = colander.SchemaNode(
|
serial2_node = colander.SchemaNode(
|
||||||
colander.Bool(),
|
colander.Bool(),
|
||||||
schema_order=colander.LAST,
|
insert_before='name',
|
||||||
)
|
)
|
||||||
|
|
||||||
class Friend(colander.Schema):
|
class Friend(colander.Schema):
|
||||||
@@ -2311,9 +2311,9 @@ class TestMappingSchemaInheritance(unittest.TestCase):
|
|||||||
[
|
[
|
||||||
iwannacomefirst2_node,
|
iwannacomefirst2_node,
|
||||||
rank_node,
|
rank_node,
|
||||||
|
serial2_node,
|
||||||
name_node,
|
name_node,
|
||||||
another_node,
|
another_node,
|
||||||
serial2_node
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2355,6 +2355,19 @@ class TestMappingSchemaInheritance(unittest.TestCase):
|
|||||||
[a2_node, b3_node, c2_node, d3_node]
|
[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):
|
class TestDeferred(unittest.TestCase):
|
||||||
def _makeOne(self, wrapped):
|
def _makeOne(self, wrapped):
|
||||||
|
@@ -537,7 +537,7 @@ One class-based schema can be inherited from another. For example:
|
|||||||
class SpecialFriend(Friend):
|
class SpecialFriend(Friend):
|
||||||
iwannacomefirst = colander.SchemaNode(
|
iwannacomefirst = colander.SchemaNode(
|
||||||
colander.String(),
|
colander.String(),
|
||||||
schema_order=colander.FIRST
|
insert_before='rank',
|
||||||
)
|
)
|
||||||
another = colander.SchemaNode(
|
another = colander.SchemaNode(
|
||||||
colander.String(),
|
colander.String(),
|
||||||
@@ -549,7 +549,7 @@ One class-based schema can be inherited from another. For example:
|
|||||||
)
|
)
|
||||||
|
|
||||||
friend = SuperSpecialFriend()
|
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:
|
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
|
* 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
|
the same name inherited from any superclass. The node remains at the child
|
||||||
order of the superclass node unless the subclass node defines a
|
order of the superclass node unless the subclass node defines an
|
||||||
``schema_order``.
|
``insert_before`` value.
|
||||||
|
|
||||||
* A node declared in a subclass of a mapping schema with a name that doesn't
|
* 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
|
override any node in a superclass will be placed *after* all nodes defined
|
||||||
in all superclasses unless the subclass node defines a ``schema_order``.
|
in all superclasses unless the subclass node defines an ``insert_before``
|
||||||
You can think of it like this: nodes added in subclasses will *follow*
|
value. You can think of it like this: nodes added in subclasses will
|
||||||
nodes added in superclasses.
|
*follow* nodes added in superclasses.
|
||||||
|
|
||||||
A ``schema_order`` attribute may be passed to the SchemaNode constructor of
|
An ``insert_before`` keyword argument may be passed to the SchemaNode
|
||||||
mapping schema child nodes. This is an integer which influences the position
|
constructor of mapping schema child nodes. This is a string which influences
|
||||||
in its mapping schema's child ordering. ``colander.FIRST`` and
|
the node's position in its mapping schema. The node will be inserted into
|
||||||
``colander.LAST`` constants are available for passing in as ``schema_order``.
|
the mapping schema before the node named by ``insert_before``. An
|
||||||
The former tries to place the node in the very first position. The latter,
|
``insert_before`` value must match the name of a schema node in a superclass
|
||||||
the very last. If ``schema_order`` is any other integer, the system will
|
or it must match the name of a schema node already defined in the class; it
|
||||||
attempt to place the node at the integer position in the ordering.
|
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
|
Defining A Schema Imperatively
|
||||||
------------------------------
|
------------------------------
|
||||||
|
Reference in New Issue
Block a user