Repository: samuelcolvin/pydantic (python)

Issue:
Accept deque as sequence-like input data type
### Checks

* [x] I added a descriptive title to this issue
* [x] I have searched (google, github) for similar issues and couldn't find anything
* [x] I have read and followed [the docs](https://pydantic-docs.helpmanual.io/) and still think this feature/change is needed
* [x] After submitting this, I commit to one of:
    * Look through open issues and helped at least one other person
    * Hit the "watch" button on this repo to receive notifications and I commit to help at least 2 people that ask questions in the future
    * Implement a Pull Request for a confirmed bug

<!-- Sorry to sound so draconian, but every second saved replying to issues is time spend improving pydantic :-) -->

# Feature Request

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.6.1
            pydantic compiled: True
                 install path: /home/quan/.local/lib/python3.8/site-packages/pydantic
               python version: 3.8.2 (default, Jul 16 2020, 14:00:26)  [GCC 9.3.0]
                     platform: Linux-5.4.0-47-generic-x86_64-with-glibc2.29
     optional deps. installed: []

```
<!-- or if you're using pydantic prior to v1.3, manually include: OS, python version and pydantic version -->

<!-- Please read the [docs](https://pydantic-docs.helpmanual.io/) and search through issues to
confirm your feature hasn't been asked for before, or already implemented. -->

Code snippet:

```py
from collections import deque
from typing import List
from pydantic import BaseModel


class Ticket(BaseModel):
    numbers: List[int] = []


Ticket.parse_obj({'numbers': deque((1, 3, 5))})
```

Got error:

```py
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pydantic/main.py", line 455, in pydantic.main.BaseModel.parse_obj
  File "pydantic/main.py", line 346, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Ticket
numbers
  value is not a valid list (type=type_error.list)

1 validation error for Ticket
numbers
  value is not a valid list (type=type_error.list)

