Skip to content

Commit f8c80b7

Browse files
authored
Merge pull request #49 from benkehoe/master
str/repr, join, and setting - for arrays
2 parents 896102d + d811454 commit f8c80b7

File tree

2 files changed

+122
-2
lines changed

2 files changed

+122
-2
lines changed

jsonpointer.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,17 @@
4444
try:
4545
from itertools import izip
4646
str = unicode
47+
encode_str = lambda u: u.encode("raw_unicode_escape")
4748
except ImportError: # Python 3
4849
izip = zip
50+
encode_str = lambda u: u
4951

5052
try:
5153
from collections.abc import Mapping, Sequence
5254
except ImportError: # Python 3
5355
from collections import Mapping, Sequence
5456

55-
from itertools import tee
57+
from itertools import tee, chain
5658
import re
5759
import copy
5860

@@ -225,7 +227,11 @@ def set(self, doc, value, inplace=True):
225227

226228
(parent, part) = self.to_last(doc)
227229

228-
parent[part] = value
230+
if isinstance(parent, Sequence) and part == '-':
231+
parent.append(value)
232+
else:
233+
parent[part] = value
234+
229235
return doc
230236

231237
@classmethod
@@ -293,6 +299,23 @@ def __contains__(self, item):
293299
""" Returns True if self contains the given ptr """
294300
return self.contains(item)
295301

302+
def join(self, suffix):
303+
""" Returns a new JsonPointer with the given suffix append to this ptr """
304+
if isinstance(suffix, JsonPointer):
305+
suffix_parts = suffix.parts
306+
elif isinstance(suffix, str):
307+
suffix_parts = JsonPointer(suffix).parts
308+
else:
309+
suffix_parts = suffix
310+
try:
311+
return JsonPointer.from_parts(chain(self.parts, suffix_parts))
312+
except:
313+
raise JsonPointerException("Invalid suffix")
314+
315+
def __truediv__(self, suffix): # Python 3
316+
return self.join(suffix)
317+
__div__ = __truediv__ # Python 2
318+
296319
@property
297320
def path(self):
298321
"""Returns the string representation of the pointer
@@ -318,6 +341,12 @@ def __eq__(self, other):
318341
def __hash__(self):
319342
return hash(tuple(self.parts))
320343

344+
def __str__(self):
345+
return encode_str(self.path)
346+
347+
def __repr__(self):
348+
return "JsonPointer(" + repr(self.path) + ")"
349+
321350
@classmethod
322351
def from_parts(cls, parts):
323352
"""Constructs a JsonPointer from a list of (unescaped) paths

tests.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,50 @@ def test_round_trip(self):
7575
new_ptr = JsonPointer.from_parts(parts)
7676
self.assertEqual(ptr, new_ptr)
7777

