Skip to content

Commit

Permalink
fix: support empty tuple type (#2319)
Browse files Browse the repository at this point in the history
  • Loading branch information
PrettyWood committed Feb 13, 2021
1 parent 12ebf0c commit 9ca8fe8
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 12 deletions.
1 change: 1 addition & 0 deletions changes/2318-PrettyWood.md
@@ -0,0 +1 @@
Support empty tuple type
18 changes: 10 additions & 8 deletions pydantic/fields.py
Expand Up @@ -452,18 +452,20 @@ def _type_analysis(self) -> None: # noqa: C901 (ignore complexity)

if issubclass(origin, Tuple): # type: ignore
# origin == Tuple without item type
if not get_args(self.type_):
args = get_args(self.type_)
if not args: # plain tuple
self.type_ = Any
self.shape = SHAPE_TUPLE_ELLIPSIS
else:
elif len(args) == 2 and args[1] is Ellipsis: # e.g. Tuple[int, ...]
self.type_ = args[0]
self.shape = SHAPE_TUPLE_ELLIPSIS
elif args == ((),): # Tuple[()] means empty tuple
self.shape = SHAPE_TUPLE
self.type_ = Any
self.sub_fields = []
for i, t in enumerate(get_args(self.type_)):
if t is Ellipsis:
self.type_ = get_args(self.type_)[0]
self.shape = SHAPE_TUPLE_ELLIPSIS
return
self.sub_fields.append(self._create_sub_type(t, f'{self.name}_{i}'))
else:
self.shape = SHAPE_TUPLE
self.sub_fields = [self._create_sub_type(t, f'{self.name}_{i}') for i, t in enumerate(args)]
return

if issubclass(origin, List):
Expand Down
20 changes: 16 additions & 4 deletions tests/test_edge_cases.py
Expand Up @@ -194,26 +194,38 @@ class Model(BaseModel):

def test_tuple_more():
class Model(BaseModel):
empty_tuple: Tuple[()]
simple_tuple: tuple = None
tuple_of_different_types: Tuple[int, float, str, bool] = None

m = Model(simple_tuple=[1, 2, 3, 4], tuple_of_different_types=[4, 3, 2, 1])
assert m.dict() == {'simple_tuple': (1, 2, 3, 4), 'tuple_of_different_types': (4, 3.0, '2', True)}
m = Model(empty_tuple=[], simple_tuple=[1, 2, 3, 4], tuple_of_different_types=[4, 3, 2, 1])
assert m.dict() == {
'empty_tuple': (),
'simple_tuple': (1, 2, 3, 4),
'tuple_of_different_types': (4, 3.0, '2', True),
}


def test_tuple_length_error():
class Model(BaseModel):
v: Tuple[int, float, bool]
w: Tuple[()]

with pytest.raises(ValidationError) as exc_info:
Model(v=[1, 2])
Model(v=[1, 2], w=[1])
assert exc_info.value.errors() == [
{
'loc': ('v',),
'msg': 'wrong tuple length 2, expected 3',
'type': 'value_error.tuple.length',
'ctx': {'actual_length': 2, 'expected_length': 3},
}
},
{
'loc': ('w',),
'msg': 'wrong tuple length 1, expected 0',
'type': 'value_error.tuple.length',
'ctx': {'actual_length': 1, 'expected_length': 0},
},
]


Expand Down

0 comments on commit 9ca8fe8

Please sign in to comment.