summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Waldon <bcwaldon@gmail.com>2012-06-02 09:47:59 -0700
committerBrian Waldon <bcwaldon@gmail.com>2012-06-02 09:47:59 -0700
commit6995f48c5ce48a472d689afd6de94aa54fa989d2 (patch)
tree106d0c4ae3f737c4b4de81729c9dfbccc5c689f9
parentc64a586a9a48c8e54e5c702376443767e723fca7 (diff)
downloadwarlock-6995f48c5ce48a472d689afd6de94aa54fa989d2.tar.gz
Use jsonschema for operation validation
-rw-r--r--test/test_core.py52
-rw-r--r--warlock/__init__.py1
-rw-r--r--warlock/core.py40
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