```

I expect to get:

```py
Ticket(numbers=[1, 3, 5])
```

Pull request description:
Adding deque to valid field types
<!-- Thank you for your contribution! -->
<!-- Unless your change is trivial, please create an issue to discuss the change before creating a PR -->
<!-- See https://pydantic-docs.helpmanual.io/contributing/ for help on Contributing -->

## Change Summary

Got bored so I PR'd an issue from @hongquan:
Adding deque as a valid sequence type for fields

## Related issue number

Closes #1934 

## Checklist

* [x] Unit tests for the changes exist
* [x] Tests pass on CI and coverage remains at 100%
* [x] Documentation reflects the changes where applicable
* [x] `changes/<pull request or issue id>-<github username>.md` file added describing change
  (see [changes/README.md](https://github.com/samuelcolvin/pydantic/blob/master/changes/README.md) for details)


Patch:
diff --git a/changes/1747-noelevans.md b/changes/1747-noelevans.md
new file mode 100644
index 000000000..9d100acf3
--- /dev/null
+++ b/changes/1747-noelevans.md
@@ -0,0 +1,1 @@
+Properly represent `Literal` Enums when `use_enum_values` is True.
diff --git a/changes/1935-wozniakty.md b/changes/1935-wozniakty.md
new file mode 100644
index 000000000..a3521ada5
--- /dev/null
+++ b/changes/1935-wozniakty.md
@@ -0,0 +1,1 @@
+add `deque` to field types
diff --git a/docs/examples/types_iterables.py b/docs/examples/types_iterables.py
index 2babe1c83..3e5a3caf2 100644
--- a/docs/examples/types_iterables.py
+++ b/docs/examples/types_iterables.py
@@ -1,4 +1,6 @@
-from typing import Dict, FrozenSet, List, Optional, Sequence, Set, Tuple, Union
+from typing import (
+    Deque, Dict, FrozenSet, List, Optional, Sequence, Set, Tuple, Union
+)
 
 from pydantic import BaseModel
 
@@ -24,6 +26,8 @@ class Model(BaseModel):
 
     compound: Dict[Union[str, bytes], List[Set[int]]] = None
 
+    deque: Deque[int] = None
+
 
 print(Model(simple_list=['1', '2', '3']).simple_list)
 print(Model(list_of_ints=['1', '2', '3']).list_of_ints)
@@ -36,3 +40,5 @@ print(Model(tuple_of_different_types=[4, 3, 2, 1]).tuple_of_different_types)
 
 print(Model(sequence_of_ints=[1, 2, 3, 4]).sequence_of_ints)
 print(Model(sequence_of_ints=(1, 2, 3, 4)).sequence_of_ints)
+
+print(Model(deque=[1, 2, 3]).deque)
diff --git a/docs/usage/types.md b/docs/usage/types.md
index 316c6a2cb..03fcb25fc 100644
--- a/docs/usage/types.md
+++ b/docs/usage/types.md
@@ -31,11 +31,11 @@ with custom properties and validation.
   and `int`, `float`, and `Decimal` are coerced using `str(v).encode()`
 
 `list`
-: allows `list`, `tuple`, `set`, `frozenset`, or generators and casts to a list;
+: allows `list`, `tuple`, `set`, `frozenset`, `deque`, or generators and casts to a list;
   see `typing.List` below for sub-type constraints
 
 `tuple`
-: allows `list`, `tuple`, `set`, `frozenset`, or generators and casts to a tuple;
+: allows `list`, `tuple`, `set`, `frozenset`, `deque`, or generators and casts to a tuple;
   see `typing.Tuple` below for sub-type constraints
 
 `dict`
@@ -43,13 +43,17 @@ with custom properties and validation.
   see `typing.Dict` below for sub-type constraints
 
 `set`
-: allows `list`, `tuple`, `set`, `frozenset`, or generators and casts to a set;
+: allows `list`, `tuple`, `set`, `frozenset`, `deque`, or generators and casts to a set;
   see `typing.Set` below for sub-type constraints
 
 `frozenset`
-: allows `list`, `tuple`, `set`, `frozenset`, or generators and casts to a frozen set;
+: allows `list`, `tuple`, `set`, `frozenset`, `deque`, or generators and casts to a frozen set;
   see `typing.FrozenSet` below for sub-type constraints
 
+`deque`
+: allows `list`, `tuple`, `set`, `frozenset`, `deque`, or generators and casts to a deque;
+  see `typing.Deque` below for sub-type constraints
+
 `datetime.date`
 : see [Datetime Types](#datetime-types) below for more detail on parsing and validation
 
@@ -90,6 +94,9 @@ with custom properties and validation.
 `typing.FrozenSet`
 : see [Typing Iterables](#typing-iterables) below for more detail on parsing and validation
 
+`typing.Deque`
+: see [Typing Iterables](#typing-iterables) below for more detail on parsing and validation
+
 `typing.Sequence`
 : see [Typing Iterables](#typing-iterables) below for more detail on parsing and validation
 
diff --git a/pydantic/errors.py b/pydantic/errors.py
index d996bb341..4e43a1f08 100644
--- a/pydantic/errors.py
+++ b/pydantic/errors.py
@@ -282,6 +282,10 @@ class FrozenSetError(PydanticTypeError):
     msg_template = 'value is not a valid frozenset'
 
 
+class DequeError(PydanticTypeError):
+    msg_template = 'value is not a valid deque'
+
+
 class TupleError(PydanticTypeError):
     msg_template = 'value is not a valid tuple'
 
diff --git a/pydantic/fields.py b/pydantic/fields.py
index d9bd85613..f2c9ba050 100644
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -1,8 +1,10 @@
 import warnings
+from collections import deque
 from collections.abc import Iterable as CollectionsIterable
 from typing import (
     TYPE_CHECKING,
     Any,
+    Deque,
     Dict,
     FrozenSet,
     Generator,
@@ -207,6 +209,7 @@ SHAPE_SEQUENCE = 7
 SHAPE_FROZENSET = 8
 SHAPE_ITERABLE = 9
 SHAPE_GENERIC = 10
+SHAPE_DEQUE = 11
 SHAPE_NAME_LOOKUP = {
     SHAPE_LIST: 'List[{}]',
     SHAPE_SET: 'Set[{}]',
@@ -214,6 +217,7 @@ SHAPE_NAME_LOOKUP = {
     SHAPE_SEQUENCE: 'Sequence[{}]',
     SHAPE_FROZENSET: 'FrozenSet[{}]',
     SHAPE_ITERABLE: 'Iterable[{}]',
+    SHAPE_DEQUE: 'Deque[{}]',
 }
 
 
@@ -477,6 +481,9 @@ class ModelField(Representation):
         elif issubclass(origin, FrozenSet):
             self.type_ = get_args(self.type_)[0]
             self.shape = SHAPE_FROZENSET
+        elif issubclass(origin, Deque):
+            self.type_ = get_args(self.type_)[0]
+            self.shape = SHAPE_DEQUE
         elif issubclass(origin, Sequence):
             self.type_ = get_args(self.type_)[0]
             self.shape = SHAPE_SEQUENCE
@@ -620,7 +627,7 @@ class ModelField(Representation):
         if errors:
             return v, errors
 
-        converted: Union[List[Any], Set[Any], FrozenSet[Any], Tuple[Any, ...], Iterator[Any]] = result
+        converted: Union[List[Any], Set[Any], FrozenSet[Any], Tuple[Any, ...], Iterator[Any], Deque[Any]] = result
 
         if self.shape == SHAPE_SET:
             converted = set(result)
@@ -628,6 +635,8 @@ class ModelField(Representation):
             converted = frozenset(result)
         elif self.shape == SHAPE_TUPLE_ELLIPSIS:
             converted = tuple(result)
+        elif self.shape == SHAPE_DEQUE:
+            converted = deque(result)
         elif self.shape == SHAPE_SEQUENCE:
             if isinstance(v, tuple):
                 converted = tuple(result)
@@ -635,6 +644,8 @@ class ModelField(Representation):
                 converted = set(result)
             elif isinstance(v, Generator):
                 converted = iter(result)
+            elif isinstance(v, deque):
+                converted = deque(result)
         return converted, None
 
     def _validate_iterable(
diff --git a/pydantic/json.py b/pydantic/json.py
index 859d373b8..9ce9d57d5 100644
--- a/pydantic/json.py
+++ b/pydantic/json.py
@@ -1,4 +1,5 @@
 import datetime
+from collections import deque
 from decimal import Decimal
 from enum import Enum
 from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
@@ -27,6 +28,7 @@ ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = {
     Decimal: float,
     Enum: lambda o: o.value,
     frozenset: list,
+    deque: list,
     GeneratorType: list,
     IPv4Address: str,
     IPv4Interface: str,
diff --git a/pydantic/main.py b/pydantic/main.py
index 55260d03e..d9d4ba34b 100644
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -672,6 +672,9 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
                 and (not value_include or value_include.is_included(i))
             )
 
+        elif isinstance(v, Enum) and getattr(cls.Config, 'use_enum_values', False):
+            return v.value
+
         else:
             return v
 
diff --git a/pydantic/utils.py b/pydantic/utils.py
index 38bce03c6..e67f336c1 100644
--- a/pydantic/utils.py
+++ b/pydantic/utils.py
@@ -126,7 +126,7 @@ def truncate(v: Union[str], *, max_len: int = 80) -> str:
 
 
 def sequence_like(v: Type[Any]) -> bool:
-    return isinstance(v, (list, tuple, set, frozenset, GeneratorType))
+    return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque))
 
 
 def validate_field_name(bases: List[Type['BaseModel']], field_name: str) -> None:
diff --git a/pydantic/validators.py b/pydantic/validators.py
index 515ffd02e..0a59f93f9 100644
--- a/pydantic/validators.py
+++ b/pydantic/validators.py
@@ -1,5 +1,5 @@
 import re
-from collections import OrderedDict
+from collections import OrderedDict, deque
 from collections.abc import Hashable
 from datetime import date, datetime, time, timedelta
 from decimal import Decimal, DecimalException
@@ -10,6 +10,7 @@ from typing import (
     TYPE_CHECKING,
     Any,
     Callable,
+    Deque,
     Dict,
     FrozenSet,
     Generator,
@@ -245,6 +246,15 @@ def frozenset_validator(v: Any) -> FrozenSet[Any]:
         raise errors.FrozenSetError()
 
 
+def deque_validator(v: Any) -> Deque[Any]:
+    if isinstance(v, deque):
+        return v
+    elif sequence_like(v):
+        return deque(v)
+    else:
+        raise errors.DequeError()
+
+
 def enum_member_validator(v: Any, field: 'ModelField', config: 'BaseConfig') -> Enum:
     try:
         enum_v = field.type_(v)
@@ -548,6 +558,7 @@ _VALIDATORS: List[Tuple[Type[Any], List[Any]]] = [
     (tuple, [tuple_validator]),
     (set, [set_validator]),
     (frozenset, [frozenset_validator]),
+    (deque, [deque_validator]),
     (UUID, [uuid_validator]),
     (Decimal, [decimal_validator]),
     (IPv4Interface, [ip_v4_interface_validator]),


Test patch:
diff --git a/tests/test_main.py b/tests/test_main.py
index b19b6f914..2bed15fe4 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -17,6 +17,7 @@ from pydantic import (
     constr,
     validator,
 )
+from pydantic.typing import Literal
 
 
 def test_success():
@@ -609,6 +610,34 @@ def test_enum_values():
     assert m.foo == 'foo'
 
 
+@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
+def test_literal_enum_values():
+    FooEnum = Enum('FooEnum', {'foo': 'foo_value', 'bar': 'bar_value'})
+
+    class Model(BaseModel):
+        baz: Literal[FooEnum.foo]
+        boo: str = 'hoo'
+
+        class Config:
+            use_enum_values = True
+
+    m = Model(baz=FooEnum.foo)
+    assert m.dict() == {'baz': 'foo_value', 'boo': 'hoo'}
+    assert m.baz.value == 'foo_value'
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(baz=FooEnum.bar)
+
+    assert exc_info.value.errors() == [
+        {
+            'loc': ('baz',),
+            'msg': "unexpected value; permitted: <FooEnum.foo: 'foo_value'>",
+            'type': 'value_error.const',
+            'ctx': {'given': FooEnum.bar, 'permitted': (FooEnum.foo,)},
+        },
+    ]
+
+
 def test_enum_raw():
     FooEnum = Enum('FooEnum', {'foo': 'foo', 'bar': 'bar'})
 
diff --git a/tests/test_types.py b/tests/test_types.py
index cde1cd0fe..2eb55bfca 100644
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -3,12 +3,13 @@ import os
 import re
 import sys
 import uuid
-from collections import OrderedDict
+from collections import OrderedDict, deque
 from datetime import date, datetime, time, timedelta
 from decimal import Decimal
 from enum import Enum, IntEnum
 from pathlib import Path
 from typing import (
+    Deque,
     Dict,
     FrozenSet,
     Iterable,
@@ -852,6 +853,7 @@ def test_dict():
         ((1, 2, '3'), [1, 2, '3']),
         ({1, 2, '3'}, list({1, 2, '3'})),
         ((i ** 2 for i in range(5)), [0, 1, 4, 9, 16]),
+        ((deque((1, 2, 3)), list(deque((1, 2, 3))))),
     ),
 )
 def test_list_success(value, result):
@@ -891,6 +893,7 @@ def test_ordered_dict():
         ((1, 2, '3'), (1, 2, '3')),
         ({1, 2, '3'}, tuple({1, 2, '3'})),
         ((i ** 2 for i in range(5)), (0, 1, 4, 9, 16)),
+        (deque([1, 2, 3]), (1, 2, 3)),
     ),
 )
 def test_tuple_success(value, result):
@@ -998,6 +1001,7 @@ def test_set_type_fails():
     (
         (int, [1, 2, 3], [1, 2, 3]),
         (int, (1, 2, 3), (1, 2, 3)),
+        (int, deque((1, 2, 3)), deque((1, 2, 3))),
         (float, {1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}),
         (Set[int], [{1, 2}, {3, 4}, {5, 6}], [{1, 2}, {3, 4}, {5, 6}]),
         (Tuple[int, str], ((1, 'a'), (2, 'b'), (3, 'c')), ((1, 'a'), (2, 'b'), (3, 'c'))),
@@ -2288,15 +2292,22 @@ def test_frozenset_field():
     assert object_under_test.set == test_set
 
 
-def test_frozenset_field_conversion():
+@pytest.mark.parametrize(
+    'value,result',
+    [
+        ([1, 2, 3], frozenset([1, 2, 3])),
+        ({1, 2, 3}, frozenset([1, 2, 3])),
+        ((1, 2, 3), frozenset([1, 2, 3])),
+        (deque([1, 2, 3]), frozenset([1, 2, 3])),
+    ],
+)
+def test_frozenset_field_conversion(value, result):
     class FrozenSetModel(BaseModel):
         set: FrozenSet[int]
 
-    test_list = [1, 2, 3]
-    test_set = frozenset(test_list)
-    object_under_test = FrozenSetModel(set=test_list)
+    object_under_test = FrozenSetModel(set=value)
 
-    assert object_under_test.set == test_set
+    assert object_under_test.set == result
 
 
 def test_frozenset_field_not_convertible():
@@ -2361,3 +2372,82 @@ def test_bytesize_raises():
     m = Model(size='1MB')
     with pytest.raises(errors.InvalidByteSizeUnit, match='byte unit'):
         m.size.to('bad_unit')
+
+
+def test_deque_success():
+    class Model(BaseModel):
+        v: deque
+
+    assert Model(v=[1, 2, 3]).v == deque([1, 2, 3])
+
+
+@pytest.mark.parametrize(
+    'cls,value,result',
+    (
+        (int, [1, 2, 3], deque([1, 2, 3])),
+        (int, (1, 2, 3), deque((1, 2, 3))),
+        (int, deque((1, 2, 3)), deque((1, 2, 3))),
+        (float, {1.0, 2.0, 3.0}, deque({1.0, 2.0, 3.0})),
+        (Set[int], [{1, 2}, {3, 4}, {5, 6}], deque([{1, 2}, {3, 4}, {5, 6}])),
+        (Tuple[int, str], ((1, 'a'), (2, 'b'), (3, 'c')), deque(((1, 'a'), (2, 'b'), (3, 'c')))),
+        (str, [w for w in 'one two three'.split()], deque(['one', 'two', 'three'])),
+        (int, frozenset([1, 2, 3]), deque([1, 2, 3])),
+    ),
+)
+def test_deque_generic_success(cls, value, result):
+    class Model(BaseModel):
+        v: Deque[cls]
+
+    assert Model(v=value).v == result
+
+
+@pytest.mark.parametrize(
+    'cls,value,errors',
+    (
+        (int, [1, 'a', 3], [{'loc': ('v', 1), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'}]),
+        (int, (1, 2, 'a'), [{'loc': ('v', 2), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'}]),
+        (float, range(10), [{'loc': ('v',), 'msg': 'value is not a valid sequence', 'type': 'type_error.sequence'}]),
+        (float, ('a', 2.2, 3.3), [{'loc': ('v', 0), 'msg': 'value is not a valid float', 'type': 'type_error.float'}]),
+        (float, (1.1, 2.2, 'a'), [{'loc': ('v', 2), 'msg': 'value is not a valid float', 'type': 'type_error.float'}]),
+        (
+            Set[int],
+            [{1, 2}, {2, 3}, {'d'}],
+            [{'loc': ('v', 2, 0), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'}],
+        ),
+        (
+            Tuple[int, str],
+            ((1, 'a'), ('a', 'a'), (3, 'c')),
+            [{'loc': ('v', 1, 0), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'}],
+        ),
+        (
+            List[int],
+            [{'a': 1, 'b': 2}, [1, 2], [2, 3]],
+            [{'loc': ('v', 0), 'msg': 'value is not a valid list', 'type': 'type_error.list'}],
+        ),
+    ),
+)
+def test_deque_fails(cls, value, errors):
+    class Model(BaseModel):
+        v: Deque[cls]
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(v=value)
+    assert exc_info.value.errors() == errors
+
+
+def test_deque_model():
+    class Model2(BaseModel):
+        x: int
+
+    class Model(BaseModel):
+        v: Deque[Model2]
+
+    seq = [Model2(x=1), Model2(x=2)]
+    assert Model(v=seq).v == deque(seq)
+
+
+def test_deque_json():
+    class Model(BaseModel):
+        v: Deque[int]
+
+    assert Model(v=deque((1, 2, 3))).json() == '{"v": [1, 2, 3]}'
diff --git a/tests/test_validators.py b/tests/test_validators.py
index 5b700e8a2..9e3f4dcce 100644
--- a/tests/test_validators.py
+++ b/tests/test_validators.py
@@ -1,3 +1,4 @@
+from collections import deque
 from datetime import datetime
 from itertools import product
 from typing import Dict, List, Optional, Tuple
@@ -56,6 +57,19 @@ def test_frozenset_validation():
     assert Model(a=(6,)).a == frozenset({6})
 
 
+def test_deque_validation():
+    class Model(BaseModel):
+        a: deque
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(a='snap')
+    assert exc_info.value.errors() == [{'loc': ('a',), 'msg': 'value is not a valid deque', 'type': 'type_error.deque'}]
+    assert Model(a={1, 2, 3}).a == deque([1, 2, 3])
+    assert Model(a=deque({1, 2, 3})).a == deque([1, 2, 3])
+    assert Model(a=[4, 5]).a == deque([4, 5])
+    assert Model(a=(6,)).a == deque([6])
+
+
 def test_validate_whole():
     class Model(BaseModel):
         a: List[int]