1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2 From: "Jan Alexander Steffens (heftig)" <jan.steffens@gmail.com>
3 Date: Tue, 11 Jan 2022 19:26:43 +0100
4 Subject: [PATCH] Fixes for Python 3.10
6 Bug 1719144 cherry-picked from Firefox Nightly.
8 python/mach/mach/config.py | 5 +-
9 python/mach/mach/decorators.py | 3 +-
10 python/mach/mach/main.py | 2 +-
11 .../mozbuild/backend/configenvironment.py | 3 +-
12 python/mozbuild/mozbuild/makeutil.py | 2 +-
13 python/mozbuild/mozbuild/util.py | 3 +-
14 taskcluster/taskgraph/util/schema.py | 3 +-
15 .../manifestparser/manifestparser/filters.py | 3 +-
16 third_party/python/gyp/pylib/gyp/common.py | 3 +-
17 third_party/python/requirements.in | 2 +-
18 third_party/python/requirements.txt | 6 +-
19 .../{ => voluptuous-0.12.1.dist-info}/COPYING | 0
20 .../METADATA} | 114 +++++++---
21 .../voluptuous-0.12.1.dist-info/RECORD | 11 +
22 .../voluptuous-0.12.1.dist-info/WHEEL | 11 +
24 .../python/voluptuous/voluptuous/__init__.py | 2 +-
25 .../python/voluptuous/voluptuous/error.py | 4 +-
26 .../voluptuous/voluptuous/schema_builder.py | 25 ++-
27 .../python/voluptuous/voluptuous/util.py | 18 +-
28 .../voluptuous/voluptuous/validators.py | 198 ++++++++++++------
29 21 files changed, 300 insertions(+), 118 deletions(-)
30 copy third_party/python/voluptuous/{ => voluptuous-0.12.1.dist-info}/COPYING (100%)
31 rename third_party/python/voluptuous/{README.md => voluptuous-0.12.1.dist-info/METADATA} (83%)
32 create mode 100644 third_party/python/voluptuous/voluptuous-0.12.1.dist-info/RECORD
33 create mode 100644 third_party/python/voluptuous/voluptuous-0.12.1.dist-info/WHEEL
34 rename third_party/python/{psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info => voluptuous/voluptuous-0.12.1.dist-info}/top_level.txt (100%)
36 diff --git a/python/mach/mach/config.py b/python/mach/mach/config.py
37 index 7210eca82308..6605e381b310 100644
38 --- a/python/mach/mach/config.py
39 +++ b/python/mach/mach/config.py
40 @@ -17,6 +17,7 @@ settings are available.
41 from __future__ import absolute_import, unicode_literals
44 +import collections.abc
48 @@ -144,7 +145,7 @@ def reraise_attribute_error(func):
52 -class ConfigSettings(collections.Mapping):
53 +class ConfigSettings(collections.abc.Mapping):
54 """Interface for configuration settings.
56 This is the main interface to the configuration.
57 @@ -190,7 +191,7 @@ class ConfigSettings(collections.Mapping):
58 will result in exceptions being raised.
61 - class ConfigSection(collections.MutableMapping, object):
62 + class ConfigSection(collections.abc.MutableMapping, object):
63 """Represents an individual config section."""
64 def __init__(self, config, name, settings):
65 object.__setattr__(self, '_config', config)
66 diff --git a/python/mach/mach/decorators.py b/python/mach/mach/decorators.py
67 index 27f7f34a6ddc..8b1a49c76dc2 100644
68 --- a/python/mach/mach/decorators.py
69 +++ b/python/mach/mach/decorators.py
70 @@ -6,6 +6,7 @@ from __future__ import absolute_import, unicode_literals
74 +import collections.abc
76 from .base import MachError
77 from .registrar import Registrar
78 @@ -140,7 +141,7 @@ def CommandProvider(cls):
79 'Conditions argument must take a list ' + \
80 'of functions. Found %s instead.'
82 - if not isinstance(command.conditions, collections.Iterable):
83 + if not isinstance(command.conditions, collections.abc.Iterable):
84 msg = msg % (command.name, type(command.conditions))
87 diff --git a/python/mach/mach/main.py b/python/mach/mach/main.py
88 index a7311806d0a4..0f2c6a16d9d2 100644
89 --- a/python/mach/mach/main.py
90 +++ b/python/mach/mach/main.py
91 @@ -16,7 +16,7 @@ import os
95 -from collections import Iterable
96 +from collections.abc import Iterable
98 from six import string_types
100 diff --git a/python/mozbuild/mozbuild/backend/configenvironment.py b/python/mozbuild/mozbuild/backend/configenvironment.py
101 index 20d1a9fa69da..898cabb73b0b 100644
102 --- a/python/mozbuild/mozbuild/backend/configenvironment.py
103 +++ b/python/mozbuild/mozbuild/backend/configenvironment.py
104 @@ -9,7 +9,8 @@ import six
108 -from collections import Iterable, OrderedDict
109 +from collections.abc import Iterable
110 +from collections import OrderedDict
111 from types import ModuleType
113 import mozpack.path as mozpath
114 diff --git a/python/mozbuild/mozbuild/makeutil.py b/python/mozbuild/mozbuild/makeutil.py
115 index 4da1a3b268e5..4ce56848cd6c 100644
116 --- a/python/mozbuild/mozbuild/makeutil.py
117 +++ b/python/mozbuild/mozbuild/makeutil.py
118 @@ -7,7 +7,7 @@ from __future__ import absolute_import, print_function, unicode_literals
122 -from collections import Iterable
123 +from collections.abc import Iterable
126 class Makefile(object):
127 diff --git a/python/mozbuild/mozbuild/util.py b/python/mozbuild/mozbuild/util.py
128 index 044cf645c1f1..ed30088c0059 100644
129 --- a/python/mozbuild/mozbuild/util.py
130 +++ b/python/mozbuild/mozbuild/util.py
131 @@ -9,6 +9,7 @@ from __future__ import absolute_import, print_function, unicode_literals
135 +import collections.abc
139 @@ -782,7 +783,7 @@ class HierarchicalStringList(object):
140 self._strings = StrictOrderingOnAppendList()
143 - class StringListAdaptor(collections.Sequence):
144 + class StringListAdaptor(collections.abc.Sequence):
145 def __init__(self, hsl):
148 diff --git a/taskcluster/taskgraph/util/schema.py b/taskcluster/taskgraph/util/schema.py
149 index 0b15d3d5fd6f..29e36793f336 100644
150 --- a/taskcluster/taskgraph/util/schema.py
151 +++ b/taskcluster/taskgraph/util/schema.py
152 @@ -7,6 +7,7 @@ from __future__ import absolute_import, print_function, unicode_literals
156 +import collections.abc
159 from six import text_type, iteritems
160 @@ -160,7 +161,7 @@ def check_schema(schema):
161 'Unexpected type in YAML schema: {} @ {}'.format(
162 type(k).__name__, path))
164 - if isinstance(sch, collections.Mapping):
165 + if isinstance(sch, collections.abc.Mapping):
166 for k, v in iteritems(sch):
167 child = "{}[{!r}]".format(path, k)
168 check_identifier(child, k)
169 diff --git a/testing/mozbase/manifestparser/manifestparser/filters.py b/testing/mozbase/manifestparser/manifestparser/filters.py
170 index 287ee033b222..b1d6080031f6 100644
171 --- a/testing/mozbase/manifestparser/manifestparser/filters.py
172 +++ b/testing/mozbase/manifestparser/manifestparser/filters.py
173 @@ -12,7 +12,8 @@ from __future__ import absolute_import
177 -from collections import defaultdict, MutableSequence
178 +from collections import defaultdict
179 +from collections.abc import MutableSequence
182 from six import string_types
183 diff --git a/third_party/python/gyp/pylib/gyp/common.py b/third_party/python/gyp/pylib/gyp/common.py
184 index b268d229a483..2195e1e458e3 100644
185 --- a/third_party/python/gyp/pylib/gyp/common.py
186 +++ b/third_party/python/gyp/pylib/gyp/common.py
188 from __future__ import with_statement
191 +import collections.abc
195 @@ -494,7 +495,7 @@ def uniquer(seq, idfun=None):
198 # Based on http://code.activestate.com/recipes/576694/.
199 -class OrderedSet(collections.MutableSet):
200 +class OrderedSet(collections.abc.MutableSet):
201 def __init__(self, iterable=None):
203 end += [None, end, end] # sentinel node for doubly linked list
204 diff --git a/third_party/python/requirements.in b/third_party/python/requirements.in
205 index b65c92e30c09..1127ab280c84 100644
206 --- a/third_party/python/requirements.in
207 +++ b/third_party/python/requirements.in
208 @@ -42,4 +42,4 @@ requests==2.9.1
214 diff --git a/third_party/python/requirements.txt b/third_party/python/requirements.txt
215 index fd0b6cb1a181..5742c9cdb771 100644
216 --- a/third_party/python/requirements.txt
217 +++ b/third_party/python/requirements.txt
218 @@ -172,9 +172,9 @@ urllib3==1.25.9 \
219 --hash=sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527 \
220 --hash=sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115 \
222 -voluptuous==0.11.5 \
223 - --hash=sha256:303542b3fc07fb52ec3d7a1c614b329cdbee13a9d681935353d8ea56a7bfa9f1 \
224 - --hash=sha256:567a56286ef82a9d7ae0628c5842f65f516abcb496e74f3f59f1d7b28df314ef \
225 +voluptuous==0.12.1 \
226 + --hash=sha256:663572419281ddfaf4b4197fd4942d181630120fb39b333e3adad70aeb56444b \
227 + --hash=sha256:8ace33fcf9e6b1f59406bfaf6b8ec7bcc44266a9f29080b4deb4fe6ff2492386
228 # via -r requirements-mach-vendor-python.in
230 # WARNING: The following packages were not pinned, but pip requires them to be
231 diff --git a/third_party/python/voluptuous/COPYING b/third_party/python/voluptuous/voluptuous-0.12.1.dist-info/COPYING
232 similarity index 100%
233 copy from third_party/python/voluptuous/COPYING
234 copy to third_party/python/voluptuous/voluptuous-0.12.1.dist-info/COPYING
235 diff --git a/third_party/python/voluptuous/README.md b/third_party/python/voluptuous/voluptuous-0.12.1.dist-info/METADATA
237 rename from third_party/python/voluptuous/README.md
238 rename to third_party/python/voluptuous/voluptuous-0.12.1.dist-info/METADATA
239 index 46e2288f4bea..914a5761d63a 100644
240 --- a/third_party/python/voluptuous/README.md
241 +++ b/third_party/python/voluptuous/voluptuous-0.12.1.dist-info/METADATA
243 +<<<<<<<< HEAD:third_party/python/voluptuous/README.md
244 +|||||||| parent of 8f49ebff11d78 (Bug 1719144 - Update voluptuous. r=firefox-build-system-reviewers,mhentges):third_party/python/voluptuous/voluptuous-0.11.5.dist-info/METADATA
245 +Metadata-Version: 2.1
248 +Summary: # Voluptuous is a Python data validation library
249 +Home-page: https://github.com/alecthomas/voluptuous
251 +Author-email: alec@swapoff.org
253 +Download-URL: https://pypi.python.org/pypi/voluptuous
255 +Classifier: Development Status :: 5 - Production/Stable
256 +Classifier: Intended Audience :: Developers
257 +Classifier: License :: OSI Approved :: BSD License
258 +Classifier: Operating System :: OS Independent
259 +Classifier: Programming Language :: Python :: 2
260 +Classifier: Programming Language :: Python :: 2.7
261 +Classifier: Programming Language :: Python :: 3
262 +Classifier: Programming Language :: Python :: 3.6
263 +Classifier: Programming Language :: Python :: 3.7
264 +Description-Content-Type: text/markdown
267 +Metadata-Version: 2.1
271 +Home-page: https://github.com/alecthomas/voluptuous
273 +Author-email: alec@swapoff.org
275 +Download-URL: https://pypi.python.org/pypi/voluptuous
277 +Classifier: Development Status :: 5 - Production/Stable
278 +Classifier: Intended Audience :: Developers
279 +Classifier: License :: OSI Approved :: BSD License
280 +Classifier: Operating System :: OS Independent
281 +Classifier: Programming Language :: Python :: 2
282 +Classifier: Programming Language :: Python :: 2.7
283 +Classifier: Programming Language :: Python :: 3
284 +Classifier: Programming Language :: Python :: 3.6
285 +Classifier: Programming Language :: Python :: 3.7
286 +Classifier: Programming Language :: Python :: 3.8
287 +Classifier: Programming Language :: Python :: 3.9
288 +Description-Content-Type: text/markdown
291 +# CONTRIBUTIONS ONLY
293 +**What does this mean?** I do not have time to fix issues myself. The only way fixes or new features will be added is by people submitting PRs.
295 +**Current status:** Voluptuous is largely feature stable. There hasn't been a need to add new features in a while, but there are some bugs that should be fixed.
297 +**Why?** I no longer use Voluptuous personally (in fact I no longer regularly write Python code). Rather than leave the project in a limbo of people filing issues and wondering why they're not being worked on, I believe this notice will more clearly set expectations.
299 +>>>>>>>> 8f49ebff11d78 (Bug 1719144 - Update voluptuous. r=firefox-build-system-reviewers,mhentges):third_party/python/voluptuous/voluptuous-0.12.1.dist-info/METADATA
300 # Voluptuous is a Python data validation library
302 -[![Build Status](https://travis-ci.org/alecthomas/voluptuous.png)](https://travis-ci.org/alecthomas/voluptuous)
303 -[![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)
304 +[![image](https://img.shields.io/pypi/v/voluptuous.svg)](https://python.org/pypi/voluptuous)
305 +[![image](https://img.shields.io/pypi/l/voluptuous.svg)](https://python.org/pypi/voluptuous)
306 +[![image](https://img.shields.io/pypi/pyversions/voluptuous.svg)](https://python.org/pypi/voluptuous)
307 +[![Build Status](https://travis-ci.org/alecthomas/voluptuous.svg)](https://travis-ci.org/alecthomas/voluptuous)
308 +[![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.svg)](https://gitter.im/alecthomas/Lobby)
310 Voluptuous, *despite* the name, is a Python data validation library. It
311 is primarily intended for validating data coming into Python as JSON,
312 @@ -32,6 +92,28 @@ The documentation is provided [here](http://alecthomas.github.io/voluptuous/).
314 See [CHANGELOG.md](https://github.com/alecthomas/voluptuous/blob/master/CHANGELOG.md).
316 +## Why use Voluptuous over another validation library?
318 +**Validators are simple callables:**
319 +No need to subclass anything, just use a function.
321 +**Errors are simple exceptions:**
322 +A validator can just `raise Invalid(msg)` and expect the user to get
325 +**Schemas are basic Python data structures:**
326 +Should your data be a dictionary of integer keys to strings?
327 +`{int: str}` does what you expect. List of integers, floats or
328 +strings? `[int, float, str]`.
330 +**Designed from the ground up for validating more than just forms:**
331 +Nested data structures are treated in the same way as any other
332 +type. Need a list of dictionaries? `[{}]`
335 +Types in the schema are checked as types. Values are compared as
336 +values. Callables are called to validate. Simple.
338 ## Show me an example
340 Twitter's [user search API](https://dev.twitter.com/rest/reference/get/users/search) accepts
341 @@ -189,9 +271,9 @@ True
348 -URL's in the schema are matched by using `urlparse` library.
349 +URLs in the schema are matched by using `urlparse` library.
352 >>> from voluptuous import Url
353 @@ -679,35 +761,13 @@ cross-field validator will not run:
354 s({'password':'123', 'password_again': 1337})
360 Voluptuous is using nosetests:
365 -## Why use Voluptuous over another validation library?
367 -**Validators are simple callables**
368 -: No need to subclass anything, just use a function.
370 -**Errors are simple exceptions.**
371 -: A validator can just `raise Invalid(msg)` and expect the user to get
374 -**Schemas are basic Python data structures.**
375 -: Should your data be a dictionary of integer keys to strings?
376 -`{int: str}` does what you expect. List of integers, floats or
377 -strings? `[int, float, str]`.
379 -**Designed from the ground up for validating more than just forms.**
380 -: Nested data structures are treated in the same way as any other
381 -type. Need a list of dictionaries? `[{}]`
384 -: Types in the schema are checked as types. Values are compared as
385 -values. Callables are called to validate. Simple.
387 ## Other libraries and inspirations
389 Voluptuous is heavily inspired by
390 diff --git a/third_party/python/voluptuous/voluptuous-0.12.1.dist-info/RECORD b/third_party/python/voluptuous/voluptuous-0.12.1.dist-info/RECORD
392 index 000000000000..5f7fde6e52bc
394 +++ b/third_party/python/voluptuous/voluptuous-0.12.1.dist-info/RECORD
396 +voluptuous/__init__.py,sha256=tSYWPAIWee6YwcMK8hxmaiagG_swokUMeH8MluJLWZM,203
397 +voluptuous/error.py,sha256=fLRmJwKp0bqRGgBM34ztg9MTxhEOf465sbQcvJlEtAk,4026
398 +voluptuous/humanize.py,sha256=hZlhdN4aVeGDIXdtSTeyEbmku65SDPRuut3mOfuRQP0,1606
399 +voluptuous/schema_builder.py,sha256=xVJpf3uJMyS1CKwzDw3rEK39ebqDiF_A2Kbq4VnZ3Aw,43677
400 +voluptuous/util.py,sha256=RXLZ2b5y-A4az3teG6UpCx2UZcXpS11sIVCdORyKar8,3150
401 +voluptuous/validators.py,sha256=xZgyKH-EVqUHCHral5gzViXq4HfEjJEsGdQy7z6llc0,32798
402 +voluptuous-0.12.1.dist-info/COPYING,sha256=JHtJdren-k2J2Vh8qlCVVh60bcVFfyJ59ipitUUq3qk,1486
403 +voluptuous-0.12.1.dist-info/METADATA,sha256=OdEydy5NydPFFzAhP8qj_YqJsQPQvoIt5ZT1t8B14Ok,20120
404 +voluptuous-0.12.1.dist-info/WHEEL,sha256=S6zePDbUAjzMmpYOg2cHDxuYFWw7WiOXt6ogM6hIB5Q,92
405 +voluptuous-0.12.1.dist-info/top_level.txt,sha256=TTdVb7M-vndb67UqTmAxuVjpAUakrlAWJYqvo3w4Iqc,11
406 +voluptuous-0.12.1.dist-info/RECORD,,
407 diff --git a/third_party/python/voluptuous/voluptuous-0.12.1.dist-info/WHEEL b/third_party/python/voluptuous/voluptuous-0.12.1.dist-info/WHEEL
409 index 000000000000..4c2552761e69
411 +++ b/third_party/python/voluptuous/voluptuous-0.12.1.dist-info/WHEEL
414 +<<<<<<<< HEAD:testing/web-platform/tests/tools/third_party/six/six-1.13.0.dist-info/WHEEL
415 +Generator: bdist_wheel (0.32.1)
416 +|||||||| parent of 8f49ebff11d78 (Bug 1719144 - Update voluptuous. r=firefox-build-system-reviewers,mhentges):third_party/python/voluptuous/voluptuous-0.11.5.dist-info/WHEEL
417 +Generator: bdist_wheel (0.31.1)
419 +Generator: bdist_wheel (0.36.1)
420 +>>>>>>>> 8f49ebff11d78 (Bug 1719144 - Update voluptuous. r=firefox-build-system-reviewers,mhentges):third_party/python/voluptuous/voluptuous-0.12.1.dist-info/WHEEL
421 +Root-Is-Purelib: true
424 diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/top_level.txt b/third_party/python/voluptuous/voluptuous-0.12.1.dist-info/top_level.txt
425 similarity index 100%
426 rename from third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/top_level.txt
427 rename to third_party/python/voluptuous/voluptuous-0.12.1.dist-info/top_level.txt
428 diff --git a/third_party/python/voluptuous/voluptuous/__init__.py b/third_party/python/voluptuous/voluptuous/__init__.py
429 index 10236d5a6670..4d09fe670fd4 100644
430 --- a/third_party/python/voluptuous/voluptuous/__init__.py
431 +++ b/third_party/python/voluptuous/voluptuous/__init__.py
432 @@ -5,5 +5,5 @@ from voluptuous.validators import *
433 from voluptuous.util import *
434 from voluptuous.error import *
436 -__version__ = '0.11.5'
437 +__version__ = '0.12.1'
438 __author__ = 'alecthomas'
439 diff --git a/third_party/python/voluptuous/voluptuous/error.py b/third_party/python/voluptuous/voluptuous/error.py
440 index 86c4e0a35933..97f37d2c7c4b 100644
441 --- a/third_party/python/voluptuous/voluptuous/error.py
442 +++ b/third_party/python/voluptuous/voluptuous/error.py
443 @@ -142,11 +142,11 @@ class BooleanInvalid(Invalid):
446 class UrlInvalid(Invalid):
447 - """The value is not a url."""
448 + """The value is not a URL."""
451 class EmailInvalid(Invalid):
452 - """The value is not a email."""
453 + """The value is not an email address."""
456 class FileInvalid(Invalid):
457 diff --git a/third_party/python/voluptuous/voluptuous/schema_builder.py b/third_party/python/voluptuous/voluptuous/schema_builder.py
458 index 8d7a81a3e3e5..df19c8da2ddf 100644
459 --- a/third_party/python/voluptuous/voluptuous/schema_builder.py
460 +++ b/third_party/python/voluptuous/voluptuous/schema_builder.py
461 @@ -22,6 +22,11 @@ else:
465 +if sys.version_info >= (3, 3):
466 + _Mapping = collections.abc.Mapping
468 + _Mapping = collections.Mapping
470 """Schema validation for Python data structures.
472 Given eg. a nested data structure like this:
473 @@ -280,7 +285,7 @@ class Schema(object):
474 return schema.__voluptuous_compile__(self)
475 if isinstance(schema, Object):
476 return self._compile_object(schema)
477 - if isinstance(schema, collections.Mapping):
478 + if isinstance(schema, _Mapping):
479 return self._compile_dict(schema)
480 elif isinstance(schema, list):
481 return self._compile_list(schema)
482 @@ -610,11 +615,11 @@ class Schema(object):
483 if not isinstance(data, seq_type):
484 raise er.SequenceTypeInvalid('expected a %s' % seq_type_name, path)
486 - # Empty seq schema, allow any data.
487 + # Empty seq schema, reject any data.
490 raise er.MultipleInvalid([
491 - er.ValueInvalid('not a valid value', [value]) for value in data
492 + er.ValueInvalid('not a valid value', path if path else data)
496 @@ -735,7 +740,7 @@ class Schema(object):
498 result = self.schema.copy()
500 - # returns the key that may have been passed as arugment to Marker constructor
501 + # returns the key that may have been passed as an argument to Marker constructor
502 def key_literal(key):
503 return (key.schema if isinstance(key, Marker) else key)
505 @@ -771,9 +776,10 @@ class Schema(object):
508 # recompile and send old object
509 + result_cls = type(self)
510 result_required = (required if required is not None else self.required)
511 result_extra = (extra if extra is not None else self.extra)
512 - return Schema(result, required=result_required, extra=result_extra)
513 + return result_cls(result, required=result_required, extra=result_extra)
516 def _compile_scalar(schema):
517 @@ -809,7 +815,7 @@ def _compile_scalar(schema):
518 def validate_callable(path, data):
521 - except ValueError as e:
523 raise er.ValueInvalid('not a valid value', path)
524 except er.Invalid as e:
526 @@ -1121,8 +1127,11 @@ class Inclusive(Optional):
530 - def __init__(self, schema, group_of_inclusion, msg=None):
531 - super(Inclusive, self).__init__(schema, msg=msg)
532 + def __init__(self, schema, group_of_inclusion,
533 + msg=None, description=None, default=UNDEFINED):
534 + super(Inclusive, self).__init__(schema, msg=msg,
536 + description=description)
537 self.group_of_inclusion = group_of_inclusion
540 diff --git a/third_party/python/voluptuous/voluptuous/util.py b/third_party/python/voluptuous/voluptuous/util.py
541 index 434c360c7e95..e0ff43f8500a 100644
542 --- a/third_party/python/voluptuous/voluptuous/util.py
543 +++ b/third_party/python/voluptuous/voluptuous/util.py
544 @@ -7,54 +7,62 @@ from voluptuous import validators
545 __author__ = 'tusharmakkar08'
549 + if sys.version_info[0] == 2 and isinstance(v, unicode):
557 """Transform a string to lower case.
559 >>> s = Schema(Lower)
563 - return str(v).lower()
564 + return _fix_str(v).lower()
568 """Transform a string to upper case.
570 >>> s = Schema(Upper)
574 - return str(v).upper()
575 + return _fix_str(v).upper()
579 """Capitalise a string.
581 >>> s = Schema(Capitalize)
585 - return str(v).capitalize()
586 + return _fix_str(v).capitalize()
590 """Title case a string.
592 >>> s = Schema(Title)
596 - return str(v).title()
597 + return _fix_str(v).title()
601 """Strip whitespace from a string.
603 >>> s = Schema(Strip)
604 >>> s(' hello world ')
607 - return str(v).strip()
608 + return _fix_str(v).strip()
611 class DefaultTo(object):
612 diff --git a/third_party/python/voluptuous/voluptuous/validators.py b/third_party/python/voluptuous/voluptuous/validators.py
613 index d5e3ed598051..fac9cc7717c0 100644
614 --- a/third_party/python/voluptuous/voluptuous/validators.py
615 +++ b/third_party/python/voluptuous/voluptuous/validators.py
616 @@ -192,33 +192,44 @@ class _WithSubValidators(object):
617 def __init__(self, *validators, **kwargs):
618 self.validators = validators
619 self.msg = kwargs.pop('msg', None)
620 + self.required = kwargs.pop('required', False)
621 + self.discriminant = kwargs.pop('discriminant', None)
623 def __voluptuous_compile__(self, schema):
626 - for v in self.validators
628 + self._compiled = []
629 + old_required = schema.required
630 + self.schema = schema
631 + for v in self.validators:
632 + schema.required = self.required
633 + self._compiled.append(schema._compile(v))
634 + schema.required = old_required
637 def _run(self, path, value):
638 + if self.discriminant is not None:
640 + self.schema._compile(v)
641 + for v in self.discriminant(value, self.validators)
644 return self._exec(self._compiled, value, path)
646 def __call__(self, v):
647 return self._exec((Schema(val) for val in self.validators), v)
650 return '%s(%s, msg=%r)' % (
651 self.__class__.__name__,
652 ", ".join(repr(v) for v in self.validators),
657 class Any(_WithSubValidators):
658 """Use the first validated value.
660 :param msg: Message to deliver to user if validation fails.
661 - :param kwargs: All other keyword arguments are passed to the sub-Schema constructors.
662 + :param kwargs: All other keyword arguments are passed to the sub-schema constructors.
663 :returns: Return value of the first validator that passes.
665 >>> validate = Schema(Any('true', 'false',
666 @@ -262,13 +273,57 @@ class Any(_WithSubValidators):
670 +class Union(_WithSubValidators):
671 + """Use the first validated value among those selected by discriminant.
673 + :param msg: Message to deliver to user if validation fails.
674 + :param discriminant(value, validators): Returns the filtered list of validators based on the value.
675 + :param kwargs: All other keyword arguments are passed to the sub-schema constructors.
676 + :returns: Return value of the first validator that passes.
678 + >>> validate = Schema(Union({'type':'a', 'a_val':'1'},{'type':'b', 'b_val':'2'},
679 + ... discriminant=lambda val, alt: filter(
680 + ... lambda v : v['type'] == val['type'] , alt)))
681 + >>> validate({'type':'a', 'a_val':'1'}) == {'type':'a', 'a_val':'1'}
683 + >>> with raises(MultipleInvalid, "not a valid value for dictionary value @ data['b_val']"):
684 + ... validate({'type':'b', 'b_val':'5'})
686 + ```discriminant({'type':'b', 'a_val':'5'}, [{'type':'a', 'a_val':'1'},{'type':'b', 'b_val':'2'}])``` is invoked
688 + Without the discriminant, the exception would be "extra keys not allowed @ data['b_val']"
691 + def _exec(self, funcs, v, path=None):
698 + return func(path, v)
699 + except Invalid as e:
700 + if error is None or len(e.path) > len(error.path):
704 + raise error if self.msg is None else AnyInvalid(
705 + self.msg, path=path)
706 + raise AnyInvalid(self.msg or 'no valid value found',
714 class All(_WithSubValidators):
715 """Value must pass all validators.
717 The output of each validator is passed as input to the next.
719 :param msg: Message to deliver to user if validation fails.
720 - :param kwargs: All other keyword arguments are passed to the sub-Schema constructors.
721 + :param kwargs: All other keyword arguments are passed to the sub-schema constructors.
723 >>> validate = Schema(All('10', Coerce(int)))
725 @@ -303,7 +358,7 @@ class Match(object):
726 >>> with raises(MultipleInvalid, 'expected string or buffer'):
729 - Pattern may also be a _compiled regular expression:
730 + Pattern may also be a compiled regular expression:
732 >>> validate = Schema(Match(re.compile(r'0x[A-F0-9]+', re.I)))
733 >>> validate('0x123ef4')
734 @@ -361,38 +416,38 @@ def _url_validation(v):
738 -@message('expected an Email', cls=EmailInvalid)
739 +@message('expected an email address', cls=EmailInvalid)
741 - """Verify that the value is an Email or not.
742 + """Verify that the value is an email address or not.
744 >>> s = Schema(Email())
745 - >>> with raises(MultipleInvalid, 'expected an Email'):
746 + >>> with raises(MultipleInvalid, 'expected an email address'):
748 - >>> with raises(MultipleInvalid, 'expected an Email'):
749 + >>> with raises(MultipleInvalid, 'expected an email address'):
751 - >>> with raises(MultipleInvalid, 'expected an Email'):
752 + >>> with raises(MultipleInvalid, 'expected an email address'):
758 if not v or "@" not in v:
759 - raise EmailInvalid("Invalid Email")
760 + raise EmailInvalid("Invalid email address")
761 user_part, domain_part = v.rsplit('@', 1)
763 if not (USER_REGEX.match(user_part) and DOMAIN_REGEX.match(domain_part)):
764 - raise EmailInvalid("Invalid Email")
765 + raise EmailInvalid("Invalid email address")
771 -@message('expected a Fully qualified domain name URL', cls=UrlInvalid)
772 +@message('expected a fully qualified domain name URL', cls=UrlInvalid)
774 - """Verify that the value is a Fully qualified domain name URL.
775 + """Verify that the value is a fully qualified domain name URL.
777 >>> s = Schema(FqdnUrl())
778 - >>> with raises(MultipleInvalid, 'expected a Fully qualified domain name URL'):
779 + >>> with raises(MultipleInvalid, 'expected a fully qualified domain name URL'):
780 ... s("http://localhost/")
781 >>> s('http://w3.org')
783 @@ -423,29 +478,29 @@ def Url(v):
787 -@message('not a file', cls=FileInvalid)
788 +@message('Not a file', cls=FileInvalid)
791 """Verify the file exists.
793 >>> os.path.basename(IsFile()(__file__)).startswith('validators.py')
795 - >>> with raises(FileInvalid, 'not a file'):
796 + >>> with raises(FileInvalid, 'Not a file'):
797 ... IsFile()("random_filename_goes_here.py")
798 >>> with raises(FileInvalid, 'Not a file'):
804 return os.path.isfile(v)
806 raise FileInvalid('Not a file')
808 raise FileInvalid('Not a file')
811 -@message('not a directory', cls=DirInvalid)
812 +@message('Not a directory', cls=DirInvalid)
815 """Verify the directory exists.
816 @@ -487,20 +542,20 @@ def PathExists(v):
817 raise PathInvalid("Not a Path")
820 -def Maybe(validator):
821 +def Maybe(validator, msg=None):
822 """Validate that the object matches given validator or is None.
824 - :raises Invalid: if the value does not match the given validator and is not
826 + :raises Invalid: If the value does not match the given validator and is not
829 >>> s = Schema(Maybe(int))
832 >>> with raises(Invalid):
836 - return Any(None, validator)
837 + return Any(validator, None, msg=msg)
841 @@ -533,23 +588,30 @@ class Range(object):
844 def __call__(self, v):
845 - if self.min_included:
846 - if self.min is not None and not v >= self.min:
847 - raise RangeInvalid(
848 - self.msg or 'value must be at least %s' % self.min)
850 - if self.min is not None and not v > self.min:
851 - raise RangeInvalid(
852 - self.msg or 'value must be higher than %s' % self.min)
853 - if self.max_included:
854 - if self.max is not None and not v <= self.max:
855 - raise RangeInvalid(
856 - self.msg or 'value must be at most %s' % self.max)
858 - if self.max is not None and not v < self.max:
859 - raise RangeInvalid(
860 - self.msg or 'value must be lower than %s' % self.max)
863 + if self.min_included:
864 + if self.min is not None and not v >= self.min:
865 + raise RangeInvalid(
866 + self.msg or 'value must be at least %s' % self.min)
868 + if self.min is not None and not v > self.min:
869 + raise RangeInvalid(
870 + self.msg or 'value must be higher than %s' % self.min)
871 + if self.max_included:
872 + if self.max is not None and not v <= self.max:
873 + raise RangeInvalid(
874 + self.msg or 'value must be at most %s' % self.max)
876 + if self.max is not None and not v < self.max:
877 + raise RangeInvalid(
878 + self.msg or 'value must be lower than %s' % self.max)
882 + # Objects that lack a partial ordering, e.g. None or strings will raise TypeError
884 + raise RangeInvalid(
885 + self.msg or 'invalid value or type (must have a partial ordering)')
888 return ('Range(min=%r, max=%r, min_included=%r,'
889 @@ -579,32 +641,44 @@ class Clamp(object):
892 def __call__(self, v):
893 - if self.min is not None and v < self.min:
895 - if self.max is not None and v > self.max:
899 + if self.min is not None and v < self.min:
901 + if self.max is not None and v > self.max:
905 + # Objects that lack a partial ordering, e.g. None or strings will raise TypeError
907 + raise RangeInvalid(
908 + self.msg or 'invalid value or type (must have a partial ordering)')
911 return 'Clamp(min=%s, max=%s)' % (self.min, self.max)
914 class Length(object):
915 """The length of a value must be in a certain range."""
917 def __init__(self, min=None, max=None, msg=None):
922 def __call__(self, v):
923 - if self.min is not None and len(v) < self.min:
924 - raise LengthInvalid(
925 - self.msg or 'length of value must be at least %s' % self.min)
926 - if self.max is not None and len(v) > self.max:
927 - raise LengthInvalid(
928 - self.msg or 'length of value must be at most %s' % self.max)
931 + if self.min is not None and len(v) < self.min:
932 + raise LengthInvalid(
933 + self.msg or 'length of value must be at least %s' % self.min)
934 + if self.max is not None and len(v) > self.max:
935 + raise LengthInvalid(
936 + self.msg or 'length of value must be at most %s' % self.max)
939 + # Objects that havbe no length e.g. None or strings will raise TypeError
941 + raise RangeInvalid(
942 + self.msg or 'invalid value or type')
945 return 'Length(min=%s, max=%s)' % (self.min, self.max)
946 @@ -663,27 +737,29 @@ class In(object):
950 - raise InInvalid(self.msg or 'value is not allowed')
951 + raise InInvalid(self.msg or
952 + 'value must be one of {}'.format(sorted(self.container)))
956 return 'In(%s)' % (self.container,)
960 """Validate that a value is not in a collection."""
962 def __init__(self, container, msg=None):
963 self.container = container
966 def __call__(self, v):
968 check = v in self.container
972 - raise NotInInvalid(self.msg or 'value is not allowed')
973 + raise NotInInvalid(self.msg or
974 + 'value must not be one of {}'.format(sorted(self.container)))
978 @@ -722,7 +798,7 @@ class ExactSequence(object):
981 :param msg: Message to deliver to user if validation fails.
982 - :param kwargs: All other keyword arguments are passed to the sub-Schema
983 + :param kwargs: All other keyword arguments are passed to the sub-schema
986 >>> from voluptuous import Schema, ExactSequence
987 @@ -887,7 +963,7 @@ class Unordered(object):
988 class Number(object):
990 Verify the number of digits that are present in the number(Precision),
991 - and the decimal places(Scale)
992 + and the decimal places(Scale).
994 :raises Invalid: If the value does not match the provided Precision and Scale.
996 @@ -951,13 +1027,13 @@ class SomeOf(_WithSubValidators):
997 The output of each validator is passed as input to the next.
999 :param min_valid: Minimum number of valid schemas.
1000 - :param validators: a list of schemas or validators to match input against
1001 + :param validators: List of schemas or validators to match input against.
1002 :param max_valid: Maximum number of valid schemas.
1003 :param msg: Message to deliver to user if validation fails.
1004 - :param kwargs: All other keyword arguments are passed to the sub-Schema constructors.
1005 + :param kwargs: All other keyword arguments are passed to the sub-schema constructors.
1007 - :raises NotEnoughValid: if the minimum number of validations isn't met
1008 - :raises TooManyValid: if the more validations than the given amount is met
1009 + :raises NotEnoughValid: If the minimum number of validations isn't met.
1010 + :raises TooManyValid: If the maximum number of validations is exceeded.
1012 >>> validate = Schema(SomeOf(min_valid=2, validators=[Range(1, 5), Any(float, int), 6.6]))