diff options
author | Stefan Kögl <stefan@skoegl.net> | 2022-02-22 10:13:36 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-22 10:13:36 +0100 |
commit | f8c80b7c0631332c979985f13cc1a803f251c6b7 (patch) | |
tree | 8dc471a90a596320a5338638622f6e734f295364 | |
parent | 896102d605605ec0f48e26bafb54fb591be66ede (diff) | |
parent | d811454192adb18fc3a9aa333fc27e4e48ddd723 (diff) | |
download | python-json-pointer-f8c80b7c0631332c979985f13cc1a803f251c6b7.tar.gz |
Merge pull request #49 from benkehoe/master
str/repr, join, and setting - for arrays
-rw-r--r-- | jsonpointer.py | 33 | ||||
-rwxr-xr-x | tests.py | 91 |
2 files changed, 122 insertions, 2 deletions
diff --git a/jsonpointer.py b/jsonpointer.py index 53191ef..d9fe0c0 100644 --- a/jsonpointer.py +++ b/jsonpointer.py @@ -44,15 +44,17 @@ __license__ = 'Modified BSD License' try: from itertools import izip str = unicode + encode_str = lambda u: u.encode("raw_unicode_escape") except ImportError: # Python 3 izip = zip + encode_str = lambda u: u try: from collections.abc import Mapping, Sequence except ImportError: # Python 3 from collections import Mapping, Sequence -from itertools import tee +from itertools import tee, chain import re import copy @@ -225,7 +227,11 @@ class JsonPointer(object): (parent, part) = self.to_last(doc) - parent[part] = value + if isinstance(parent, Sequence) and part == '-': + parent.append(value) + else: + parent[part] = value + return doc @classmethod @@ -293,6 +299,23 @@ class JsonPointer(object): """ Returns True if self contains the given ptr """ return self.contains(item) + def join(self, suffix): + """ Returns a new JsonPointer with the given suffix append to this ptr """ + if isinstance(suffix, JsonPointer): + suffix_parts = suffix.parts + elif isinstance(suffix, str): + suffix_parts = JsonPointer(suffix).parts + else: + suffix_parts = suffix + try: + return JsonPointer.from_parts(chain(self.parts, suffix_parts)) + except: + raise JsonPointerException("Invalid suffix") + + def __truediv__(self, suffix): # Python 3 + return self.join(suffix) + __div__ = __truediv__ # Python 2 + @property def path(self): """Returns the string representation of the pointer @@ -318,6 +341,12 @@ class JsonPointer(object): def __hash__(self): return hash(tuple(self.parts)) + def __str__(self): + return encode_str(self.path) + + def __repr__(self): + return "JsonPointer(" + repr(self.path) + ")" + @classmethod def from_parts(cls, parts): """Constructs a JsonPointer from a list of (unescaped) paths @@ -75,6 +75,50 @@ class SpecificationTests(unittest.TestCase): new_ptr = JsonPointer.from_parts(parts) self.assertEqual(ptr, new_ptr) + def test_str_and_repr(self): + paths = [ + ("", "", "JsonPointer({u}'')"), + ("/foo", "/foo", "JsonPointer({u}'/foo')"), + ("/foo/0", "/foo/0", "JsonPointer({u}'/foo/0')"), + ("/", "/", "JsonPointer({u}'/')"), + ("/a~1b", "/a~1b", "JsonPointer({u}'/a~1b')"), + ("/c%d", "/c%d", "JsonPointer({u}'/c%d')"), + ("/e^f", "/e^f", "JsonPointer({u}'/e^f')"), + ("/g|h", "/g|h", "JsonPointer({u}'/g|h')"), + ("/i\\j", "/i\\j", "JsonPointer({u}'/i\\\\j')"), + ("/k\"l", "/k\"l", "JsonPointer({u}'/k\"l')"), + ("/ ", "/ ", "JsonPointer({u}'/ ')"), + ("/m~0n", "/m~0n", "JsonPointer({u}'/m~0n')"), + ] + for path, ptr_str, ptr_repr in paths: + ptr = JsonPointer(path) + self.assertEqual(path, ptr.path) + + if sys.version_info[0] == 2: + u_str = "u" + else: + u_str = "" + self.assertEqual(ptr_str, str(ptr)) + self.assertEqual(ptr_repr.format(u=u_str), repr(ptr)) + + if sys.version_info[0] == 2: + path = "/\xee" + ptr_str = b"/\xee" + ptr_repr = "JsonPointer(u'/\\xee')" + else: + path = "/\xee" + ptr_str = "/\xee" + ptr_repr = "JsonPointer('/\xee')" + ptr = JsonPointer(path) + self.assertEqual(path, ptr.path) + + self.assertEqual(ptr_str, str(ptr)) + self.assertEqual(ptr_repr, repr(ptr)) + + # should not be unicode in Python 2 + self.assertIsInstance(str(ptr), str) + self.assertIsInstance(repr(ptr), str) + def test_parts(self): paths = [ ("", []), @@ -131,6 +175,42 @@ class ComparisonTests(unittest.TestCase): self.assertTrue(self.ptr1 in self.ptr1) self.assertFalse(self.ptr3 in self.ptr1) + def test_join(self): + + ptr12a = self.ptr1.join(self.ptr2) + self.assertEqual(ptr12a.path, "/a/b/c/a/b") + + ptr12b = self.ptr1.join(self.ptr2.parts) + self.assertEqual(ptr12b.path, "/a/b/c/a/b") + + ptr12c = self.ptr1.join(self.ptr2.parts[0:1]) + self.assertEqual(ptr12c.path, "/a/b/c/a") + + ptr12d = self.ptr1.join("/a/b") + self.assertEqual(ptr12d.path, "/a/b/c/a/b") + + ptr12e = self.ptr1.join(["a", "b"]) + self.assertEqual(ptr12e.path, "/a/b/c/a/b") + + self.assertRaises(JsonPointerException, self.ptr1.join, 0) + + def test_join_magic(self): + + ptr12a = self.ptr1 / self.ptr2 + self.assertEqual(ptr12a.path, "/a/b/c/a/b") + + ptr12b = self.ptr1 / self.ptr2.parts + self.assertEqual(ptr12b.path, "/a/b/c/a/b") + + ptr12c = self.ptr1 / self.ptr2.parts[0:1] + self.assertEqual(ptr12c.path, "/a/b/c/a") + + ptr12d = self.ptr1 / "/a/b" + self.assertEqual(ptr12d.path, "/a/b/c/a/b") + + ptr12e = self.ptr1 / ["a", "b"] + self.assertEqual(ptr12e.path, "/a/b/c/a/b") + class WrongInputTests(unittest.TestCase): def test_no_start_slash(self): @@ -193,6 +273,12 @@ class SetTests(unittest.TestCase): newdoc = set_pointer(doc, "/foo/1", "cod", inplace=False) self.assertEqual(resolve_pointer(newdoc, "/foo/1"), "cod") + self.assertEqual(len(doc["foo"]), 2) + newdoc = set_pointer(doc, "/foo/-", "xyz", inplace=False) + self.assertEqual(resolve_pointer(newdoc, "/foo/2"), "xyz") + self.assertEqual(len(doc["foo"]), 2) + self.assertEqual(len(newdoc["foo"]), 3) + newdoc = set_pointer(doc, "/", 9, inplace=False) self.assertEqual(resolve_pointer(newdoc, "/"), 9) @@ -209,6 +295,11 @@ class SetTests(unittest.TestCase): set_pointer(doc, "/foo/1", "cod") self.assertEqual(resolve_pointer(doc, "/foo/1"), "cod") + self.assertEqual(len(doc["foo"]), 2) + set_pointer(doc, "/foo/-", "xyz") + self.assertEqual(resolve_pointer(doc, "/foo/2"), "xyz") + self.assertEqual(len(doc["foo"]), 3) + set_pointer(doc, "/", 9) self.assertEqual(resolve_pointer(doc, "/"), 9) |