-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Perform validation on assignment to attribute #94
Conversation
petroswork
commented
Oct 25, 2017
•
edited
Loading
edited
- Add config variable "validate_assignment" defaulting to False.
- Add unit test.
- Validate on assignment to attribute #92
* Add config variable "validate_assignment" defaulting to False. * Add unit test.
Codecov Report
@@ Coverage Diff @@
## master #94 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 11 11
Lines 821 827 +6
Branches 182 184 +2
=====================================
+ Hits 821 827 +6 |
I'm not certain what the second parameter of |
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.
Thanks so much for your contribution, but a few things to fix here.
By the way, you don't need to delete your pull request and create another one to make an update. Instead just push more commits to the branch which you used for this pull request (in this case your master
).
pydantic/main.py
Outdated
@@ -111,7 +112,13 @@ def __setattr__(self, name, value): | |||
raise ValueError(f'"{self.__class__.__name__}" object has no field "{name}"') | |||
elif not self.config.allow_mutation: | |||
raise TypeError(f'"{self.__class__.__name__}" is immutable and does not support item assignment') | |||
self.__values__[name] = value | |||
if self.config.validate_assignment: |
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.
elif
pydantic/main.py
Outdated
value_, error_ = self.fields[name].validate(value, {}) | ||
if error_: | ||
raise ValidationError({name: error_}) | ||
self.__values__[name] = value_ |
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 use
if error_:
raise ValidationError({name: error_})
else:
self.__values__[name] = value_
I know the else
is not required but I think it makes the code more readable.
tests/test_main.py
Outdated
|
||
|
||
def test_validating_assignment(): | ||
import pydantic |
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 don't need this import
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.
So add from pydantic import constr
to the top of the file.
tests/test_main.py
Outdated
def test_validating_assignment(): | ||
import pydantic | ||
|
||
class Pedantic(BaseModel): |
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.
use ValidateAssignmentModel
as the name here and move outside the test so it can be used in multiple tests.
tests/test_main.py
Outdated
p.b = "hi" | ||
assert p.b == "hi" | ||
assert p.values() == {"a": 2, "b": "hi"} | ||
with pytest.raises(ValidationError) as exc_info: |
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.
split the failure into a separate test.
tests/test_main.py
Outdated
class Config: | ||
validate_assignment = True | ||
|
||
p = Pedantic(a=5, b="hello") |
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.
use single quotes for strings unless they include single quotes.
tests/test_main.py
Outdated
p.b = "" | ||
assert "error validating input" in str(exc_info) | ||
|
||
class Permissive(BaseModel): |
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.
move all this into separate tests.
pydantic/main.py
Outdated
@@ -111,7 +112,13 @@ def __setattr__(self, name, value): | |||
raise ValueError(f'"{self.__class__.__name__}" object has no field "{name}"') | |||
elif not self.config.allow_mutation: | |||
raise TypeError(f'"{self.__class__.__name__}" is immutable and does not support item assignment') | |||
self.__values__[name] = value | |||
if self.config.validate_assignment: | |||
value_, error_ = self.fields[name].validate(value, {}) |
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 second argument to validate()
should be the other values on the modal in case validation of this field needs to see these values. Here I guess you should use self.values(exclude={name})
.
You should also add a test for this case.
* Improved tests per maintainer's suggestions.
The second parameter to |
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.
Nearly there!
We also need to add a section to docs and a line to HISTORY.rst. If that's too boring, I'll do it once this is ready and we've merged it.
tests/test_main.py
Outdated
class ValidateAssignmentModel(BaseModel): | ||
a: int = 2 | ||
b: constr(min_length=1) | ||
c: constr(min_length=a) |
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 doesn't make sense. Remove c
completely.
As per #29 I need to improve how validators work, so I guess it's not really possible to test this case. 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.
tests/test_main.py
Outdated
validate_assignment = True | ||
|
||
|
||
def test_validating_assignment(): |
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.
Unit tests are supposed to test one thing, so if the test fails you know what part of the code has gone wrong.
This isn't always simple, but we can do better here. Split this into test_validating_assignment_pass
and test_validating_assignment_fail
.
tests/test_main.py
Outdated
assert p.values() == {'a': 2, 'b': 'hi', 'c': 'testing'} | ||
with pytest.raises(ValidationError) as exc_info: | ||
p.a = 'b' | ||
assert 'error validating input' in str(exc_info) |
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.
almost all ValidationErrors
will include 'error validating input'
either remove this or check the inner error is right.
tests/test_main.py
Outdated
with pytest.raises(ValidationError) as exc_info: | ||
p.c = 'c' | ||
assert 'error validating input' in str(exc_info) | ||
p.a = 100 |
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 guess remove from here on, I'll add a test once I improve validators.
Awesome, thank you. |