Bug 1466211 - Vendor voluptuous via |mach vendor python|; r?ahal draft
authorDave Hunt <dhunt@mozilla.com>
Fri, 01 Jun 2018 18:56:23 +0100
changeset 809501 c6d1864612830e17d424d36f41d3c857599e57b7
parent 809500 7eb99737c62c3f2edbc277a9a454e6a8aa5b828b
child 809502 c4a4c95db4ad57d2a50122b295136e0d0ba4cdca
push id113698
push userbmo:dave.hunt@gmail.com
push dateFri, 22 Jun 2018 09:54:34 +0000
reviewersahal
bugs1466211
milestone62.0a1
Bug 1466211 - Vendor voluptuous via |mach vendor python|; r?ahal MozReview-Commit-ID: 21q9i0lStU3
Pipfile
Pipfile.lock
third_party/python/voluptuous/CHANGELOG.md
third_party/python/voluptuous/COPYING
third_party/python/voluptuous/MANIFEST.in
third_party/python/voluptuous/PKG-INFO
third_party/python/voluptuous/README.md
third_party/python/voluptuous/README.rst
third_party/python/voluptuous/setup.cfg
third_party/python/voluptuous/setup.py
third_party/python/voluptuous/voluptuous/tests/__init__.py
third_party/python/voluptuous/voluptuous/tests/tests.md
third_party/python/voluptuous/voluptuous/tests/tests.py
--- a/Pipfile
+++ b/Pipfile
@@ -10,12 +10,13 @@ pipenv = "==2018.5.18"
 virtualenv = "==15.2.0"
 six = "==1.10.0"
 attrs = "==18.1.0"
 pytest = "==3.2.5"
 jsmin = "==2.1.0"
 python-hglib = "==2.4"
 requests = "==2.9.1"
 json-e = "==2.5.0"
