Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds the Validator flag 'purge_readonly' #413

Merged
merged 1 commit into from
Jun 20, 2018
Merged
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
15 changes: 11 additions & 4 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,22 @@ In Development

- Drop support for Python 2.6 and 3.3.
- New: The ``contains`` rule. (Frank Sachsenheim)
- Change ``allowed`` rule to use containers instead of lists. Closes :issue:`384` (Scott Crunkleton).
- New: All fields that are defined as ``readonly`` are removed from a document
when a validator has the ``purge_readonly`` flag set to ``True``. Closes
:issue:`240`. (Frank Sachsenheim)
- Change ``allowed`` rule to use containers instead of lists. Closes
:issue:`384`. (Scott Crunkleton)
- Check the ``empty`` rule against values of type ``Sized``.
- Remove ``utils.is_class``
- Remove ``Registry`` from top level namespace. Closes :issue:`354`.
- Fix: Normalization rules defined within the ``items`` rule are applied. (Evgeny Odegov)
- Fix: Normalization rules defined within the ``items`` rule are applied.
(Evgeny Odegov)
- Update homepage URL in package metadata. Closes :issue:`382`.
- Docs: add feature freeze note to CONTRIBUTING and note on Python support in README (Frank Sachsenheim).
- Docs: add feature freeze note to CONTRIBUTING and note on Python support in
README. (Frank Sachsenheim)
- Docs: add the intent of a ``dataclasses`` module to ROADMAP.md
- Docs: update README link. Make it point to the new PYPI website. (Frank Sachsenheim)
- Docs: update README link. Make it point to the new PYPI website.
(Frank Sachsenheim)
- Docs: improve documentation of the regex rule. Closes :issue:`389`.
- Docs: expand upon `validator` rules. Closes :issue:`320` (Connor Zapfel).
- Docs: include all errors definitions in API docs. Closes :issue:`404`.
Expand Down
15 changes: 15 additions & 0 deletions cerberus/tests/test_normalization.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-

from copy import deepcopy
from tempfile import NamedTemporaryFile

from cerberus import Validator, errors
Expand Down Expand Up @@ -517,3 +518,17 @@ def test_allow_unknown_with_purge_unknown_subdocument():
document = {'foo': {'bar': 'baz', 'corge': False}, 'thud': 'xyzzy'}
expected = {'foo': {'bar': 'baz', 'corge': False}}
assert_normalized(document, expected, schema, validator)


def test_purge_readonly():
schema = {
'description': {'type': 'string', 'maxlength': 500},
'last_updated': {'readonly': True}
}
validator = Validator(schema=schema, purge_readonly=True)
document = {
'description': 'it is a thing',
}
expected = deepcopy(document)
document['last_updated'] = 'future'
assert_normalized(document, expected, validator=validator)
2 changes: 1 addition & 1 deletion cerberus/tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def test_validated_schema_cache():
v = Validator({'foozifix': {'coerce': int}})
assert len(v._valid_schemas) == cache_size

max_cache_size = 152
max_cache_size = 155
assert cache_size <= max_cache_size, \
"There's an unexpected high amount (%s) of cached valid " \
"definition schemas. Unless you added further tests, " \
Expand Down
35 changes: 29 additions & 6 deletions cerberus/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ class BareValidator(object):
:param purge_unknown: See :attr:`~cerberus.Validator.purge_unknown`.
Defaults to to ``False``.
:type purge_unknown: :class:`bool`
:param purge_readonly: Removes all fields that are defined as ``readonly`` in the
normalization phase.
:type purge_readonly: :class:`bool`
:param error_handler: The error handler that formats the result of
:attr:`~cerberus.Validator.errors`.
When given as two-value tuple with an error-handler
Expand Down Expand Up @@ -135,7 +138,7 @@ def __init__(self, *args, **kwargs):
""" The arguments will be treated as with this signature:

__init__(self, schema=None, ignore_none_values=False,
allow_unknown=False, purge_unknown=False,
allow_unknown=False, purge_unknown=False, purge_readonly=False,
error_handler=errors.BasicErrorHandler)
"""

Expand Down Expand Up @@ -197,7 +200,7 @@ def __init_error_handler(kwargs):
def __store_config(self, args, kwargs):
""" Assign args to kwargs and store configuration. """
signature = ('schema', 'ignore_none_values', 'allow_unknown',
'purge_unknown')
'purge_unknown', 'purge_readonly')
for i, p in enumerate(signature[:len(args)]):
if p in kwargs:
raise TypeError("__init__ got multiple values for argument "
Expand Down Expand Up @@ -459,7 +462,7 @@ def _is_normalized(self, value):

@property
def purge_unknown(self):
""" If ``True`` unknown fields will be deleted from the document
""" If ``True``, unknown fields will be deleted from the document
unless a validation is called with disabled normalization.
Also see :ref:`purging-unknown-fields`. Type: :class:`bool` """
return self._config.get('purge_unknown', False)
Expand All @@ -468,6 +471,17 @@ def purge_unknown(self):
def purge_unknown(self, value):
self._config['purge_unknown'] = value

@property
def purge_readonly(self):
""" If ``True``, fields declared as readonly will be deleted from the
document unless a validation is called with disabled normalization.
Type: :class:`bool` """
return self._config.get('purge_readonly', False)

@purge_readonly.setter
def purge_readonly(self, value):
self._config['purge_readonly'] = value

@property
def root_allow_unknown(self):
""" The :attr:`~cerberus.Validator.allow_unknown` attribute of the
Expand Down Expand Up @@ -612,6 +626,8 @@ def __normalize_mapping(self, mapping, schema):
self.__normalize_rename_fields(mapping, schema)
if self.purge_unknown and not self.allow_unknown:
self._normalize_purge_unknown(mapping, schema)
if self.purge_readonly:
self.__normalize_purge_readonly(mapping, schema)
# Check `readonly` fields before applying default values because
# a field's schema definition might contain both `readonly` and
# `default`.
Expand Down Expand Up @@ -769,12 +785,19 @@ def __normalize_sequence_per_items(self, field, mapping, schema):
self._drop_nodes_from_errorpaths(validator._errors, [], [2])
self._error(validator._errors)

@staticmethod
def __normalize_purge_readonly(mapping, schema):
for field in [
x for x in mapping if schema.get(x, {}).get('readonly', False)
]:
mapping.pop(field)
return mapping

@staticmethod
def _normalize_purge_unknown(mapping, schema):
""" {'type': 'boolean'} """
for field in tuple(mapping):
if field not in schema:
del mapping[field]
for field in [x for x in mapping if x not in schema]:
mapping.pop(field)
return mapping

def __normalize_rename_fields(self, mapping, schema):
Expand Down
8 changes: 6 additions & 2 deletions docs/validation-rules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -538,8 +538,12 @@ readonly
--------
If ``True`` the value is readonly. Validation will fail if this field is
present in the target dictionary. This is useful, for example, when receiving
a payload which is to be validated before it is sent to the datastore. The field
might be provided by the datastore, but should not writable.
a payload which is to be validated before it is sent to the datastore. The
field might be provided by the datastore, but should not writable.

A validator can be configured with the initialization argument
``purge_readonly`` and the property with the same name to let it delete all
fields that have this rule defined positively.

.. versionchanged:: 1.0.2
Can be used in conjunction with ``default`` and ``default_setter``,
Expand Down