3636
3737# Will be parsed by setup.py to determine package metadata
3838__author__ = 'Stefan Kögl <stefan@skoegl.net>'
39- __version__ = '1.11 '
39+ __version__ = '1.10 '
4040__website__ = 'https://github.com/stefankoegl/python-json-pointer'
4141__license__ = 'Modified BSD License'
4242
4343
4444try :
4545 from urllib import unquote
46+ from itertools import izip
4647 str = unicode
4748except ImportError : # Python 3
4849 from urllib .parse import unquote
50+ izip = zip
4951
5052try :
5153 from collections .abc import Mapping , Sequence
5254except ImportError : # Python 3
5355 from collections import Mapping , Sequence
5456
57+ from itertools import tee
5558import re
5659import copy
5760
@@ -108,6 +111,26 @@ def resolve_pointer(doc, pointer, default=_nothing):
108111 return pointer .resolve (doc , default )
109112
110113
114+ def pairwise (iterable ):
115+ """ Transforms a list to a list of tuples of adjacent items
116+
117+ s -> (s0,s1), (s1,s2), (s2, s3), ...
118+
119+ >>> list(pairwise([]))
120+ []
121+
122+ >>> list(pairwise([1]))
123+ []
124+
125+ >>> list(pairwise([1, 2, 3, 4]))
126+ [(1, 2), (2, 3), (3, 4)]
127+ """
128+ a , b = tee (iterable )
129+ for _ in b :
130+ break
131+ return izip (a , b )
132+
133+
111134class JsonPointerException (Exception ):
112135 pass
113136
@@ -145,62 +168,23 @@ def to_last(self, doc):
145168 if not self .parts :
146169 return doc , None
147170
148- doc = self .resolve (doc , parts = self .parts [:- 1 ])
149- last = self .parts [- 1 ]
150- ptype = type (doc )
151- if ptype == dict :
152- pass
153- elif ptype == list or isinstance (doc , Sequence ):
154- if not self ._RE_ARRAY_INDEX .match (str (last )):
155- raise JsonPointerException (
156- "'%s' is not a valid list index" % (last , )
157- )
158- last = int (last )
159-
160- return doc , last
161-
162- def resolve (self , doc , default = _nothing , parts = None ):
163- """ Resolves the pointer against doc, returns the referenced object """
164- if parts is None :
165- parts = self .parts
171+ for part in self .parts [:- 1 ]:
172+ doc = self .walk (doc , part )
166173
167- try :
168- for part in parts :
169- ptype = type (doc )
170- if ptype == dict :
171- doc = doc [part ]
172- elif ptype == list or isinstance (doc , Sequence ):
173- if part == '-' :
174- doc = EndOfList (doc )
175- else :
176- if not self ._RE_ARRAY_INDEX .match (str (part )):
177- raise JsonPointerException (
178- "'%s' is not a valid list index" % (part , )
179- )
180- doc = doc [int (part )]
174+ return doc , self .get_part (doc , self .parts [- 1 ])
175+
176+ def resolve (self , doc , default = _nothing ):
177+ """Resolves the pointer against doc and returns the referenced object"""
178+
179+ for part in self .parts :
180+
181+ try :
182+ doc = self .walk (doc , part )
183+ except JsonPointerException :
184+ if default is _nothing :
185+ raise
181186 else :
182- doc = doc [part ]
183- except KeyError :
184- if default is not _nothing :
185- return default
186- raise JsonPointerException (
187- "member '%s' not found in %s" % (part , doc )
188- )
189-
190- except IndexError :
191- if default is not _nothing :
192- return default
193- raise JsonPointerException (
194- "index '%s' is out of bounds" % (part , )
195- )
196-
197- except TypeError :
198- if default is not _nothing :
199- return default
200- raise JsonPointerException (
201- "Document '%s' does not support indexing, must be dict/list "
202- "or support __getitem__" % type (doc )
203- )
187+ return default
204188
205189 return doc
206190
@@ -225,19 +209,17 @@ def set(self, doc, value, inplace=True):
225209 def get_part (self , doc , part ):
226210 """Returns the next step in the correct type"""
227211
228- # Optimize for common cases of doc being a dict or list, but not a
229- # sub-class (because isinstance() is far slower)
230- ptype = type (doc )
231- if ptype == dict :
212+ if isinstance (doc , Mapping ):
232213 return part
233- if ptype == list or isinstance (doc , Sequence ):
214+
215+ elif isinstance (doc , Sequence ):
216+
234217 if part == '-' :
235218 return part
236219
237220 if not self ._RE_ARRAY_INDEX .match (str (part )):
238- raise JsonPointerException (
239- "'%s' is not a valid list index" % (part , )
240- )
221+ raise JsonPointerException ("'%s' is not a valid sequence index" % part )
222+
241223 return int (part )
242224
243225 elif hasattr (doc , '__getitem__' ):
@@ -246,42 +228,34 @@ def get_part(self, doc, part):
246228 return part
247229
248230 else :
249- raise JsonPointerException (
250- "Document '%s' does not support indexing, must be "
251- "mapping/sequence or support __getitem__" % type (doc )
252- )
231+ raise JsonPointerException ("Document '%s' does not support indexing, "
232+ "must be mapping/sequence or support __getitem__" % type (doc ))
233+
253234
254235 def walk (self , doc , part ):
255236 """ Walks one step in doc and returns the referenced part """
256237
257238 part = self .get_part (doc , part )
258239
259- if part == '-' and isinstance (doc , Sequence ):
260- return EndOfList (doc )
240+ assert hasattr (doc , '__getitem__' ), "invalid document type %s" % (type (doc ),)
261241
262- try :
263- return doc [part ]
242+ if isinstance (doc , Sequence ):
243+ if part == '-' :
244+ return EndOfList (doc )
264245
265- except KeyError :
266- raise JsonPointerException (
267- "member '%s' not found in %s" % (part , doc )
268- )
246+ try :
247+ return doc [part ]
269248
270- except IndexError :
271- raise JsonPointerException (
272- "index '%s' is out of bounds" % (part , )
273- )
249+ except IndexError :
250+ raise JsonPointerException ("index '%s' is out of bounds" % (part , ))
274251
275- except TypeError :
276- raise JsonPointerException (
277- "Document '%s' does not support indexing, must be dict/list "
278- "or support __getitem__" % type (doc )
279- )
252+ # Else the object is a mapping or supports __getitem__(so assume custom indexing)
253+ try :
254+ return doc [part ]
280255
281256 except KeyError :
282- raise JsonPointerException (
283- "member '%s' not found in %s" % (part , doc )
284- )
257+ raise JsonPointerException ("member '%s' not found in %s" % (part , doc ))
258+
285259
286260 def contains (self , ptr ):
287261 """ Returns True if self contains the given ptr """
0 commit comments