Skip to content

Commit cec75f0

Browse files
committed
Handle the whole-document pointer in the sequence branch
A whole-document pointer makes JsonPointer.to_last return part None. The MutableMapping branch replaces the document in that case, and ReplaceOperation checks for it before branching, but the MutableSequence branch went straight to `part > len(subobj)` and raised a bare TypeError, which is not part of the exception hierarchy callers document. Add the same `part is None` arm the mapping branch has. move and copy delegate to AddOperation, so they are fixed by the same change. Also make the existing object-root test compare rather than pass the expected value as assertTrue's failure message, which asserted nothing. Fixes #190
1 parent d8e1a6e commit cec75f0

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

‎jsonpatch.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,10 @@ def apply(self, obj):
282282
subobj, part = self.pointer.to_last(obj)
283283

284284
if isinstance(subobj, MutableSequence):
285-
if part == '-':
285+
if part is None:
286+
return value # we're replacing the root
287+
288+
elif part == '-':
286289
subobj.append(value) # pylint: disable=E1103
287290

288291
elif part > len(subobj) or part < 0:

‎tests.py‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,40 @@ def test_add_replace_whole_document(self):
107107
obj = {'foo': 'bar'}
108108
new_obj = {'baz': 'qux'}
109109
res = jsonpatch.apply_patch(obj, [{'op': 'add', 'path': '', 'value': new_obj}])
110-
self.assertTrue(res, new_obj)
110+
# assertTrue(res, new_obj) passed a dict as the failure message, so this
111+
# asserted nothing; it is the object-root counterpart of the array-root
112+
# test below, so it needs to actually compare.
113+
self.assertEqual(res, new_obj)
114+
115+
def test_add_replace_whole_document_list_root(self):
116+
# a whole document pointer resolves to part None, which has to replace
117+
# the document no matter whether the root is an object or an array
118+
obj = ['foo', 'bar']
119+
new_obj = {'baz': 'qux'}
120+
res = jsonpatch.apply_patch(obj, [{'op': 'add', 'path': '', 'value': new_obj}])
121+
self.assertEqual(res, new_obj)
122+
123+
def test_move_whole_document_list_root(self):
124+
# move and copy reuse AddOperation, so they need the same treatment
125+
obj = ['foo', 'bar']
126+
res = jsonpatch.apply_patch(obj, [{'op': 'move', 'from': '/0', 'path': ''}])
127+
self.assertEqual(res, 'foo')
128+
129+
def test_copy_whole_document_list_root(self):
130+
obj = ['foo', 'bar']
131+
res = jsonpatch.apply_patch(obj, [{'op': 'copy', 'from': '/1', 'path': ''}])
132+
self.assertEqual(res, 'bar')
133+
134+
def test_add_whole_document_list_root_raises_no_bare_typeerror(self):
135+
# a bare TypeError is not part of the documented exception hierarchy, so
136+
# callers cannot catch it; the sequence branch must not fall through to it
137+
obj = ['foo', 'bar']
138+
try:
139+
jsonpatch.apply_patch(obj, [{'op': 'add', 'path': '', 'value': 'R'}])
140+
except jsonpatch.JsonPatchException:
141+
self.fail("root add on an array root should succeed, not raise")
142+
except TypeError:
143+
self.fail("root add on an array root raised a bare TypeError")
111144

112145
def test_replace_array_item(self):
113146
obj = {'foo': ['bar', 'qux', 'baz']}

0 commit comments

Comments
 (0)