Skip to content

Commit

Permalink
Allows Optional lists with unique_items check
Browse files Browse the repository at this point in the history
When using `unique_items` with an `Optional[List[T]]` field, the field
validator would raise the following error if the field was not provided:
> `'NoneType' object is not iterable (type=type_error)`

Updating the validator to return `None` in these cases avoids the issue.

Fixes #3957, #4050, #4119
  • Loading branch information
mfulgo committed Sep 26, 2022
1 parent da0b756 commit 3ba804e
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 2 deletions.
1 change: 1 addition & 0 deletions changes/4568-mfulgo.md
@@ -0,0 +1 @@
Fixes error passing None for optional lists with `unique_items`
5 changes: 4 additions & 1 deletion pydantic/types.py
Expand Up @@ -599,7 +599,10 @@ def list_length_validator(cls, v: 'Optional[List[T]]') -> 'Optional[List[T]]':
return v

@classmethod
def unique_items_validator(cls, v: 'List[T]') -> 'List[T]':
def unique_items_validator(cls, v: 'Optional[List[T]]') -> 'Optional[List[T]]':
if v is None:
return None

for i, value in enumerate(v, start=1):
if value in v[i:]:
raise errors.ListUniqueItemsError()
Expand Down
18 changes: 17 additions & 1 deletion tests/test_validators.py
Expand Up @@ -7,7 +7,7 @@
import pytest
from typing_extensions import Literal

from pydantic import BaseModel, ConfigError, Extra, Field, ValidationError, errors, validator
from pydantic import BaseModel, ConfigError, Extra, Field, ValidationError, conlist, errors, validator
from pydantic.class_validators import make_generic_validator, root_validator


Expand Down Expand Up @@ -1329,3 +1329,19 @@ def post_root(cls, values):

B(x='pika')
assert validate_stub.call_args_list == [mocker.call('B', 'pre'), mocker.call('B', 'post')]


def test_list_unique_items_with_optional():
class Model(BaseModel):
foo: Optional[List[str]] = Field(None, unique_items=True)
bar: conlist(str, unique_items=True) = Field(None)

assert Model().dict() == {'foo': None, 'bar': None}
assert Model(foo=None, bar=None).dict() == {'foo': None, 'bar': None}
assert Model(foo=['k1'], bar=['k1']).dict() == {'foo': ['k1'], 'bar': ['k1']}
with pytest.raises(ValidationError) as exc_info:
Model(foo=['k1', 'k1'], bar=['k1', 'k1'])
assert exc_info.value.errors() == [
{'loc': ('foo',), 'msg': 'the list has duplicated items', 'type': 'value_error.list.unique_items'},
{'loc': ('bar',), 'msg': 'the list has duplicated items', 'type': 'value_error.list.unique_items'},
]

0 comments on commit 3ba804e

Please sign in to comment.