diff --git a/.travis.yml b/.travis.yml
index 9065721..f67ebbe 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,20 +1,32 @@
 language: python
 python:
-  - "2.6"
-  - "2.7"
-  - "3.3"
-  - "3.4"
-  - "3.5"
-  - "pypy"
-  - "pypy3"
-
+- '2.7'
+- '3.4'
+- '3.5'
+- '3.6'
+- 3.6-dev
+- 3.7-dev
+- nightly
+- pypy
+- pypy3
 install:
-  - travis_retry pip install coveralls
-
+- travis_retry pip install coveralls
 script:
-  - coverage run --source=jsonpointer tests.py
-
+- coverage run --source=jsonpointer tests.py
 after_script:
-  - coveralls
-
+- coveralls
 sudo: false
+addons:
+  apt:
+    packages:
+    - pandoc
+before_deploy:
+- pip install -r requirements-dev.txt
+deploy:
+  provider: pypi
+  user: skoegl
+  password:
+    secure: bKET/1sK+uWetPM3opPTt4qHHfZ6bMcjuLGe3Z/EUfNnGazcbDezy9CHJSqofuMXynF3xc8yluEojjfaqos3Ge/Y4o8pdFMY8ABp8KkxMnAJYGtYzbneSHgdgxPKsmdcUMVtIfioqkeNJTJClWUhRikWSlpKZ7TtkK4AmWtKNwc=
+  on:
+    tags: true
+  distributions: sdist bdist_wheel
diff --git a/COPYING b/LICENSE.txt
similarity index 100%
rename from COPYING
rename to LICENSE.txt
diff --git a/MANIFEST.in b/MANIFEST.in
index dcb2e4a..0f9b9f6 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
 include AUTHORS
-include COPYING
+include LICENSE.txt
 include README.md
 include tests.py
