-
Notifications
You must be signed in to change notification settings - Fork 241
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
add require_all
validator argument
#421
Conversation
The problem place resolved badly, since I'm not familiar with codebase. Need help here. |
i don't have the time to look at it now, but i think a complementary rule like the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
beside the mentioned accompanying rule for subdocuments this needs tests. and not few of them. please mind at least theses aspects:
- fields with overriding
'required': False
- uses in conjunction with
excludes
- proper error data
cerberus/validator.py
Outdated
@@ -84,6 +84,9 @@ class BareValidator(object): | |||
:param allow_unknown: See :attr:`~cerberus.Validator.allow_unknown`. | |||
Defaults to ``False``. | |||
:type allow_unknown: :class:`bool` or any :term:`mapping` | |||
:param require_all: See :attr:`~cerberus.Validator.require_all`. | |||
Defaults to ``False``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this has a surplus of indentation.
cerberus/validator.py
Outdated
@@ -281,7 +287,7 @@ def _error(self, *args): | |||
if rule == 'nullable': | |||
constraint = field_definitions.get(rule, False) | |||
else: | |||
constraint = field_definitions[rule] | |||
constraint = field_definitions.get(rule) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this a special case for required
? i'd rather keep that prone to a KeyError
exception than have possibly incorrect None
values in the error data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it correct?
if rule == 'nullable':
constraint = field_definitions.get(rule, False)
elif rule == 'required':
constraint = field_definitions.get(rule, self.require_all)
else:
constraint = field_definitions[rule]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good.
cerberus/validator.py
Outdated
|
||
@require_all.setter | ||
def require_all(self, value): | ||
if not (self.is_child or isinstance(value, (bool, DefinitionSchema))): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you musn't copy that from allow_unknown
where it has the purpose to validate the provided constraint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
require_all
should not have setter? I'm not familiar with internals, sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it should have a setter. but the check in line 447-448 is specific to allow_unknown
. just assign the value like purge_readonly
for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i really tend to insist on a full check of the submitted error data.
ValidationError
has the property schema_path that references the failed rule in the used schema. when the cause of the failure is defined by the validator option, i propose to set that property to __require_all__
similar to here.
cerberus/tests/test_validation.py
Outdated
|
||
def test_require_all_simple(): | ||
v = Validator({'foo': {'type': 'string'}}, require_all=True) | ||
assert not v({}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of assert statements you could use the helper functions assert_fail and assert_success that are more helpful for debugging. with the function that expects a failure one can also pass the expected error data, there are some uses of this as reference.
cerberus/tests/test_validation.py
Outdated
def test_require_all_errors(): | ||
v = Validator({'foo': {'type': 'string'}}, require_all=True) | ||
assert not v({}) | ||
assert v.errors == {'foo': ['required field']} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a more detailed test (one should be enough) of the error data (all the properties of a ValidationError) with the functionality mentioned before would be preferable.
your test also checks the result of the default error handler, its behaviour could change and that wouldn't relate to your feature / these tests.
Sorry, have no idea what is going on here and how to make some similar changes to |
{}, | ||
schema, | ||
validator, | ||
error=('foo', ('foo', 'required'), errors.REQUIRED_FIELD, True), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as a start replace the second tuple value here with '__require_all__'
. as far as i can oversee the situation, the schema_path
variable (which is eventually compared with that value) in the Validator._error
method must then be figured out with regards to the presence of the required
rule for the submitted field
in the schema when an errors.REQUIRED_FIELD
is processed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is obvious... and this is test, I mean, at first we need to fix code, so it will result __require_all__
in error schema path and only after tests should be fixed. The problem is to fix validator.py
and replace required
with __require_all__
in __validate_requried_fields
in some cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and this is test, I mean, at first we need to fix code
following the paradigm of test-driven development, one first formulates assertions in a test.
__validate_required_fields
calls _error
which only takes a reference to the field and an error definition as argument, hence the detemination of the correct (pseudo-) schema path has to happen in the latter method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
following the paradigm of test-driven development, one first formulates assertions in a test.
Ok, but this does not help me at all. TDD is for developers, this PR won't be accepted without tests and implementation, so there is no reason to order changes.
hence the detemination of the correct (pseudo-) schema path has to happen in the latter method.
I don't get it. We have __validate_unknown_fields
, which calls _error
with some preparation and we have __validate_required_fields
, which calls _error
without something. Why this cases are not equal? What is "later method" in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't get it.
TDD is for developers, this PR won't be accepted without tests and implementation, so there is no reason to order changes.
afaict you are developing a feature here, hence you're in the role of a developer. i don't care how you work if the result is fine from a variety of angles, one of these are tests that define expected behaviour and are passing. at the moment the test defines an undesired behaviour.
Why this cases are not equal?
__validate_unknown_fields
uses a 'child validator' that handles the schema path (that may eventually be used in _error
).
__validate_required_fields
, which calls_error
without something
no. that never happens.
What is "later method" in this case?
the latter that was mentioned in the previous part in the sentence, _error
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the result is fine from a variety of angles, one of these are tests that define expected behaviour and are passing. at the moment the test defines an undesired behaviour.
That is the point. I understand what behaviour we need, but I don't understand how to do it. And advice to "start with test" sound like a joke. 😄
the latter that was mentioned in the previous part in the sentence,
_error
.
You suggest to add __require_all__
to schema_path
in _error
method, am I right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And advice to "start with test" sound like a joke.
neither did i intend to be funny with that statement nor can i find traces of humour in it.
so far, my successes with that approach were really fun, but not laughable.
You suggest to add
__require_all__
to schema_path in_error
method, am I right?
more specificly: __require_all__
is the schema_path
when the error was defined by the require_all
option of the root validator.
a hint on a not so obvious spot: the third paragraph in |
@funkyfuture |
no, i mean the third paragraph in the first section. on that occasion: what's your overall status on this feature? |
Sorry, I'm not native english speaker, so I don't get you... As I understand you, then answer is: «I can't check readiness of this feature, since I'm contributor and you are reviewer. It's up to you do decide, that my implementation is good enough or not». |
i'm not a native english speaker as well (okay, i have an advantage w/ my mother tongue) and even as a citizen of the gdr i didn't learn russian. it seems we have no other choice than using the international language of science and technology. maybe you can get support with that. regarding the documentation i can provide corrected formulations when reviewing. so far the changes look good, but the whole feature is still lacking these to major aspects:
again, the |
cerberus/schema.py
Outdated
@@ -67,6 +67,7 @@ def __init__(self, validator, schema={}): | |||
self.schema_validator = SchemaValidator( | |||
None, | |||
allow_unknown=self.validation_schema, | |||
require_all=self.validation_schema, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this assigning of a schema supposed to achieve?
cerberus/schema.py
Outdated
@@ -275,6 +276,7 @@ class SchemaValidationSchema(UnvalidatedSchema): | |||
def __init__(self, validator): | |||
self.schema = { | |||
'allow_unknown': False, | |||
'require_all': False, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's no need to set the default again.
cerberus/tests/test_validation.py
Outdated
sub_schema = {'foo': {'type': 'string'}} | ||
schema = {'foo': {'type': 'dict', 'require_all': True, 'schema': sub_schema}} | ||
validator = Validator(require_all=False) | ||
assert_success({'foo': {'foo': 'bar'}}, schema, validator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using the same field name at different levels make debugging much harder.
cerberus/tests/test_validation.py
Outdated
assert_success({'foo': 'bar'}, schema, validator) | ||
|
||
|
||
def test_require_all_override_by_subdoc_require_all(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could this test be better readable by parametrizing it? at least adding empty lines to group the setup and test of different circumstances should make it better comprehendable.
cerberus/validator.py
Outdated
@@ -1043,6 +1072,7 @@ def validate_rule(rule): | |||
{'type': ['dict', 'string'], | |||
'check_with': 'bulk_schema'}]} """ | |||
) | |||
_validate_require_all = dummy_for_rule_validation("{'type': 'boolean'}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please keep the methods sorted alphabetically in order to keep it easy to find them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like they are not in alphabetical order right now:
logical
anyof
nullable
keysrules
docs/schemas.rst
Outdated
@@ -15,7 +15,8 @@ which is expected to be a string not longer than 10 characters. Something like | |||
very long string'}`` or ``{'name': 99}`` would not. | |||
|
|||
By default all keys in a document are optional unless the :ref:`required`-rule | |||
is set for a key. | |||
is set for a key or validator attribute :attr:`~cerberus.Validator.require_all` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"is set True
for individual fields or the validator's :attr:~cerberus.Validator.require_all
is set to True
in order to expect all schema-defined fields to be present in the document.
docs/usage.rst
Outdated
>>> v.validate({}, schema) | ||
True | ||
|
||
However, you can require all document keys pairs by either setting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if there's an 'either', what's the other case?
docs/usage.rst
Outdated
@@ -162,6 +162,67 @@ mapping that is checked against the :ref:`schema <schema_dict-rule>` rule: | |||
``allow_unknown`` can also be set to a validation schema. | |||
|
|||
|
|||
.. _requiring-all: | |||
|
|||
Requiring all |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks like copied from the allow_unknown
section. given that require_all
is less complex, a sufficient explanation should be doable with less prosa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have no idea how to reduce text. Exclude some examples?
docs/validation-rules.rst
Outdated
:attr:`~cerberus.Validator.require_all` property of the validator for the | ||
subdocument. | ||
For a full elaboration refer to :ref:`this paragraph <requiring-all>`. | ||
|
||
.. _required: | ||
|
||
required |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add a short cross-reference to the section in usage.rst
here.
docs/usage.rst
Outdated
|
||
>>> v.validate({'a_dict': {'address': 'foobar'}}, schema) | ||
True | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add a short cross-reference to the require
and require_all
rules here.
cerberus/tests/test_validation.py
Outdated
@@ -5,6 +5,7 @@ | |||
from datetime import datetime, date | |||
from random import choice | |||
from string import ascii_lowercase | |||
import itertools |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please shift this above re
.
docs/usage.rst
Outdated
|
||
By default any keys defined in the schema are not required: | ||
|
||
.. doctest:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't think this example is needed.
docs/usage.rst
Outdated
>>> v.validate({}, schema) | ||
True | ||
|
||
However, you can require all document keys pairs by setting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think that suffices as a sentence after the explanation of the variant when it is set at initialization. an inline example (validator_require_all = True
) should also be enough, no need for a code block.
docs/usage.rst
Outdated
>>> v = Validator({'name': {'type': 'string', 'maxlength': 10}}, require_all=True) | ||
>>> v.validate({}) | ||
False | ||
>>> v.require_all = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually, this shoull be enough with regards to the validator attribute.
docs/usage.rst
Outdated
>>> v.validate({}) | ||
True | ||
|
||
``require_all`` can also be set as rule to configure a validator for a nested |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm not sure, but i think we commonly use the term subdocument instead of 'nested mapping' through the docs.
docs/usage.rst
Outdated
... 'name': {'type': 'string'}, | ||
... 'a_dict': { | ||
... 'type': 'dict', | ||
... 'require_all': True, # this overrides the behaviour for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the comments' content has already been stated.
docs/usage.rst
Outdated
... } | ||
... } | ||
|
||
>>> v.validate({'a_dict': {}}, schema) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the example would make more sense if there was another field defined in the schema at the top level, that doesn't exist in the document. the output of v.errors
could conclude the story.
docs/usage.rst
Outdated
>>> v.validate({'a_dict': {}}, schema) | ||
False | ||
|
||
>>> v.validate({'a_dict': {'address': 'foobar'}}, schema) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not really needed imo.
docs/validation-rules.rst
Outdated
.. _required: | ||
|
||
required | ||
-------- | ||
See also :ref:`this paragraph <requiring-all>`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rather put this in a first note admonition below, a little more elaborated: "To define all fields of a document ..."
what about this? schema = {
'foo': {
'type': 'dict',
'require_all': True,
'allow_unknown': {…},
}
} would that make something explode? should |
At first look they are not crossing anywhere. |
should the child validator that is dispatched for |
docs/usage.rst
Outdated
``require_all`` can also be set as rule to configure a validator for a nested | ||
mapping that is checked against the :ref:`schema <schema_dict-rule>` rule: | ||
``require_all`` to ``True`` at validator initialization (``v = Validator(…, require_all=True)``) | ||
or change latter via attribute access (``v.require_all = True``). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
… change it later …
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, i think beside these minor changes we're getting close to resolve this.
could you please squash all your commits into one, so a final review would be easier.
Hm, if it validates only unknown fields, then «yes», I think. |
9e4fcdb
to
4b5c50c
Compare
It looks like squash broke all merge logic, since all history goes away. And I don't known how to squash only my commits, cause there was merges from master to my branch between. You can squash all my pull to one commit on merge actually, and squashed changes are on "Files" tab above. Anyway, do not know how to restore this PR to previous state. 😞 |
sorry, i don't know what the situation in your local repository actually looks like. i hope you have all your changes still on your machine. just apply them on a new branch checked out from master an open a new pr. |
#417