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

Make dict validation stricter #1268

Closed
and-semakin opened this issue Feb 28, 2020 · 14 comments
Closed

Make dict validation stricter #1268

and-semakin opened this issue Feb 28, 2020 · 14 comments
Labels
Change Suggested alteration to pydantic, not a new feature nor a bug strictness related to how strict pydantic's parsing/validation is

Comments

@and-semakin
Copy link

and-semakin commented Feb 28, 2020

Bug

Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":

             pydantic version: 1.4
            pydantic compiled: False
                 install path: /Users/andreisemakin/.local/share/virtualenvs/popapi-7C7w-VGz/lib/python3.7/site-packages/pydantic
               python version: 3.7.6 (default, Dec 30 2019, 19:38:26)  [Clang 11.0.0 (clang-1100.0.33.16)]
                     platform: Darwin-19.3.0-x86_64-i386-64bit
     optional deps. installed: ['typing-extensions']

Consider following code snippet:

from pydantic import BaseModel, constr
from typing import Dict, Union


NotEmptyStr = constr(strict=True, min_length=1)


class RequestModel(BaseModel):
    value: Union[None, NotEmptyStr, Dict[str, NotEmptyStr]] = None


model = RequestModel.validate({"value": ""})

What would you expect model.value to be equal? I would expect it to raise ValidationError, because empty strings are not allowed, but apparently it coerces to dictionary:

In [4]: model.value
Out[4]: {}
@samuelcolvin samuelcolvin added bug V1 Bug related to Pydantic V1.X and removed feature request labels Mar 1, 2020
@samuelcolvin
Copy link
Member

I think a string (empty or not) should never be valid as a dict.

Only question is whether this counts as a "breaking change" and therefore needs to wait for v2?

@dmontagu @tiangolo thoughts?

@and-semakin
Copy link
Author

Ok, then I'll split out condict part from this issue to a separate one.

@and-semakin and-semakin changed the title Constrained dictionaries (condict) Empty string coerces to dict Mar 2, 2020
@and-semakin
Copy link
Author

and-semakin commented Mar 2, 2020

More cases (continuation of previous snippet):

# list
In [5]: model2 = RequestModel.validate({"value": []})

In [6]: model2
Out[6]: RequestModel(value={})

# tuple
In [7]: model3 = RequestModel.validate({"value": ()})

In [8]: model3
Out[8]: RequestModel(value={})

# set
In [10]: model4 = RequestModel.validate({"value": set()})

In [11]: model4
Out[11]: RequestModel(value={})

# empty range
In [14]: model5 = RequestModel.validate({"value": range(100, 0)})

In [15]: model5
Out[15]: RequestModel(value={})

So I guess any empty iterable can be coerced to dictionary.

@samuelcolvin
Copy link
Member

Yes, so can a list of pairs.

All we really do is call dict() on the thing which is not sufficient. But to block all of this would definitely be a breaking change.

@and-semakin
Copy link
Author

and-semakin commented Mar 2, 2020

It may be a perfect use case for condict(strict=True) 🤔
It won't be a breaking change, just another feature. What do you think @samuelcolvin ?

@dmontagu
Copy link
Contributor

dmontagu commented Mar 3, 2020

The problem is that python can and will convert any iterable to a dictionary via dict(...) if all of the items are pairs.

As a result, any iterable that has no items will be converted to {}, including the empty string.

I personally would vote to change this behavior in v2 so that anything that isn't a subclass of dict just fails the dict validator. It wouldn't surprise me if there were reasonable uses of pydantic that this would break, but I personally feel this is important enough and should break little enough code that it meets the bar for the sort of backwards compatibility breakage that we should be considering in a major version update.

I'd be curious if there are any common uses of this functionality that could pose a more serious obstacle to making this change. (For example, if we need pydantic to be able to parse a model to a Dict[str, Any] under some circumstances, or similar.)

If we are going to change this behavior in v2, I would suggest we don't add condict as a type in the main pydantic library. But @and-semakin that wouldn't stop you from making use of a custom implementation in your own code in the mean time.

Thoughts?

@and-semakin
Copy link
Author

Connected issue: #558

@and-semakin
Copy link
Author

Do we have any plans or estimation about when v2 will be ready?

I agree with your arguments @dmontagu. Do we want to save current behaviour of dict, maybe under some other name?

@samuelcolvin
Copy link
Member

I agree with @dmontagu. I too thought there must be a situation where lists of pairs are coerced to a dict, but then couldn't think of one.

In terms of v2, I was originally hoping for the end of March, but I think that's no unrealistic.

Hopefully not too long through.

@samuelcolvin samuelcolvin changed the title Empty string coerces to dict Make dict validation stricter Mar 4, 2020
@samuelcolvin samuelcolvin added Change Suggested alteration to pydantic, not a new feature nor a bug and removed bug V1 Bug related to Pydantic V1.X labels Mar 4, 2020
@samuelcolvin samuelcolvin added this to the Version 2 milestone Mar 4, 2020
@spacemanspiff2007
Copy link

I have another case:

"options": [
    {
        "value": "OPEN",
        "label": "Open"
    },
    {
        "value": "CLOSED",
        "label": "Closed"
    }
]

validated with

options: Dict[str, str] = {}

becomes

{'value': 'label'}

which is quite wrong!

@samuelcolvin
Copy link
Member

Agreed, I think for simplicity we should not allow lists as inputs to dicts in V2.

@tiangolo
Copy link
Member

Yeah 🤔

Agreed with @dmontagu as well. ☝️

@HansBrende
Copy link

Here's a slightly less intrusive tweak that would fix many of these bugs while remaining backwards-compatible with the existing ability to coerce a dict from a list of tuples. Maybe something like this can be added sooner than v2? #2513 (comment)

@samuelcolvin
Copy link
Member

This is fixed on main and will be included in V2, you can try it now on the alpha release.

RajatRajdeep pushed a commit to RajatRajdeep/pydantic that referenced this issue May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Change Suggested alteration to pydantic, not a new feature nor a bug strictness related to how strict pydantic's parsing/validation is
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants