-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Bug
Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":
pydantic version: 1.5.1
pydantic compiled: False
install path: /home/spirus/projects/pydantic/pydantic
python version: 3.8.3 (default, May 17 2020, 18:15:42) [GCC 10.1.0]
platform: Linux-5.6.14-arch1-1-x86_64-with-glibc2.2.5
optional deps. installed: ['typing-extensions', 'email-validator', 'devtools']
As I was experimenting with FastAPI and pydantic, I thought that it would be nice to use exclude in order to avoid some Schema duplication (or complex inheritances). Ultimately, I came across a weird bug when trying to use exclude on nested sequences. Below is a minimal example for reproducing the bug.
from typing import List
from pydantic import BaseModel
class A(BaseModel):
a: int = 1
b: int = 2
c: int = 3
class B(BaseModel):
a: List[A]
class C(BaseModel):
b: List[B]
def test():
c = C(b=[B(a=[A()])])
print(c.dict(exclude={"b": {0: {"a": {"__all__": {"c"}}}}}))
print(c.dict(exclude={"b": {"__all__": {"a": {"__all__": {"c"}}}}}))
assert c.dict(exclude={"b": {0: {"a": {"__all__": {"c"}}}}}) == {"b": [{"a": [{"a": 1, "b": 2}]}]}
assert c.dict(exclude={"b": {"__all__": {"a": {"__all__": {"c"}}}}}) == {"b": [{"a": [{"a": 1, "b": 2}]}]}Running the above function with pytest the output is:
Test session starts (platform: linux, Python 3.8.3, pytest 5.3.5, pytest-sugar 0.9.2)
rootdir: /home/spirus/projects/pydantic, inifile: setup.cfg
plugins: cov-2.8.1, mock-3.1.0, sugar-0.9.2
collecting ...
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― test ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
def test():
c = C(b=[B(a=[A()])])
print(c.dict(exclude={"b": {0: {"a": {"__all__": {"c"}}}}}))
print(c.dict(exclude={"b": {"__all__": {"a": {"__all__": {"c"}}}}}))
assert c.dict(exclude={"b": {0: {"a": {"__all__": {"c"}}}}}) == {"b": [{"a": [{"a": 1, "b": 2}]}]}
> assert c.dict(exclude={"b": {"__all__": {"a": {"__all__": {"c"}}}}}) == {"b": [{"a": [{"a": 1, "b": 2}]}]}
E AssertionError: assert {'b': [{}]} == {'b': [{'a': ...1, 'b': 2}]}]}
E Differing items:
E {'b': [{}]} != {'b': [{'a': [{'a': 1, 'b': 2}]}]}
E Use -v to get the full diff
example.py:25: AssertionError
------------------------------------------------------------ Captured stdout call ------------------------------------------------------------
{'b': [{'a': [{'a': 1, 'b': 2}]}]}
{'b': [{}]}
example.py ⨯ 100% ██████████
Results (0.05s):
1 failed
- example.py:20 test
I inspected the codebase a little bit, and found out that the bug is produced because of how _normalize_indexes, as it assumes that when the items is a dict containing the __all__ key, then the value is considered by default to be a set. So calling the update, information may be missed.
I am not certain if my usage violates how the __all__ key should be used, but if this should have been correct, I can create a PR with a solution.