diff --git a/CHANGELOG.md b/CHANGELOG.md index aad10a6..f8032ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * [#539](https://github.com/alecthomas/voluptuous/pull/539): Raise `Invalid` instead of leaking `TypeError`/`ValueError` for non-numeric input to the `Number` validator * [#542](https://github.com/alecthomas/voluptuous/pull/542): Raise `Invalid` instead of leaking a raw `TypeError` for `Infinity`/`NaN` input to the `Number` validator +* [#299](https://github.com/alecthomas/voluptuous/issues/299): Accept `Mapping` instances, not only `dict`, when validating dictionary schemas ## [0.16.0] diff --git a/voluptuous/schema_builder.py b/voluptuous/schema_builder.py index 3673e86..0d6aa50 100644 --- a/voluptuous/schema_builder.py +++ b/voluptuous/schema_builder.py @@ -531,7 +531,7 @@ def _compile_dict(self, schema): g.append(node) def validate_dict(path, data): - if not isinstance(data, dict): + if not isinstance(data, collections.abc.Mapping): raise er.DictInvalid('expected a dictionary', path) errors = [] @@ -572,7 +572,7 @@ def validate_dict(path, data): if errors: raise er.MultipleInvalid(errors) - out = data.__class__() + out = data.__class__() if isinstance(data, dict) else {} return base_validate(path, data.items(), out) return validate_dict diff --git a/voluptuous/tests/tests.py b/voluptuous/tests/tests.py index 0608548..17e4e65 100644 --- a/voluptuous/tests/tests.py +++ b/voluptuous/tests/tests.py @@ -991,6 +991,25 @@ def test_schema_empty_dict(): assert False, "Did not raise correct Invalid" +def test_schema_accepts_mapping(): + class QueryArgs(collections.abc.Mapping): + def __init__(self, data): + self._data = data + + def __getitem__(self, key): + return self._data[key] + + def __iter__(self): + return iter(self._data) + + def __len__(self): + return len(self._data) + + s = Schema({'page': Coerce(int), Optional('q', default=''): str}) + + assert s(QueryArgs({'page': '1'})) == {'page': 1, 'q': ''} + + def test_schema_empty_dict_key(): """https://github.com/alecthomas/voluptuous/pull/434""" s = Schema({'var': []})