diff options
author | Brian Waldon <bcwaldon@gmail.com> | 2012-06-02 09:47:59 -0700 |
---|---|---|
committer | Brian Waldon <bcwaldon@gmail.com> | 2012-06-02 09:47:59 -0700 |
commit | 6995f48c5ce48a472d689afd6de94aa54fa989d2 (patch) | |
tree | 106d0c4ae3f737c4b4de81729c9dfbccc5c689f9 | |
parent | c64a586a9a48c8e54e5c702376443767e723fca7 (diff) | |
download | warlock-6995f48c5ce48a472d689afd6de94aa54fa989d2.tar.gz |
Use jsonschema for operation validation
-rw-r--r-- | test/test_core.py | 52 | ||||
-rw-r--r-- | warlock/__init__.py | 1 | ||||
-rw-r--r-- | warlock/core.py | 40 |
3 files changed, 74 insertions, 19 deletions
diff --git a/test/test_core.py b/test/test_core.py index 1aced02..aa31198 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -1,23 +1,49 @@ +import copy import unittest import warlock -class TestCore(unittest.TestCase): - def test_core(self): - schema = { - 'name': 'Country', - 'properties': { - 'name': {'type': 'string'}, - 'abbreviation': {'type': 'string'}, - }, - } +fixture = { + 'name': 'Country', + 'properties': { + 'name': {'type': 'string'}, + 'population': {'type': 'integer'}, + }, + 'additionalProperties': False, +} + - Country = warlock.model_factory(schema) +class TestCore(unittest.TestCase): + def test_create_invalid_object(self): + Country = warlock.model_factory(fixture) + self.assertRaises(ValueError, Country, name=1) - sweden = Country(name='Sweden', abbreviation='SE') + def test_invalid_operations(self): + Country = warlock.model_factory(fixture) + sweden = Country(name='Sweden', population=9379116) + # Ensure a valid object was created self.assertEqual(sweden.name, 'Sweden') - self.assertEqual(sweden.abbreviation, 'SE') + self.assertEqual(sweden.population, 9379116) + + # Specific exceptions should be raised for invalid operations self.assertRaises(AttributeError, getattr, sweden, 'overlord') - self.assertRaises(AttributeError, setattr, sweden, 'overlord', 'Bears') + exc = warlock.InvalidOperation + self.assertRaises(exc, setattr, sweden, 'overlord', 'Bears') + self.assertRaises(exc, setattr, sweden, 'name', 5) + self.assertRaises(exc, setattr, sweden, 'population', 'N/A') + + def test_no_mask_arbitrary_properties(self): + fixture_copy = copy.deepcopy(fixture) + fixture_copy['additionalProperties'] = {'type': 'string'} + Country = warlock.model_factory(fixture_copy) + + # We should still depend on the schema for validation + self.assertRaises(ValueError, Country, GDP=56956) + + # But arbitrary properties should be allowed if they check out + sweden = Country(overlord='Waldon') + sweden.abbreviation = 'SE' + exc = warlock.InvalidOperation + self.assertRaises(exc, setattr, sweden, 'abbreviation', 0) diff --git a/warlock/__init__.py b/warlock/__init__.py index f9c0313..400c081 100644 --- a/warlock/__init__.py +++ b/warlock/__init__.py @@ -2,3 +2,4 @@ # pylint: disable=W0611 from warlock.core import model_factory +from warlock.core import InvalidOperation diff --git a/warlock/core.py b/warlock/core.py index 1753d24..e4a070a 100644 --- a/warlock/core.py +++ b/warlock/core.py @@ -1,6 +1,16 @@ """Core Warlock functionality""" -#import jsonschema +import copy + +import jsonschema + + +class InvalidOperation(RuntimeError): + pass + + +class ValidationError(ValueError): + pass def model_factory(schema): @@ -8,10 +18,25 @@ def model_factory(schema): :param schema: dict representing valid JSON schema """ + schema = copy.deepcopy(schema) + + def validator(obj): + """Apply a JSON schema to an object""" + try: + jsonschema.validate(obj, schema) + except jsonschema.ValidationError: + raise ValidationError() + class Model(object): """Self-validating model for arbitrary objects""" def __init__(self, **kwargs): - self.__dict__['raw'] = kwargs + self.__dict__['validator'] = validator + try: + self.__dict__['validator'](kwargs) + except ValidationError: + raise ValueError() + else: + self.__dict__['raw'] = kwargs def __getattr__(self, key): try: @@ -20,10 +45,13 @@ def model_factory(schema): raise AttributeError(key) def __setattr__(self, key, value): - if key in self.__dict__['raw']: - self.__dict__['raw'][key] = value - else: - raise AttributeError(key) + mutation = copy.deepcopy(self.__dict__['raw']) + mutation[key] = value + try: + self.__dict__['validator'](mutation) + except ValidationError: + raise InvalidOperation() + self.__dict__['raw'] = mutation Model.__name__ = schema['name'] return Model |