Skip to content

Commit e9fa652

Browse files
committed
Merge branch 'thekafkaf-refactoring'
2 parents 45b04c7 + 7992366 commit e9fa652

File tree

1 file changed

+77
-82
lines changed

1 file changed

+77
-82
lines changed

jsonpointer.py

Lines changed: 77 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
# are met:
1212
#
1313
# 1. Redistributions of source code must retain the above copyright
14-
# notice, this list of conditions and the following disclaimer.
14+
# notice, this list of conditions and the following disclaimer.
1515
# 2. Redistributions in binary form must reproduce the above copyright
16-
# notice, this list of conditions and the following disclaimer in the
17-
# documentation and/or other materials provided with the distribution.
16+
# notice, this list of conditions and the following disclaimer in the
17+
# documentation and/or other materials provided with the distribution.
1818
# 3. The name of the author may not be used to endorse or promote products
19-
# derived from this software without specific prior written permission.
19+
# derived from this software without specific prior written permission.
2020
#
2121
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2222
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -30,14 +30,9 @@
3030
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131
#
3232

33-
from __future__ import unicode_literals
34-
3533
""" Identify specific nodes in a JSON document (RFC 6901) """
3634

37-
try:
38-
from collections.abc import Mapping, Sequence
39-
except ImportError:
40-
from collections import Mapping, Sequence
35+
from __future__ import unicode_literals
4136

4237
# Will be parsed by setup.py to determine package metadata
4338
__author__ = 'Stefan Kögl <[email protected]>'
@@ -50,43 +45,48 @@
5045
from urllib import unquote
5146
from itertools import izip
5247
str = unicode
53-
except ImportError: # Python 3
48+
except ImportError: # Python 3
5449
from urllib.parse import unquote
5550
izip = zip
5651

52+
try:
53+
from collections.abc import Mapping, Sequence
54+
except ImportError: # Python 3
55+
from collections import Mapping, Sequence
56+
5757
from itertools import tee
5858
import re
5959
import copy
6060

6161

62-
# array indices must not contain leading zeros, signs, spaces, decimals, etc
63-
RE_ARRAY_INDEX=re.compile('0|[1-9][0-9]*$')
64-
65-
66-
class JsonPointerException(Exception):
67-
pass
62+
_nothing = object()
6863

6964

70-
class EndOfList(object):
71-
""" Result of accessing element "-" of a list """
65+
def set_pointer(doc, pointer, value, inplace=True):
66+
"""Resolves pointer against doc and sets the value of the target within doc.
7267
73-
def __init__(self, list_):
74-
self.list_ = list_
68+
With inplace set to true, doc is modified as long as pointer is not the
69+
root.
7570
71+
>>> obj = {'foo': {'anArray': [ {'prop': 44}], 'another prop': {'baz': 'A string' }}}
7672
77-
def __repr__(self):
78-
return '{cls}({lst})'.format(cls=self.__class__.__name__,
79-
lst=repr(self.list_))
73+
>>> set_pointer(obj, '/foo/anArray/0/prop', 55) == \
74+
{'foo': {'another prop': {'baz': 'A string'}, 'anArray': [{'prop': 55}]}}
75+
True
8076
77+
>>> set_pointer(obj, '/foo/yet%20another%20prop', 'added prop') == \
78+
{'foo': {'another prop': {'baz': 'A string'}, 'yet another prop': 'added prop', 'anArray': [{'prop': 55}]}}
79+
True
80+
"""
8181

82-
_nothing = object()
82+
pointer = JsonPointer(pointer)
83+
return pointer.set(doc, value, inplace)
8384

8485