+voluptuous = "==0.10.5"
 
 
 [requires]
 python_version = "2.7"
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,12 +1,12 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "04222e29219efa63e83dca85d6692b34f362ecbb0ea154c2e309ba5df708e7b8"
+            "sha256": "af4e239c88ce3d74e2e3dd7d352c3e8a203ce476c7369b2a4dc0eea7114996ba"
         },
         "pipfile-spec": 6,
         "requires": {
             "python_version": "2.7"
         },
         "sources": [
             {
                 "name": "pypi",
@@ -100,12 +100,19 @@
             "version": "==15.2.0"
         },
         "virtualenv-clone": {
             "hashes": [
                 "sha256:4507071d81013fd03ea9930ec26bc8648b997927a11fa80e8ee81198b57e0ac7",
                 "sha256:b5cfe535d14dc68dfc1d1bb4ac1209ea28235b91156e2bba8e250d291c3fb4f8"
             ],
             "version": "==0.3.0"
+        },
+        "voluptuous": {
+            "hashes": [
+                "sha256:7a7466f8dc3666a292d186d1d871a47bf2120836ccb900d5ba904674957a2396"
+            ],
+            "index": "pypi",
+            "version": "==0.10.5"
         }
     },
     "develop": {}
 }
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/CHANGELOG.md
@@ -0,0 +1,75 @@
+# Changelog
+
+## [Unreleased]
+
+## [0.10.5]
+
+- [#278](https://github.com/alecthomas/voluptuous/pull/278): Unicode
+translation to python 2 issue fixed.
+
+## [0.10.2]
+
+**Changes**:
+
+- [#195](https://github.com/alecthomas/voluptuous/pull/195):
+  `Range` raises `RangeInvalid` when testing `math.nan`.
+- [#215](https://github.com/alecthomas/voluptuous/pull/215):
+  `{}` and `[]` now always evaluate as is, instead of as any dict or any list.
+  To specify a free-form list, use `list` instead of `[]`. To specify a
+  free-form dict, use `dict` instead of `Schema({}, extra=ALLOW_EXTRA)`.
+- [#224](https://github.com/alecthomas/voluptuous/pull/224):
+  Change the encoding of keys in error messages from Unicode to UTF-8.
+
+**New**:
+
+- [#185](https://github.com/alecthomas/voluptuous/pull/185):
+  Add argument validation decorator.
+- [#199](https://github.com/alecthomas/voluptuous/pull/199):
+  Add `Unordered`.
+- [#200](https://github.com/alecthomas/voluptuous/pull/200):
+  Add `Equal`.
+- [#207](https://github.com/alecthomas/voluptuous/pull/207):
+  Add `Number`.
+- [#210](https://github.com/alecthomas/voluptuous/pull/210):
+  Add `Schema` equality check.
+- [#212](https://github.com/alecthomas/voluptuous/pull/212):
+  Add `coveralls`.
+- [#227](https://github.com/alecthomas/voluptuous/pull/227):
+  Improve `Marker` management in `Schema`.
+- [#232](https://github.com/alecthomas/voluptuous/pull/232):
+  Add `Maybe`.
+- [#234](https://github.com/alecthomas/voluptuous/pull/234):
+  Add `Date`.
+- [#236](https://github.com/alecthomas/voluptuous/pull/236), [#237](https://github.com/alecthomas/voluptuous/pull/237), and [#238](https://github.com/alecthomas/voluptuous/pull/238):
+  Add script for updating `gh-pages`.
+- [#256](https://github.com/alecthomas/voluptuous/pull/256):
+  Add support for `OrderedDict` validation.
+- [#258](https://github.com/alecthomas/voluptuous/pull/258):
+  Add `Contains`.
+
+**Fixes**:
+
+- [#197](https://github.com/alecthomas/voluptuous/pull/197):
+  `ExactSequence` checks sequences are the same length.
+- [#201](https://github.com/alecthomas/voluptuous/pull/201):
+  Empty lists are evaluated as is.
+- [#205](https://github.com/alecthomas/voluptuous/pull/205):
+  Filepath validators correctly handle `None`.
+- [#206](https://github.com/alecthomas/voluptuous/pull/206):
+  Handle non-subscriptable types in `humanize_error`.
+- [#231](https://github.com/alecthomas/voluptuous/pull/231):
+  Validate `namedtuple` as a `tuple`.
+- [#235](https://github.com/alecthomas/voluptuous/pull/235):
+  Update docstring.
+- [#249](https://github.com/alecthomas/voluptuous/pull/249):
+  Update documentation.
+- [#262](https://github.com/alecthomas/voluptuous/pull/262):
+  Fix a performance issue of exponential complexity where all of the dict keys were matched against all keys in the schema.
+  This resulted in O(n*m) complexity where n is the number of keys in the dict being validated and m is the number of keys in the schema.
+  The fix ensures that each key in the dict is matched against the relevant schema keys only. It now works in O(n).
+- [#266](https://github.com/alecthomas/voluptuous/pull/266):
+  Remove setuptools as a dependency.
+
+## 0.9.3 (2016-08-03)
+
+Changelog not kept for 0.9.3 and earlier releases.
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/COPYING
@@ -0,0 +1,25 @@
+Copyright (c) 2010, Alec Thomas
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ - Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+ - 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.
+ - Neither the name of SwapOff.org nor the names of its contributors may
+   be used to endorse or promote products derived from this software without
+   specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/MANIFEST.in
@@ -0,0 +1,4 @@
+include *.md
+include COPYING
+include voluptuous/tests/*.py
+include voluptuous/tests/*.md
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/PKG-INFO
@@ -0,0 +1,666 @@
+Metadata-Version: 1.1
+Name: voluptuous
+Version: 0.10.5
+Summary: Voluptuous is a Python data validation library
+Home-page: https://github.com/alecthomas/voluptuous
+Author: Alec Thomas
+Author-email: alec@swapoff.org
+License: BSD
+Download-URL: https://pypi.python.org/pypi/voluptuous
+Description: Voluptuous is a Python data validation library
+        ==============================================
+        
+        |Build Status| |Coverage Status| |Gitter chat|
+        
+        Voluptuous, *despite* the name, is a Python data validation library. It
+        is primarily intended for validating data coming into Python as JSON,
+        YAML, etc.
+        
+        It has three goals:
+        
+        1. Simplicity.
+        2. Support for complex data structures.
+        3. Provide useful error messages.
+        
+        Contact
+        -------
+        
+        Voluptuous now has a mailing list! Send a mail to
+        `<voluptuous@librelist.com> <mailto:voluptuous@librelist.com>`__ to
+        subscribe. Instructions will follow.
+        
+        You can also contact me directly via `email <mailto:alec@swapoff.org>`__
+        or `Twitter <https://twitter.com/alecthomas>`__.
+        
+        To file a bug, create a `new
+        issue <https://github.com/alecthomas/voluptuous/issues/new>`__ on GitHub
+        with a short example of how to replicate the issue.
+        
+        Documentation
+        -------------
+        
+        The documentation is provided [here]
+        (http://alecthomas.github.io/voluptuous/).
+        
+        Changelog
+        ---------
+        
+        See `CHANGELOG.md <CHANGELOG.md>`__.
+        
+        Show me an example
+        ------------------
+        
+        Twitter's `user search
+        API <https://dev.twitter.com/rest/reference/get/users/search>`__ accepts
+        query URLs like:
+        
+        ::
+        
+            $ curl 'http://api.twitter.com/1.1/users/search.json?q=python&per_page=20&page=1'
+        
+        To validate this we might use a schema like:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import Schema
+            >>> schema = Schema({
+            ...   'q': str,
+            ...   'per_page': int,
+            ...   'page': int,
+            ... })
+        
+        This schema very succinctly and roughly describes the data required by
+        the API, and will work fine. But it has a few problems. Firstly, it
+        doesn't fully express the constraints of the API. According to the API,
+        ``per_page`` should be restricted to at most 20, defaulting to 5, for
+        example. To describe the semantics of the API more accurately, our
+        schema will need to be more thoroughly defined:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import Required, All, Length, Range
+            >>> schema = Schema({
+            ...   Required('q'): All(str, Length(min=1)),
+            ...   Required('per_page', default=5): All(int, Range(min=1, max=20)),
+            ...   'page': All(int, Range(min=0)),
+            ... })
+        
+        This schema fully enforces the interface defined in Twitter's
+        documentation, and goes a little further for completeness.
+        
+        "q" is required:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import MultipleInvalid, Invalid
+            >>> try:
+            ...   schema({})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "required key not provided @ data['q']"
+            True
+        
+        ...must be a string:
+        
+        .. code:: pycon
+        
+            >>> try:
+            ...   schema({'q': 123})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "expected str for dictionary value @ data['q']"
+            True
+        
+        ...and must be at least one character in length:
+        
+        .. code:: pycon
+        
+            >>> try:
+            ...   schema({'q': ''})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "length of value must be at least 1 for dictionary value @ data['q']"
+            True
+            >>> schema({'q': '#topic'}) == {'q': '#topic', 'per_page': 5}
+            True
+        
+        "per\_page" is a positive integer no greater than 20:
+        
+        .. code:: pycon
+        
+            >>> try:
+            ...   schema({'q': '#topic', 'per_page': 900})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "value must be at most 20 for dictionary value @ data['per_page']"
+            True
+            >>> try:
+            ...   schema({'q': '#topic', 'per_page': -10})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "value must be at least 1 for dictionary value @ data['per_page']"
+            True
+        
+        "page" is an integer >= 0:
+        
+        .. code:: pycon
+        
+            >>> try:
+            ...   schema({'q': '#topic', 'per_page': 'one'})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc)
+            "expected int for dictionary value @ data['per_page']"
+            >>> schema({'q': '#topic', 'page': 1}) == {'q': '#topic', 'page': 1, 'per_page': 5}
+            True
+        
+        Defining schemas
+        ----------------
+        
+        Schemas are nested data structures consisting of dictionaries, lists,
+        scalars and *validators*. Each node in the input schema is pattern
+        matched against corresponding nodes in the input data.
+        
+        Literals
+        ~~~~~~~~
+        
+        Literals in the schema are matched using normal equality checks:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema(1)
+            >>> schema(1)
+            1
+            >>> schema = Schema('a string')
+            >>> schema('a string')
+            'a string'
+        
+        Types
+        ~~~~~
+        
+        Types in the schema are matched by checking if the corresponding value
+        is an instance of the type:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema(int)
+            >>> schema(1)
+            1
+            >>> try:
+            ...   schema('one')
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "expected int"
+            True
+        
+        URL's
+        ~~~~~
+        
+        URL's in the schema are matched by using ``urlparse`` library.
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import Url
+            >>> schema = Schema(Url())
+            >>> schema('http://w3.org')
+            'http://w3.org'
+            >>> try:
+            ...   schema('one')
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "expected a URL"
+            True
+        
+        Lists
+        ~~~~~
+        
+        Lists in the schema are treated as a set of valid values. Each element
+        in the schema list is compared to each value in the input data:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema([1, 'a', 'string'])
+            >>> schema([1])
+            [1]
+            >>> schema([1, 1, 1])
+            [1, 1, 1]
+            >>> schema(['a', 1, 'string', 1, 'string'])
+            ['a', 1, 'string', 1, 'string']
+        
+        However, an empty list (``[]``) is treated as is. If you want to specify
+        a list that can contain anything, specify it as ``list``:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema([])
+            >>> try:
+            ...   schema([1])
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "not a valid value"
+            True
+            >>> schema([])
+            []
+            >>> schema = Schema(list)
+            >>> schema([])
+            []
+            >>> schema([1, 2])
+            [1, 2]
+        
+        Validation functions
+        ~~~~~~~~~~~~~~~~~~~~
+        
+        Validators are simple callables that raise an ``Invalid`` exception when
+        they encounter invalid data. The criteria for determining validity is
+        entirely up to the implementation; it may check that a value is a valid
+        username with ``pwd.getpwnam()``, it may check that a value is of a
+        specific type, and so on.
+        
+        The simplest kind of validator is a Python function that raises
+        ValueError when its argument is invalid. Conveniently, many builtin
+        Python functions have this property. Here's an example of a date
+        validator:
+        
+        .. code:: pycon
+        
+            >>> from datetime import datetime
+            >>> def Date(fmt='%Y-%m-%d'):
+            ...   return lambda v: datetime.strptime(v, fmt)
+        
+        .. code:: pycon
+        
+            >>> schema = Schema(Date())
+            >>> schema('2013-03-03')
+            datetime.datetime(2013, 3, 3, 0, 0)
+            >>> try:
+            ...   schema('2013-03')
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "not a valid value"
+            True
+        
+        In addition to simply determining if a value is valid, validators may
+        mutate the value into a valid form. An example of this is the
+        ``Coerce(type)`` function, which returns a function that coerces its
+        argument to the given type:
+        
+        .. code:: python
+        
+            def Coerce(type, msg=None):
+                """Coerce a value to a type.
+        
+                If the type constructor throws a ValueError, the value will be marked as
+                Invalid.
+                """
+                def f(v):
+                    try:
+                        return type(v)
+                    except ValueError:
+                        raise Invalid(msg or ('expected %s' % type.__name__))
+                return f
+        
+        This example also shows a common idiom where an optional human-readable
+        message can be provided. This can vastly improve the usefulness of the
+        resulting error messages.
+        
+        Dictionaries
+        ~~~~~~~~~~~~
+        
+        Each key-value pair in a schema dictionary is validated against each
+        key-value pair in the corresponding data dictionary:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema({1: 'one', 2: 'two'})
+            >>> schema({1: 'one'})
+            {1: 'one'}
+        
+        Extra dictionary keys
+        ^^^^^^^^^^^^^^^^^^^^^
+        
+        By default any additional keys in the data, not in the schema will
+        trigger exceptions:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema({2: 3})
+            >>> try:
+            ...   schema({1: 2, 2: 3})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "extra keys not allowed @ data[1]"
+            True
+        
+        This behaviour can be altered on a per-schema basis. To allow additional
+        keys use ``Schema(..., extra=ALLOW_EXTRA)``:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import ALLOW_EXTRA
+            >>> schema = Schema({2: 3}, extra=ALLOW_EXTRA)
+            >>> schema({1: 2, 2: 3})
+            {1: 2, 2: 3}
+        
+        To remove additional keys use ``Schema(..., extra=REMOVE_EXTRA)``:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import REMOVE_EXTRA
+            >>> schema = Schema({2: 3}, extra=REMOVE_EXTRA)
+            >>> schema({1: 2, 2: 3})
+            {2: 3}
+        
+        It can also be overridden per-dictionary by using the catch-all marker
+        token ``extra`` as a key:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import Extra
+            >>> schema = Schema({1: {Extra: object}})
+            >>> schema({1: {'foo': 'bar'}})
+            {1: {'foo': 'bar'}}
+        
+        However, an empty dict (``{}``) is treated as is. If you want to specify
+        a list that can contain anything, specify it as ``dict``:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema({}, extra=ALLOW_EXTRA)  # don't do this
+            >>> try:
+            ...   schema({'extra': 1})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "not a valid value"
+            True
+            >>> schema({})
+            {}
+            >>> schema = Schema(dict)  # do this instead
+            >>> schema({})
+            {}
+            >>> schema({'extra': 1})
+            {'extra': 1}
+        
+        Required dictionary keys
+        ^^^^^^^^^^^^^^^^^^^^^^^^
+        
+        By default, keys in the schema are not required to be in the data:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema({1: 2, 3: 4})
+            >>> schema({3: 4})
+            {3: 4}
+        
+        Similarly to how extra\_ keys work, this behaviour can be overridden
+        per-schema:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema({1: 2, 3: 4}, required=True)
+            >>> try:
+            ...   schema({3: 4})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "required key not provided @ data[1]"
+            True
+        
+        And per-key, with the marker token ``Required(key)``:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema({Required(1): 2, 3: 4})
+            >>> try:
+            ...   schema({3: 4})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "required key not provided @ data[1]"
+            True
+            >>> schema({1: 2})
+            {1: 2}
+        
+        Optional dictionary keys
+        ^^^^^^^^^^^^^^^^^^^^^^^^
+        
+        If a schema has ``required=True``, keys may be individually marked as
+        optional using the marker token ``Optional(key)``:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import Optional
+            >>> schema = Schema({1: 2, Optional(3): 4}, required=True)
+            >>> try:
+            ...   schema({})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "required key not provided @ data[1]"
+            True
+            >>> schema({1: 2})
+            {1: 2}
+            >>> try:
+            ...   schema({1: 2, 4: 5})
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "extra keys not allowed @ data[4]"
+            True
+        
+        .. code:: pycon
+        
+            >>> schema({1: 2, 3: 4})
+            {1: 2, 3: 4}
+        
+        Recursive schema
+        ~~~~~~~~~~~~~~~~
+        
+        There is no syntax to have a recursive schema. The best way to do it is
+        to have a wrapper like this:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import Schema, Any
+            >>> def s2(v):
+            ...     return s1(v)
+            ...
+            >>> s1 = Schema({"key": Any(s2, "value")})
+            >>> s1({"key": {"key": "value"}})
+            {'key': {'key': 'value'}}
+        
+        Extending an existing Schema
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        
+        Often it comes handy to have a base ``Schema`` that is extended with
+        more requirements. In that case you can use ``Schema.extend`` to create
+        a new ``Schema``:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import Schema
+            >>> person = Schema({'name': str})
+            >>> person_with_age = person.extend({'age': int})
+            >>> sorted(list(person_with_age.schema.keys()))
+            ['age', 'name']
+        
+        The original ``Schema`` remains unchanged.
+        
+        Objects
+        ~~~~~~~
+        
+        Each key-value pair in a schema dictionary is validated against each
+        attribute-value pair in the corresponding object:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import Object
+            >>> class Structure(object):
+            ...     def __init__(self, q=None):
+            ...         self.q = q
+            ...     def __repr__(self):
+            ...         return '<Structure(q={0.q!r})>'.format(self)
+            ...
+            >>> schema = Schema(Object({'q': 'one'}, cls=Structure))
+            >>> schema(Structure(q='one'))
+            <Structure(q='one')>
+        
+        Allow None values
+        ~~~~~~~~~~~~~~~~~
+        
+        To allow value to be None as well, use Any:
+        
+        .. code:: pycon
+        
+            >>> from voluptuous import Any
+        
+            >>> schema = Schema(Any(None, int))
+            >>> schema(None)
+            >>> schema(5)
+            5
+        
+        Error reporting
+        ---------------
+        
+        Validators must throw an ``Invalid`` exception if invalid data is passed
+        to them. All other exceptions are treated as errors in the validator and
+        will not be caught.
+        
+        Each ``Invalid`` exception has an associated ``path`` attribute
+        representing the path in the data structure to our currently validating
+        value, as well as an ``error_message`` attribute that contains the
+        message of the original exception. This is especially useful when you
+        want to catch ``Invalid`` exceptions and give some feedback to the user,
+        for instance in the context of an HTTP API.
+        
+        .. code:: pycon
+        
+            >>> def validate_email(email):
+            ...     """Validate email."""
+            ...     if not "@" in email:
+            ...         raise Invalid("This email is invalid.")
+            ...     return email
+            >>> schema = Schema({"email": validate_email})
+            >>> exc = None
+            >>> try:
+            ...     schema({"email": "whatever"})
+            ... except MultipleInvalid as e:
+            ...     exc = e
+            >>> str(exc)
+            "This email is invalid. for dictionary value @ data['email']"
+            >>> exc.path
+            ['email']
+            >>> exc.msg
+            'This email is invalid.'
+            >>> exc.error_message
+            'This email is invalid.'
+        
+        The ``path`` attribute is used during error reporting, but also during
+        matching to determine whether an error should be reported to the user or
+        if the next match should be attempted. This is determined by comparing
+        the depth of the path where the check is, to the depth of the path where
+        the error occurred. If the error is more than one level deeper, it is
+        reported.
+        
+        The upshot of this is that *matching is depth-first and fail-fast*.
+        
+        To illustrate this, here is an example schema:
+        
+        .. code:: pycon
+        
+            >>> schema = Schema([[2, 3], 6])
+        
+        Each value in the top-level list is matched depth-first in-order. Given
+        input data of ``[[6]]``, the inner list will match the first element of
+        the schema, but the literal ``6`` will not match any of the elements of
+        that list. This error will be reported back to the user immediately. No
+        backtracking is attempted:
+        
+        .. code:: pycon
+        
+            >>> try:
+            ...   schema([[6]])
+            ...   raise AssertionError('MultipleInvalid not raised')
+            ... except MultipleInvalid as e:
+            ...   exc = e
+            >>> str(exc) == "not a valid value @ data[0][0]"
+            True
+        
+        If we pass the data ``[6]``, the ``6`` is not a list type and so will
+        not recurse into the first element of the schema. Matching will continue
+        on to the second element in the schema, and succeed:
+        
+        .. code:: pycon
+        
+            >>> schema([6])
+            [6]
+        
+        Running tests.
+        --------------
+        
+        Voluptuous is using nosetests:
+        
+        ::
+        
+            $ nosetests
+        
+        Why use Voluptuous over another validation library?
+        ---------------------------------------------------
+        
+        **Validators are simple callables**
+            No need to subclass anything, just use a function.
+        **Errors are simple exceptions.**
+            A validator can just ``raise Invalid(msg)`` and expect the user to
+            get useful messages.
+        **Schemas are basic Python data structures.**
+            Should your data be a dictionary of integer keys to strings?
+            ``{int: str}`` does what you expect. List of integers, floats or
+            strings? ``[int, float, str]``.
+        **Designed from the ground up for validating more than just forms.**
+            Nested data structures are treated in the same way as any other
+            type. Need a list of dictionaries? ``[{}]``
+        **Consistency.**
+            Types in the schema are checked as types. Values are compared as
+            values. Callables are called to validate. Simple.
+        
+        Other libraries and inspirations
+        --------------------------------
+        
+        Voluptuous is heavily inspired by
+        `Validino <http://code.google.com/p/validino/>`__, and to a lesser
+        extent, `jsonvalidator <http://code.google.com/p/jsonvalidator/>`__ and
+        `json\_schema <http://blog.sendapatch.se/category/json_schema.html>`__.
+        
+        I greatly prefer the light-weight style promoted by these libraries to
+        the complexity of libraries like FormEncode.
+        
+        .. |Build Status| image:: https://travis-ci.org/alecthomas/voluptuous.png
+           :target: https://travis-ci.org/alecthomas/voluptuous
+        .. |Coverage Status| image:: https://coveralls.io/repos/github/alecthomas/voluptuous/badge.svg?branch=master
+           :target: https://coveralls.io/github/alecthomas/voluptuous?branch=master
+        .. |Gitter chat| image:: https://badges.gitter.im/alecthomas.png
+           :target: https://gitter.im/alecthomas/Lobby
+        
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.1
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/README.md
@@ -0,0 +1,649 @@
+# Voluptuous is a Python data validation library
+
+[![Build Status](https://travis-ci.org/alecthomas/voluptuous.png)](https://travis-ci.org/alecthomas/voluptuous)
+[![Coverage Status](https://coveralls.io/repos/github/alecthomas/voluptuous/badge.svg?branch=master)](https://coveralls.io/github/alecthomas/voluptuous?branch=master) [![Gitter chat](https://badges.gitter.im/alecthomas.png)](https://gitter.im/alecthomas/Lobby)
+
+Voluptuous, *despite* the name, is a Python data validation library. It
+is primarily intended for validating data coming into Python as JSON,
+YAML, etc.
+
+It has three goals:
+
+1.  Simplicity.
+2.  Support for complex data structures.
+3.  Provide useful error messages.
+
+## Contact
+
+Voluptuous now has a mailing list! Send a mail to
+[<voluptuous@librelist.com>](mailto:voluptuous@librelist.com) to subscribe. Instructions
+will follow.
+
+You can also contact me directly via [email](mailto:alec@swapoff.org) or
+[Twitter](https://twitter.com/alecthomas).
+
+To file a bug, create a [new issue](https://github.com/alecthomas/voluptuous/issues/new) on GitHub with a short example of how to replicate the issue.
+
+## Documentation
+
+The documentation is provided [here] (http://alecthomas.github.io/voluptuous/). 
+
+## Changelog
+
+See [CHANGELOG.md](CHANGELOG.md).
+
+## Show me an example
+
+Twitter's [user search API](https://dev.twitter.com/rest/reference/get/users/search) accepts
+query URLs like:
+
+```
+$ curl 'http://api.twitter.com/1.1/users/search.json?q=python&per_page=20&page=1'
+```
+
+To validate this we might use a schema like:
+
+```pycon
+>>> from voluptuous import Schema
+>>> schema = Schema({
+...   'q': str,
+...   'per_page': int,
+...   'page': int,
+... })
+
+```
+
+This schema very succinctly and roughly describes the data required by
+the API, and will work fine. But it has a few problems. Firstly, it
+doesn't fully express the constraints of the API. According to the API,
+`per_page` should be restricted to at most 20, defaulting to 5, for
+example. To describe the semantics of the API more accurately, our
+schema will need to be more thoroughly defined:
+
+```pycon
+>>> from voluptuous import Required, All, Length, Range
+>>> schema = Schema({
+...   Required('q'): All(str, Length(min=1)),
+...   Required('per_page', default=5): All(int, Range(min=1, max=20)),
+...   'page': All(int, Range(min=0)),
+... })
+
+```
+
+This schema fully enforces the interface defined in Twitter's
+documentation, and goes a little further for completeness.
+
+"q" is required:
+
+```pycon
+>>> from voluptuous import MultipleInvalid, Invalid
+>>> try:
+...   schema({})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "required key not provided @ data['q']"
+True
+
+```
+
+...must be a string:
+
+```pycon
+>>> try:
+...   schema({'q': 123})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "expected str for dictionary value @ data['q']"
+True
+
+```
+
+...and must be at least one character in length:
+
+```pycon
+>>> try:
+...   schema({'q': ''})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "length of value must be at least 1 for dictionary value @ data['q']"
+True
+>>> schema({'q': '#topic'}) == {'q': '#topic', 'per_page': 5}
+True
+
+```
+
+"per\_page" is a positive integer no greater than 20:
+
+```pycon
+>>> try:
+...   schema({'q': '#topic', 'per_page': 900})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "value must be at most 20 for dictionary value @ data['per_page']"
+True
+>>> try:
+...   schema({'q': '#topic', 'per_page': -10})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "value must be at least 1 for dictionary value @ data['per_page']"
+True
+
+```
+
+"page" is an integer \>= 0:
+
+```pycon
+>>> try:
+...   schema({'q': '#topic', 'per_page': 'one'})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc)
+"expected int for dictionary value @ data['per_page']"
+>>> schema({'q': '#topic', 'page': 1}) == {'q': '#topic', 'page': 1, 'per_page': 5}
+True
+
+```
+
+## Defining schemas
+
+Schemas are nested data structures consisting of dictionaries, lists,
+scalars and *validators*. Each node in the input schema is pattern
+matched against corresponding nodes in the input data.
+
+### Literals
+
+Literals in the schema are matched using normal equality checks:
+
+```pycon
+>>> schema = Schema(1)
+>>> schema(1)
+1
+>>> schema = Schema('a string')
+>>> schema('a string')
+'a string'
+
+```
+
+### Types
+
+Types in the schema are matched by checking if the corresponding value
+is an instance of the type:
+
+```pycon
+>>> schema = Schema(int)
+>>> schema(1)
+1
+>>> try:
+...   schema('one')
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "expected int"
+True
+
+```
+
+### URL's
+
+URL's in the schema are matched by using `urlparse` library.
+
+```pycon
+>>> from voluptuous import Url
+>>> schema = Schema(Url())
+>>> schema('http://w3.org')
+'http://w3.org'
+>>> try:
+...   schema('one')
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "expected a URL"
+True
+
+```
+
+### Lists
+
+Lists in the schema are treated as a set of valid values. Each element
+in the schema list is compared to each value in the input data:
+
+```pycon
+>>> schema = Schema([1, 'a', 'string'])
+>>> schema([1])
+[1]
+>>> schema([1, 1, 1])
+[1, 1, 1]
+>>> schema(['a', 1, 'string', 1, 'string'])
+['a', 1, 'string', 1, 'string']
+
+```
+
+However, an empty list (`[]`) is treated as is. If you want to specify a list that can
+contain anything, specify it as `list`:
+
+```pycon
+>>> schema = Schema([])
+>>> try:
+...   schema([1])
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "not a valid value"
+True
+>>> schema([])
+[]
+>>> schema = Schema(list)
+>>> schema([])
+[]
+>>> schema([1, 2])
+[1, 2]
+
+```
+
+### Validation functions
+
+Validators are simple callables that raise an `Invalid` exception when
+they encounter invalid data. The criteria for determining validity is
+entirely up to the implementation; it may check that a value is a valid
+username with `pwd.getpwnam()`, it may check that a value is of a
+specific type, and so on.
+
+The simplest kind of validator is a Python function that raises
+ValueError when its argument is invalid. Conveniently, many builtin
+Python functions have this property. Here's an example of a date
+validator:
+
+```pycon
+>>> from datetime import datetime
+>>> def Date(fmt='%Y-%m-%d'):
+...   return lambda v: datetime.strptime(v, fmt)
+
+```
+
+```pycon
+>>> schema = Schema(Date())
+>>> schema('2013-03-03')
+datetime.datetime(2013, 3, 3, 0, 0)
+>>> try:
+...   schema('2013-03')
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "not a valid value"
+True
+
+```
+
+In addition to simply determining if a value is valid, validators may
+mutate the value into a valid form. An example of this is the
+`Coerce(type)` function, which returns a function that coerces its
+argument to the given type:
+
+```python
+def Coerce(type, msg=None):
+    """Coerce a value to a type.
+
+    If the type constructor throws a ValueError, the value will be marked as
+    Invalid.
+    """
+    def f(v):
+        try:
+            return type(v)
+        except ValueError:
+            raise Invalid(msg or ('expected %s' % type.__name__))
+    return f
+
+```
+
+This example also shows a common idiom where an optional human-readable
+message can be provided. This can vastly improve the usefulness of the
+resulting error messages.
+
+### Dictionaries
+
+Each key-value pair in a schema dictionary is validated against each
+key-value pair in the corresponding data dictionary:
+
+```pycon
+>>> schema = Schema({1: 'one', 2: 'two'})
+>>> schema({1: 'one'})
+{1: 'one'}
+
+```
+
+#### Extra dictionary keys
+
+By default any additional keys in the data, not in the schema will
+trigger exceptions:
+
+```pycon
+>>> schema = Schema({2: 3})
+>>> try:
+...   schema({1: 2, 2: 3})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "extra keys not allowed @ data[1]"
+True
+
+```
+
+This behaviour can be altered on a per-schema basis. To allow
+additional keys use
+`Schema(..., extra=ALLOW_EXTRA)`:
+
+```pycon
+>>> from voluptuous import ALLOW_EXTRA
+>>> schema = Schema({2: 3}, extra=ALLOW_EXTRA)
+>>> schema({1: 2, 2: 3})
+{1: 2, 2: 3}
+
+```
+
+To remove additional keys use
+`Schema(..., extra=REMOVE_EXTRA)`:
+
+```pycon
+>>> from voluptuous import REMOVE_EXTRA
+>>> schema = Schema({2: 3}, extra=REMOVE_EXTRA)
+>>> schema({1: 2, 2: 3})
+{2: 3}
+
+```
+
+It can also be overridden per-dictionary by using the catch-all marker
+token `extra` as a key:
+
+```pycon
+>>> from voluptuous import Extra
+>>> schema = Schema({1: {Extra: object}})
+>>> schema({1: {'foo': 'bar'}})
+{1: {'foo': 'bar'}}
+
+```
+
+However, an empty dict (`{}`) is treated as is. If you want to specify a list that can
+contain anything, specify it as `dict`:
+
+```pycon
+>>> schema = Schema({}, extra=ALLOW_EXTRA)  # don't do this
+>>> try:
+...   schema({'extra': 1})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "not a valid value"
+True
+>>> schema({})
+{}
+>>> schema = Schema(dict)  # do this instead
+>>> schema({})
+{}
+>>> schema({'extra': 1})
+{'extra': 1}
+
+```
+
+#### Required dictionary keys
+
+By default, keys in the schema are not required to be in the data:
+
+```pycon
+>>> schema = Schema({1: 2, 3: 4})
+>>> schema({3: 4})
+{3: 4}
+
+```
+
+Similarly to how extra\_ keys work, this behaviour can be overridden
+per-schema:
+
+```pycon
+>>> schema = Schema({1: 2, 3: 4}, required=True)
+>>> try:
+...   schema({3: 4})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "required key not provided @ data[1]"
+True
+
+```
+
+And per-key, with the marker token `Required(key)`:
+
+```pycon
+>>> schema = Schema({Required(1): 2, 3: 4})
+>>> try:
+...   schema({3: 4})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "required key not provided @ data[1]"
+True
+>>> schema({1: 2})
+{1: 2}
+
+```
+
+#### Optional dictionary keys
+
+If a schema has `required=True`, keys may be individually marked as
+optional using the marker token `Optional(key)`:
+
+```pycon
+>>> from voluptuous import Optional
+>>> schema = Schema({1: 2, Optional(3): 4}, required=True)
+>>> try:
+...   schema({})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "required key not provided @ data[1]"
+True
+>>> schema({1: 2})
+{1: 2}
+>>> try:
+...   schema({1: 2, 4: 5})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "extra keys not allowed @ data[4]"
+True
+
+```
+
+```pycon
+>>> schema({1: 2, 3: 4})
+{1: 2, 3: 4}
+
+```
+
+### Recursive schema
+
+There is no syntax to have a recursive schema. The best way to do it is to have a wrapper like this:
+
+```pycon
+>>> from voluptuous import Schema, Any
+>>> def s2(v):
+...     return s1(v)
+...
+>>> s1 = Schema({"key": Any(s2, "value")})
+>>> s1({"key": {"key": "value"}})
+{'key': {'key': 'value'}}
+
+```
+
+### Extending an existing Schema
+
+Often it comes handy to have a base `Schema` that is extended with more
+requirements. In that case you can use `Schema.extend` to create a new
+`Schema`:
+
+```pycon
+>>> from voluptuous import Schema
+>>> person = Schema({'name': str})
+>>> person_with_age = person.extend({'age': int})
+>>> sorted(list(person_with_age.schema.keys()))
+['age', 'name']
+
+```
+
+The original `Schema` remains unchanged.
+
+### Objects
+
+Each key-value pair in a schema dictionary is validated against each
+attribute-value pair in the corresponding object:
+
+```pycon
+>>> from voluptuous import Object
+>>> class Structure(object):
+...     def __init__(self, q=None):
+...         self.q = q
+...     def __repr__(self):
+...         return '<Structure(q={0.q!r})>'.format(self)
+...
+>>> schema = Schema(Object({'q': 'one'}, cls=Structure))
+>>> schema(Structure(q='one'))
+<Structure(q='one')>
+
+```
+
+### Allow None values
+
+To allow value to be None as well, use Any:
+
+```pycon
+>>> from voluptuous import Any
+
+>>> schema = Schema(Any(None, int))
+>>> schema(None)
+>>> schema(5)
+5
+
+```
+
+## Error reporting
+
+Validators must throw an `Invalid` exception if invalid data is passed
+to them. All other exceptions are treated as errors in the validator and
+will not be caught.
+
+Each `Invalid` exception has an associated `path` attribute representing
+the path in the data structure to our currently validating value, as well
+as an `error_message` attribute that contains the message of the original
+exception. This is especially useful when you want to catch `Invalid`
+exceptions and give some feedback to the user, for instance in the context of
+an HTTP API.
+
+
+```pycon
+>>> def validate_email(email):
+...     """Validate email."""
+...     if not "@" in email:
+...         raise Invalid("This email is invalid.")
+...     return email
+>>> schema = Schema({"email": validate_email})
+>>> exc = None
+>>> try:
+...     schema({"email": "whatever"})
+... except MultipleInvalid as e:
+...     exc = e
+>>> str(exc)
+"This email is invalid. for dictionary value @ data['email']"
+>>> exc.path
+['email']
+>>> exc.msg
+'This email is invalid.'
+>>> exc.error_message
+'This email is invalid.'
+
+```
+
+The `path` attribute is used during error reporting, but also during matching
+to determine whether an error should be reported to the user or if the next
+match should be attempted. This is determined by comparing the depth of the
+path where the check is, to the depth of the path where the error occurred. If
+the error is more than one level deeper, it is reported.
+
+The upshot of this is that *matching is depth-first and fail-fast*.
+
+To illustrate this, here is an example schema:
+
+```pycon
+>>> schema = Schema([[2, 3], 6])
+
+```
+
+Each value in the top-level list is matched depth-first in-order. Given
+input data of `[[6]]`, the inner list will match the first element of
+the schema, but the literal `6` will not match any of the elements of
+that list. This error will be reported back to the user immediately. No
+backtracking is attempted:
+
+```pycon
+>>> try:
+...   schema([[6]])
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "not a valid value @ data[0][0]"
+True
+
+```
+
+If we pass the data `[6]`, the `6` is not a list type and so will not
+recurse into the first element of the schema. Matching will continue on
+to the second element in the schema, and succeed:
+
+```pycon
+>>> schema([6])
+[6]
+
+```
+
+## Running tests.
+
+Voluptuous is using nosetests:
+
+    $ nosetests
+
+
+## Why use Voluptuous over another validation library?
+
+**Validators are simple callables**
+:   No need to subclass anything, just use a function.
+
+**Errors are simple exceptions.**
+:   A validator can just `raise Invalid(msg)` and expect the user to get
+useful messages.
+
+**Schemas are basic Python data structures.**
+:   Should your data be a dictionary of integer keys to strings?
+`{int: str}` does what you expect. List of integers, floats or
+strings? `[int, float, str]`.
+
+**Designed from the ground up for validating more than just forms.**
+:   Nested data structures are treated in the same way as any other
+type. Need a list of dictionaries? `[{}]`
+
+**Consistency.**
+:   Types in the schema are checked as types. Values are compared as
+values. Callables are called to validate. Simple.
+
+## Other libraries and inspirations
+
+Voluptuous is heavily inspired by
+[Validino](http://code.google.com/p/validino/), and to a lesser extent,
+[jsonvalidator](http://code.google.com/p/jsonvalidator/) and
+[json\_schema](http://blog.sendapatch.se/category/json_schema.html).
+
+I greatly prefer the light-weight style promoted by these libraries to
+the complexity of libraries like FormEncode.
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/README.rst
@@ -0,0 +1,644 @@
+Voluptuous is a Python data validation library
+==============================================
+
+|Build Status| |Coverage Status| |Gitter chat|
+
+Voluptuous, *despite* the name, is a Python data validation library. It
+is primarily intended for validating data coming into Python as JSON,
+YAML, etc.
+
+It has three goals:
+
+1. Simplicity.
+2. Support for complex data structures.
+3. Provide useful error messages.
+
+Contact
+-------
+
+Voluptuous now has a mailing list! Send a mail to
+`<voluptuous@librelist.com> <mailto:voluptuous@librelist.com>`__ to
+subscribe. Instructions will follow.
+
+You can also contact me directly via `email <mailto:alec@swapoff.org>`__
+or `Twitter <https://twitter.com/alecthomas>`__.
+
+To file a bug, create a `new
+issue <https://github.com/alecthomas/voluptuous/issues/new>`__ on GitHub
+with a short example of how to replicate the issue.
+
+Documentation
+-------------
+
+The documentation is provided [here]
+(http://alecthomas.github.io/voluptuous/).
+
+Changelog
+---------
+
+See `CHANGELOG.md <CHANGELOG.md>`__.
+
+Show me an example
+------------------
+
+Twitter's `user search
+API <https://dev.twitter.com/rest/reference/get/users/search>`__ accepts
+query URLs like:
+
+::
+
+    $ curl 'http://api.twitter.com/1.1/users/search.json?q=python&per_page=20&page=1'
+
+To validate this we might use a schema like:
+
+.. code:: pycon
+
+    >>> from voluptuous import Schema
+    >>> schema = Schema({
+    ...   'q': str,
+    ...   'per_page': int,
+    ...   'page': int,
+    ... })
+
+This schema very succinctly and roughly describes the data required by
+the API, and will work fine. But it has a few problems. Firstly, it
+doesn't fully express the constraints of the API. According to the API,
+``per_page`` should be restricted to at most 20, defaulting to 5, for
+example. To describe the semantics of the API more accurately, our
+schema will need to be more thoroughly defined:
+
+.. code:: pycon
+
+    >>> from voluptuous import Required, All, Length, Range
+    >>> schema = Schema({
+    ...   Required('q'): All(str, Length(min=1)),
+    ...   Required('per_page', default=5): All(int, Range(min=1, max=20)),
+    ...   'page': All(int, Range(min=0)),
+    ... })
+
+This schema fully enforces the interface defined in Twitter's
+documentation, and goes a little further for completeness.
+
+"q" is required:
+
+.. code:: pycon
+
+    >>> from voluptuous import MultipleInvalid, Invalid
+    >>> try:
+    ...   schema({})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "required key not provided @ data['q']"
+    True
+
+...must be a string:
+
+.. code:: pycon
+
+    >>> try:
+    ...   schema({'q': 123})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "expected str for dictionary value @ data['q']"
+    True
+
+...and must be at least one character in length:
+
+.. code:: pycon
+
+    >>> try:
+    ...   schema({'q': ''})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "length of value must be at least 1 for dictionary value @ data['q']"
+    True
+    >>> schema({'q': '#topic'}) == {'q': '#topic', 'per_page': 5}
+    True
+
+"per\_page" is a positive integer no greater than 20:
+
+.. code:: pycon
+
+    >>> try:
+    ...   schema({'q': '#topic', 'per_page': 900})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "value must be at most 20 for dictionary value @ data['per_page']"
+    True
+    >>> try:
+    ...   schema({'q': '#topic', 'per_page': -10})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "value must be at least 1 for dictionary value @ data['per_page']"
+    True
+
+"page" is an integer >= 0:
+
+.. code:: pycon
+
+    >>> try:
+    ...   schema({'q': '#topic', 'per_page': 'one'})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc)
+    "expected int for dictionary value @ data['per_page']"
+    >>> schema({'q': '#topic', 'page': 1}) == {'q': '#topic', 'page': 1, 'per_page': 5}
+    True
+
+Defining schemas
+----------------
+
+Schemas are nested data structures consisting of dictionaries, lists,
+scalars and *validators*. Each node in the input schema is pattern
+matched against corresponding nodes in the input data.
+
+Literals
+~~~~~~~~
+
+Literals in the schema are matched using normal equality checks:
+
+.. code:: pycon
+
+    >>> schema = Schema(1)
+    >>> schema(1)
+    1
+    >>> schema = Schema('a string')
+    >>> schema('a string')
+    'a string'
+
+Types
+~~~~~
+
+Types in the schema are matched by checking if the corresponding value
+is an instance of the type:
+
+.. code:: pycon
+
+    >>> schema = Schema(int)
+    >>> schema(1)
+    1
+    >>> try:
+    ...   schema('one')
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "expected int"
+    True
+
+URL's
+~~~~~
+
+URL's in the schema are matched by using ``urlparse`` library.
+
+.. code:: pycon
+
+    >>> from voluptuous import Url
+    >>> schema = Schema(Url())
+    >>> schema('http://w3.org')
+    'http://w3.org'
+    >>> try:
+    ...   schema('one')
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "expected a URL"
+    True
+
+Lists
+~~~~~
+
+Lists in the schema are treated as a set of valid values. Each element
+in the schema list is compared to each value in the input data:
+
+.. code:: pycon
+
+    >>> schema = Schema([1, 'a', 'string'])
+    >>> schema([1])
+    [1]
+    >>> schema([1, 1, 1])
+    [1, 1, 1]
+    >>> schema(['a', 1, 'string', 1, 'string'])
+    ['a', 1, 'string', 1, 'string']
+
+However, an empty list (``[]``) is treated as is. If you want to specify
+a list that can contain anything, specify it as ``list``:
+
+.. code:: pycon
+
+    >>> schema = Schema([])
+    >>> try:
+    ...   schema([1])
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "not a valid value"
+    True
+    >>> schema([])
+    []
+    >>> schema = Schema(list)
+    >>> schema([])
+    []
+    >>> schema([1, 2])
+    [1, 2]
+
+Validation functions
+~~~~~~~~~~~~~~~~~~~~
+
+Validators are simple callables that raise an ``Invalid`` exception when
+they encounter invalid data. The criteria for determining validity is
+entirely up to the implementation; it may check that a value is a valid
+username with ``pwd.getpwnam()``, it may check that a value is of a
+specific type, and so on.
+
+The simplest kind of validator is a Python function that raises
+ValueError when its argument is invalid. Conveniently, many builtin
+Python functions have this property. Here's an example of a date
+validator:
+
+.. code:: pycon
+
+    >>> from datetime import datetime
+    >>> def Date(fmt='%Y-%m-%d'):
+    ...   return lambda v: datetime.strptime(v, fmt)
+
+.. code:: pycon
+
+    >>> schema = Schema(Date())
+    >>> schema('2013-03-03')
+    datetime.datetime(2013, 3, 3, 0, 0)
+    >>> try:
+    ...   schema('2013-03')
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "not a valid value"
+    True
+
+In addition to simply determining if a value is valid, validators may
+mutate the value into a valid form. An example of this is the
+``Coerce(type)`` function, which returns a function that coerces its
+argument to the given type:
+
+.. code:: python
+
+    def Coerce(type, msg=None):
+        """Coerce a value to a type.
+
+        If the type constructor throws a ValueError, the value will be marked as
+        Invalid.
+        """
+        def f(v):
+            try:
+                return type(v)
+            except ValueError:
+                raise Invalid(msg or ('expected %s' % type.__name__))
+        return f
+
+This example also shows a common idiom where an optional human-readable
+message can be provided. This can vastly improve the usefulness of the
+resulting error messages.
+
+Dictionaries
+~~~~~~~~~~~~
+
+Each key-value pair in a schema dictionary is validated against each
+key-value pair in the corresponding data dictionary:
+
+.. code:: pycon
+
+    >>> schema = Schema({1: 'one', 2: 'two'})
+    >>> schema({1: 'one'})
+    {1: 'one'}
+
+Extra dictionary keys
+^^^^^^^^^^^^^^^^^^^^^
+
+By default any additional keys in the data, not in the schema will
+trigger exceptions:
+
+.. code:: pycon
+
+    >>> schema = Schema({2: 3})
+    >>> try:
+    ...   schema({1: 2, 2: 3})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "extra keys not allowed @ data[1]"
+    True
+
+This behaviour can be altered on a per-schema basis. To allow additional
+keys use ``Schema(..., extra=ALLOW_EXTRA)``:
+
+.. code:: pycon
+
+    >>> from voluptuous import ALLOW_EXTRA
+    >>> schema = Schema({2: 3}, extra=ALLOW_EXTRA)
+    >>> schema({1: 2, 2: 3})
+    {1: 2, 2: 3}
+
+To remove additional keys use ``Schema(..., extra=REMOVE_EXTRA)``:
+
+.. code:: pycon
+
+    >>> from voluptuous import REMOVE_EXTRA
+    >>> schema = Schema({2: 3}, extra=REMOVE_EXTRA)
+    >>> schema({1: 2, 2: 3})
+    {2: 3}
+
+It can also be overridden per-dictionary by using the catch-all marker
+token ``extra`` as a key:
+
+.. code:: pycon
+
+    >>> from voluptuous import Extra
+    >>> schema = Schema({1: {Extra: object}})
+    >>> schema({1: {'foo': 'bar'}})
+    {1: {'foo': 'bar'}}
+
+However, an empty dict (``{}``) is treated as is. If you want to specify
+a list that can contain anything, specify it as ``dict``:
+
+.. code:: pycon
+
+    >>> schema = Schema({}, extra=ALLOW_EXTRA)  # don't do this
+    >>> try:
+    ...   schema({'extra': 1})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "not a valid value"
+    True
+    >>> schema({})
+    {}
+    >>> schema = Schema(dict)  # do this instead
+    >>> schema({})
+    {}
+    >>> schema({'extra': 1})
+    {'extra': 1}
+
+Required dictionary keys
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+By default, keys in the schema are not required to be in the data:
+
+.. code:: pycon
+
+    >>> schema = Schema({1: 2, 3: 4})
+    >>> schema({3: 4})
+    {3: 4}
+
+Similarly to how extra\_ keys work, this behaviour can be overridden
+per-schema:
+
+.. code:: pycon
+
+    >>> schema = Schema({1: 2, 3: 4}, required=True)
+    >>> try:
+    ...   schema({3: 4})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "required key not provided @ data[1]"
+    True
+
+And per-key, with the marker token ``Required(key)``:
+
+.. code:: pycon
+
+    >>> schema = Schema({Required(1): 2, 3: 4})
+    >>> try:
+    ...   schema({3: 4})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "required key not provided @ data[1]"
+    True
+    >>> schema({1: 2})
+    {1: 2}
+
+Optional dictionary keys
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+If a schema has ``required=True``, keys may be individually marked as
+optional using the marker token ``Optional(key)``:
+
+.. code:: pycon
+
+    >>> from voluptuous import Optional
+    >>> schema = Schema({1: 2, Optional(3): 4}, required=True)
+    >>> try:
+    ...   schema({})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "required key not provided @ data[1]"
+    True
+    >>> schema({1: 2})
+    {1: 2}
+    >>> try:
+    ...   schema({1: 2, 4: 5})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "extra keys not allowed @ data[4]"
+    True
+
+.. code:: pycon
+
+    >>> schema({1: 2, 3: 4})
+    {1: 2, 3: 4}
+
+Recursive schema
+~~~~~~~~~~~~~~~~
+
+There is no syntax to have a recursive schema. The best way to do it is
+to have a wrapper like this:
+
+.. code:: pycon
+
+    >>> from voluptuous import Schema, Any
+    >>> def s2(v):
+    ...     return s1(v)
+    ...
+    >>> s1 = Schema({"key": Any(s2, "value")})
+    >>> s1({"key": {"key": "value"}})
+    {'key': {'key': 'value'}}
+
+Extending an existing Schema
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Often it comes handy to have a base ``Schema`` that is extended with
+more requirements. In that case you can use ``Schema.extend`` to create
+a new ``Schema``:
+
+.. code:: pycon
+
+    >>> from voluptuous import Schema
+    >>> person = Schema({'name': str})
+    >>> person_with_age = person.extend({'age': int})
+    >>> sorted(list(person_with_age.schema.keys()))
+    ['age', 'name']
+
+The original ``Schema`` remains unchanged.
+
+Objects
+~~~~~~~
+
+Each key-value pair in a schema dictionary is validated against each
+attribute-value pair in the corresponding object:
+
+.. code:: pycon
+
+    >>> from voluptuous import Object
+    >>> class Structure(object):
+    ...     def __init__(self, q=None):
+    ...         self.q = q
+    ...     def __repr__(self):
+    ...         return '<Structure(q={0.q!r})>'.format(self)
+    ...
+    >>> schema = Schema(Object({'q': 'one'}, cls=Structure))
+    >>> schema(Structure(q='one'))
+    <Structure(q='one')>
+
+Allow None values
+~~~~~~~~~~~~~~~~~
+
+To allow value to be None as well, use Any:
+
+.. code:: pycon
+
+    >>> from voluptuous import Any
+
+    >>> schema = Schema(Any(None, int))
+    >>> schema(None)
+    >>> schema(5)
+    5
+
+Error reporting
+---------------
+
+Validators must throw an ``Invalid`` exception if invalid data is passed
+to them. All other exceptions are treated as errors in the validator and
+will not be caught.
+
+Each ``Invalid`` exception has an associated ``path`` attribute
+representing the path in the data structure to our currently validating
+value, as well as an ``error_message`` attribute that contains the
+message of the original exception. This is especially useful when you
+want to catch ``Invalid`` exceptions and give some feedback to the user,
+for instance in the context of an HTTP API.
+
+.. code:: pycon
+
+    >>> def validate_email(email):
+    ...     """Validate email."""
+    ...     if not "@" in email:
+    ...         raise Invalid("This email is invalid.")
+    ...     return email
+    >>> schema = Schema({"email": validate_email})
+    >>> exc = None
+    >>> try:
+    ...     schema({"email": "whatever"})
+    ... except MultipleInvalid as e:
+    ...     exc = e
+    >>> str(exc)
+    "This email is invalid. for dictionary value @ data['email']"
+    >>> exc.path
+    ['email']
+    >>> exc.msg
+    'This email is invalid.'
+    >>> exc.error_message
+    'This email is invalid.'
+
+The ``path`` attribute is used during error reporting, but also during
+matching to determine whether an error should be reported to the user or
+if the next match should be attempted. This is determined by comparing
+the depth of the path where the check is, to the depth of the path where
+the error occurred. If the error is more than one level deeper, it is
+reported.
+
+The upshot of this is that *matching is depth-first and fail-fast*.
+
+To illustrate this, here is an example schema:
+
+.. code:: pycon
+
+    >>> schema = Schema([[2, 3], 6])
+
+Each value in the top-level list is matched depth-first in-order. Given
+input data of ``[[6]]``, the inner list will match the first element of
+the schema, but the literal ``6`` will not match any of the elements of
+that list. This error will be reported back to the user immediately. No
+backtracking is attempted:
+
+.. code:: pycon
+
+    >>> try:
+    ...   schema([[6]])
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == "not a valid value @ data[0][0]"
+    True
+
+If we pass the data ``[6]``, the ``6`` is not a list type and so will
+not recurse into the first element of the schema. Matching will continue
+on to the second element in the schema, and succeed:
+
+.. code:: pycon
+
+    >>> schema([6])
+    [6]
+
+Running tests.
+--------------
+
+Voluptuous is using nosetests:
+
+::
+
+    $ nosetests
+
+Why use Voluptuous over another validation library?
+---------------------------------------------------
+
+**Validators are simple callables**
+    No need to subclass anything, just use a function.
+**Errors are simple exceptions.**
+    A validator can just ``raise Invalid(msg)`` and expect the user to
+    get useful messages.
+**Schemas are basic Python data structures.**
+    Should your data be a dictionary of integer keys to strings?
+    ``{int: str}`` does what you expect. List of integers, floats or
+    strings? ``[int, float, str]``.
+**Designed from the ground up for validating more than just forms.**
+    Nested data structures are treated in the same way as any other
+    type. Need a list of dictionaries? ``[{}]``
+**Consistency.**
+    Types in the schema are checked as types. Values are compared as
+    values. Callables are called to validate. Simple.
+
+Other libraries and inspirations
+--------------------------------
+
+Voluptuous is heavily inspired by
+`Validino <http://code.google.com/p/validino/>`__, and to a lesser
+extent, `jsonvalidator <http://code.google.com/p/jsonvalidator/>`__ and
+`json\_schema <http://blog.sendapatch.se/category/json_schema.html>`__.
+
+I greatly prefer the light-weight style promoted by these libraries to
+the complexity of libraries like FormEncode.
+
+.. |Build Status| image:: https://travis-ci.org/alecthomas/voluptuous.png
+   :target: https://travis-ci.org/alecthomas/voluptuous
+.. |Coverage Status| image:: https://coveralls.io/repos/github/alecthomas/voluptuous/badge.svg?branch=master
+   :target: https://coveralls.io/github/alecthomas/voluptuous?branch=master
+.. |Gitter chat| image:: https://badges.gitter.im/alecthomas.png
+   :target: https://gitter.im/alecthomas/Lobby
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/setup.cfg
@@ -0,0 +1,9 @@
+[nosetests]
+doctest-extension = md
+with-doctest = 1
+where = .
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/setup.py
@@ -0,0 +1,51 @@
+try:
+    from setuptools import setup
+except ImportError:
+    from distutils.core import setup
+
+import sys
+import os
+import atexit
+sys.path.insert(0, '.')
+version = __import__('voluptuous').__version__
+
+try:
+    import pypandoc
+    long_description = pypandoc.convert('README.md', 'rst')
+    with open('README.rst', 'w') as f:
+        f.write(long_description)
+    atexit.register(lambda: os.unlink('README.rst'))
+except (ImportError, OSError):
+    print('WARNING: Could not locate pandoc, using Markdown long_description.')
+    with open('README.md') as f:
+        long_description = f.read()
+
+description = long_description.splitlines()[0].strip()
+
+
+setup(
+    name='voluptuous',
+    url='https://github.com/alecthomas/voluptuous',
+    download_url='https://pypi.python.org/pypi/voluptuous',
+    version=version,
+    description=description,
+    long_description=long_description,
+    license='BSD',
+    platforms=['any'],
+    packages=['voluptuous'],
+    author='Alec Thomas',
+    author_email='alec@swapoff.org',
+    classifiers=[
+        'Development Status :: 5 - Production/Stable',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.1',
+        'Programming Language :: Python :: 3.2',
+        'Programming Language :: Python :: 3.3',
+        'Programming Language :: Python :: 3.4',
+    ]
+)
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/voluptuous/tests/__init__.py
@@ -0,0 +1,1 @@
+__author__ = 'tusharmakkar08'
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/voluptuous/tests/tests.md
@@ -0,0 +1,268 @@
+Error reporting should be accurate:
+
+    >>> from voluptuous import *
+    >>> schema = Schema(['one', {'two': 'three', 'four': ['five'],
+    ...                          'six': {'seven': 'eight'}}])
+    >>> schema(['one'])
+    ['one']
+    >>> schema([{'two': 'three'}])
+    [{'two': 'three'}]
+
+It should show the exact index and container type, in this case a list
+value:
+
+    >>> try:
+    ...   schema(['one', 'two'])
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc) == 'expected a dictionary @ data[1]'
+    True
+
+It should also be accurate for nested values:
+
+    >>> try:
+    ...   schema([{'two': 'nine'}])
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc)
+    "not a valid value for dictionary value @ data[0]['two']"
+
+    >>> try:
+    ...   schema([{'four': ['nine']}])
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc)
+    "not a valid value @ data[0]['four'][0]"
+
+    >>> try:
+    ...   schema([{'six': {'seven': 'nine'}}])
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc)
+    "not a valid value for dictionary value @ data[0]['six']['seven']"
+
+Errors should be reported depth-first:
+
+    >>> validate = Schema({'one': {'two': 'three', 'four': 'five'}})
+    >>> try:
+    ...   validate({'one': {'four': 'six'}})
+    ... except Invalid as e:
+    ...   print(e)
+    ...   print(e.path)
+    not a valid value for dictionary value @ data['one']['four']
+    ['one', 'four']
+
+Voluptuous supports validation when extra fields are present in the
+data:
+
+    >>> schema = Schema({'one': 1, Extra: object})
+    >>> schema({'two': 'two', 'one': 1}) == {'two': 'two', 'one': 1}
+    True
+    >>> schema = Schema({'one': 1})
+    >>> try:
+    ...   schema({'two': 2})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc)
+    "extra keys not allowed @ data['two']"
+
+dict, list, and tuple should be available as type validators:
+
+    >>> Schema(dict)({'a': 1, 'b': 2}) == {'a': 1, 'b': 2}
+    True
+    >>> Schema(list)([1,2,3])
+    [1, 2, 3]
+    >>> Schema(tuple)((1,2,3))
+    (1, 2, 3)
+
+Validation should return instances of the right types when the types are
+subclasses of dict or list:
+
+    >>> class Dict(dict):
+    ...   pass
+    >>>
+    >>> d = Schema(dict)(Dict(a=1, b=2))
+    >>> d == {'a': 1, 'b': 2}
+    True
+    >>> type(d) is Dict
+    True
+    >>> class List(list):
+    ...   pass
+    >>>
+    >>> l = Schema(list)(List([1,2,3]))
+    >>> l
+    [1, 2, 3]
+    >>> type(l) is List
+    True
+
+Multiple errors are reported:
+
+    >>> schema = Schema({'one': 1, 'two': 2})
+    >>> try:
+    ...   schema({'one': 2, 'two': 3, 'three': 4})
+    ... except MultipleInvalid as e:
+    ...   errors = sorted(e.errors, key=lambda k: str(k))
+    ...   print([str(i) for i in errors])  # doctest: +NORMALIZE_WHITESPACE
+    ["extra keys not allowed @ data['three']",
+     "not a valid value for dictionary value @ data['one']",
+     "not a valid value for dictionary value @ data['two']"]
+    >>> schema = Schema([[1], [2], [3]])
+    >>> try:
+    ...   schema([1, 2, 3])
+    ... except MultipleInvalid as e:
+    ...   print([str(i) for i in e.errors])  # doctest: +NORMALIZE_WHITESPACE
+    ['expected a list @ data[0]',
+     'expected a list @ data[1]',
+     'expected a list @ data[2]']
+
+Required fields in dictionary which are invalid should not have required :
+
+    >>> from voluptuous import *
+    >>> schema = Schema({'one': {'two': 3}}, required=True)
+    >>> try:
+    ...   schema({'one': {'two': 2}})
+    ... except MultipleInvalid as e:
+    ...   errors = e.errors
+    >>> 'required' in ' '.join([x.msg for x in errors])
+    False
+
+Multiple errors for nested fields in dicts and objects:
+
+> \>\>\> from collections import namedtuple \>\>\> validate = Schema({
+> ... 'anobject': Object({ ... 'strfield': str, ... 'intfield': int ...
+> }) ... }) \>\>\> try: ... SomeObj = namedtuple('SomeObj', ('strfield',
+> 'intfield')) ... validate({'anobject': SomeObj(strfield=123,
+> intfield='one')}) ... except MultipleInvalid as e: ...
+> print(sorted(str(i) for i in e.errors)) \# doctest:
+> +NORMALIZE\_WHITESPACE ["expected int for object value @
+> data['anobject']['intfield']", "expected str for object value @
+> data['anobject']['strfield']"]
+
+Custom classes validate as schemas:
+
+    >>> class Thing(object):
+    ...     pass
+    >>> schema = Schema(Thing)
+    >>> t = schema(Thing())
+    >>> type(t) is Thing
+    True
+
+Classes with custom metaclasses should validate as schemas:
+
+    >>> class MyMeta(type):
+    ...     pass
+    >>> class Thing(object):
+    ...     __metaclass__ = MyMeta
+    >>> schema = Schema(Thing)
+    >>> t = schema(Thing())
+    >>> type(t) is Thing
+    True
+
+Schemas built with All() should give the same error as the original
+validator (Issue \#26):
+
+    >>> schema = Schema({
+    ...   Required('items'): All([{
+    ...     Required('foo'): str
+    ...   }])
+    ... })
+
+    >>> try:
+    ...   schema({'items': [{}]})
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc)
+    "required key not provided @ data['items'][0]['foo']"
+
+Validator should return same instance of the same type for object:
+
+    >>> class Structure(object):
+    ...     def __init__(self, q=None):
+    ...         self.q = q
+    ...     def __repr__(self):
+    ...         return '{0.__name__}(q={1.q!r})'.format(type(self), self)
+    ...
+    >>> schema = Schema(Object({'q': 'one'}, cls=Structure))
+    >>> type(schema(Structure(q='one'))) is Structure
+    True
+
+Object validator should treat cls argument as optional. In this case it
+shouldn't check object type:
+
+    >>> from collections import namedtuple
+    >>> NamedTuple = namedtuple('NamedTuple', ('q',))
+    >>> schema = Schema(Object({'q': 'one'}))
+    >>> named = NamedTuple(q='one')
+    >>> schema(named) == named
+    True
+    >>> schema(named)
+    NamedTuple(q='one')
+
+If cls argument passed to object validator we should check object type:
+
+    >>> schema = Schema(Object({'q': 'one'}, cls=Structure))
+    >>> schema(NamedTuple(q='one'))  # doctest: +IGNORE_EXCEPTION_DETAIL
+    Traceback (most recent call last):
+    ...
+    MultipleInvalid: expected a <class 'Structure'>
+    >>> schema = Schema(Object({'q': 'one'}, cls=NamedTuple))
+    >>> schema(NamedTuple(q='one'))
+    NamedTuple(q='one')
+
+Ensure that objects with \_\_slots\_\_ supported properly:
+
+    >>> class SlotsStructure(Structure):
+    ...     __slots__ = ['q']
+    ...
+    >>> schema = Schema(Object({'q': 'one'}))
+    >>> schema(SlotsStructure(q='one'))
+    SlotsStructure(q='one')
+    >>> class DictStructure(object):
+    ...     __slots__ = ['q', '__dict__']
+    ...     def __init__(self, q=None, page=None):
+    ...         self.q = q
+    ...         self.page = page
+    ...     def __repr__(self):
+    ...         return '{0.__name__}(q={1.q!r}, page={1.page!r})'.format(type(self), self)
+    ...
+    >>> structure = DictStructure(q='one')
+    >>> structure.page = 1
+    >>> try:
+    ...   schema(structure)
+    ...   raise AssertionError('MultipleInvalid not raised')
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> str(exc)
+    "extra keys not allowed @ data['page']"
+
+    >>> schema = Schema(Object({'q': 'one', Extra: object}))
+    >>> schema(structure)
+    DictStructure(q='one', page=1)
+
+Ensure that objects can be used with other validators:
+
+    >>> schema = Schema({'meta': Object({'q': 'one'})})
+    >>> schema({'meta': Structure(q='one')})
+    {'meta': Structure(q='one')}
+
+Ensure that subclasses of Invalid of are raised as is.
+
+    >>> class SpecialInvalid(Invalid):
+    ...   pass
+    ...
+    >>> def custom_validator(value):
+    ...   raise SpecialInvalid('boom')
+    ...
+    >>> schema = Schema({'thing': custom_validator})
+    >>> try:
+    ...   schema({'thing': 'not an int'})
+    ... except MultipleInvalid as e:
+    ...   exc = e
+    >>> exc.errors[0].__class__.__name__
+    'SpecialInvalid'
new file mode 100644
--- /dev/null
+++ b/third_party/python/voluptuous/voluptuous/tests/tests.py
@@ -0,0 +1,830 @@
+import copy
+import collections
+import sys
+from nose.tools import assert_equal, assert_raises, assert_true
+
+from voluptuous import (
+    Schema, Required, Optional, Extra, Invalid, In, Remove, Literal,
+    Url, MultipleInvalid, LiteralInvalid, NotIn, Match, Email,
+    Replace, Range, Coerce, All, Any, Length, FqdnUrl, ALLOW_EXTRA, PREVENT_EXTRA,
+    validate, ExactSequence, Equal, Unordered, Number, Maybe, Datetime, Date,
+    Contains, Marker)
+from voluptuous.humanize import humanize_error
+from voluptuous.util import u
+
+
+def test_exact_sequence():
+    schema = Schema(ExactSequence([int, int]))
+    try:
+        schema([1, 2, 3])
+    except Invalid:
+        assert True
+    else:
+        assert False, "Did not raise Invalid"
+    assert_equal(schema([1, 2]), [1, 2])
+
+
+def test_required():
+    """Verify that Required works."""
+    schema = Schema({Required('q'): 1})
+    # Can't use nose's raises (because we need to access the raised
+    # exception, nor assert_raises which fails with Python 2.6.9.
+    try:
+        schema({})
+    except Invalid as e:
+        assert_equal(str(e), "required key not provided @ data['q']")
+    else:
+        assert False, "Did not raise Invalid"
+
+
+def test_extra_with_required():
+    """Verify that Required does not break Extra."""
+    schema = Schema({Required('toaster'): str, Extra: object})
+    r = schema({'toaster': 'blue', 'another_valid_key': 'another_valid_value'})
+    assert_equal(
+        r, {'toaster': 'blue', 'another_valid_key': 'another_valid_value'})
+
+
+def test_iterate_candidates():
+    """Verify that the order for iterating over mapping candidates is right."""
+    schema = {
+        "toaster": str,
+        Extra: object,
+    }
+    # toaster should be first.
+    from voluptuous.schema_builder import _iterate_mapping_candidates
+    assert_equal(_iterate_mapping_candidates(schema)[0][0], 'toaster')
+
+
+def test_in():
+    """Verify that In works."""
+    schema = Schema({"color": In(frozenset(["blue", "red", "yellow"]))})
+    schema({"color": "blue"})
+
+
+def test_not_in():
+    """Verify that NotIn works."""
+    schema = Schema({"color": NotIn(frozenset(["blue", "red", "yellow"]))})
+    schema({"color": "orange"})
+    try:
+        schema({"color": "blue"})
+    except Invalid as e:
+        assert_equal(str(e), "value is not allowed for dictionary value @ data['color']")
+    else:
+        assert False, "Did not raise NotInInvalid"
+
+
+def test_contains():
+    """Verify contains validation method."""
+    schema = Schema({'color': Contains('red')})
+    schema({'color': ['blue', 'red', 'yellow']})
+    try:
+        schema({'color': ['blue', 'yellow']})
+    except Invalid as e:
+        assert_equal(str(e),
+                     "value is not allowed for dictionary value @ data['color']")
+
+
+def test_remove():
+    """Verify that Remove works."""
+    # remove dict keys
+    schema = Schema({"weight": int,
+                     Remove("color"): str,
+                     Remove("amount"): int})
+    out_ = schema({"weight": 10, "color": "red", "amount": 1})
+    assert "color" not in out_ and "amount" not in out_
+
+    # remove keys by type
+    schema = Schema({"weight": float,
+                     "amount": int,
+                     # remvove str keys with int values
+                     Remove(str): int,
+                     # keep str keys with str values
+                     str: str})
+    out_ = schema({"weight": 73.4,
+                   "condition": "new",
+                   "amount": 5,
+                   "left": 2})
+    # amount should stay since it's defined
+    # other string keys with int values will be removed
+    assert "amount" in out_ and "left" not in out_
+    # string keys with string values will stay
+    assert "condition" in out_
+
+    # remove value from list
+    schema = Schema([Remove(1), int])
+    out_ = schema([1, 2, 3, 4, 1, 5, 6, 1, 1, 1])
+    assert_equal(out_, [2, 3, 4, 5, 6])
+
+    # remove values from list by type
+    schema = Schema([1.0, Remove(float), int])
+    out_ = schema([1, 2, 1.0, 2.0, 3.0, 4])
+    assert_equal(out_, [1, 2, 1.0, 4])
+
+
+def test_extra_empty_errors():
+    schema = Schema({'a': {Extra: object}}, required=True)
+    schema({'a': {}})
+
+
+def test_literal():
+    """ test with Literal """
+
+    schema = Schema([Literal({"a": 1}), Literal({"b": 1})])
+    schema([{"a": 1}])
+    schema([{"b": 1}])
+    schema([{"a": 1}, {"b": 1}])
+
+    try:
+        schema([{"c": 1}])
+    except Invalid as e:
+        assert_equal(str(e), "{'c': 1} not match for {'b': 1} @ data[0]")
+    else:
+        assert False, "Did not raise Invalid"
+
+    schema = Schema(Literal({"a": 1}))
+    try:
+        schema({"b": 1})
+    except MultipleInvalid as e:
+        assert_equal(str(e), "{'b': 1} not match for {'a': 1}")
+        assert_equal(len(e.errors), 1)
+        assert_equal(type(e.errors[0]), LiteralInvalid)
+    else:
+        assert False, "Did not raise Invalid"
+
+
+def test_email_validation():
+    """ test with valid email """
+    schema = Schema({"email": Email()})
+    out_ = schema({"email": "example@example.com"})
+
+    assert 'example@example.com"', out_.get("url")
+
+
+def test_email_validation_with_none():
+    """ test with invalid None Email"""
+    schema = Schema({"email": Email()})
+    try:
+        schema({"email": None})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "expected an Email for dictionary value @ data['email']")
+    else:
+        assert False, "Did not raise Invalid for None url"
+
+
+def test_email_validation_with_empty_string():
+    """ test with empty string Email"""
+    schema = Schema({"email": Email()})
+    try:
+        schema({"email": ''})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "expected an Email for dictionary value @ data['email']")
+    else:
+        assert False, "Did not raise Invalid for empty string url"
+
+
+def test_email_validation_without_host():
+    """ test with empty host name in email """
+    schema = Schema({"email": Email()})
+    try:
+        schema({"email": 'a@.com'})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "expected an Email for dictionary value @ data['email']")
+    else:
+        assert False, "Did not raise Invalid for empty string url"
+
+
+def test_fqdn_url_validation():
+    """ test with valid fully qualified domain name url """
+    schema = Schema({"url": FqdnUrl()})
+    out_ = schema({"url": "http://example.com/"})
+
+    assert 'http://example.com/', out_.get("url")
+
+
+def test_fqdn_url_without_domain_name():
+    """ test with invalid fully qualified domain name url """
+    schema = Schema({"url": FqdnUrl()})
+    try:
+        schema({"url": "http://localhost/"})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "expected a Fully qualified domain name URL for dictionary value @ data['url']")
+    else:
+        assert False, "Did not raise Invalid for None url"
+
+
+def test_fqdnurl_validation_with_none():
+    """ test with invalid None FQDN url"""
+    schema = Schema({"url": FqdnUrl()})
+    try:
+        schema({"url": None})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "expected a Fully qualified domain name URL for dictionary value @ data['url']")
+    else:
+        assert False, "Did not raise Invalid for None url"
+
+
+def test_fqdnurl_validation_with_empty_string():
+    """ test with empty string FQDN URL """
+    schema = Schema({"url": FqdnUrl()})
+    try:
+        schema({"url": ''})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "expected a Fully qualified domain name URL for dictionary value @ data['url']")
+    else:
+        assert False, "Did not raise Invalid for empty string url"
+
+
+def test_fqdnurl_validation_without_host():
+    """ test with empty host FQDN URL """
+    schema = Schema({"url": FqdnUrl()})
+    try:
+        schema({"url": 'http://'})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "expected a Fully qualified domain name URL for dictionary value @ data['url']")
+    else:
+        assert False, "Did not raise Invalid for empty string url"
+
+
+def test_url_validation():
+    """ test with valid URL """
+    schema = Schema({"url": Url()})
+    out_ = schema({"url": "http://example.com/"})
+
+    assert 'http://example.com/', out_.get("url")
+
+
+def test_url_validation_with_none():
+    """ test with invalid None url"""
+    schema = Schema({"url": Url()})
+    try:
+        schema({"url": None})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "expected a URL for dictionary value @ data['url']")
+    else:
+        assert False, "Did not raise Invalid for None url"
+
+
+def test_url_validation_with_empty_string():
+    """ test with empty string URL """
+    schema = Schema({"url": Url()})
+    try:
+        schema({"url": ''})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "expected a URL for dictionary value @ data['url']")
+    else:
+        assert False, "Did not raise Invalid for empty string url"
+
+
+def test_url_validation_without_host():
+    """ test with empty host URL """
+    schema = Schema({"url": Url()})
+    try:
+        schema({"url": 'http://'})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "expected a URL for dictionary value @ data['url']")
+    else:
+        assert False, "Did not raise Invalid for empty string url"
+
+
+def test_copy_dict_undefined():
+    """ test with a copied dictionary """
+    fields = {
+        Required("foo"): int
+    }
+    copied_fields = copy.deepcopy(fields)
+
+    schema = Schema(copied_fields)
+
+    # This used to raise a `TypeError` because the instance of `Undefined`
+    # was a copy, so object comparison would not work correctly.
+    try:
+        schema({"foo": "bar"})
+    except Exception as e:
+        assert isinstance(e, MultipleInvalid)
+
+
+def test_sorting():
+    """ Expect alphabetic sorting """
+    foo = Required('foo')
+    bar = Required('bar')
+    items = [foo, bar]
+    expected = [bar, foo]
+    result = sorted(items)
+    assert result == expected
+
+
+def test_schema_extend():
+    """Verify that Schema.extend copies schema keys from both."""
+
+    base = Schema({'a': int}, required=True)
+    extension = {'b': str}
+    extended = base.extend(extension)
+
+    assert base.schema == {'a': int}
+    assert extension == {'b': str}
+    assert extended.schema == {'a': int, 'b': str}
+    assert extended.required == base.required
+    assert extended.extra == base.extra
+
+
+def test_schema_extend_overrides():
+    """Verify that Schema.extend can override required/extra parameters."""
+
+    base = Schema({'a': int}, required=True)
+    extended = base.extend({'b': str}, required=False, extra=ALLOW_EXTRA)
+
+    assert base.required is True
+    assert base.extra == PREVENT_EXTRA
+    assert extended.required is False
+    assert extended.extra == ALLOW_EXTRA
+
+
+def test_schema_extend_key_swap():
+    """Verify that Schema.extend can replace keys, even when different markers are used"""
+
+    base = Schema({Optional('a'): int})
+    extension = {Required('a'): int}
+    extended = base.extend(extension)
+
+    assert_equal(len(base.schema), 1)
+    assert_true(isinstance(list(base.schema)[0], Optional))
+    assert_equal(len(extended.schema), 1)
+    assert_true((list(extended.schema)[0], Required))
+
+
+def test_subschema_extension():
+    """Verify that Schema.extend adds and replaces keys in a subschema"""
+
+    base = Schema({'a': {'b': int, 'c': float}})
+    extension = {'d': str, 'a': {'b': str, 'e': int}}
+    extended = base.extend(extension)
+
+    assert_equal(base.schema, {'a': {'b': int, 'c': float}})
+    assert_equal(extension, {'d': str, 'a': {'b': str, 'e': int}})
+    assert_equal(extended.schema, {'a': {'b': str, 'c': float, 'e': int}, 'd': str})
+
+
+def test_repr():
+    """Verify that __repr__ returns valid Python expressions"""
+    match = Match('a pattern', msg='message')
+    replace = Replace('you', 'I', msg='you and I')
+    range_ = Range(min=0, max=42, min_included=False,
+                   max_included=False, msg='number not in range')
+    coerce_ = Coerce(int, msg="moo")
+    all_ = All('10', Coerce(int), msg='all msg')
+    maybe_int = Maybe(int)
+
+    assert_equal(repr(match), "Match('a pattern', msg='message')")
+    assert_equal(repr(replace), "Replace('you', 'I', msg='you and I')")
+    assert_equal(
+        repr(range_),
+        "Range(min=0, max=42, min_included=False, max_included=False, msg='number not in range')"
+    )
+    assert_equal(repr(coerce_), "Coerce(int, msg='moo')")
+    assert_equal(repr(all_), "All('10', Coerce(int, msg=None), msg='all msg')")
+    assert_equal(repr(maybe_int), "Maybe(%s)" % str(int))
+
+
+def test_list_validation_messages():
+    """ Make sure useful error messages are available """
+
+    def is_even(value):
+        if value % 2:
+            raise Invalid('%i is not even' % value)
+        return value
+
+    schema = Schema(dict(even_numbers=[All(int, is_even)]))
+
+    try:
+        schema(dict(even_numbers=[3]))
+    except Invalid as e:
+        assert_equal(len(e.errors), 1, e.errors)
+        assert_equal(str(e.errors[0]), "3 is not even @ data['even_numbers'][0]")
+        assert_equal(str(e), "3 is not even @ data['even_numbers'][0]")
+    else:
+        assert False, "Did not raise Invalid"
+
+
+def test_nested_multiple_validation_errors():
+    """ Make sure useful error messages are available """
+
+    def is_even(value):
+        if value % 2:
+            raise Invalid('%i is not even' % value)
+        return value
+
+    schema = Schema(dict(even_numbers=All([All(int, is_even)],
+                                          Length(min=1))))
+
+    try:
+        schema(dict(even_numbers=[3]))
+    except Invalid as e:
+        assert_equal(len(e.errors), 1, e.errors)
+        assert_equal(str(e.errors[0]), "3 is not even @ data['even_numbers'][0]")
+        assert_equal(str(e), "3 is not even @ data['even_numbers'][0]")
+    else:
+        assert False, "Did not raise Invalid"
+
+
+def test_humanize_error():
+    data = {
+        'a': 'not an int',
+        'b': [123]
+    }
+    schema = Schema({
+        'a': int,
+        'b': [str]
+    })
+    try:
+        schema(data)
+    except MultipleInvalid as e:
+        assert_equal(
+            humanize_error(data, e),
+            "expected int for dictionary value @ data['a']. Got 'not an int'\n"
+            "expected str @ data['b'][0]. Got 123"
+        )
+    else:
+        assert False, 'Did not raise MultipleInvalid'
+
+
+def test_fix_157():
+    s = Schema(All([Any('one', 'two', 'three')]), Length(min=1))
+    assert_equal(['one'], s(['one']))
+    assert_raises(MultipleInvalid, s, ['four'])
+
+
+def test_range_exlcudes_nan():
+    s = Schema(Range(min=0, max=10))
+    assert_raises(MultipleInvalid, s, float('nan'))
+
+
+def test_equal():
+    s = Schema(Equal(1))
+    s(1)
+    assert_raises(Invalid, s, 2)
+    s = Schema(Equal('foo'))
+    s('foo')
+    assert_raises(Invalid, s, 'bar')
+    s = Schema(Equal([1, 2]))
+    s([1, 2])
+    assert_raises(Invalid, s, [])
+    assert_raises(Invalid, s, [1, 2, 3])
+    # Evaluates exactly, not through validators
+    s = Schema(Equal(str))
+    assert_raises(Invalid, s, 'foo')
+
+
+def test_unordered():
+    # Any order is OK
+    s = Schema(Unordered([2, 1]))
+    s([2, 1])
+    s([1, 2])
+    # Amount of errors is OK
+    assert_raises(Invalid, s, [2, 0])
+    assert_raises(MultipleInvalid, s, [0, 0])
+    # Different length is NOK
+    assert_raises(Invalid, s, [1])
+    assert_raises(Invalid, s, [1, 2, 0])
+    assert_raises(MultipleInvalid, s, [1, 2, 0, 0])
+    # Other type than list or tuple is NOK
+    assert_raises(Invalid, s, 'foo')
+    assert_raises(Invalid, s, 10)
+    # Validators are evaluated through as schemas
+    s = Schema(Unordered([int, str]))
+    s([1, '2'])
+    s(['1', 2])
+    s = Schema(Unordered([{'foo': int}, []]))
+    s([{'foo': 3}, []])
+    # Most accurate validators must be positioned on left
+    s = Schema(Unordered([int, 3]))
+    assert_raises(Invalid, s, [3, 2])
+    s = Schema(Unordered([3, int]))
+    s([3, 2])
+
+
+def test_maybe():
+    assert_raises(TypeError, Maybe, lambda x: x)
+
+    s = Schema(Maybe(int))
+    assert s(1) == 1
+    assert s(None) is None
+
+    assert_raises(Invalid, s, 'foo')
+
+
+def test_empty_list_as_exact():
+    s = Schema([])
+    assert_raises(Invalid, s, [1])
+    s([])
+
+
+def test_empty_dict_as_exact():
+    # {} always evaluates as {}
+    s = Schema({})
+    assert_raises(Invalid, s, {'extra': 1})
+    s = Schema({}, extra=ALLOW_EXTRA)  # this should not be used
+    assert_raises(Invalid, s, {'extra': 1})
+
+    # {...} evaluates as Schema({...})
+    s = Schema({'foo': int})
+    assert_raises(Invalid, s, {'foo': 1, 'extra': 1})
+    s = Schema({'foo': int}, extra=ALLOW_EXTRA)
+    s({'foo': 1, 'extra': 1})
+
+    # dict matches {} or {...}
+    s = Schema(dict)
+    s({'extra': 1})
+    s({})
+    s = Schema(dict, extra=PREVENT_EXTRA)
+    s({'extra': 1})
+    s({})
+
+    # nested {} evaluate as {}
+    s = Schema({
+        'inner': {}
+    }, extra=ALLOW_EXTRA)
+    assert_raises(Invalid, s, {'inner': {'extra': 1}})
+    s({})
+    s = Schema({
+        'inner': Schema({}, extra=ALLOW_EXTRA)
+    })
+    assert_raises(Invalid, s, {'inner': {'extra': 1}})
+    s({})
+
+
+def test_schema_decorator_match_with_args():
+    @validate(int)
+    def fn(arg):
+        return arg
+
+    fn(1)
+
+
+def test_schema_decorator_unmatch_with_args():
+    @validate(int)
+    def fn(arg):
+        return arg
+
+    assert_raises(Invalid, fn, 1.0)
+
+
+def test_schema_decorator_match_with_kwargs():
+    @validate(arg=int)
+    def fn(arg):
+        return arg
+
+    fn(1)
+
+
+def test_schema_decorator_unmatch_with_kwargs():
+    @validate(arg=int)
+    def fn(arg):
+        return arg
+
+    assert_raises(Invalid, fn, 1.0)
+
+
+def test_schema_decorator_match_return_with_args():
+    @validate(int, __return__=int)
+    def fn(arg):
+        return arg
+
+    fn(1)
+
+
+def test_schema_decorator_unmatch_return_with_args():
+    @validate(int, __return__=int)
+    def fn(arg):
+        return "hello"
+
+    assert_raises(Invalid, fn, 1)
+
+
+def test_schema_decorator_match_return_with_kwargs():
+    @validate(arg=int, __return__=int)
+    def fn(arg):
+        return arg
+
+    fn(1)
+
+
+def test_schema_decorator_unmatch_return_with_kwargs():
+    @validate(arg=int, __return__=int)
+    def fn(arg):
+        return "hello"
+
+    assert_raises(Invalid, fn, 1)
+
+
+def test_schema_decorator_return_only_match():
+    @validate(__return__=int)
+    def fn(arg):
+        return arg
+
+    fn(1)
+
+
+def test_schema_decorator_return_only_unmatch():
+    @validate(__return__=int)
+    def fn(arg):
+        return "hello"
+
+    assert_raises(Invalid, fn, 1)
+
+
+def test_unicode_as_key():
+    if sys.version_info >= (3,):
+        text_type = str
+    else:
+        text_type = unicode
+    schema = Schema({text_type: int})
+    schema({u("foobar"): 1})
+
+
+def test_number_validation_with_string():
+    """ test with Number with string"""
+    schema = Schema({"number": Number(precision=6, scale=2)})
+    try:
+        schema({"number": 'teststr'})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "Value must be a number enclosed with string for dictionary value @ data['number']")
+    else:
+        assert False, "Did not raise Invalid for String"
+
+
+def test_number_validation_with_invalid_precision_invalid_scale():
+    """ test with Number with invalid precision and scale"""
+    schema = Schema({"number": Number(precision=6, scale=2)})
+    try:
+        schema({"number": '123456.712'})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "Precision must be equal to 6, and Scale must be equal to 2 for dictionary value @ data['number']")
+    else:
+        assert False, "Did not raise Invalid for String"
+
+
+def test_number_validation_with_valid_precision_scale_yield_decimal_true():
+    """ test with Number with valid precision and scale"""
+    schema = Schema({"number": Number(precision=6, scale=2, yield_decimal=True)})
+    out_ = schema({"number": '1234.00'})
+    assert_equal(float(out_.get("number")), 1234.00)
+
+
+def test_number_when_precision_scale_none_yield_decimal_true():
+    """ test with Number with no precision and scale"""
+    schema = Schema({"number": Number(yield_decimal=True)})
+    out_ = schema({"number": '12345678901234'})
+    assert_equal(out_.get("number"), 12345678901234)
+
+
+def test_number_when_precision_none_n_valid_scale_case1_yield_decimal_true():
+    """ test with Number with no precision and valid scale case 1"""
+    schema = Schema({"number": Number(scale=2, yield_decimal=True)})
+    out_ = schema({"number": '123456789.34'})
+    assert_equal(float(out_.get("number")), 123456789.34)
+
+
+def test_number_when_precision_none_n_valid_scale_case2_yield_decimal_true():
+    """ test with Number with no precision and valid scale case 2 with zero in decimal part"""
+    schema = Schema({"number": Number(scale=2, yield_decimal=True)})
+    out_ = schema({"number": '123456789012.00'})
+    assert_equal(float(out_.get("number")), 123456789012.00)
+
+
+def test_number_when_precision_none_n_invalid_scale_yield_decimal_true():
+    """ test with Number with no precision and invalid scale"""
+    schema = Schema({"number": Number(scale=2, yield_decimal=True)})
+    try:
+        schema({"number": '12345678901.234'})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "Scale must be equal to 2 for dictionary value @ data['number']")
+    else:
+        assert False, "Did not raise Invalid for String"
+
+
+def test_number_when_valid_precision_n_scale_none_yield_decimal_true():
+    """ test with Number with no precision and valid scale"""
+    schema = Schema({"number": Number(precision=14, yield_decimal=True)})
+    out_ = schema({"number": '1234567.8901234'})
+    assert_equal(float(out_.get("number")), 1234567.8901234)
+
+
+def test_number_when_invalid_precision_n_scale_none_yield_decimal_true():
+    """ test with Number with no precision and invalid scale"""
+    schema = Schema({"number": Number(precision=14, yield_decimal=True)})
+    try:
+        schema({"number": '12345674.8901234'})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "Precision must be equal to 14 for dictionary value @ data['number']")
+    else:
+        assert False, "Did not raise Invalid for String"
+
+
+def test_number_validation_with_valid_precision_scale_yield_decimal_false():
+    """ test with Number with valid precision, scale and no yield_decimal"""
+    schema = Schema({"number": Number(precision=6, scale=2, yield_decimal=False)})
+    out_ = schema({"number": '1234.00'})
+    assert_equal(out_.get("number"), '1234.00')
+
+
+def test_named_tuples_validate_as_tuples():
+    NT = collections.namedtuple('NT', ['a', 'b'])
+    nt = NT(1, 2)
+    t = (1, 2)
+
+    Schema((int, int))(nt)
+    Schema((int, int))(t)
+    Schema(NT(int, int))(nt)
+    Schema(NT(int, int))(t)
+
+
+def test_datetime():
+    schema = Schema({"datetime": Datetime()})
+    schema({"datetime": "2016-10-24T14:01:57.102152Z"})
+    assert_raises(MultipleInvalid, schema, {"datetime": "2016-10-24T14:01:57"})
+
+
+def test_date():
+    schema = Schema({"date": Date()})
+    schema({"date": "2016-10-24"})
+    assert_raises(MultipleInvalid, schema, {"date": "2016-10-2"})
+    assert_raises(MultipleInvalid, schema, {"date": "2016-10-24Z"})
+
+
+def test_ordered_dict():
+    if not hasattr(collections, 'OrderedDict'):
+        # collections.OrderedDict was added in Python2.7; only run if present
+        return
+    schema = Schema({Number(): Number()})  # x, y pairs (for interpolation or something)
+    data = collections.OrderedDict([(5.0, 3.7), (24.0, 8.7), (43.0, 1.5),
+                                    (62.0, 2.1), (71.5, 6.7), (90.5, 4.1),
+                                    (109.0, 3.9)])
+    out = schema(data)
+    assert isinstance(out, collections.OrderedDict), 'Collection is no longer ordered'
+    assert data.keys() == out.keys(), 'Order is not consistent'
+
+
+def test_marker_hashable():
+    """Verify that you can get schema keys, even if markers were used"""
+    definition = {
+        Required('x'): int, Optional('y'): float,
+        Remove('j'): int, Remove(int): str, int: int
+    }
+    assert_equal(definition.get('x'), int)
+    assert_equal(definition.get('y'), float)
+    assert_true(Required('x') == Required('x'))
+    assert_true(Required('x') != Required('y'))
+    # Remove markers are not hashable
+    assert_equal(definition.get('j'), None)
+
+
+def test_validation_performance():
+    """
+    This test comes to make sure the validation complexity of dictionaries is done in a linear time.
+    to achieve this a custom marker is used in the scheme that counts each time it is evaluated.
+    By doing so we can determine if the validation is done in linear complexity.
+    Prior to issue https://github.com/alecthomas/voluptuous/issues/259 this was exponential
+    """
+    num_of_keys = 1000
+
+    schema_dict = {}
+    data = {}
+    data_extra_keys = {}
+
+    counter = [0]
+
+    class CounterMarker(Marker):
+        def __call__(self, *args, **kwargs):
+            counter[0] += 1
+            return super(CounterMarker, self).__call__(*args, **kwargs)
+
+    for i in range(num_of_keys):
+        schema_dict[CounterMarker(str(i))] = str
+        data[str(i)] = str(i)
+        data_extra_keys[str(i*2)] = str(i)  # half of the keys are present, and half aren't
+
+    schema = Schema(schema_dict, extra=ALLOW_EXTRA)
+
+    schema(data)
+
+    assert counter[0] <= num_of_keys, "Validation complexity is not linear! %s > %s" % (counter[0], num_of_keys)
+
+    counter[0] = 0  # reset counter
+    schema(data_extra_keys)
+
+    assert counter[0] <= num_of_keys, "Validation complexity is not linear! %s > %s" % (counter[0], num_of_keys)