E5E4 dataclass validation by samuelcolvin · Pull Request #334 · pydantic/pydantic · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
support for tuple or dict in dataclasses
  • Loading branch information
samuelcolvin committed Dec 27, 2018
commit 70a73d5ecff3e65bdcd4e26c639a6508a70cade9
1 change: 1 addition & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ v0.17.0 (unreleased)
* fix schema generation for fields annotated as ``: dict``, #330 by @nkonin
* support for passing Config class in dataclasses decorator, #276 by @jarekkar
(**breaking change**: this supersedes the ``validate_assignment`` argument with ``config``)
* support for nested dataclasses, #334 by @samuelcolvin

v0.16.1 (2018-12-10)
....................
Expand Down
4 changes: 4 additions & 0 deletions pydantic/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ def _get_validators(cls):
def _validate_dataclass(v):
if isinstance(v, cls):
return v
elif isinstance(v, (cls, list, tuple)):
return cls(*v)
elif isinstance(v, dict):
return cls(**v)
else:
raise errors.DataclassTypeError(class_name=cls.__name__)

Expand Down
2 changes: 1 addition & 1 deletion pydantic/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,4 @@ class PatternError(PydanticValueError):

class DataclassTypeError(PydanticTypeError):
code = 'dataclass'
msg_template = 'must be an instance of {class_name}'
msg_template = 'instance of {class_name}, tuple or dict expected'
60 changes: 49 additions & 11 deletions tests/test_dataclasses.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import dataclasses

import pytest

import pydantic
<<<<<<< HEAD
from pydantic import BaseConfig, ValidationError
=======
from pydantic import BaseModel, ValidationError
>>>>>>> dataclass validation, fix #273


def test_simple():
Expand Down Expand Up @@ -159,7 +157,7 @@ class MyDataclass:
with pytest.raises(ValidationError) as exc_info:
d.a = 'xxxx'

assert issubclass(MyDataclass.__pydantic_model__.__config__, BaseConfig)
assert issubclass(MyDataclass.__pydantic_model__.__config__, BaseModel.Config)
assert exc_info.value.errors() == [
{
'loc': ('a',),
Expand All @@ -185,26 +183,66 @@ class MyDataclass:
assert d.a == 'xxxx'


def test_dataclass_subtype():
def test_nested_dataclass():
@pydantic.dataclasses.dataclass
class Nested:
number: int

class Outer(BaseModel):
n: Nested

navbar = Outer(n=Nested(number='1'))
assert isinstance(navbar.n, Nested)
assert navbar.n.number == 1

navbar = Outer(n=('2',))
assert isinstance(navbar.n, Nested)
assert navbar.n.number == 2

navbar = Outer(n={'number': '3'})
assert isinstance(navbar.n, Nested)
assert navbar.n.number == 3

with pytest.raises(ValidationError) as exc_info:
Outer(n='not nested')
assert exc_info.value.errors() == [
{
'loc': ('n',),
'msg': 'instance of Nested, tuple or dict expected',
'type': 'type_error.dataclass',
'ctx': {'class_name': 'Nested'},
}
]

with pytest.raises(ValidationError) as exc_info:
Outer(n=('x',))
assert exc_info.value.errors() == [
{'loc': ('n', 'number'), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'}
]


def test_arbitrary_types_allowed():
@dataclasses.dataclass
class Button:
href: str

class Navbar(BaseModel):
button: Button

class Config:
arbitrary_types_allowed = True

btn = Button(href='a')
navbar = Navbar(button=btn)
assert isinstance(navbar.button, Button)
assert navbar.button.href == 'a'

with pytest.raises(ValidationError) as exc_info:
Navbar(button='not button')
Navbar(button=('b',))
assert exc_info.value.errors() == [
{
'ctx': {'class_name': 'Button'},
'loc': ('button',),
'msg': 'must be an instance of Button',
'type': 'type_error.dataclass',
'msg': 'instance of Button expected',
'type': 'type_error.arbitrary_type',
'ctx': {'expected_arbitrary_type': 'Button'},
}
]
0