diff --git a/CHANGELOG.md b/CHANGELOG.md index 4576953..9b97fd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,11 @@ ## [Unreleased] - [#280](https://github.com/alecthomas/voluptuous/pull/280): Making -`IsDir()`, `IsFile()` and `PathExists()` consistent between different python versions. + `IsDir()`, `IsFile()` and `PathExists()` consistent between different python versions. - [#279](https://github.com/alecthomas/voluptuous/pull/279): Treat Python 2 old-style classes like types when validating. +- [324](https://github.com/alecthomas/voluptuous/pull/324): + Default values MUST now pass validation just as any regular value. ## [0.10.5] diff --git a/voluptuous/schema_builder.py b/voluptuous/schema_builder.py index dff3e55..88a0203 100644 --- a/voluptuous/schema_builder.py +++ b/voluptuous/schema_builder.py @@ -334,11 +334,25 @@ def _compile_mapping(self, schema, invalid_msg=None): def validate_mapping(path, iterable, out): required_keys = all_required_keys.copy() - # keeps track of all default keys that haven't been filled - default_keys = all_default_keys.copy() + + # Build a map of all provided key-value pairs. + # The type(out) is used to retain ordering in case a ordered + # map type is provided as input. + key_value_map = type(out)() + for key, value in iterable: + key_value_map[key] = value + + # Insert default values for non-existing keys. + for key in all_default_keys: + if not isinstance(key.default, Undefined) and \ + key.schema not in key_value_map: + # A default value has been specified for this missing + # key, insert it. + key_value_map[key.schema] = key.default() + error = None errors = [] - for key, value in iterable: + for key, value in key_value_map.items(): key_path = path + [key] remove_key = False @@ -388,12 +402,10 @@ def validate_mapping(path, iterable, out): required_keys.discard(skey) break - # Key and value okay, mark any Required() fields as found. + # Key and value okay, mark as found in case it was + # a Required() field. required_keys.discard(skey) - # No need for a default if it was filled - default_keys.discard(skey) - break else: if remove_key: @@ -405,13 +417,6 @@ def validate_mapping(path, iterable, out): errors.append(er.Invalid('extra keys not allowed', key_path)) # else REMOVE_EXTRA: ignore the key so it's removed from output - # set defaults for any that can have defaults - for key in default_keys: - if not isinstance(key.default, Undefined): # if the user provides a default with the node - out[key.schema] = key.default() - if key in required_keys: - required_keys.discard(key) - # for any required keys left that weren't found and don't have defaults: for key in required_keys: msg = key.msg if hasattr(key, 'msg') and key.msg else 'required key not provided'