diff options
author | Rob <rob.s.brit@gmail.com> | 2012-11-27 11:16:13 -0500 |
---|---|---|
committer | Brian Waldon <bcwaldon@gmail.com> | 2012-12-22 15:39:09 -0800 |
commit | 98a44c660136b7089cf3e8ed95254b475ab4d085 (patch) | |
tree | d5ddbceef40053632558b96e72fffcbeaa123f07 /warlock/model.py | |
parent | e8f0a5ade79156cfe18ae5ec25c5737cc1f7e532 (diff) | |
download | warlock-98a44c660136b7089cf3e8ed95254b475ab4d085.tar.gz |
Break up core pieces into separate modules
- Create model.py to hold the core Model class
- Create exceptions.py to hold all custom exceptions
Diffstat (limited to 'warlock/model.py')
-rw-r--r-- | warlock/model.py | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/warlock/model.py b/warlock/model.py new file mode 100644 index 0000000..6dff70a --- /dev/null +++ b/warlock/model.py @@ -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 |