diff --git a/README.md b/README.md
index cae175d..5248dc8 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,22 @@
-python-json-pointer [![Build Status](https://secure.travis-ci.org/stefankoegl/python-json-pointer.png?branch=master)](https://travis-ci.org/stefankoegl/python-json-pointer) [![Coverage Status](https://coveralls.io/repos/stefankoegl/python-json-pointer/badge.png?branch=master)](https://coveralls.io/r/stefankoegl/python-json-pointer?branch=master) ![Downloads](https://pypip.in/d/jsonpointer/badge.png) ![Version](https://pypip.in/v/jsonpointer/badge.png)
+python-json-pointer
 ===================
 
+[![PyPI version](https://img.shields.io/pypi/v/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)
+[![Supported Python versions](https://img.shields.io/pypi/pyversions/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)
+[![Build Status](https://travis-ci.org/stefankoegl/python-json-pointer.png?branch=master)](https://travis-ci.org/stefankoegl/python-json-pointer)
+[![Coverage Status](https://coveralls.io/repos/stefankoegl/python-json-pointer/badge.png?branch=master)](https://coveralls.io/r/stefankoegl/python-json-pointer?branch=master)
+
+
 Resolve JSON Pointers in Python
 -------------------------------
 
 Library to resolve JSON Pointers according to
 [RFC 6901](http://tools.ietf.org/html/rfc6901)
 
-See Sourcecode for Examples
+See source code for examples
 * Website: https://github.com/stefankoegl/python-json-pointer
 * Repository: https://github.com/stefankoegl/python-json-pointer.git
 * Documentation: https://python-json-pointer.readthedocs.org/
 * PyPI: https://pypi.python.org/pypi/jsonpointer
-* Travis-CI: https://travis-ci.org/stefankoegl/python-json-pointer
+* Travis CI: https://travis-ci.org/stefankoegl/python-json-pointer
 * Coveralls: https://coveralls.io/r/stefankoegl/python-json-pointer
diff --git a/bin/jsonpointer b/bin/jsonpointer
index 1d49fae..d577d01 100755
--- a/bin/jsonpointer
+++ b/bin/jsonpointer
@@ -12,8 +12,17 @@ import argparse
 
 parser = argparse.ArgumentParser(
     description='Resolve a JSON pointer on JSON files')
-parser.add_argument('POINTER', type=argparse.FileType('r'),
-                    help='File containing a JSON pointer expression')
+
+# Accept pointer as argument or as file
+ptr_group = parser.add_mutually_exclusive_group(required=True)
+
+ptr_group.add_argument('-f', '--pointer-file', type=argparse.FileType('r'),
+                       nargs='?',
+                       help='File containing a JSON pointer expression')
+
+ptr_group.add_argument('POINTER',  type=str, nargs='?',
+                       help='A JSON pointer expression')
+
 parser.add_argument('FILE', type=argparse.FileType('r'), nargs='+',
                     help='Files for which the pointer should be resolved')
 parser.add_argument('--indent', type=int, default=None,
@@ -29,10 +38,24 @@ def main():
         sys.exit(1)
 
 
+def parse_pointer(args):
+    if args.POINTER:
+        ptr = args.POINTER
+    elif args.pointer_file:
+        ptr = args.pointer_file.read().strip()
+    else:
+        parser.print_usage()
+        sys.exit(1)
+
+    return ptr
+
+
 def resolve_files():
     """ Resolve a JSON pointer on JSON files """
     args = parser.parse_args()
-    ptr = json.load(args.POINTER)
+
+    ptr = parse_pointer(args)
+
     for f in args.FILE:
         doc = json.load(f)
         try:
diff --git a/debian/changelog b/debian/changelog
index 950d516..07dc4e3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,12 @@
-python-json-pointer (1.10-3) UNRELEASED; urgency=medium
+python-json-pointer (2.0-1) UNRELEASED; urgency=medium
 
+  [ Ondřej Nový ]
   * Run wrap-and-sort -bastk.
 
- -- Ondřej Nový <onovy@debian.org>  Fri, 18 Oct 2019 16:30:04 +0200
+  [ Debian Janitor ]
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Fri, 27 Mar 2020 07:55:28 +0000
 
 python-json-pointer (1.10-2) unstable; urgency=medium
 
diff --git a/doc/index.rst b/doc/index.rst
index dbdf2c6..53b1dea 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -7,7 +7,7 @@ python-json-pointer
 ===================
 
 *python-json-pointer* is a Python library for resolving JSON pointers (`RFC
-6901 <http://tools.ietf.org/html/rfc6901>`_). Python 2.6, 2.7, 3.2, 3.3
+6901 <http://tools.ietf.org/html/rfc6901>`_). Python 2.7, 3.4+
 and PyPy are supported.
 
 **Contents**
diff --git a/jsonpointer.py b/jsonpointer.py
index eea100c..1ef908a 100644
--- a/jsonpointer.py
+++ b/jsonpointer.py
@@ -11,12 +11,12 @@
 # are met:
 #
 # 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
+# notice, this list of conditions and the following disclaimer.
 # 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
 # 3. The name of the author may not be used to endorse or promote products
-#    derived from this software without specific prior written permission.
+# derived from this software without specific prior written permission.
 #
 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -30,63 +30,66 @@
 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #
 
-from __future__ import unicode_literals
-
 """ Identify specific nodes in a JSON document (RFC 6901) """
 
-try:
-    from collections.abc import Mapping, Sequence
-except ImportError:
-    from collections import Mapping, Sequence
+from __future__ import unicode_literals
 
 # Will be parsed by setup.py to determine package metadata
 __author__ = 'Stefan Kögl <stefan@skoegl.net>'
-__version__ = '1.10'
+__version__ = '2.0'
 __website__ = 'https://github.com/stefankoegl/python-json-pointer'
 __license__ = 'Modified BSD License'
 
 
 try:
-    from urllib import unquote
     from itertools import izip
     str = unicode
-except ImportError: # Python 3
-    from urllib.parse import unquote
+except ImportError:  # Python 3
     izip = zip
 
+try:
+    from collections.abc import Mapping, Sequence
+except ImportError:  # Python 3
+    from collections import Mapping, Sequence
+
 from itertools import tee
 import re
 import copy
 
 
-# array indices must not contain leading zeros, signs, spaces, decimals, etc
-RE_ARRAY_INDEX=re.compile('0|[1-9][0-9]*$')
-
+_nothing = object()
 
-class JsonPointerException(Exception):
-    pass
 
+def set_pointer(doc, pointer, value, inplace=True):
+    """Resolves pointer against doc and sets the value of the target within doc.
 
-class EndOfList(object):
-    """ Result of accessing element "-" of a list """
+    With inplace set to true, doc is modified as long as pointer is not the
+    root.
 
-    def __init__(self, list_):
-        self.list_ = list_
+    >>> obj = {'foo': {'anArray': [ {'prop': 44}], 'another prop': {'baz': 'A string' }}}
 
+    >>> set_pointer(obj, '/foo/anArray/0/prop', 55) == \
+    {'foo': {'another prop': {'baz': 'A string'}, 'anArray': [{'prop': 55}]}}
+    True
 
-    def __repr__(self):
-        return '{cls}({lst})'.format(cls=self.__class__.__name__,
-                lst=repr(self.list_))
+    >>> set_pointer(obj, '/foo/yet another prop', 'added prop') == \
+    {'foo': {'another prop': {'baz': 'A string'}, 'yet another prop': 'added prop', 'anArray': [{'prop': 55}]}}
+    True
 
+    >>> obj = {'foo': {}}
+    >>> set_pointer(obj, '/foo/a%20b', 'x') == \
+    {'foo': {'a%20b': 'x' }}
+    True
+    """
 
-_nothing = object()
+    pointer = JsonPointer(pointer)
+    return pointer.set(doc, value, inplace)
 
 
 def resolve_pointer(doc, pointer, default=_nothing):
-    """
-    Resolves pointer against doc and returns the referenced object
+    """ Resolves pointer against doc and returns the referenced object
 
-    >>> obj = {"foo": {"anArray": [ {"prop": 44}], "another prop": {"baz": "A string" }}}
+    >>> obj = {'foo': {'anArray': [ {'prop': 44}], 'another prop': {'baz': 'A string' }}, 'a%20b': 1, 'c d': 2}
 
     >>> resolve_pointer(obj, '') == obj
     True
@@ -94,10 +97,10 @@ def resolve_pointer(doc, pointer, default=_nothing):
     >>> resolve_pointer(obj, '/foo') == obj['foo']
     True
 
-    >>> resolve_pointer(obj, '/foo/another%20prop') == obj['foo']['another prop']
+    >>> resolve_pointer(obj, '/foo/another prop') == obj['foo']['another prop']
     True
 
-    >>> resolve_pointer(obj, '/foo/another%20prop/baz') == obj['foo']['another prop']['baz']
+    >>> resolve_pointer(obj, '/foo/another prop/baz') == obj['foo']['another prop']['baz']
     True
 
     >>> resolve_pointer(obj, '/foo/anArray/0') == obj['foo']['anArray'][0]
@@ -106,50 +109,83 @@ def resolve_pointer(doc, pointer, default=_nothing):
     >>> resolve_pointer(obj, '/some/path', None) == None
     True
 
+    >>> resolve_pointer(obj, '/a b', None) == None
+    True
+
+    >>> resolve_pointer(obj, '/a%20b') == 1
+    True
+
+    >>> resolve_pointer(obj, '/c d') == 2
+    True
+
+    >>> resolve_pointer(obj, '/c%20d', None) == None
+    True
     """
 
     pointer = JsonPointer(pointer)
     return pointer.resolve(doc, default)
 
-def set_pointer(doc, pointer, value, inplace=True):
-    """
-    Resolves pointer against doc and sets the value of the target within doc.
 
-    With inplace set to true, doc is modified as long as pointer is not the
-    root.
+def pairwise(iterable):
+    """ Transforms a list to a list of tuples of adjacent items
 
-    >>> obj = {"foo": {"anArray": [ {"prop": 44}], "another prop": {"baz": "A string" }}}
+    s -> (s0,s1), (s1,s2), (s2, s3), ...
 
-    >>> set_pointer(obj, '/foo/anArray/0/prop', 55) == \
-    {'foo': {'another prop': {'baz': 'A string'}, 'anArray': [{'prop': 55}]}}
-    True
+    >>> list(pairwise([]))
+    []
 
-    >>> set_pointer(obj, '/foo/yet%20another%20prop', 'added prop') == \
-    {'foo': {'another prop': {'baz': 'A string'}, 'yet another prop': 'added prop', 'anArray': [{'prop': 55}]}}
-    True
+    >>> list(pairwise([1]))
+    []
 
+    >>> list(pairwise([1, 2, 3, 4]))
+    [(1, 2), (2, 3), (3, 4)]
     """
+    a, b = tee(iterable)
+    for _ in b:
+        break
+    return izip(a, b)
 
-    pointer = JsonPointer(pointer)
-    return pointer.set(doc, value, inplace)
+
+class JsonPointerException(Exception):
+    pass
+
+
+class EndOfList(object):
+    """Result of accessing element "-" of a list"""
+
+    def __init__(self, list_):
+        self.list_ = list_
+
+    def __repr__(self):
+        return '{cls}({lst})'.format(cls=self.__class__.__name__,
+                                     lst=repr(self.list_))
 
 
 class JsonPointer(object):
-    """ A JSON Pointer that can reference parts of an JSON document """
+    """A JSON Pointer that can reference parts of an JSON document"""
+
+    # Array indices must not contain:
+    # leading zeros, signs, spaces, decimals, etc
+    _RE_ARRAY_INDEX = re.compile('0|[1-9][0-9]*$')
+    _RE_INVALID_ESCAPE = re.compile('(~[^01]|~$)')
 
     def __init__(self, pointer):
+
+        # validate escapes
+        invalid_escape = self._RE_INVALID_ESCAPE.search(pointer)
+        if invalid_escape:
+            raise JsonPointerException('Found invalid escape {}'.format(
+                invalid_escape.group()))
+
         parts = pointer.split('/')
         if parts.pop(0) != '':
             raise JsonPointerException('location must starts with /')
 
-        parts = map(unquote, parts)
-        parts = [part.replace('~1', '/') for part in parts]
-        parts = [part.replace('~0', '~') for part in parts]
+        parts = [unescape(part) for part in parts]
         self.parts = parts
 
-
     def to_last(self, doc):
-        """ Resolves ptr until the last step, returns (sub-doc, last-step) """
+        """Resolves ptr until the last step, returns (sub-doc, last-step)"""
 
         if not self.parts:
             return doc, None
@@ -159,7 +195,6 @@ class JsonPointer(object):
 
         return doc, self.get_part(doc, self.parts[-1])
 
-
     def resolve(self, doc, default=_nothing):
         """Resolves the pointer against doc and returns the referenced object"""
 
@@ -175,11 +210,10 @@ class JsonPointer(object):
 
         return doc
 
-
     get = resolve
 
     def set(self, doc, value, inplace=True):
-        """ Resolve the pointer against the doc and replace the target with value. """
+        """Resolve the pointer against the doc and replace the target with value."""
 
         if len(self.parts) == 0:
             if inplace:
@@ -195,7 +229,7 @@ class JsonPointer(object):
         return doc
 
     def get_part(self, doc, part):
-        """ Returns the next step in the correct type """
+        """Returns the next step in the correct type"""
 
         if isinstance(doc, Mapping):
             return part
@@ -205,18 +239,19 @@ class JsonPointer(object):
             if part == '-':
                 return part
 
-            if not RE_ARRAY_INDEX.match(str(part)):
-                raise JsonPointerException("'%s' is not a valid list index" % (part, ))
+            if not self._RE_ARRAY_INDEX.match(str(part)):
+                raise JsonPointerException("'%s' is not a valid sequence index" % part)
 
             return int(part)
 
         elif hasattr(doc, '__getitem__'):
-            # Allow indexing via ducktyping if the target has defined __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))
+                                       "must be mapping/sequence or support __getitem__" % type(doc))
 
 
     def walk(self, doc, part):
@@ -224,17 +259,9 @@ 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):
+        assert hasattr(doc, '__getitem__'), "invalid document type %s" % (type(doc),)
 
+        if isinstance(doc, Sequence):
             if part == '-':
                 return EndOfList(doc)
 
@@ -244,72 +271,61 @@ class JsonPointer(object):
             except IndexError:
                 raise JsonPointerException("index '%s' is out of bounds" % (part, ))
 
-        else:
-            # Object supports __getitem__, assume custom indexing
+        # Else the object is a mapping or supports __getitem__(so assume custom indexing)
+        try:
             return doc[part]
 
+        except KeyError:
+            raise JsonPointerException("member '%s' not found in %s" % (part, doc))
+
+
     def contains(self, ptr):
-        """Returns True if self contains the given ptr"""
+        """ Returns True if self contains the given ptr """
         return self.parts[:len(ptr.parts)] == ptr.parts
 
     def __contains__(self, item):
-        """Returns True if self contains the given ptr"""
+        """ Returns True if self contains the given ptr """
         return self.contains(item)
 
     @property
     def path(self):
-        """ Returns the string representation of the pointer
+        """Returns the string representation of the pointer
 
         >>> ptr = JsonPointer('/~0/0/~1').path == '/~0/0/~1'
         """
-        parts = [part.replace('~', '~0') for part in self.parts]
-        parts = [part.replace('/', '~1') for part in parts]
+        parts = [escape(part) for part in self.parts]
         return ''.join('/' + part for part in parts)
 
     def __eq__(self, other):
-        """ compares a pointer to another object
+        """Compares a pointer to another object
 
         Pointers can be compared by comparing their strings (or splitted
         strings), because no two different parts can point to the same
-        structure in an object (eg no different number representations) """
+        structure in an object (eg no different number representations)
+        """
 
         if not isinstance(other, JsonPointer):
             return False
 
         return self.parts == other.parts
 
-
     def __hash__(self):
         return hash(tuple(self.parts))
 
     @classmethod
     def from_parts(cls, parts):
-        """ Constructs a JsonPointer from a list of (unescaped) paths
+        """Constructs a JsonPointer from a list of (unescaped) paths
 
         >>> JsonPointer.from_parts(['a', '~', '/', 0]).path == '/a/~0/~1/0'
         True
         """
-        parts = [str(part) for part in parts]
-        parts = [part.replace('~', '~0') for part in parts]
-        parts = [part.replace('/', '~1') for part in parts]
+        parts = [escape(str(part)) for part in parts]
         ptr = cls(''.join('/' + part for part in parts))
         return ptr
 
 
+def escape(s):
+    return s.replace('~', '~0').replace('/', '~1')
 
-def pairwise(iterable):
-    """ s -> (s0,s1), (s1,s2), (s2, s3), ...
-
-    >>> list(pairwise([]))
-    []
-
-    >>> list(pairwise([1]))
-    []
-
-    >>> list(pairwise([1, 2, 3, 4]))
-    [(1, 2), (2, 3), (3, 4)]
-    """
-    a, b = tee(iterable)
-    for _ in b:
-        break
-    return izip(a, b)
+def unescape(s):
+    return s.replace('~1', '/').replace('~0', '~')
diff --git a/requirements-dev.txt b/requirements-dev.txt
index fd0fd6c..0dc8f0c 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,2 +1,2 @@
 wheel
-pandoc==1.0.0-alpha.3
+pypandoc==1.4
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..2a9acf1
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[bdist_wheel]
+universal = 1
diff --git a/setup.py b/setup.py
index 2c65e44..21660f5 100644
--- a/setup.py
+++ b/setup.py
@@ -42,12 +42,11 @@ CLASSIFIERS = [
     'Operating System :: OS Independent',
     'Programming Language :: Python',
     'Programming Language :: Python :: 2',
-    'Programming Language :: Python :: 2.6',
     'Programming Language :: Python :: 2.7',
     'Programming Language :: Python :: 3',
-    'Programming Language :: Python :: 3.3',
     'Programming Language :: Python :: 3.4',
     'Programming Language :: Python :: 3.5',
+    'Programming Language :: Python :: 3.6',
     'Programming Language :: Python :: Implementation :: CPython',
     'Programming Language :: Python :: Implementation :: PyPy',
     'Topic :: Software Development :: Libraries',
@@ -65,4 +64,5 @@ setup(name=PACKAGE,
       py_modules=MODULES,
       scripts=['bin/jsonpointer'],
       classifiers=CLASSIFIERS,
+      python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
 )
diff --git a/tests.py b/tests.py
index cf5c61a..54ca436 100755
--- a/tests.py
+++ b/tests.py
@@ -10,6 +10,7 @@ import copy
 from jsonpointer import resolve_pointer, EndOfList, JsonPointerException, \
          JsonPointer, set_pointer
 
+
 class SpecificationTests(unittest.TestCase):
     """ Tests all examples from the JSON Pointer specification """
 
@@ -73,6 +74,7 @@ class SpecificationTests(unittest.TestCase):
             new_ptr = JsonPointer.from_parts(parts)
             self.assertEqual(ptr, new_ptr)
 
+
 class ComparisonTests(unittest.TestCase):
 
     def setUp(self):
@@ -108,7 +110,6 @@ class ComparisonTests(unittest.TestCase):
         self.assertTrue(self.ptr1 in self.ptr1)
         self.assertFalse(self.ptr3 in self.ptr1)
 
-
 class WrongInputTests(unittest.TestCase):
 
     def test_no_start_slash(self):
@@ -125,6 +126,12 @@ class WrongInputTests(unittest.TestCase):
         doc = [0, 1, 2]
         self.assertRaises(JsonPointerException, resolve_pointer, doc, '/10')
 
+    def test_trailing_escape(self):
+        self.assertRaises(JsonPointerException, JsonPointer, '/foo/bar~')
+
+    def test_invalid_escape(self):
+        self.assertRaises(JsonPointerException, JsonPointer, '/foo/bar~2')
+
 
 class ToLastTests(unittest.TestCase):
 
@@ -192,43 +199,53 @@ class SetTests(unittest.TestCase):
 
         self.assertRaises(JsonPointerException, set_pointer, doc, "", 9)
 
+
 class AltTypesTests(unittest.TestCase):
 
-    def test_alttypes(self):
-        JsonPointer.alttypes = True
+    class Node(object):
+        def __init__(self, name, parent=None):
+            self.name = name
+            self.parent = parent
+            self.left = None
+            self.right = None
+
+        def set_left(self, node):
+            node.parent = self
+            self.left = node
 
-        class Node(object):
-            def __init__(self, name, parent=None):
-                self.name = name
-                self.parent = parent
-                self.left = None
-                self.right = None
+        def set_right(self, node):
+            node.parent = self
+            self.right = node
 
-            def set_left(self, node):
-                node.parent = self
-                self.left = node
+        def __getitem__(self, key):
+            if key == 'left':
+                return self.left
+            if key == 'right':
+                return self.right
 
-            def set_right(self, node):
-                node.parent = self
-                self.right = node
+            raise KeyError("Only left and right supported")
 
-            def __getitem__(self, key):
-                if key == 'left':
-                    return self.left
-                if key == 'right':
-                    return self.right
+        def __setitem__(self, key, val):
+            if key == 'left':
+                return self.set_left(val)
+            if key == 'right':
+                return self.set_right(val)
 
-                raise KeyError("Only left and right supported")
+            raise KeyError("Only left and right supported: %s" % key)
 
-            def __setitem__(self, key, val):
-                if key == 'left':
-                    return self.set_left(val)
-                if key == 'right':
-                    return self.set_right(val)
+    class mdict(object):
+        def __init__(self, d):
+            self._d = d
+        def __getitem__(self, item):
+            return self._d[item]
 
-                raise KeyError("Only left and right supported: %s" % key)
+    mdict = mdict({'root': {'1': {'2': '3'}}})
+    Node = Node
 
 
+    def test_alttypes(self):
+        Node = self.Node
+
         root = Node('root')
         root.set_left(Node('a'))
         root.left.set_left(Node('aa'))
@@ -249,6 +266,39 @@ class AltTypesTests(unittest.TestCase):
         set_pointer(root, '/left/right', Node('AB'))
         self.assertEqual(resolve_pointer(root, '/left/right').name, 'AB')
 
+    def test_mock_dict_sanity(self):
+        doc = self.mdict
+        default = None
+
+        # TODO: Generate this automatically for any given object
+        path_to_expected_value = {
+            '/root/1': {'2': '3'},
+            '/root': {'1': {'2': '3'}},
+            '/root/1/2': '3',
+        }
+
+        for path, expected_value in iter(path_to_expected_value.items()):
+            self.assertEqual(resolve_pointer(doc, path, default), expected_value)
+
+    def test_mock_dict_returns_default(self):
+        doc = self.mdict
+        default = None
+
+        path_to_expected_value = {
+            '/foo': default,
+            '/x/y/z/d': default
+        }
+
+        for path, expected_value in iter(path_to_expected_value.items()):
+            self.assertEqual(resolve_pointer(doc, path, default), expected_value)
+
+    def test_mock_dict_raises_key_error(self):
+        doc = self.mdict
+        self.assertRaises(JsonPointerException, resolve_pointer, doc, '/foo')
+        self.assertRaises(JsonPointerException, resolve_pointer, doc, '/root/1/2/3/4')
+
+
+
 suite = unittest.TestSuite()
 suite.addTest(unittest.makeSuite(SpecificationTests))
 suite.addTest(unittest.makeSuite(ComparisonTests))