8586
def resolve_pointer(doc, pointer, default=_nothing):
86-
"""
87-
Resolves pointer against doc and returns the referenced object
87+
""" Resolves pointer against doc and returns the referenced object
8888
89-
>>> obj = {"foo": {"anArray": [ {"prop": 44}], "another prop": {"baz": "A string" }}}
89+
>>> obj = {'foo': {'anArray': [ {'prop': 44}], 'another prop': {'baz': 'A string' }}}
9090
9191
>>> resolve_pointer(obj, '') == obj
9292
True
@@ -105,37 +105,53 @@ def resolve_pointer(doc, pointer, default=_nothing):
105105
106106
>>> resolve_pointer(obj, '/some/path', None) == None
107107
True
108-
109108
"""
110109

111110
pointer = JsonPointer(pointer)
112111
return pointer.resolve(doc, default)
113112

114-
def set_pointer(doc, pointer, value, inplace=True):
115-
"""
116-
Resolves pointer against doc and sets the value of the target within doc.
117113

118-
With inplace set to true, doc is modified as long as pointer is not the
119-
root.
114+
def pairwise(iterable):
115+
""" Transforms a list to a list of tuples of adjacent items
120116
121-
>>> obj = {"foo": {"anArray": [ {"prop": 44}], "another prop": {"baz": "A string" }}}
117+
s -> (s0,s1), (s1,s2), (s2, s3), ...
122118
123-
>>> set_pointer(obj, '/foo/anArray/0/prop', 55) == \
124-
{'foo': {'another prop': {'baz': 'A string'}, 'anArray': [{'prop': 55}]}}
125-
True
119+
>>> list(pairwise([]))
120+
[]
126121
127-
>>> set_pointer(obj, '/foo/yet%20another%20prop', 'added prop') == \
128-
{'foo': {'another prop': {'baz': 'A string'}, 'yet another prop': 'added prop', 'anArray': [{'prop': 55}]}}
129-
True
122+
>>> list(pairwise([1]))
123+
[]
130124
125+
>>> list(pairwise([1, 2, 3, 4]))
126+
[(1, 2), (2, 3), (3, 4)]
131127
"""
128+
a, b = tee(iterable)
129+
for _ in b:
130+
break
131+
return izip(a, b)
132132

133-
pointer = JsonPointer(pointer)
134-
return pointer.set(doc, value, inplace)
133+
134+
class JsonPointerException(Exception):
135+
pass
136+
137+
138+
class EndOfList(object):
139+
"""Result of accessing element "-" of a list"""
140+
141+
def __init__(self, list_):
142+
self.list_ = list_
143+
144+
def __repr__(self):
145+
return '{cls}({lst})'.format(cls=self.__class__.__name__,
146+
lst=repr(self.list_))
135147

136148

137149
class JsonPointer(object):
138-
""" A JSON Pointer that can reference parts of an JSON document """
150+
"""A JSON Pointer that can reference parts of an JSON document"""
151+
152+
# Array indices must not contain:
153+
# leading zeros, signs, spaces, decimals, etc
154+
_RE_ARRAY_INDEX = re.compile('0|[1-9][0-9]*$')
139155

140156
def __init__(self, pointer):
141157
parts = pointer.split('/')
@@ -146,9 +162,8 @@ def __init__(self, pointer):
146162
parts = [unescape(part) for part in parts]
147163
self.parts = parts
148164

149-
150165
def to_last(self, doc):
151-
""" Resolves ptr until the last step, returns (sub-doc, last-step) """
166+
"""Resolves ptr until the last step, returns (sub-doc, last-step)"""
152167

153168
if not self.parts:
154169
return doc, None
@@ -158,7 +173,6 @@ def to_last(self, doc):
158173

159174
return doc, self.get_part(doc, self.parts[-1])
160175

161-
162176
def resolve(self, doc, default=_nothing):
163177
"""Resolves the pointer against doc and returns the referenced object"""
164178

@@ -174,11 +188,10 @@ def resolve(self, doc, default=_nothing):
174188

175189
return doc
176190

177-
178191
get = resolve
179192

180193
def set(self, doc, value, inplace=True):
181-
""" Resolve the pointer against the doc and replace the target with value. """
194+
"""Resolve the pointer against the doc and replace the target with value."""
182195

