Break up core pieces into separate modules

- Create model.py to hold the core Model class
- Create exceptions.py to hold all custom exceptions
This commit is contained in:
Rob 2012-11-27 11:16:13 -05:00 committed by Brian Waldon
parent e8f0a5ade7
commit 98a44c6601
5 changed files with 128 additions and 107 deletions

2
.gitignore vendored
View File

@ -3,3 +3,5 @@ build/
dist/
warlock.egg-info/
.tox/
*.swp
*.pyc

View File

@ -2,4 +2,4 @@
# pylint: disable=W0611
from warlock.core import model_factory
from warlock.core import InvalidOperation
from warlock.exceptions import InvalidOperation

View File

@ -3,18 +3,12 @@
import copy
import jsonschema
import jsonpatch
import model
import exceptions
class InvalidOperation(RuntimeError):
pass
class ValidationError(ValueError):
pass
def model_factory(schema):
def model_factory(schema, base_class=model.Model):
"""Generate a model class based on the provided JSON Schema
:param schema: dict representing valid JSON schema
@ -26,104 +20,12 @@ def model_factory(schema):
try:
jsonschema.validate(obj, schema)
except jsonschema.ValidationError as exc:
raise ValidationError(str(exc))
class Model(dict):
"""Self-validating model for arbitrary objects"""
raise exceptions.ValidationError(str(exc))
class Model(base_class):
def __init__(self, *args, **kwargs):
d = dict(*args, **kwargs)
# we overload setattr so set this manually
self.__dict__['validator'] = validator
try:
self.validator(d)
except ValidationError as exc:
raise ValueError(str(exc))
else:
dict.__init__(self, d)
self.__dict__['changes'] = {}
self.__dict__['__original__'] = copy.deepcopy(d)
def __getattr__(self, key):
try:
return self.__getitem__(key)
except KeyError:
raise AttributeError(key)
def __setitem__(self, key, value):
mutation = dict(self.items())
mutation[key] = value
try:
self.validator(mutation)
except ValidationError:
msg = "Unable to set '%s' to '%s'" % (key, value)
raise InvalidOperation(msg)
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):
mutation = dict(self.items())
del mutation[key]
try:
self.validator(mutation)
except ValidationError:
msg = "Unable to delete attribute '%s'" % (key)
raise InvalidOperation(msg)
dict.__delitem__(self, key)
def __delattr__(self, key):
self.__delitem__(key)
# 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 as exc:
raise InvalidOperation(str(exc))
dict.update(self, other)
def iteritems(self):
return copy.deepcopy(dict(self)).iteritems()
def items(self):
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):
return copy.deepcopy(self.__dict__['changes'])
@property
def patch(self):
original = self.__dict__['__original__']
return jsonpatch.make_patch(original, dict(self)).to_string()
kwargs.setdefault('validator', validator)
base_class.__init__(self, *args, **kwargs)
Model.__name__ = str(schema['name'])
return Model

9
warlock/exceptions.py Normal file
View File

@ -0,0 +1,9 @@
""" List of errors used in warlock """
class InvalidOperation(RuntimeError):
pass
class ValidationError(ValueError):
pass

108
warlock/model.py Normal file
View File

@ -0,0 +1,108 @@
"""Self-validating model for arbitrary objects"""
import copy
import jsonpatch
import exceptions
class Model(dict):
def __init__(self, *args, **kwargs):
# Load the validator from the kwargs
#self.__dict__['validator'] = kwargs.pop('validator', self.default_validator)
# we overload setattr so set this manually
d = dict(*args, **kwargs)
try:
self.validator(d)
except exceptions.ValidationError as exc:
raise ValueError(str(exc))
else:
dict.__init__(self, d)
self.__dict__['changes'] = {}
self.__dict__['__original__'] = copy.deepcopy(d)
def __getattr__(self, key):
try:
return self.__getitem__(key)
except KeyError:
raise AttributeError(key)
def __setitem__(self, key, value):
mutation = dict(self.items())
mutation[key] = value
try:
self.validator(mutation)
except exceptions.ValidationError:
msg = "Unable to set '%s' to '%s'" % (key, value)
raise exceptions.InvalidOperation(msg)
dict.__setitem__(self, key, value)
self.__dict__['changes'][key] = value
def __setattr__(self, key, value):
self.__setitem__(key, value)
def clear(self):
raise exceptions.InvalidOperation()
def pop(self, key, default=None):
raise exceptions.InvalidOperation()
def popitem(self):
raise exceptions.InvalidOperation()
def __delitem__(self, key):
mutation = dict(self.items())
del mutation[key]
try:
self.validator(mutation)
except exceptions.ValidationError:
msg = "Unable to delete attribute '%s'" % (key)
raise exceptions.InvalidOperation(msg)
dict.__delitem__(self, key)
def __delattr__(self, key):
self.__delitem__(key)
# 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 exceptions.ValidationError as exc:
raise exceptions.InvalidOperation(str(exc))
dict.update(self, other)
def iteritems(self):
return copy.deepcopy(dict(self)).iteritems()
def items(self):
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):
return copy.deepcopy(self.__dict__['changes'])
@property
def patch(self):
original = self.__dict__['__original__']
return jsonpatch.make_patch(original, dict(self)).to_string()
def default_validator(self, *args, **kwargs):
return True