diff --git a/jsonpatch.py b/jsonpatch.py index 84f6fb3..a88c7ae 100644 --- a/jsonpatch.py +++ b/jsonpatch.py @@ -74,6 +74,15 @@ class JsonPatchException(Exception): """Base Json Patch exception""" + def __init__(self, op=None): + """ + Initialize a new instance of a JsonPatchException + :param op: The operation that failed. Attach to exception if you + desire to catch and modify the original patch operations. + :type op: Type[PatchOperation] + """ + self.op = op + class InvalidJsonPatch(JsonPatchException): """ Raised if an invalid JSON Patch is created """ @@ -238,7 +247,7 @@ def apply(self, obj): del subobj[part] except (KeyError, IndexError) as ex: msg = "can't remove a non-existent object '{0}'".format(part) - raise JsonPatchConflict(msg) + raise JsonPatchConflict(msg, op=self) return obj @@ -267,7 +276,7 @@ def apply(self, obj): value = self.operation["value"] except KeyError as ex: raise InvalidJsonPatch( - "The operation does not contain a 'value' member") + "The operation does not contain a 'value' member", op=self) subobj, part = self.pointer.to_last(obj) @@ -276,7 +285,7 @@ def apply(self, obj): subobj.append(value) # pylint: disable=E1103 elif part > len(subobj) or part < 0: - raise JsonPatchConflict("can't insert outside of list") + raise JsonPatchConflict("can't insert outside of list", op=self) else: subobj.insert(part, value) # pylint: disable=E1103 @@ -291,7 +300,7 @@ def apply(self, obj): if part is None: raise TypeError("invalid document type {0}".format(type(subobj))) else: - raise JsonPatchConflict("unable to fully resolve json pointer {0}, part {1}".format(self.location, part)) + raise JsonPatchConflict("unable to fully resolve json pointer {0}, part {1}".format(self.location, part), op=self) return obj def _on_undo_remove(self, path, key): @@ -319,7 +328,7 @@ def apply(self, obj): value = self.operation["value"] except KeyError as ex: raise InvalidJsonPatch( - "The operation does not contain a 'value' member") + "The operation does not contain a 'value' member", op=self) subobj, part = self.pointer.to_last(obj) @@ -327,21 +336,21 @@ def apply(self, obj): return value if part == "-": - raise InvalidJsonPatch("'path' with '-' can't be applied to 'replace' operation") + raise InvalidJsonPatch("'path' with '-' can't be applied to 'replace' operation", op=self) if isinstance(subobj, MutableSequence): if part >= len(subobj) or part < 0: - raise JsonPatchConflict("can't replace outside of list") + raise JsonPatchConflict("can't replace outside of list", op=self) elif isinstance(subobj, MutableMapping): if part not in subobj: msg = "can't replace a non-existent object '{0}'".format(part) - raise JsonPatchConflict(msg) + raise JsonPatchConflict(msg, op=self) else: if part is None: raise TypeError("invalid document type {0}".format(type(subobj))) else: - raise JsonPatchConflict("unable to fully resolve json pointer {0}, part {1}".format(self.location, part)) + raise JsonPatchConflict("unable to fully resolve json pointer {0}, part {1}".format(self.location, part), op=self) subobj[part] = value return obj @@ -364,13 +373,13 @@ def apply(self, obj): from_ptr = self.pointer_cls(self.operation['from']) except KeyError as ex: raise InvalidJsonPatch( - "The operation does not contain a 'from' member") + "The operation does not contain a 'from' member", op=self) subobj, part = from_ptr.to_last(obj) try: value = subobj[part] except (KeyError, IndexError) as ex: - raise JsonPatchConflict(str(ex)) + raise JsonPatchConflict(str(ex), op=self) # If source and target are equal, this is a no-op if self.pointer == from_ptr: @@ -378,7 +387,7 @@ def apply(self, obj): if isinstance(subobj, MutableMapping) and \ self.pointer.contains(from_ptr): - raise JsonPatchConflict('Cannot move values into their own children') + raise JsonPatchConflict('Cannot move values into their own children', op=self) obj = RemoveOperation({ 'op': 'remove', @@ -450,18 +459,18 @@ def apply(self, obj): else: val = self.pointer.walk(subobj, part) except JsonPointerException as ex: - raise JsonPatchTestFailed(str(ex)) + raise JsonPatchTestFailed(str(ex), op=self) try: value = self.operation['value'] except KeyError as ex: raise InvalidJsonPatch( - "The operation does not contain a 'value' member") + "The operation does not contain a 'value' member", op=self) if val != value: msg = '{0} ({1}) is not equal to tested value {2} ({3})' raise JsonPatchTestFailed(msg.format(val, type(val), - value, type(value))) + value, type(value)), op=self) return obj @@ -474,13 +483,13 @@ def apply(self, obj): from_ptr = self.pointer_cls(self.operation['from']) except KeyError as ex: raise InvalidJsonPatch( - "The operation does not contain a 'from' member") + "The operation does not contain a 'from' member", op=self) subobj, part = from_ptr.to_last(obj) try: value = copy.deepcopy(subobj[part]) except (KeyError, IndexError) as ex: - raise JsonPatchConflict(str(ex)) + raise JsonPatchConflict(str(ex), op=self) obj = AddOperation({ 'op': 'add', @@ -672,15 +681,15 @@ def apply(self, obj, in_place=False): def _get_operation(self, operation): if 'op' not in operation: - raise InvalidJsonPatch("Operation does not contain 'op' member") + raise InvalidJsonPatch("Operation does not contain 'op' member", op=operation) op = operation['op'] if not isinstance(op, basestring): - raise InvalidJsonPatch("Operation must be a string") + raise InvalidJsonPatch("Operation must be a string", op=operation) if op not in self.operations: - raise InvalidJsonPatch("Unknown operation {0!r}".format(op)) + raise InvalidJsonPatch("Unknown operation {0!r}".format(op), op=operation) cls = self.operations[op] return cls(operation, pointer_cls=self.pointer_cls)