183196
if len(self.parts) == 0:
184197
if inplace:
@@ -194,7 +207,7 @@ def set(self, doc, value, inplace=True):
194207
return doc
195208

196209
def get_part(self, doc, part):
197-
""" Returns the next step in the correct type """
210+
"""Returns the next step in the correct type"""
198211

199212
if isinstance(doc, Mapping):
200213
return part
@@ -204,26 +217,27 @@ def get_part(self, doc, part):
204217
if part == '-':
205218
return part
206219

207-
if not RE_ARRAY_INDEX.match(str(part)):
208-
raise JsonPointerException("'%s' is not a valid list index" % (part, ))
220+
if not self._RE_ARRAY_INDEX.match(str(part)):
221+
raise JsonPointerException("'%s' is not a valid sequence index" % part)
209222

210223
return int(part)
211224

212225
elif hasattr(doc, '__getitem__'):
213-
# Allow indexing via ducktyping if the target has defined __getitem__
226+
# Allow indexing via ducktyping
227+
# if the target has defined __getitem__
214228
return part
215229

216230
else:
217-
raise JsonPointerException("Document '%s' does not support indexing, "
218-
"must be dict/list or support __getitem__" % type(doc))
219-
231+
raise JsonPointerException("document '%s' does not support indexing, "
232+
"must be mapping/sequence or support __getitem__" % type(doc))
220233

221234
def walk(self, doc, part):
222-
""" Walks one step in doc and returns the referenced part """
235+
"""Walks one step in doc and returns the referenced part"""
223236

224237
part = self.get_part(doc, part)
225238

226-
assert (type(doc) in (dict, list) or hasattr(doc, '__getitem__')), "invalid document type %s" % (type(doc),)
239+
assert hasattr(doc, '__getitem__'), \
240+
'invalid document type %s' % type(doc)
227241

228242
if isinstance(doc, Mapping):
229243
try:
@@ -257,32 +271,32 @@ def __contains__(self, item):
257271

258272
@property
259273
def path(self):
260-
""" Returns the string representation of the pointer
274+
"""Returns the string representation of the pointer
261275
262276
>>> ptr = JsonPointer('/~0/0/~1').path == '/~0/0/~1'
263277
"""
264278
parts = [escape(part) for part in self.parts]
265279
return ''.join('/' + part for part in parts)
266280

267281
def __eq__(self, other):
268-
""" compares a pointer to another object
282+
"""Compares a pointer to another object
269283
270284
Pointers can be compared by comparing their strings (or splitted
271285
strings), because no two different parts can point to the same
272-
structure in an object (eg no different number representations) """
286+
structure in an object (eg no different number representations)
287+
"""
273288

274289
if not isinstance(other, JsonPointer):
275290
return False
276291

277292
return self.parts == other.parts
278293

279-
280294
def __hash__(self):
281295
return hash(tuple(self.parts))
282296

283297
@classmethod
284298
def from_parts(cls, parts):
285-
""" Constructs a JsonPointer from a list of (unescaped) paths
299+
"""Constructs a JsonPointer from a list of (unescaped) paths
286300
287301
>>> JsonPointer.from_parts(['a', '~', '/', 0]).path == '/a/~0/~1/0'
288302
True
@@ -292,25 +306,6 @@ def from_parts(cls, parts):
292306
return ptr
293307

294308

295-
296-
def pairwise(iterable):
297-
""" s -> (s0,s1), (s1,s2), (s2, s3), ...
298-
299-
>>> list(pairwise([]))
300-
[]
301-
302-
>>> list(pairwise([1]))
303-
[]
304-
305-
>>> list(pairwise([1, 2, 3, 4]))
306-
[(1, 2), (2, 3), (3, 4)]
307-
"""
308-
a, b = tee(iterable)
309-
for _ in b:
310-
break
311-
return izip(a, b)
312-
313-
314309
def escape(s):
315310
return s.replace('~', '~0').replace('/', '~1')
316311

0 commit comments

Comments
 (0)