78+
def test_str_and_repr(self):
79+
paths = [
80+
("", "", "JsonPointer({u}'')"),
81+
("/foo", "/foo", "JsonPointer({u}'/foo')"),
82+
("/foo/0", "/foo/0", "JsonPointer({u}'/foo/0')"),
83+
("/", "/", "JsonPointer({u}'/')"),
84+
("/a~1b", "/a~1b", "JsonPointer({u}'/a~1b')"),
85+
("/c%d", "/c%d", "JsonPointer({u}'/c%d')"),
86+
("/e^f", "/e^f", "JsonPointer({u}'/e^f')"),
87+
("/g|h", "/g|h", "JsonPointer({u}'/g|h')"),
88+
("/i\\j", "/i\\j", "JsonPointer({u}'/i\\\\j')"),
89+
("/k\"l", "/k\"l", "JsonPointer({u}'/k\"l')"),
90+
("/ ", "/ ", "JsonPointer({u}'/ ')"),
91+
("/m~0n", "/m~0n", "JsonPointer({u}'/m~0n')"),
92+
]
93+
for path, ptr_str, ptr_repr in paths:
94+
ptr = JsonPointer(path)
95+
self.assertEqual(path, ptr.path)
96+
97+
if sys.version_info[0] == 2:
98+
u_str = "u"
99+
else:
100+
u_str = ""
101+
self.assertEqual(ptr_str, str(ptr))
102+
self.assertEqual(ptr_repr.format(u=u_str), repr(ptr))
103+
104+
if sys.version_info[0] == 2:
105+
path = "/\xee"
106+
ptr_str = b"/\xee"
107+
ptr_repr = "JsonPointer(u'/\\xee')"
108+
else:
109+
path = "/\xee"
110+
ptr_str = "/\xee"
111+
ptr_repr = "JsonPointer('/\xee')"
112+
ptr = JsonPointer(path)
113+
self.assertEqual(path, ptr.path)
114+
115+
self.assertEqual(ptr_str, str(ptr))
116+
self.assertEqual(ptr_repr, repr(ptr))
117+
118+
# should not be unicode in Python 2
119+
self.assertIsInstance(str(ptr), str)
120+
self.assertIsInstance(repr(ptr), str)
121+
78122
def test_parts(self):
79123
paths = [
80124
("", []),
@@ -131,6 +175,42 @@ def test_contains_magic(self):
131175
self.assertTrue(self.ptr1 in self.ptr1)
132176
self.assertFalse(self.ptr3 in self.ptr1)
133177

178+
def test_join(self):
179+
180+
ptr12a = self.ptr1.join(self.ptr2)
181+
self.assertEqual(ptr12a.path, "/a/b/c/a/b")
182+
183+
ptr12b = self.ptr1.join(self.ptr2.parts)
184+
self.assertEqual(ptr12b.path, "/a/b/c/a/b")
185+
186+
ptr12c = self.ptr1.join(self.ptr2.parts[0:1])
187+
self.assertEqual(ptr12c.path, "/a/b/c/a")
188+
189+
ptr12d = self.ptr1.join("/a/b")
190+
self.assertEqual(ptr12d.path, "/a/b/c/a/b")
191+
192+
ptr12e = self.ptr1.join(["a", "b"])
193+
self.assertEqual(ptr12e.path, "/a/b/c/a/b")
194+
195+
self.assertRaises(JsonPointerException, self.ptr1.join, 0)
196+
197+
def test_join_magic(self):
198+
199+
ptr12a = self.ptr1 / self.ptr2
200+
self.assertEqual(ptr12a.path, "/a/b/c/a/b")
201+
202+
ptr12b = self.ptr1 / self.ptr2.parts
203+
self.assertEqual(ptr12b.path, "/a/b/c/a/b")
204+
205+
ptr12c = self.ptr1 / self.ptr2.parts[0:1]
206+
self.assertEqual(ptr12c.path, "/a/b/c/a")
207+
208+
ptr12d = self.ptr1 / "/a/b"
209+
self.assertEqual(ptr12d.path, "/a/b/c/a/b")
210+
211+
ptr12e = self.ptr1 / ["a", "b"]
212+
self.assertEqual(ptr12e.path, "/a/b/c/a/b")
213+
134214
class WrongInputTests(unittest.TestCase):
135215

136216
def test_no_start_slash(self):
@@ -193,6 +273,12 @@ def test_set(self):
193273
newdoc = set_pointer(doc, "/foo/1", "cod", inplace=False)
194274
self.assertEqual(resolve_pointer(newdoc, "/foo/1"), "cod")
195275

276+
self.assertEqual(len(doc["foo"]), 2)
277+
newdoc = set_pointer(doc, "/foo/-", "xyz", inplace=False)
278+
self.assertEqual(resolve_pointer(newdoc, "/foo/2"), "xyz")
279+
self.assertEqual(len(doc["foo"]), 2)
280+
self.assertEqual(len(newdoc["foo"]), 3)
281+
196282
newdoc = set_pointer(doc, "/", 9, inplace=False)
197283
self.assertEqual(resolve_pointer(newdoc, "/"), 9)
198284

@@ -209,6 +295,11 @@ def test_set(self):
209295
set_pointer(doc, "/foo/1", "cod")
210296
self.assertEqual(resolve_pointer(doc, "/foo/1"), "cod")
211297

298+
self.assertEqual(len(doc["foo"]), 2)
299+
set_pointer(doc, "/foo/-", "xyz")
300+
self.assertEqual(resolve_pointer(doc, "/foo/2"), "xyz")
301+
self.assertEqual(len(doc["foo"]), 3)
302+
212303
set_pointer(doc, "/", 9)
213304
self.assertEqual(resolve_pointer(doc, "/"), 9)
214305

0 commit comments

Comments
 (0)