diff options
author | termie <github@anarkystic.com> | 2012-10-08 16:14:32 -0700 |
---|---|---|
committer | termie <github@anarkystic.com> | 2012-10-08 17:06:41 -0700 |
commit | 1001d393fe6012ef5c0a2d1cc7744fd5f240aea6 (patch) | |
tree | dbc475cb7f5876f5a75c01149a42b8d19994c273 | |
parent | 0a6f016295dc7609bf88b1d36458aeab2aeb4014 (diff) | |
download | warlock-1001d393fe6012ef5c0a2d1cc7744fd5f240aea6.tar.gz |
switch to a dict-based object
-rw-r--r-- | test/test_core.py | 52 | ||||
-rw-r--r-- | warlock/core.py | 58 |
2 files changed, 98 insertions, 12 deletions
diff --git a/test/test_core.py b/test/test_core.py index bc2828c..5feb9f1 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -14,6 +14,15 @@ fixture = { } +complex_fixture = { + 'name': 'Mixmaster', + 'properties': { + 'sub': {'type': 'object', + 'properties': {'foo': {'type': 'string'}}} + }, +} + + class TestCore(unittest.TestCase): def test_create_invalid_object(self): Country = warlock.model_factory(fixture) @@ -63,6 +72,49 @@ class TestCore(unittest.TestCase): self.assertEqual(set(sweden.items()), set([('name', 'Sweden'), ('population', 9379116)])) + def test_update(self): + Country = warlock.model_factory(fixture) + sweden = Country(name='Sweden', population=9379116) + exc = warlock.InvalidOperation + self.assertRaises(exc, sweden.update, {'population': 'N/A'}) + self.assertRaises(exc, sweden.update, {'overloard': 'Bears'}) + + def test_deepcopy(self): + """Make sure we aren't leaking references.""" + Mixmaster = warlock.model_factory(complex_fixture) + mike = Mixmaster(sub={'foo': 'mike'}) + + self.assertEquals(mike.sub['foo'], 'mike') + + mike_1 = mike.copy() + mike_1['sub']['foo'] = 'james' + self.assertEquals(mike.sub['foo'], 'mike') + + mike_2 = dict(mike.iteritems()) + mike_2['sub']['foo'] = 'james' + self.assertEquals(mike.sub['foo'], 'mike') + + mike_2 = dict(mike.items()) + mike_2['sub']['foo'] = 'james' + self.assertEquals(mike.sub['foo'], 'mike') + + mike_3_sub = list(mike.itervalues())[0] + mike_3_sub['foo'] = 'james' + self.assertEquals(mike.sub['foo'], 'mike') + + mike_3_sub = list(mike.values())[0] + mike_3_sub['foo'] = 'james' + self.assertEquals(mike.sub['foo'], 'mike') + + def test_forbidden_methods(self): + Country = warlock.model_factory(fixture) + sweden = Country(name='Sweden', population=9379116) + exc = warlock.InvalidOperation + self.assertRaises(exc, sweden.clear) + self.assertRaises(exc, sweden.pop, 0) + self.assertRaises(exc, sweden.popitem) + self.assertRaises(exc, sweden.__delitem__, 'name') + def test_dict_syntax(self): Country = warlock.model_factory(fixture) sweden = Country(name='Sweden', population=9379116) diff --git a/warlock/core.py b/warlock/core.py index eacd7ab..ad3f3bd 100644 --- a/warlock/core.py +++ b/warlock/core.py @@ -27,16 +27,20 @@ def model_factory(schema): except jsonschema.ValidationError: raise ValidationError() - class Model(object): + class Model(dict): """Self-validating model for arbitrary objects""" - def __init__(self, **kwargs): + + def __init__(self, *args, **kwargs): + d = dict(*args, **kwargs) + + # we overload setattr so set this manually self.__dict__['validator'] = validator try: - self.__dict__['validator'](kwargs) + self.validator(d) except ValidationError: raise ValueError() else: - self.__dict__['raw'] = kwargs + dict.__init__(self, d) self.__dict__['changes'] = {} @@ -46,27 +50,57 @@ def model_factory(schema): except KeyError: raise AttributeError(key) - def __getitem__(self, key): - return self.__dict__['raw'][key] - def __setitem__(self, key, value): - mutation = copy.deepcopy(self.__dict__['raw']) + mutation = dict(self.items()) mutation[key] = value try: - self.__dict__['validator'](mutation) + self.validator(mutation) except ValidationError: raise InvalidOperation() - self.__dict__['raw'] = mutation + + dict.__setitem__(self, key, value) + self.__dict__['changes'][key] = value def __setattr__(self, key, value): self.__setitem__(key, value) + def clear(self): + raise InvalidOperation() + + def pop(self, key, default=None): + raise InvalidOperation() + + def popitem(self): + raise InvalidOperation() + + def __delitem__(self, key): + raise InvalidOperation() + + # NOTE(termie): This is kind of the opposite of what copy usually does + def copy(self): + return copy.deepcopy(dict(self)) + + def update(self, other): + mutation = dict(self.items()) + mutation.update(other) + try: + self.validator(mutation) + except ValidationError: + raise InvalidOperation() + dict.update(self, other) + def iteritems(self): - return copy.deepcopy(self.__dict__['raw']).iteritems() + return copy.deepcopy(dict(self)).iteritems() def items(self): - return copy.deepcopy(self.__dict__['raw']).items() + return copy.deepcopy(dict(self)).items() + + def itervalues(self): + return copy.deepcopy(dict(self)).itervalues() + + def values(self): + return copy.deepcopy(dict(self)).values() @property def changes(self): |