@@ -168,23 +168,62 @@ def to_last(self, doc):
168168 if not self .parts :
169169 return doc , None
170170
171- for part in self .parts [:- 1 ]:
172- doc = self .walk (doc , part )
171+ doc = self .resolve (doc , parts = self .parts [:- 1 ])
172+ last = self .parts [- 1 ]
173+ ptype = type (doc )
174+ if ptype == dict :
175+ pass
176+ elif ptype == list or isinstance (doc , Sequence ):
177+ if not self ._RE_ARRAY_INDEX .match (str (last )):
178+ raise JsonPointerException (
179+ "'%s' is not a valid list index" % (last , )
180+ )
181+ last = int (last )
182+
183+ return doc , last
184+
185+ def resolve (self , doc , default = _nothing , parts = None ):
186+ """ Resolves the pointer against doc, returns the referenced object """
187+ if parts is None :
188+ parts = self .parts
173189
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
190+ try :
191+ for part in parts :
192+ ptype = type (doc )
193+ if ptype == dict :
194+ doc = doc [part ]
195+ elif ptype == list or isinstance (doc , Sequence ):
196+ if part == '-' :
197+ doc = EndOfList (doc )
198+ else :
199+ if not self ._RE_ARRAY_INDEX .match (str (part )):
200+ raise JsonPointerException (
201+ "'%s' is not a valid list index" % (part , )
202+ )
203+ doc = doc [int (part )]
186204 else :
187- return default
205+ doc = doc [part ]
206+ except KeyError :
207+ if default is not _nothing :
208+ return default
209+ raise JsonPointerException (
210+ "member '%s' not found in %s" % (part , doc )
211+ )
212+
213+ except IndexError :
214+ if default is not _nothing :
215+ return default
216+ raise JsonPointerException (
217+ "index '%s' is out of bounds" % (part , )
218+ )
219+
220+ except TypeError :
221+ if default is not _nothing :
222+ return default
223+ raise JsonPointerException (
224+ "Document '%s' does not support indexing, must be dict/list "
225+ "or support __getitem__" % type (doc )
226+ )
188227
189228 return doc
190229
@@ -209,17 +248,19 @@ def set(self, doc, value, inplace=True):
209248 def get_part (self , doc , part ):
210249 """Returns the next step in the correct type"""
211250
212- if isinstance (doc , Mapping ):
251+ # Optimize for common cases of doc being a dict or list, but not a
252+ # sub-class (because isinstance() is far slower)
253+ ptype = type (doc )
254+ if ptype == dict :
213255 return part
214-
215- elif isinstance (doc , Sequence ):
216-
256+ if ptype == list or isinstance (doc , Sequence ):
217257 if part == '-' :
218258 return part
219259
220260 if not self ._RE_ARRAY_INDEX .match (str (part )):
221- raise JsonPointerException ("'%s' is not a valid sequence index" % part )
222-
261+ raise JsonPointerException (
262+ "'%s' is not a valid list index" % (part , )
263+ )
223264 return int (part )
224265
225266 elif hasattr (doc , '__getitem__' ):
@@ -228,34 +269,42 @@ def get_part(self, doc, part):
228269 return part
229270
230271 else :
231- raise JsonPointerException ("Document '%s' does not support indexing, "
232- "must be mapping/sequence or support __getitem__" % type (doc ))
233-
272+ raise JsonPointerException (
273+ "Document '%s' does not support indexing, must be "
274+ "mapping/sequence or support __getitem__" % type (doc )
275+ )
234276
235277 def walk (self , doc , part ):
236278 """ Walks one step in doc and returns the referenced part """
237279
238280 part = self .get_part (doc , part )
239281
240- assert hasattr (doc , '__getitem__' ), "invalid document type %s" % (type (doc ),)
241-
242- if isinstance (doc , Sequence ):
243- if part == '-' :
244- return EndOfList (doc )
245-
246- try :
247- return doc [part ]
282+ if part == '-' and isinstance (doc , Sequence ):
283+ return EndOfList (doc )
248284
249- except IndexError :
250- raise JsonPointerException ("index '%s' is out of bounds" % (part , ))
251-
252- # Else the object is a mapping or supports __getitem__(so assume custom indexing)
253285 try :
254286 return doc [part ]
255287
256288 except KeyError :
257- raise JsonPointerException ("member '%s' not found in %s" % (part , doc ))
289+ raise JsonPointerException (
290+ "member '%s' not found in %s" % (part , doc )
291+ )
292+
293+ except IndexError :
294+ raise JsonPointerException (
295+ "index '%s' is out of bounds" % (part , )
296+ )
258297
298+ except TypeError :
299+ raise JsonPointerException (
300+ "Document '%s' does not support indexing, must be dict/list "
301+ "or support __getitem__" % type (doc )
302+ )
303+
304+ except KeyError :
305+ raise JsonPointerException (
306+ "member '%s' not found in %s" % (part , doc )
307+ )
259308
260309 def contains (self , ptr ):
261310 """ Returns True if self contains the given ptr """
0 commit comments