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: check only first sublevel for validators with each_item #1991

Merged
merged 3 commits into from Feb 11, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions changes/1933-PrettyWood.md
@@ -0,0 +1,2 @@
**Breaking Change:** always validate only first sublevel items with `each_item`.
There were indeed some edge cases with some compound types where the validated items were the last sublevel ones.
18 changes: 17 additions & 1 deletion pydantic/fields.py
Expand Up @@ -518,10 +518,26 @@ def _type_analysis(self) -> None: # noqa: C901 (ignore complexity)
self.sub_fields = [self._create_sub_type(self.type_, '_' + self.name)]

def _create_sub_type(self, type_: Type[Any], name: str, *, for_keys: bool = False) -> 'ModelField':
if for_keys:
class_validators = None
else:
# validators for sub items should not have `each_item` as we want to check only the first sublevel
class_validators = {
k: Validator(
func=v.func,
pre=v.pre,
each_item=False,
always=v.always,
check_fields=v.check_fields,
skip_on_failure=v.skip_on_failure,
)
for k, v in self.class_validators.items()
if v.each_item
}
return self.__class__(
type_=type_,
name=name,
class_validators=None if for_keys else {k: v for k, v in self.class_validators.items() if v.each_item},
class_validators=class_validators,
model_config=self.model_config,
)

Expand Down
13 changes: 13 additions & 0 deletions tests/test_validators.py
Expand Up @@ -575,6 +575,19 @@ def check_foobar(cls, v):
assert Model(foobar={1: 1}).foobar == {1: 2}


def test_validation_each_item_one_sublevel():
class Model(BaseModel):
foobar: List[Tuple[int, int]]

@validator('foobar', each_item=True)
def check_foobar(cls, v: Tuple[int, int]) -> Tuple[int, int]:
v1, v2 = v
assert v1 == v2
return v

assert Model(foobar=[(1, 1), (2, 2)]).foobar == [(1, 1), (2, 2)]


def test_key_validation():
class Model(BaseModel):
foobar: Dict[int, int]
Expand Down