From 3f460a54c2004802cc96b1c619b5edc011ec555b Mon Sep 17 00:00:00 2001 From: Alexander Dutton Date: Sun, 15 Feb 2015 19:45:43 +0000 Subject: Simplify `walk` method. No need to look before we leap. --- jsonpointer.py | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/jsonpointer.py b/jsonpointer.py index 0f115d7..85afe00 100644 --- a/jsonpointer.py +++ b/jsonpointer.py @@ -223,29 +223,14 @@ class JsonPointer(object): part = self.get_part(doc, part) - assert (type(doc) in (dict, list) or hasattr(doc, '__getitem__')), "invalid document type %s" % (type(doc),) - - if isinstance(doc, Mapping): - try: - return doc[part] - - except KeyError: - raise JsonPointerException("member '%s' not found in %s" % (part, doc)) - - elif isinstance(doc, Sequence): - - if part == '-': - return EndOfList(doc) - - try: - return doc[part] - - except IndexError: - raise JsonPointerException("index '%s' is out of bounds" % (part, )) - - else: - # Object supports __getitem__, assume custom indexing + if part == '-' and isinstance(doc, Sequence): + return EndOfList(doc) + try: return doc[part] + except KeyError: + raise JsonPointerException("member '%s' not found in %s" % (part, doc)) + except IndexError: + raise JsonPointerException("index '%s' is out of bounds" % (part, )) def contains(self, ptr): """Returns True if self contains the given ptr""" -- cgit v1.2.1 From 76b542b3fbfb3ef2a1f9fd289e8a74765c5cb53f Mon Sep 17 00:00:00 2001 From: Alexander Dutton Date: Sun, 15 Feb 2015 20:00:41 +0000 Subject: Reduce number of isinstance() calls in get_part --- jsonpointer.py | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/jsonpointer.py b/jsonpointer.py index 85afe00..29a71f5 100644 --- a/jsonpointer.py +++ b/jsonpointer.py @@ -196,27 +196,14 @@ class JsonPointer(object): def get_part(self, doc, part): """ Returns the next step in the correct type """ - if isinstance(doc, Mapping): - return part - - elif isinstance(doc, Sequence): - + if isinstance(doc, Sequence): if part == '-': return part - if not RE_ARRAY_INDEX.match(str(part)): raise JsonPointerException("'%s' is not a valid list index" % (part, )) - return int(part) - - elif hasattr(doc, '__getitem__'): - # Allow indexing via ducktyping if the target has defined __getitem__ - return part - else: - raise JsonPointerException("Document '%s' does not support indexing, " - "must be dict/list or support __getitem__" % type(doc)) - + return part def walk(self, doc, part): """ Walks one step in doc and returns the referenced part """ @@ -231,6 +218,10 @@ class JsonPointer(object): raise JsonPointerException("member '%s' not found in %s" % (part, doc)) except IndexError: raise JsonPointerException("index '%s' is out of bounds" % (part, )) + except TypeError: + raise JsonPointerException("Document '%s' does not support indexing, " + "must be dict/list or support __getitem__" % type(doc)) + def contains(self, ptr): """Returns True if self contains the given ptr""" -- cgit v1.2.1 From c4372cdb3829138a1e5287e1c4ef8dc9c6c5b5a5 Mon Sep 17 00:00:00 2001 From: Alexander Dutton Date: Sun, 15 Feb 2015 20:06:05 +0000 Subject: Optimize in get_part for the common cases of doc being dict or list --- jsonpointer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/jsonpointer.py b/jsonpointer.py index 29a71f5..3e8b31c 100644 --- a/jsonpointer.py +++ b/jsonpointer.py @@ -196,7 +196,12 @@ class JsonPointer(object): def get_part(self, doc, part): """ Returns the next step in the correct type """ - if isinstance(doc, Sequence): + # Optimize for common cases of doc being a dict or list, but not a + # sub-class (because isinstance() is far slower) + ptype = type(doc) + if ptype == dict: + return part + if ptype == list or isinstance(doc, Sequence): if part == '-': return part if not RE_ARRAY_INDEX.match(str(part)): -- cgit v1.2.1 From 9653d1be314e14ae00d86b13807d2d22c0d55e98 Mon Sep 17 00:00:00 2001 From: Alexander Dutton Date: Thu, 19 Feb 2015 00:22:03 +0000 Subject: Re-factor resolve and to_last to not make lots of function calls. Also means that isinstance(..., Sequence) doesn't get called twice for non-list sequences (once in walk, and one in get_part) --- jsonpointer.py | 53 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/jsonpointer.py b/jsonpointer.py index 3e8b31c..72e5f93 100644 --- a/jsonpointer.py +++ b/jsonpointer.py @@ -153,28 +153,53 @@ class JsonPointer(object): if not self.parts: return doc, None - for part in self.parts[:-1]: - doc = self.walk(doc, part) - - return doc, self.get_part(doc, self.parts[-1]) + doc = self.resolve(doc, parts=self.parts[:-1]) + last = self.parts[-1] + ptype = type(doc) + if ptype == dict: + pass + elif ptype == list or isinstance(doc, Sequence): + if not RE_ARRAY_INDEX.match(str(last)): + raise JsonPointerException("'%s' is not a valid list index" % (last, )) + last = int(last) + return doc, last - def resolve(self, doc, default=_nothing): + def resolve(self, doc, default=_nothing, parts=None): """Resolves the pointer against doc and returns the referenced object""" + if parts is None: + parts = self.parts - for part in self.parts: - - try: - doc = self.walk(doc, part) - except JsonPointerException: - if default is _nothing: - raise + try: + for part in parts: + ptype = type(doc) + if ptype == dict: + doc = doc[part] + elif ptype == list or isinstance(doc, Sequence): + if part == '-': + doc = EndOfList(doc) + else: + if not RE_ARRAY_INDEX.match(str(part)): + raise JsonPointerException("'%s' is not a valid list index" % (part, )) + doc = doc[int(part)] else: - return default + doc = doc[part] + except KeyError: + if default is not _nothing: + return default + raise JsonPointerException("member '%s' not found in %s" % (part, doc)) + except IndexError: + if default is not _nothing: + return default + raise JsonPointerException("index '%s' is out of bounds" % (part, )) + except TypeError: + if default is not _nothing: + return default + raise JsonPointerException("Document '%s' does not support indexing, " + "must be dict/list or support __getitem__" % type(doc)) return doc - get = resolve def set(self, doc, value, inplace=True): -- cgit v1.2.1