diff --git a/IPython/external/jsonpointer/__init__.py b/IPython/external/jsonpointer/__init__.py
new file mode 100644
index 0000000..4643cb6
--- /dev/null
+++ b/IPython/external/jsonpointer/__init__.py
@@ -0,0 +1,4 @@
+try:
+    from jsonpointer import *
+except ImportError :
+    from _jsonpointer import *
diff --git a/IPython/external/jsonpointer/_jsonpointer.py b/IPython/external/jsonpointer/_jsonpointer.py
new file mode 100644
index 0000000..8683303
--- /dev/null
+++ b/IPython/external/jsonpointer/_jsonpointer.py
@@ -0,0 +1,222 @@
+# -*- coding: utf-8 -*-
+#
+# python-json-pointer - An implementation of the JSON Pointer syntax
+# https://github.com/stefankoegl/python-json-pointer
+#
+# Copyright (c) 2011 Stefan Kögl <stefan@skoegl.net>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    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.
+# 3. The name of the author may not be used to endorse or promote products
+#    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
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+""" Identify specific nodes in a JSON document according to
+http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-04 """
+
+# Will be parsed by setup.py to determine package metadata
+__author__ = 'Stefan K�gl <stefan@skoegl.net>'
+__version__ = '0.3'
+__website__ = 'https://github.com/stefankoegl/python-json-pointer'
+__license__ = 'Modified BSD License'
+
+
+import urllib
+from itertools import tee, izip
+
+
+class JsonPointerException(Exception):
+    pass
+
+
+_nothing = object()
+
+
+def resolve_pointer(doc, pointer, default=_nothing):
+    """
+    Resolves pointer against doc and returns the referenced object
+
+    >>> obj = {"foo": {"anArray": [ {"prop": 44}], "another prop": {"baz": "A string" }}}
+
+    >>> resolve_pointer(obj, '') == obj
+    True
+
+    >>> resolve_pointer(obj, '/foo') == obj['foo']
+    True
+
+    >>> resolve_pointer(obj, '/foo/another%20prop') == obj['foo']['another prop']
+    True
+
+    >>> resolve_pointer(obj, '/foo/another%20prop/baz') == obj['foo']['another prop']['baz']
+    True
+
+    >>> resolve_pointer(obj, '/foo/anArray/0') == obj['foo']['anArray'][0]
+    True
+
+    >>> resolve_pointer(obj, '/some/path', None) == None
+    True
+
+    """
+
+    pointer = JsonPointer(pointer)
+    return pointer.resolve(doc, default)
+
+
+def set_pointer(doc, pointer, value):
+    """
+    Set a field to a given value
+
+    The field is indicates by a base location that is given in the constructor,
+    and an optional relative location in the call to set. If the path doesn't
+    exist, it is created if possible
+
+    >>> obj = {"foo": 2}
+    >>> pointer = JsonPointer('/bar')
+    >>> pointer.set(obj, 'one', '0')
+    >>> pointer.set(obj, 'two', '1')
+    >>> obj
+    {'foo': 2, 'bar': ['one', 'two']}
+
+    >>> obj = {"foo": 2, "bar": []}
+    >>> pointer = JsonPointer('/bar')
+    >>> pointer.set(obj, 5, '0/x')
+    >>> obj
+    {'foo': 2, 'bar': [{'x': 5}]}
+
+    >>> obj = {'foo': 2, 'bar': [{'x': 5}]}
+    >>> pointer = JsonPointer('/bar/0')
+    >>> pointer.set(obj, 10, 'y/0')
+    >>> obj
+    {'foo': 2, 'bar': [{'y': [10], 'x': 5}]}
+    """
+
+    pointer = JsonPointer(pointer)
+    pointer.set(doc, value)
+
+
+class JsonPointer(object):
+    """ A JSON Pointer that can reference parts of an JSON document """
+
+    def __init__(self, pointer):
+        parts = pointer.split('/')
+        if parts.pop(0) != '':
+            raise JsonPointerException('location must starts with /')
+
+        parts = map(urllib.unquote, parts)
+        parts = [part.replace('~1', '/') for part in parts]
+        parts = [part.replace('~0', '~') for part in parts]
+        self.parts = parts
+
+
+
+    def resolve(self, doc, default=_nothing):
+        """Resolves the pointer against doc and returns the referenced object"""
+
+        for part in self.parts:
+
+            try:
+                doc = self.walk(doc, part)
+            except JsonPointerException:
+                if default is _nothing:
+                    raise
+                else:
+                    return default
+
+        return doc
+
+
+    get = resolve
+
+
+    def set(self, doc, value, path=None):
+        """ Sets a field of doc to value
+
+        The location of the field is given by the pointers base location and
+        the optional path which is relative to the base location """
+
+        fullpath = list(self.parts)
+
+        if path:
+            fullpath += path.split('/')
+
+
+        for part, nextpart in pairwise(fullpath):
+            try:
+                doc = self.walk(doc, part)
+            except JsonPointerException:
+                step_val = [] if nextpart.isdigit() else {}
+                doc = self._set_value(doc, part, step_val)
+
+        self._set_value(doc, fullpath[-1], value)
+
+
+    @staticmethod
+    def _set_value(doc, part, value):
+        part = int(part) if part.isdigit() else part
+
+        if isinstance(doc, dict):
+            doc[part] = value
+
+        if isinstance(doc, list):
+            if len(doc) < part:
+                doc[part] = value
+
+            if len(doc) == part:
+                doc.append(value)
+
+            else:
+                raise IndexError
+
+        return doc[part]
+
+
+    def walk(self, doc, part):
+        """ Walks one step in doc and returns the referenced part """
+
+        # Its not clear if a location "1" should be considered as 1 or "1"
+        # We prefer the integer-variant if possible
+        part_variants = self._try_parse(part) + [part]
+
+        for variant in part_variants:
+            try:
+                return doc[variant]
+            except:
+                continue
+
+        raise JsonPointerException("'%s' not found in %s" % (part, doc))
+
+
+    @staticmethod
+    def _try_parse(val, cls=int):
+        try:
+            return [cls(val)]
+        except:
+            return []
+
+
+
+def pairwise(iterable):
+    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
+    a, b = tee(iterable)
+    next(b, None)
+    return izip(a, b)
+__author__ = 'Stefan Kögl <stefan@skoegl.net>'
diff --git a/IPython/external/jsonschema/__init__.py b/IPython/external/jsonschema/__init__.py
new file mode 100644
index 0000000..59817d5
--- /dev/null
+++ b/IPython/external/jsonschema/__init__.py
@@ -0,0 +1,4 @@
+try:
+    from jsonschema import *
+except ImportError :
+    from _jsonschema import *
diff --git a/IPython/external/jsonschema/_jsonschema.py b/IPython/external/jsonschema/_jsonschema.py
new file mode 100755
index 0000000..c7d662b
--- /dev/null
+++ b/IPython/external/jsonschema/_jsonschema.py
@@ -0,0 +1,653 @@
+"""
+An implementation of JSON Schema for Python
+
+The main functionality is provided by the :class:`Validator` class, with the
+:function:`validate` function being the most common way to quickly create a
+:class:`Validator` object and validate an instance with a given schema.
+
+The :class:`Validator` class generally attempts to be as strict as possible
+under the JSON Schema specification. See its docstring for details.
+
+"""
+
+from __future__ import division, unicode_literals
+
+import collections
+import itertools
+import operator
+import re
+import sys
+import warnings
+
+
+PY3 = sys.version_info[0] >= 3
+
+if PY3:
+    basestring = unicode = str
+    iteritems = operator.methodcaller("items")
+else:
+    from itertools import izip as zip
+    iteritems = operator.methodcaller("iteritems")
+
+
+def _uniq(container):
+    """
+    Check if all of a container's elements are unique.
+
+    Successively tries first to rely that the elements are hashable, then
+    falls back on them being sortable, and finally falls back on brute
+    force.
+
+    """
+
+    try:
+        return len(set(container)) == len(container)
+    except TypeError:
+        try:
+            sort = sorted(container)
+            sliced = itertools.islice(container, 1, None)
+            for i, j in zip(container, sliced):
+                if i == j:
+                    return False
+        except (NotImplementedError, TypeError):
+            seen = []
+            for e in container:
+                if e in seen:
+                    return False
+                seen.append(e)
+    return True
+
+
+__version__ = "0.5"
+
+
+DRAFT_3 = {
+    "$schema" : "http://json-schema.org/draft-03/schema#",
+    "id" : "http://json-schema.org/draft-03/schema#",
+    "type" : "object",
+
+    "properties" : {
+        "type" : {
+            "type" : ["string", "array"],
+            "items" : {"type" : ["string", {"$ref" : "#"}]},
+            "uniqueItems" : True,
+            "default" : "any"
+        },
+        "properties" : {
+            "type" : "object",
+            "additionalProperties" : {"$ref" : "#", "type": "object"},
+            "default" : {}
+        },
+        "patternProperties" : {
+            "type" : "object",
+            "additionalProperties" : {"$ref" : "#"},
+            "default" : {}
+        },
+        "additionalProperties" : {
+            "type" : [{"$ref" : "#"}, "boolean"], "default" : {}
+        },
+        "items" : {
+            "type" : [{"$ref" : "#"}, "array"],
+            "items" : {"$ref" : "#"},
+            "default" : {}
+        },
+        "additionalItems" : {
+            "type" : [{"$ref" : "#"}, "boolean"], "default" : {}
+        },
+        "required" : {"type" : "boolean", "default" : False},
+        "dependencies" : {
+            "type" : ["string", "array", "object"],
+            "additionalProperties" : {
+                "type" : ["string", "array", {"$ref" : "#"}],
+                "items" : {"type" : "string"}
+            },
+            "default" : {}
+        },
+        "minimum" : {"type" : "number"},
+        "maximum" : {"type" : "number"},
+        "exclusiveMinimum" : {"type" : "boolean", "default" : False},
+        "exclusiveMaximum" : {"type" : "boolean", "default" : False},
+        "minItems" : {"type" : "integer", "minimum" : 0, "default" : 0},
+        "maxItems" : {"type" : "integer", "minimum" : 0},
+        "uniqueItems" : {"type" : "boolean", "default" : False},
+        "pattern" : {"type" : "string", "format" : "regex"},
+        "minLength" : {"type" : "integer", "minimum" : 0, "default" : 0},
+        "maxLength" : {"type" : "integer"},
+        "enum" : {"type" : "array", "minItems" : 1, "uniqueItems" : True},
+        "default" : {"type" : "any"},
+        "title" : {"type" : "string"},
+        "description" : {"type" : "string"},
+        "format" : {"type" : "string"},
+        "maxDecimal" : {"type" : "number", "minimum" : 0},
+        "divisibleBy" : {
+            "type" : "number",
+            "minimum" : 0,
+            "exclusiveMinimum" : True,
+            "default" : 1
+        },
+        "disallow" : {
+            "type" : ["string", "array"],
+            "items" : {"type" : ["string", {"$ref" : "#"}]},
+            "uniqueItems" : True
+        },
+        "extends" : {
+            "type" : [{"$ref" : "#"}, "array"],
+            "items" : {"$ref" : "#"},
+            "default" : {}
+        },
+        "id" : {"type" : "string", "format" : "uri"},
+        "$ref" : {"type" : "string", "format" : "uri"},
+        "$schema" : {"type" : "string", "format" : "uri"},
+    },
+    "dependencies" : {
+        "exclusiveMinimum" : "minimum", "exclusiveMaximum" : "maximum"
+    },
+}
+
+EPSILON = 10 ** -15
+
+
+class SchemaError(Exception):
+    """
+    The provided schema is malformed.
+
+    The same attributes exist for ``SchemaError``s as for ``ValidationError``s.
+
+    """
+
+    validator = None
+
+    def __init__(self, message):
+        super(SchemaError, self).__init__(message)
+        self.message = message
+        self.path = []
+
+
+class ValidationError(Exception):
+    """
+    The instance didn't properly validate with the provided schema.
+
+    Relevant attributes are:
+        * ``message`` : a human readable message explaining the error
+        * ``path`` : a list containing the path to the offending element (or []
+                     if the error happened globally) in *reverse* order (i.e.
+                     deepest index first).
+
+    """
+
+    # the failing validator will be set externally at whatever recursion level
+    # is immediately above the validation failure
+    validator = None
+
+    def __init__(self, message):
+        super(ValidationError, self).__init__(message)
+        self.message = message
+
+        # Any validator that recurses must append to the ValidationError's
+        # path (e.g., properties and items)
+        self.path = []
+
+
+class Validator(object):
+    """
+    A JSON Schema validator.
+
+    """
+
+    DEFAULT_TYPES = {
+        "array" : list, "boolean" : bool, "integer" : int, "null" : type(None),
+        "number" : (int, float), "object" : dict, "string" : basestring,
+    }
+
+    def __init__(
+        self, version=DRAFT_3, unknown_type="skip",
+        unknown_property="skip", types=(),
+    ):
+        """
+        Initialize a Validator.
+
+        ``version`` specifies which version of the JSON Schema specification to
+        validate with. Currently only draft-03 is supported (and is the
+        default).
+
+        ``unknown_type`` and ``unknown_property`` control what to do when an
+        unknown type (resp. property) is encountered. By default, the
+        metaschema is respected (which e.g. for draft 3 allows a schema to have
+        additional properties), but if for some reason you want to modify this
+        behavior, you can do so without needing to modify the metaschema by
+        passing ``"error"`` or ``"warn"`` to these arguments.
+
+        ``types`` is a mapping (or iterable of 2-tuples) containing additional
+        types or alternate types to verify via the 'type' property. For
+        instance, the default types for the 'number' JSON Schema type are
+        ``int`` and ``float``.  To override this behavior (e.g. for also
+        allowing ``decimal.Decimal``), pass ``types={"number" : (int, float,
+        decimal.Decimal)} *including* the default types if so desired, which
+        are fairly obvious but can be accessed via ``Validator.DEFAULT_TYPES``
+        if necessary.
+
+        """
+
+        self._unknown_type = unknown_type
+        self._unknown_property = unknown_property
+        self._version = version
+
+        self._types = dict(self.DEFAULT_TYPES)
+        self._types.update(types)
+        self._types["any"] = tuple(self._types.values())
+
+    def is_type(self, instance, type):
+        """
+        Check if an ``instance`` is of the provided ``type``.
+
+        """
+
+        py_type = self._types.get(type)
+
+        if py_type is None:
+            return self.schema_error(
+                self._unknown_type, "%r is not a known type" % (type,)
+            )
+
+        # the only thing we're careful about here is evading bool inheriting
+        # from int, so let's be even dirtier than usual
+
+        elif (
+            # it's not a bool, so no worries
+            not isinstance(instance, bool) or
+
+            # it is a bool, but we're checking for a bool, so no worries
+            (
+                py_type is bool or
+                isinstance(py_type, tuple) and bool in py_type
+            )
+
+        ):
+            return isinstance(instance, py_type)
+
+    def schema_error(self, level, msg):
+        if level == "skip":
+            return
+        elif level == "warn":
+            warnings.warn(msg)
+        else:
+            raise SchemaError(msg)
+
+    def is_valid(self, instance, schema, meta_validate=True):
+        """
+        Check if the ``instance`` is valid under the ``schema``.
+
+        Returns a bool indicating whether validation succeeded.
+
+        """
+
+        error = next(self.iter_errors(instance, schema, meta_validate), None)
+        return error is None
+
+    def iter_errors(self, instance, schema, meta_validate=True):
+        """
+        Lazily yield each of the errors in the given ``instance``.
+
+        If you are unsure whether your schema itself is valid,
+        ``meta_validate`` will first validate that the schema is valid before
+        attempting to validate the instance. ``meta_validate`` is ``True`` by
+        default, since setting it to ``False`` can lead to confusing error
+        messages with an invalid schema. If you're sure your schema is in fact
+        valid, or don't care, feel free to set this to ``False``. The meta
+        validation will be done using the appropriate ``version``.
+
+        """
+
+        if meta_validate:
+            for error in self.iter_errors(
+                schema, self._version, meta_validate=False
+            ):
+                s = SchemaError(error.message)
+                s.path = error.path
+                s.validator = error.validator
+                # I think we're safer raising these always, not yielding them
+                raise s
+
+        for k, v in iteritems(schema):
+            validator = getattr(self, "validate_%s" % (k.lstrip("$"),), None)
+
+            if validator is None:
+                errors = self.unknown_property(k, instance, schema)
+            else:
+                errors = validator(v, instance, schema)
+
+            for error in errors or ():
+                # if the validator hasn't already been set (due to recursion)
+                # make sure to set it
+                error.validator = error.validator or k
+                yield error
+
+    def validate(self, *args, **kwargs):
+        """
+        Validate an ``instance`` under the given ``schema``.
+
+        """
+
+        for error in self.iter_errors(*args, **kwargs):
+            raise error
+
+    def unknown_property(self, property, instance, schema):
+        self.schema_error(
+            self._unknown_property,
+            "%r is not a known schema property" % (property,)
+        )
+
+    def validate_type(self, types, instance, schema):
+        types = _list(types)
+
+        for type in types:
+            # Ouch. Brain hurts. Two paths here, either we have a schema, then
+            # check if the instance is valid under it
+            if ((
+                self.is_type(type, "object") and
+                self.is_valid(instance, type)
+
+            # Or we have a type as a string, just check if the instance is that
+            # type. Also, HACK: we can reach the `or` here if skip_types is
+            # something other than error. If so, bail out.
+
+            ) or (
+                self.is_type(type, "string") and
+                (self.is_type(instance, type) or type not in self._types)
+            )):
+                return
+        else:
+            yield ValidationError(
+                "%r is not of type %r" % (instance, _delist(types))
+            )
+
+    def validate_properties(self, properties, instance, schema):
+        if not self.is_type(instance, "object"):
+            return
+
+        for property, subschema in iteritems(properties):
+            if property in instance:
+                dependencies = _list(subschema.get("dependencies", []))
+                if self.is_type(dependencies, "object"):
+                    for error in self.iter_errors(
+                        instance, dependencies, meta_validate=False
+                    ):
+                        yield error
+                else:
+                    for dependency in dependencies:
+                        if dependency not in instance:
+                            yield ValidationError(
+                            "%r is a dependency of %r" % (dependency, property)
+                            )
+
+                for error in self.iter_errors(
+                    instance[property], subschema, meta_validate=False
+                ):
+                    error.path.append(property)
+                    yield error
+            elif subschema.get("required", False):
+                error = ValidationError(
+                    "%r is a required property" % (property,)
+                )
+                error.path.append(property)
+                error.validator = "required"
+                yield error
+
+    def validate_patternProperties(self, patternProperties, instance, schema):
+        for pattern, subschema in iteritems(patternProperties):
+            for k, v in iteritems(instance):
+                if re.match(pattern, k):
+                    for error in self.iter_errors(
+                        v, subschema, meta_validate=False
+                    ):
+                        yield error
+
+    def validate_additionalProperties(self, aP, instance, schema):
+        if not self.is_type(instance, "object"):
+            return
+
+        # no viewkeys in <2.7, and pypy seems to fail on vk - vk anyhow, so...
+        extras = set(instance) - set(schema.get("properties", {}))
+
+        if self.is_type(aP, "object"):
+            for extra in extras:
+                for error in self.iter_errors(
+                    instance[extra], aP, meta_validate=False
+                ):
+                    yield error
+        elif not aP and extras:
+            error = "Additional properties are not allowed (%s %s unexpected)"
+            yield ValidationError(error % _extras_msg(extras))
+
+    def validate_items(self, items, instance, schema):
+        if not self.is_type(instance, "array"):
+            return
+
+        if self.is_type(items, "object"):
+            for index, item in enumerate(instance):
+                for error in self.iter_errors(
+                    item, items, meta_validate=False
+                ):
+                    error.path.append(index)
+                    yield error
+        else:
+            for (index, item), subschema in zip(enumerate(instance), items):
+                for error in self.iter_errors(
+                    item, subschema, meta_validate=False
+                ):
+                    error.path.append(index)
+                    yield error
+
+    def validate_additionalItems(self, aI, instance, schema):
+        if not self.is_type(instance, "array"):
+            return
+
+        if self.is_type(aI, "object"):
+            for item in instance[len(schema):]:
+                for error in self.iter_errors(item, aI, meta_validate=False):
+                    yield error
+        elif not aI and len(instance) > len(schema.get("items", [])):
+            error = "Additional items are not allowed (%s %s unexpected)"
+            yield ValidationError(
+                error % _extras_msg(instance[len(schema) - 1:])
+            )
+
+    def validate_minimum(self, minimum, instance, schema):
+        if not self.is_type(instance, "number"):
+            return
+
+        instance = float(instance)
+        if schema.get("exclusiveMinimum", False):
+            failed = instance <= minimum
+            cmp = "less than or equal to"
+        else:
+            failed = instance < minimum
+            cmp = "less than"
+
+        if failed:
+            yield ValidationError(
+                "%r is %s the minimum of %r" % (instance, cmp, minimum)
+            )
+
+    def validate_maximum(self, maximum, instance, schema):
+        if not self.is_type(instance, "number"):
+            return
+
+        instance = float(instance)
+        if schema.get("exclusiveMaximum", False):
+            failed = instance >= maximum
+            cmp = "greater than or equal to"
+        else:
+            failed = instance > maximum
+            cmp = "greater than"
+
+        if failed:
+            yield ValidationError(
+                "%r is %s the maximum of %r" % (instance, cmp, maximum)
+            )
+
+    def validate_minItems(self, mI, instance, schema):
+        if self.is_type(instance, "array") and len(instance) < mI:
+            yield ValidationError("%r is too short" % (instance,))
+
+    def validate_maxItems(self, mI, instance, schema):
+        if self.is_type(instance, "array") and len(instance) > mI:
+            yield ValidationError("%r is too long" % (instance,))
+
+    def validate_uniqueItems(self, uI, instance, schema):
+        if uI and self.is_type(instance, "array") and not _uniq(instance):
+            yield ValidationError("%r has non-unique elements" % instance)
+
+    def validate_pattern(self, patrn, instance, schema):
+        if self.is_type(instance, "string") and not re.match(patrn, instance):
+            yield ValidationError("%r does not match %r" % (instance, patrn))
+
+    def validate_minLength(self, mL, instance, schema):
+        if self.is_type(instance, "string") and len(instance) < mL:
+            yield ValidationError("%r is too short" % (instance,))
+
+    def validate_maxLength(self, mL, instance, schema):
+        if self.is_type(instance, "string") and len(instance) > mL:
+            yield ValidationError("%r is too long" % (instance,))
+
+    def validate_enum(self, enums, instance, schema):
+        if instance not in enums:
+            yield ValidationError("%r is not one of %r" % (instance, enums))
+
+    def validate_divisibleBy(self, dB, instance, schema):
+        if not self.is_type(instance, "number"):
+            return
+
+        if isinstance(dB, float):
+            mod = instance % dB
+            failed = (mod > EPSILON) and (dB - mod) > EPSILON
+        else:
+            failed = instance % dB
+
+        if failed:
+            yield ValidationError("%r is not divisible by %r" % (instance, dB))
+
+    def validate_disallow(self, disallow, instance, schema):
+        for disallowed in _list(disallow):
+            if self.is_valid(instance, {"type" : [disallowed]}):
+                yield ValidationError(
+                    "%r is disallowed for %r" % (disallowed, instance)
+                )
+
+    def validate_extends(self, extends, instance, schema):
+        if self.is_type(extends, "object"):
+            extends = [extends]
+        for subschema in extends:
+            for error in self.iter_errors(
+                instance, subschema, meta_validate=False
+            ):
+                yield error
+
+
+for no_op in [                                  # handled in:
+    "dependencies", "required",                 # properties
+    "exclusiveMinimum", "exclusiveMaximum",     # min*/max*
+    "default", "description", "format", "id",   # no validation needed
+    "links", "name", "title",
+    "ref", "schema",                            # not yet supported
+]:
+    setattr(Validator, "validate_" + no_op, lambda *args, **kwargs : None)
+
+
+class ErrorTree(object):
+    """
+    ErrorTrees make it easier to check which validations failed.
+
+    """
+
+    def __init__(self, errors=()):
+        self.errors = {}
+        self._contents = collections.defaultdict(self.__class__)
+
+        for error in errors:
+            container = self
+            for element in reversed(error.path):
+                container = container[element]
+            container.errors[error.validator] = error
+
+    def __contains__(self, k):
+        return k in self._contents
+
+    def __getitem__(self, k):
+        return self._contents[k]
+
+    def __setitem__(self, k, v):
+        self._contents[k] = v
+
+    def __iter__(self):
+        return iter(self._contents)
+
+    def __len__(self):
+        child_errors = sum(len(tree) for _, tree in iteritems(self._contents))
+        return len(self.errors) + child_errors
+
+    def __repr__(self):
+        return "<%s (%s errors)>" % (self.__class__.__name__, len(self))
+
+
+def _extras_msg(extras):
+    """
+    Create an error message for extra items or properties.
+
+    """
+
+    if len(extras) == 1:
+        verb = "was"
+    else:
+        verb = "were"
+    return ", ".join(repr(extra) for extra in extras), verb
+
+
+def _list(thing):
+    """
+    Wrap ``thing`` in a list if it's a single str.
+
+    Otherwise, return it unchanged.
+
+    """
+
+    if isinstance(thing, basestring):
+        return [thing]
+    return thing
+
+
+def _delist(thing):
+    """
+    Unwrap ``thing`` to a single element if its a single str in a list.
+
+    Otherwise, return it unchanged.
+
+    """
+
+    if (
+        isinstance(thing, list) and
+        len(thing) == 1
+        and isinstance(thing[0], basestring)
+    ):
+        return thing[0]
+    return thing
+
+
+def validate(
+    instance, schema, meta_validate=True, cls=Validator, *args, **kwargs
+):
+    """
+    Validate an ``instance`` under the given ``schema``.
+
+    By default, the :class:`Validator` class from this module is used to
+    perform the validation. To use another validator, pass it into the ``cls``
+    argument.
+
+    Any other provided positional and keyword arguments will be provided to the
+    ``cls``. See the :class:`Validator` class' docstring for details on the
+    arguments it accepts.
+
+    """
+
+    validator = cls(*args, **kwargs)
+    validator.validate(instance, schema, meta_validate=meta_validate)
diff --git a/IPython/nbformat/v3/v3.withref.json b/IPython/nbformat/v3/v3.withref.json
index 5641a2b..b16036f 100644
--- a/IPython/nbformat/v3/v3.withref.json
+++ b/IPython/nbformat/v3/v3.withref.json
@@ -158,7 +158,7 @@
         },
         "prompt_number": {
             "type": ["integer","null"],
-            "required": true
+            "required": true,
             "minimum": 0
         },
         "language": {
diff --git a/IPython/nbformat/v3/validator.py b/IPython/nbformat/v3/validator.py
index d739edf..06eac96 100755
--- a/IPython/nbformat/v3/validator.py
+++ b/IPython/nbformat/v3/validator.py
@@ -1,8 +1,8 @@
 #!/usr/bin/env python
 # -*- coding: utf8 -*-
 
-from jsonschema import  Validator, validate, ValidationError
-import jsonpointer
+from IPython.external.jsonschema import  Validator, validate, ValidationError
+import IPython.external.jsonpointer as jsonpointer
 import argparse
 import traceback
 import json