Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/configobj.rst
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ validate

.. code-block:: python

validate(validator, preserve_errors=False, copy=False)
validate(validator, preserve_errors=False, copy=False, strict_spec=False)

.. code-block:: python

Expand Down
21 changes: 19 additions & 2 deletions src/configobj/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2098,7 +2098,7 @@ def write(self, outfile=None, section=None):
h.write(output_bytes)

def validate(self, validator, preserve_errors=False, copy=False,
section=None):
section=None, strict_spec=False):
"""
Test the ConfigObj against a configspec.

Expand Down Expand Up @@ -2129,6 +2129,13 @@ def validate(self, validator, preserve_errors=False, copy=False,

You must have the validate module to use ``preserve_errors=True``.

If ``strict_spec`` is ``True`` (``False`` is default) then instead of
ignoring entries undefined by the configspec, unrecognized entries will
fail validation and be inserted into the return dictionary with an
informational ``ValidateError``.

You must have the validate module to use ``strict_spec=True``.

You can then use the ``flatten_errors`` function to turn your nested
results dictionary into a flattened list of failures - useful for
displaying meaningful error messages.
Expand All @@ -2139,7 +2146,8 @@ def validate(self, validator, preserve_errors=False, copy=False,
if preserve_errors:
# We do this once to remove a top level dependency on the validate module
# Which makes importing configobj faster
from configobj.validate import VdtMissingValue
from configobj.validate import VdtMissingDefinition, VdtMissingValue
self._vdtMissingDefinition = VdtMissingDefinition
self._vdtMissingValue = VdtMissingValue

section = self
Expand Down Expand Up @@ -2263,6 +2271,15 @@ def validate_entry(entry, spec, val, missing, ret_true, ret_false):
ret_false = False
msg = 'Section %r was provided as a single value' % entry
out[entry] = validator.baseErrorClass(msg)
if strict_spec:
for entry in unvalidated:
if not preserve_errors:
out[entry] = False
else:
ret_false = False
msg = 'Value %r was unrecognized' % entry
out[entry] = self._vdtMissingDefinition(msg)


# Missing sections will have been created as empty ones when the
# configspec was read.
Expand Down
5 changes: 5 additions & 0 deletions src/configobj/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
'VdtValueTooBigError',
'VdtValueTooShortError',
'VdtValueTooLongError',
'VdtMissingDefinition',
'VdtMissingValue',
'Validator',
'is_integer',
Expand Down Expand Up @@ -368,6 +369,10 @@ class ValidateError(Exception):
"""


class VdtMissingDefinition(ValidateError):
"""A check was requested on an undefined entry"""


class VdtMissingValue(ValidateError):
"""No value was supplied to a check that needed one."""

Expand Down
5 changes: 5 additions & 0 deletions src/tests/test_validate_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ def test_validate_preserve_errors(conf):
assert not section['missing-subsection']


def test_validate_strict_spec(conf):
conf['undefined-entry'] = True
assert not conf.validate(Validator(), strict_spec=True)


def test_validate_extra_values(conf):
conf.validate(Validator(), preserve_errors=True)

Expand Down