Pydantic: A Union[Dict[str, str], List[Any]] field parses dict, when it should parse List[str] for 2-character inputs

Created on 20 Jan 2021  路  1Comment  路  Source: samuelcolvin/pydantic

Checks

  • [x] I added a descriptive title to this issue
  • [x] I have searched (google, github) for similar issues and couldn't find anything
  • [x] I have read and followed the docs and still think this is a bug

Versions

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

             pydantic version: 1.7.3
            pydantic compiled: True
                 install path: /home/moose/.pyenv/versions/3.9.1/lib/python3.9/site-packages/pydantic
               python version: 3.9.1 (default, Jan 16 2021, 13:41:30)  [GCC 9.3.0]
                     platform: Linux-5.4.0-62-generic-x86_64-with-glibc2.31
     optional deps. installed: ['typing-extensions']

Bug

When I pass a List[str] with the string being 2 characters long (e.g. ["ab"]) to a field which is annotated with Union[Dict[str, str], List[Any]], I get a dictionary {"a": "b"} instead of ["ab"].

Example:

from typing import Union, List, Any, Dict

from pydantic import BaseModel


class Foo(BaseModel):
    values: Union[Dict[str, str], List[Any]]

class Foo2(BaseModel):
    values: Union[List[Any], Dict[str, str]]


print(Foo.parse_obj({"values": ["L1"]}))
print(Foo.parse_obj({"values": ["L11"]}))
print(Foo2.parse_obj({"values": ["L1"]}))

Results in:

values={'L': '1'}  # Unexpected - why do I get a dict?
values=['L11']  # expected value, but surprising after seeing output (1)
values=['L1']  # expected value, but surprising after seeing output (1)
bug

Most helpful comment

Hello @MartinThoma
Yes it's a very common issue on _pydantic_ as it tries to coerce the value with types inside the Union in order (so the first one will take precedence over the second one if possible).
So here it first tries to coerce ["L1"] with Dict[str, str], which works because dict(["11"]) == {'1': '1'}.
I'm working on a way to prevent this in https://github.com/samuelcolvin/pydantic/pull/2092. The whole behaviour will change in v2.
Please have a look at linked issues to this PR if you want more information

>All comments

Hello @MartinThoma
Yes it's a very common issue on _pydantic_ as it tries to coerce the value with types inside the Union in order (so the first one will take precedence over the second one if possible).
So here it first tries to coerce ["L1"] with Dict[str, str], which works because dict(["11"]) == {'1': '1'}.
I'm working on a way to prevent this in https://github.com/samuelcolvin/pydantic/pull/2092. The whole behaviour will change in v2.
Please have a look at linked issues to this PR if you want more information

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mgresko picture mgresko  路  3Comments

samuelcolvin picture samuelcolvin  路  3Comments

sbv-trueenergy picture sbv-trueenergy  路  3Comments

sommd picture sommd  路  3Comments

iwoloschin picture iwoloschin  路  3Comments