diff --git a/changes/4568-mfulgo.md b/changes/4568-mfulgo.md new file mode 100644 index 0000000000..dfbe63d9fc --- /dev/null +++ b/changes/4568-mfulgo.md @@ -0,0 +1 @@ +Fixes error passing None for optional lists with `unique_items` diff --git a/pydantic/types.py b/pydantic/types.py index eaf679d48e..9438a6a829 100644 --- a/pydantic/types.py +++ b/pydantic/types.py @@ -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() diff --git a/tests/test_validators.py b/tests/test_validators.py index 778085880d..de67ffe472 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -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 @@ -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'}, + ]