Repository: alecthomas/voluptuous

Issue:
Regression with Any and required
Hi,

I've observed a change of behaviour in the recent version of voluptuous

Here is a sample

```
from voluptuous import Schema, Any, Optional, All, Length

anyschema = Schema(Any({
    'name': All(unicode, Length(min=1, max=128)),
    Optional('protected'): bool,
}, {
    'name': All(unicode, Length(min=1, max=128)),
    Optional('dynamic_ip_required'): bool,
}, required=True))

try:
    anyschema({})
except Exception as e:
    print(e)
```	

```
$ sudo pip install 'voluptuous==0.11.5'
$ python t.py 
```
no output

```
$ sudo pip install 'voluptuous==0.10.5'
$ python t.py 
required key not provided @ data['name']
```

My understanding is the behavior of 0.10.5 is correct as we want a "required schema"
But it is not the case with 0.11.5 and above, required seem to be ignored

Is it normal?



Discussion:
alecthomas: Aaah, nice find. Thank you for the PR. Just one minor change and I'm happy to merge.
CedricCabessa: I just amend my commit with your remark. Thanks

However, I just found out that some case do not work :-(

like "dict in dict", the empty dict is considered valid but should not

```
schema = Schema(Any({'a': Any(float, int)},
                         {'b': int},
                         {'c' : {'aa': int}},
                         required=True))
```

If it's ok, I'll try to come back to you with another commit on this branch to fix that.
Or you can merge this and I'll open another PR, as you prefer

CedricCabessa: Here is the 2nd part of the patch to allow "recursive schema"
Let me know what you think :-)

Patch:
diff --git a/voluptuous/validators.py b/voluptuous/validators.py
index 8c6a86d..81566b9 100644
--- a/voluptuous/validators.py
+++ b/voluptuous/validators.py
@@ -192,12 +192,13 @@ class _WithSubValidators(object):
     def __init__(self, *validators, **kwargs):
         self.validators = validators
         self.msg = kwargs.pop('msg', None)
+        self.required = kwargs.pop('required', False)
 
     def __voluptuous_compile__(self, schema):
-        self._compiled = [
-            schema._compile(v)
-            for v in self.validators
-        ]
+        self._compiled = []
+        for v in self.validators:
+            schema.required = self.required
+            self._compiled.append(schema._compile(v))
         return self._run
 
     def _run(self, path, value):


Test patch:
diff --git a/voluptuous/tests/tests.py b/voluptuous/tests/tests.py
index bc16a12..f2db8da 100644
--- a/voluptuous/tests/tests.py
+++ b/voluptuous/tests/tests.py
@@ -1322,3 +1322,30 @@ def test_strip_util_handles_various_inputs():
     assert Strip(u"3") == u"3"
     assert Strip(b'\xe2\x98\x83'.decode("UTF-8")) == b'\xe2\x98\x83'.decode("UTF-8")
     assert Strip(u" aaa ") == u"aaa"
+
+
+def test_any_required():
+    schema = Schema(Any({'a': int}, {'b': str}, required=True))
+
+    try:
+        schema({})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "required key not provided @ data['a']")
+    else:
+        assert False, "Did not raise Invalid for MultipleInvalid"
+
+
+def test_any_required_with_subschema():
+    schema = Schema(Any({'a': Any(float, int)},
+                        {'b': int},
+                        {'c': {'aa': int}},
+                    required=True))
+
+    try:
+        schema({})
+    except MultipleInvalid as e:
+        assert_equal(str(e),
+                     "required key not provided @ data['a']")
+    else:
+        assert False, "Did not raise Invalid for MultipleInvalid"