Bug 1466211 - Vendor voluptuous via |mach vendor python|; r=ahal
authorDave Hunt <dhunt@mozilla.com>
Fri, 01 Jun 2018 18:56:23 +0100
changeset 423548 980204366869bc7506672bd124d2ec3d8488efee
parent 423547 cbdc1c8ef5679d1608ee187e4bd1713dd0142974
child 423549 f761d8aed904b846ec3daa453d7ede79d2357a19
push id104603
push usernerli@mozilla.com
push dateMon, 25 Jun 2018 19:16:20 +0000
treeherdermozilla-inbound@b9353dfa681c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahal
bugs1466211
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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)