Adam Lowry avatar Adam Lowry committed 1596aef

Initial SparseDict implementation (mostly Dict refactoring)

Comments (0)

Files changed (4)

flatland/__init__.py

                 'Skip',
                 'SkipAll',
                 'SkipAllFalse',
+                'SparseDict',
                 'String',
                 'Time',
                 'Unevaluated',

flatland/schema/__init__.py

     Mapping,
     MultiValue,
     Sequence,
+    SparseDict,
     )
 from .compound import (
     Compound,

flatland/schema/containers.py

 
     @property
     def children(self):
-        # order not guaranteed
-        return self.itervalues()
+        for member_schema in self.field_schema:
+            if member_schema.name in self:
+                yield self[member_schema.name]
+            else:
+                yield member_schema(parent=self)
+
 
     def set(self, value):
         """TODO: doc set()"""
         # FIXME: pivot on length of pairs: top loop either fields or pairs
         # FIXME2: wtf does that mean
 
-        for field in self:
+        for member_schema in self.field_schema:
+            field = member_schema.name
             accum = []
             for key, value in possibles:
                 if key.startswith(field):
                     accum.append((key, value))
             if accum:
+                if field not in self:
+                    dict.__setitem__(self, field, member_schema(parent=self))
                 self[field].set_flat(accum, sep)
 
     def set_default(self):
         seen = set()
         for key, value in pairs:
             if key not in self:
-                if policy != 'duck':
-                    raise KeyError(
-                        'Dict %r schema does not allow key %r' % (
-                            self.name, key))
-                continue
+                for member_schema in self.field_schema:
+                    if key == member_schema.name:
+                        dict.__setitem__(self, key, member_schema(parent=self))
+                        break
+                else:
+                    if policy != 'duck':
+                        raise KeyError(
+                            'Dict %r schema does not allow key %r' % (
+                                self.name, key))
+                    continue
             self[key].set(value)
             seen.add(key)
 
                 include=include, omit=omit, rename=rename, key=key))
 
 
+class SparseDict(Dict):
+    """Dict container that does not create subelements not explicity set"""
+
+    def _reset(self):
+        """Do nothing when initializing the mapping"""
+        pass
+
+
 for cls_name in __all__:
     autodocument_from_superclasses(globals()[cls_name])
 del cls_name

tests/schema/test_dicts.py

     Dict,
     Integer,
     String,
+    SparseDict,
     )
 from flatland.util import Unspecified, keyslice_pairs
 from tests._util import (
     yield same_, {u'x': u'X', u'y': u'Y'}, dict(omit=[u'x'])
     yield same_, {u'x': u'X', u'y': u'Y'}, dict(omit=[u'x'],
                                                 rename={u'y': u'z'})
+
+
+def test_sparse_dict():
+    schema = SparseDict.of(String.named(u'x'), String.named(u'y'))
+    element = schema({'x': 'foo'})
+    assert 'y' not in element
+
+    element = schema({'x': 'foo', 'y': 'bar'})
+    eq_(element.value, {'x': 'foo', 'y': 'bar'})
+
+    assert_raises(KeyError, schema, {'x': 'foo', 'y': 'bar', 'z': 'baz'})
+    element = schema({'x': 'foo', 'y': 'bar', 'z': 'baz'}, policy='duck')
+    eq_(element.value, {'x': 'foo', 'y': 'bar'})
+
+    element = schema.from_flat({'x': 'foo'})
+    assert 'y' not in element
+    eq_(element.value, {'x': 'foo'})
+    element.set_flat({'x': 'foo'})
+    assert 'y' not in element
+    eq_(element.value, {'x': 'foo'})
+    element.set_flat({'x': 'foo', 'y': 'bar'})
+    eq_(element.value, {'x': 'foo', 'y': 'bar'})
+
+
+def test_sparse_dict_validation():
+    schema = SparseDict.of(
+        String.named(u'x'),
+        String.named(u'y').using(optional=False))
+
+    element = schema({'x': 'foo'})
+    assert not element.validate()
+
+    element = schema({'x': 'foo', 'y': 'bar'})
+    assert element.validate()
+
+    element = schema({'x': 'foo', 'y': 'bar', 'z': 'baz'}, policy='duck')
+    assert element.validate()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.