Skip to content

Commit

Permalink
Add tests for Field defaults with Annotated (some failing/todo)
Browse files Browse the repository at this point in the history
  • Loading branch information
JacobHayes committed Dec 4, 2020
1 parent e0e57c8 commit bcdf1a0
Showing 1 changed file with 67 additions and 19 deletions.
86 changes: 67 additions & 19 deletions tests/test_main.py
Expand Up @@ -1443,45 +1443,63 @@ class M(BaseModel):
get_type_hints(M.__config__)


_5 = 5


def _annotated(*args):
return Annotated[args] if Annotated else None


@pytest.mark.skipif(not Annotated, reason='typing_extensions not installed')
@pytest.mark.parametrize(
['annotated_extras', 'value', 'subclass_ctx', 'empty_init_ctx'],
['hint', 'value', 'subclass_ctx', 'empty_init_ctx'],
[
# Test non-pydantic Annotated uses (random metadata)
([0], Undefined, nullcontext(), pytest.raises(ValueError, match='field required')),
([0], 5, nullcontext(), nullcontext()),
([0], Field(default=5), nullcontext(), nullcontext()),
([0], Field(default=5, ge=0), nullcontext(), nullcontext()),
# Now, test Field in Annotated
([Field(...)], Undefined, nullcontext(), pytest.raises(ValueError, match='field required')),
([Field(default=5)], Undefined, nullcontext(), nullcontext()),
([Field(default=5, ge=0)], Undefined, nullcontext(), nullcontext()),
# Now, test multiple Field specifications (model errors)
(_annotated(int, 0), Undefined, None, pytest.raises(ValueError, match='field required')),
(_annotated(int, 0), _5, None, None),
(_annotated(int, 0), Field(default=_5), None, None),
(_annotated(int, 0), Field(default=_5, ge=0), None, None),
# Test Field in Annotated
(_annotated(int, Field(...)), Undefined, None, pytest.raises(ValueError, match='field required')),
(_annotated(int, Field(default=_5)), Undefined, None, None),
(_annotated(int, Field(default=_5, ge=0)), Undefined, None, None),
# Test default value configurations
(_annotated(int, Field(...)), _5, None, None),
(
_annotated(int, Field(_5)),
_5,
pytest.raises(ValueError, match='`Field` default cannot be set in `Annotated`'),
None,
),
# Test multiple Field specifications (model errors)
(
[Field(...), Field(...)],
_annotated(int, Field(...), Field(...)),
Undefined,
pytest.raises(ValueError, match='cannot specify multiple `Annotated` `Field`s'),
nullcontext(),
None,
),
(
[Field(...)],
_annotated(int, Field(...)),
Field(...),
pytest.raises(ValueError, match='cannot specify `Annotated` and value `Field`'),
nullcontext(),
None,
),
],
)
def test_annotated(annotated_extras, value, subclass_ctx, empty_init_ctx):
M, hint = None, Annotated[(int, *annotated_extras)]
with subclass_ctx:
def test_annotated(hint, value, subclass_ctx, empty_init_ctx):
if hint is None:
pytest.skip('typing_extensions not installed')

M = None
with (subclass_ctx or nullcontext()):

class M(BaseModel):
x: hint = value

if M is None:
return
with empty_init_ctx:
assert M().x == 5 # Defaults above are 5
with (empty_init_ctx or nullcontext()):
assert M().x == _5
assert M(x=10).x == 10

# get_type_hints doesn't recognize typing_extensions.Annotated, so will return the full
Expand All @@ -1492,3 +1510,33 @@ class M(BaseModel):
assert get_type_hints(M, include_extras=True)['x'] == hint
else:
assert get_type_hints(M)['x'] == hint


@pytest.mark.parametrize(
['hint', 'value', 'subclass_ctx'],
[
# Test Field value with different calling conventions for default
(int, Field(_5), None),
(int, Field(default=_5), None),
(Annotated[int, Field()], _5, None),
# Test Field default error cases
(
Annotated[int, Field(_5)],
Undefined,
pytest.raises(ValueError, match='`Field` default cannot be set in `Annotated`'),
),
],
)
def test_field_defaults(hint, value, subclass_ctx):
if hint is None:
pytest.skip('typing_extensions not installed')

M = None
with (subclass_ctx or nullcontext()):

class M(BaseModel):
x: hint = value

if M is None:
return
assert M().x == _5

0 comments on commit bcdf1a0

Please sign in to comment.