Skip to content

Conversation

samuelcolvin
Copy link
Member

@samuelcolvin samuelcolvin commented Oct 17, 2019

Change Summary

modify get_annotation_from_field_info to look at sub-types of typing objects like Union or List and thus correctly apply validators.

Related issue number

fix #779

Checklist

  • Unit tests for the changes exist
  • Tests pass on CI and coverage remains at 100%
  • Documentation reflects the changes where applicable
  • changes/<pull request or issue id>-<github username>.md file added describing change
    (see changes/README.md for details)

@codecov
Copy link

codecov bot commented Oct 17, 2019

Codecov Report

Merging #909 into master will not change coverage.
The diff coverage is 100%.

@@          Coverage Diff          @@
##           master   #909   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          16     16           
  Lines        2766   2791   +25     
  Branches      528    540   +12     
=====================================
+ Hits         2766   2791   +25
Impacted Files Coverage Δ
pydantic/fields.py 100% <100%> (ø) ⬆️
pydantic/schema.py 100% <100%> (ø) ⬆️
pydantic/typing.py 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 78921da...9a8a712. Read the comment docs.

unused_constraints = [
f for f in validation_attribute_to_schema_keyword if f not in attrs and getattr(field_info, f) is not None
]
if unused_constraints:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dmontagu @tiangolo I need your feedback/advice on this.

I think we should tell the user if they add constraints but they're not applied, however are there situations where a custom could set a constraint which would show in schema, but not be applied here? I couldn't find any in tests, but that doesn't mean there aren't any.

If so we might need a config argument or extra argument to Field() to skip this check.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of any, but it feels a little strong to me to raise an error rather than a warning or having a flag to allow it or similar.

That said, specifying schema_extra would work as a way to add these to the schema without validation if you really wanted to right?

If that's right, I think this is fine, but maybe it would be worth calling this out in the error message?


More generally, I think it would be nice if it were easier to implement the constraint_func for the class and have pydantic pick it up, so you could create custom classes with custom validation as a function of the specified schema. But that is probably outside the scope of what should be handled now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More generally, I think it would be nice if it were easier to implement the constraint_func for the class and have pydantic pick it up, so you could create custom classes with custom validation as a function of the specified schema. But that is probably outside the scope of what should be handled now.

Surely that should be done via type annotations?

If that's right, I think this is fine, but maybe it would be worth calling this out in the error message?

Yes agreed.

Copy link
Member Author

@samuelcolvin samuelcolvin Oct 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after the changes I've just made, I can't think of a scenario where constraints would be valid in schema, but not enforced.

To add an example to the docs and an explanation of the work around we need at least one example where this error would be incorrectly raised :-) done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have strong opinions about this.

I see how it could feel cumbersome if it suddenly raises errors for models that didn't before.

But at the same time, I can imagine someone expecting it to show validation errors for some data with constraints in Field that are not actually applied. And I can see how they could forget to check the warnings on the logs...


Something I can think of to mitigate these use cases is, in the docs, add an alternative with pure Field for those cases, I suggested it in the PR review.

Another idea that comes to mind is to show a warning first as @dmontagu suggests, and make it a deprecation warning. And in some next version, throw an error. Just an idea, not that I think that's necessary, both approaches seem fine to me.

elif issubclass(origin, Tuple): # type: ignore
for i, t in enumerate(args):
if t is Ellipsis:
return args[0]
Copy link
Contributor

@dmontagu dmontagu Oct 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if it is a tuple of optionals? (The same goes for the various container types below.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, agreed this could all be way more powerful, but would become massively more complex. Hence the error above so at least you know when it does and doesn't work.

args: Tuple[Any, ...] = annotation.__args__

if origin is Union:
if sum(a is not NoneType for a in args) == 1: # type: ignore
Copy link
Contributor

@dmontagu dmontagu Oct 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels kind of fragile, for example, what if you had Optional[Union[str, bytes]]? It seems like there is no reason the get_annotation_from_field_info function shouldn't be able to handle that, but this would reject it.


I'm wondering if maybe there is a way to refactor this pair of functions so that it is applied on a per-union-member basis? That might also make handling more robust for the container types below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above.

@samuelcolvin
Copy link
Member Author

oh, after going through this, I ended up rewriting the entire thing.

Type analysis should now work recursively.

I along the way, I discovered:

  • numerous field shapes, eg. SHAPE_TUPLE_ELLIPSIS were not supported by schema(), that's now fixed
  • min_items and max_items are not reflected by schema(), I'll fix this in another PR.

This was referenced Oct 18, 2019
Copy link
Contributor

@tiangolo tiangolo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! I think this is fine. 🚀

I just have a couple of small docs suggestions.

@samuelcolvin samuelcolvin mentioned this pull request Oct 18, 2019
3 tasks
@samuelcolvin samuelcolvin merged commit 7db098f into master Oct 18, 2019
@samuelcolvin samuelcolvin deleted the field-validator-fix branch October 22, 2019 16:32
andreshndz pushed a commit to cuenca-mx/pydantic that referenced this pull request Jan 17, 2020
* get item type in get_annotation_from_field_info, fix pydantic#779

* check constraints are enforced

* add change

* tests for phony constraints

* rewrite get_annotation_from_field_info

* fix tests

* add constaint enforcement to docs

* fix get_annotation_from_field_info coverage

* fix linting

* update docs as per @tiangolo suggestions
alexdrydew pushed a commit to alexdrydew/pydantic that referenced this pull request Dec 23, 2023
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Optional fields loose some validation
3 participants