@@ -168,23 +168,62 @@ def to_last(self, doc):
168
168
if not self .parts :
169
169
return doc , None
170
170
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
173
189
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 )]
186
204
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
+ )
188
227
189
228
return doc
190
229
@@ -209,17 +248,19 @@ def set(self, doc, value, inplace=True):
209
248
def get_part (self , doc , part ):
210
249
"""Returns the next step in the correct type"""
211
250
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 :
213
255
return part
214
-
215
- elif isinstance (doc , Sequence ):
216
-
256
+ if ptype == list or isinstance (doc , Sequence ):
217
257
if part == '-' :
218
258
return part
219
259
220
260
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
+ )
223
264
return int (part )
224
265
225
266
elif hasattr (doc , '__getitem__' ):
@@ -228,34 +269,42 @@ def get_part(self, doc, part):
228
269
return part
229
270
230
271
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
+ )
234
276
235
277
def walk (self , doc , part ):
236
278
""" Walks one step in doc and returns the referenced part """
237
279
238
280
part = self .get_part (doc , part )
239
281
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 )
248
284
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)
253
285
try :
254
286
return doc [part ]
255
287
256
288
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
+ )
258
297
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
+ )
259
308
260
309
def contains (self , ptr ):
261
310
""" Returns True if self contains the given ptr """
0 commit comments