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

Fix ForwardRef collection bug #450

Merged
merged 5 commits into from Apr 3, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 7 additions & 5 deletions docs/examples/forward_ref.py
Expand Up @@ -4,12 +4,14 @@
Foo = ForwardRef('Foo')

class Foo(BaseModel):
a: int = 123
b: Foo = None
a: int = None
b: Dict[str, FooType] = {}
Copy link
Member

Choose a reason for hiding this comment

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

No need to change the example, unless otherwise required examples should be as simple as possible.

c: List[Foo] = []


Foo.update_forward_refs()

print(Foo())
#> Foo a=123 b=None
print(Foo(b={'a': '321'}))
#> Foo a=123 b=<Foo a=321 b=None>
#> Foo a=123 b={} c=[]
print(Foo(b={'bar': {'a': '321'}}, c=[{'a': 345}]))
#> Foo a=123 b={'bar': <Foo a=321 b={} c=[]>} c=[<Foo a=345 b={} c=[]>]
4 changes: 2 additions & 2 deletions pydantic/validators.py
Expand Up @@ -10,7 +10,7 @@

from . import errors
from .datetime_parse import parse_date, parse_datetime, parse_duration, parse_time
from .utils import AnyCallable, AnyType, change_exception, display_as_type, is_callable_type, sequence_like
from .utils import AnyCallable, AnyType, ForwardRef, change_exception, display_as_type, is_callable_type, sequence_like

if TYPE_CHECKING: # pragma: no cover
from .fields import Field
Expand Down Expand Up @@ -357,7 +357,7 @@ def pattern_validator(v: Any) -> Pattern[str]:


def find_validators(type_: AnyType, arbitrary_types_allowed: bool = False) -> List[AnyCallable]:
if type_ is Any:
if type_ is Any or type(type_) == ForwardRef:
return []
if type_ is Pattern:
return pattern_validators
Expand Down
56 changes: 55 additions & 1 deletion tests/test_py37.py
Expand Up @@ -5,7 +5,7 @@

import pytest

from pydantic import ConfigError
from pydantic import ConfigError, ValidationError

skip_not_37 = pytest.mark.skipif(sys.version_info < (3, 7), reason='testing >= 3.7 behaviour only')

Expand Down Expand Up @@ -83,6 +83,60 @@ class Foo(BaseModel):
assert module.Foo(b={'a': '321'}).dict() == {'a': 123, 'b': {'a': 321, 'b': None}}


@skip_not_37
def test_self_forward_ref_collection(create_module):
module = create_module(
"""
from typing import ForwardRef, List, Dict
from pydantic import BaseModel

Foo = ForwardRef('Foo')

class Foo(BaseModel):
a: int = 123
b: Foo = None
c: List[Foo] = []
d: Dict[str, Foo] = {}

Foo.update_forward_refs()
"""
)

assert module.Foo().dict() == {'a': 123, 'b': None, 'c': [], 'd': {}}
assert module.Foo(b={'a': '321'}, c=[{'a': 234}], d={'bar': {'a': 345}}).dict() == {
'a': 123,
'b': {'a': 321, 'b': None, 'c': [], 'd': {}},
'c': [{'a': 234, 'b': None, 'c': [], 'd': {}}],
'd': {'bar': {'a': 345, 'b': None, 'c': [], 'd': {}}},
}


@skip_not_37
def test_self_forward_ref_validation(create_module):
module = create_module(
"""
Copy link
Member

Choose a reason for hiding this comment

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

If this string is identical to above, please define it once and reuse.

from typing import ForwardRef, List, Dict
from pydantic import BaseModel

Foo = ForwardRef('Foo')

class Foo(BaseModel):
a: int = 123
b: Foo = None
c: List[Foo] = []
d: Dict[str, Foo] = {}

Foo.update_forward_refs()
"""
)

with pytest.raises(ValidationError) as exc_info:
module.Foo(b={'a': '321'}, c=[{'b': 234}], d={'bar': {'a': 345}})
assert exc_info.value.errors() == [
{'loc': ('c', 0, 'b'), 'msg': 'value is not a valid dict', 'type': 'type_error.dict'}
]


@skip_not_37
def test_self_forward_ref_local(create_module):
module = create_module(
Expand Down