From 67c057d63056bcd34f6d1ea0ca2de3f87102ee20 Mon Sep 17 00:00:00 2001 From: Daniel Dourvaris Date: Sat, 17 Oct 2015 23:22:03 +0300 Subject: [PATCH] Add setitem, delitem to Field so you can: field['name'] = Field() del(field['name']) --- deform/field.py | 43 ++++++++++++++++++++++++- deform/tests/test_field.py | 65 ++++++++++++++++++++++++++++++++------ 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/deform/field.py b/deform/field.py index 40fb0341..1d333b6f 100644 --- a/deform/field.py +++ b/deform/field.py @@ -272,6 +272,47 @@ def __contains__(self, name): return True return False + def __setitem__(self, name, new_child): + """ Set a field directly by name, this will replace a field if it exists + or add it to the end of the children if it doesn't. """ + new_child = new_child.clone() + new_child._parent = weakref.ref(self) + new_child.name = name + + found = False + new_children = [] + + for child in self.children: + if child.name == new_child.name: + new_children.append(new_child) + new_child.order = child.order + new_child.oid = 'deformField%s' % child.order + found = True + else: + new_children.append(child) + + if not found: + new_children.append(new_child) + new_child.order = next(self.counter) + new_child.oid = 'deformField%s' % new_child.order + + self.children = new_children + + def __delitem__(self, name): + """ Removes a field by name, raises KeyError if field doesn't exist """ + new_children = [] + found = False + for child in self.children: + if child.name == name: + found = True + continue + new_children.append(child) + + if not found: + raise KeyError(name) + + self.children = new_children + def clone(self): """ Clone the field and its subfields, retaining attribute information. Return the cloned field. The ``order`` @@ -319,7 +360,7 @@ def widget(self): def _default_item_css_class(self): if not self.name: return None - + css_class = unicodedata.normalize('NFKD', compat.text_type(self.name)).encode('ascii', 'ignore').decode('ascii') css_class = re.sub('[^\w\s-]', '', css_class).strip().lower() css_class = re.sub('[-\s]+', '-', css_class) diff --git a/deform/tests/test_field.py b/deform/tests/test_field.py index c903bab1..dd58f696 100644 --- a/deform/tests/test_field.py +++ b/deform/tests/test_field.py @@ -14,7 +14,7 @@ class TestField(unittest.TestCase): def _getTargetClass(self): from deform.field import Field return Field - + def _makeOne(self, schema, **kw): cls = self._getTargetClass() return cls(schema, **kw) @@ -45,7 +45,7 @@ def test_ctor_with_children_in_schema(self): grandchild = DummySchema(name='grandchild') child = DummySchema(children=[grandchild], name='child') root = DummySchema(children=[child], name='root') - + root_field = self._makeOne(root, renderer='abc') self.assertEqual(len(root_field.children), 1) self.assertEqual(root_field.parent, None) @@ -70,7 +70,7 @@ def test_get_root(self): grandchild_field = field.children[0].children[0] root = grandchild_field.get_root() self.assertEqual(root.name, 'root') - + def test_ctor_with_resource_registry(self): from deform.field import Field schema = DummySchema() @@ -183,7 +183,7 @@ def test_normalizes_css_class(self): def test_widget_no_maker_with_default_widget_maker(self): from deform.widget import MappingWidget from colander import Mapping - schema = DummySchema() + schema = DummySchema() schema.typ = Mapping() field = self._makeOne(schema) widget = field.widget @@ -196,7 +196,7 @@ def test_widget_no_maker_with_derived_from_default_field(self): class CustomSequence(Sequence): pass - schema = DummySchema() + schema = DummySchema() schema.typ = CustomSequence() field = self._makeOne(schema) widget = field.widget @@ -352,7 +352,7 @@ def test___getitem__success(self): child = DummyField() field.children = [child] self.assertEqual(field['name'], child) - + def test___getitem__fail(self): schema = DummySchema() field = self._makeOne(schema) @@ -366,7 +366,51 @@ def test___contains__success(self): child = DummyField() field.children = [child] self.assertTrue('name' in field) - + + def test___contains__fail(self): + schema = DummySchema() + field = self._makeOne(schema) + child = DummyField() + field.children = [child] + self.assertFalse('nope' in field) + + def test___setitem__empty(self): + schema = DummySchema() + field = self._makeOne(schema) + child = DummyField() + field['name'] = child + self.assertEqual(field['name'], child) + field['name'] = child # should only set it once + self.assertEqual(field.children, [child]) + + def test___setitem__existing(self): + schema = DummySchema() + field = self._makeOne(schema) + child1 = DummyField(name='child1') + child2 = DummyField(name='child2') + field.children = [child1, child2] + replacer = DummyField(name='replacer') + field['child1'] = replacer + self.assertEqual(field['child1'], replacer) + + def test___delitem__existing(self): + schema = DummySchema() + field = self._makeOne(schema) + child1 = DummyField(name='child1') + child2 = DummyField(name='child2') + field.children = [child1, child2] + del(field['child1']) + self.assertEqual(field.children, [child2]) + del(field['child2']) + self.assertEqual(field.children, []) + + def test___delitem__missing(self): + schema = DummySchema() + field = self._makeOne(schema) + child = DummyField() + field.children = [child] + self.assertRaises(KeyError, field.__delitem__, 'nope') + def test___contains__fail(self): schema = DummySchema() field = self._makeOne(schema) @@ -378,7 +422,7 @@ def test_errormsg_error_None(self): schema = DummySchema() field = self._makeOne(schema) self.assertEqual(field.errormsg, None) - + def test_errormsg_error_not_None(self): schema = DummySchema() field = self._makeOne(schema) @@ -581,7 +625,7 @@ def test_start_mapping_withname(self): result, '' ) - + def test_start_mapping_withoutname(self): schema = DummySchema() field = self._makeOne(schema) @@ -688,6 +732,7 @@ def __init__(self, schema=None, renderer=None, name='name'): self.schema = schema self.renderer = renderer self.name = name + self.order = 1 def clone(self): self.cloned = True @@ -727,7 +772,7 @@ def cstruct_children(self, cstruct): class DummyType(object): def __init__(self, maker=None): self.widget_maker = maker - + class DummyWidget(object): rendered = None def __init__(self, exc=None, **kw):