mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
e5540296cc
* Fix info with in-place modification of field info * add changes * add test for 3714 * Update changes/4067-adriangb.md Co-authored-by: Samuel Colvin <samcolvin@gmail.com> Co-authored-by: Samuel Colvin <samcolvin@gmail.com>
155 lines
4.2 KiB
Python
155 lines
4.2 KiB
Python
from typing import List
|
|
|
|
import pytest
|
|
from typing_extensions import Annotated
|
|
|
|
from pydantic import BaseModel, Field
|
|
from pydantic.fields import Undefined
|
|
from pydantic.typing import get_all_type_hints
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
['hint_fn', 'value'],
|
|
[
|
|
# Test Annotated types with arbitrary metadata
|
|
pytest.param(
|
|
lambda: Annotated[int, 0],
|
|
5,
|
|
id='misc-default',
|
|
),
|
|
pytest.param(
|
|
lambda: Annotated[int, 0],
|
|
Field(default=5, ge=0),
|
|
id='misc-field-default-constraint',
|
|
),
|
|
# Test valid Annotated Field uses
|
|
pytest.param(
|
|
lambda: Annotated[int, Field(description='Test')], # noqa: F821
|
|
5,
|
|
id='annotated-field-value-default',
|
|
),
|
|
pytest.param(
|
|
lambda: Annotated[int, Field(default_factory=lambda: 5, description='Test')], # noqa: F821
|
|
Undefined,
|
|
id='annotated-field-default_factory',
|
|
),
|
|
],
|
|
)
|
|
def test_annotated(hint_fn, value):
|
|
hint = hint_fn()
|
|
|
|
class M(BaseModel):
|
|
x: hint = value
|
|
|
|
assert M().x == 5
|
|
assert M(x=10).x == 10
|
|
assert get_all_type_hints(M)['x'] == hint
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
['hint_fn', 'value', 'subclass_ctx'],
|
|
[
|
|
pytest.param(
|
|
lambda: Annotated[int, Field(5)],
|
|
Undefined,
|
|
pytest.raises(ValueError, match='`Field` default cannot be set in `Annotated`'),
|
|
id='annotated-field-default',
|
|
),
|
|
pytest.param(
|
|
lambda: Annotated[int, Field(), Field()],
|
|
Undefined,
|
|
pytest.raises(ValueError, match='cannot specify multiple `Annotated` `Field`s'),
|
|
id='annotated-field-dup',
|
|
),
|
|
pytest.param(
|
|
lambda: Annotated[int, Field()],
|
|
Field(),
|
|
pytest.raises(ValueError, match='cannot specify `Annotated` and value `Field`'),
|
|
id='annotated-field-value-field-dup',
|
|
),
|
|
pytest.param(
|
|
lambda: Annotated[int, Field(default_factory=lambda: 5)], # The factory is not used
|
|
5,
|
|
pytest.raises(ValueError, match='cannot specify both default and default_factory'),
|
|
id='annotated-field-default_factory-value-default',
|
|
),
|
|
],
|
|
)
|
|
def test_annotated_model_exceptions(hint_fn, value, subclass_ctx):
|
|
hint = hint_fn()
|
|
with subclass_ctx:
|
|
|
|
class M(BaseModel):
|
|
x: hint = value
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
['hint_fn', 'value', 'empty_init_ctx'],
|
|
[
|
|
pytest.param(
|
|
lambda: Annotated[int, 0],
|
|
Undefined,
|
|
pytest.raises(ValueError, match='field required'),
|
|
id='misc-no-default',
|
|
),
|
|
pytest.param(
|
|
lambda: Annotated[int, Field()],
|
|
Undefined,
|
|
pytest.raises(ValueError, match='field required'),
|
|
id='annotated-field-no-default',
|
|
),
|
|
],
|
|
)
|
|
def test_annotated_instance_exceptions(hint_fn, value, empty_init_ctx):
|
|
hint = hint_fn()
|
|
|
|
class M(BaseModel):
|
|
x: hint = value
|
|
|
|
with empty_init_ctx:
|
|
assert M().x == 5
|
|
|
|
|
|
def test_field_reuse():
|
|
field = Field(description='Long description')
|
|
|
|
class Model(BaseModel):
|
|
one: int = field
|
|
|
|
assert Model(one=1).dict() == {'one': 1}
|
|
|
|
class AnnotatedModel(BaseModel):
|
|
one: Annotated[int, field]
|
|
|
|
assert AnnotatedModel(one=1).dict() == {'one': 1}
|
|
|
|
|
|
def test_config_field_info():
|
|
class Foo(BaseModel):
|
|
a: Annotated[int, Field(foobar='hello')] # noqa: F821
|
|
|
|
class Config:
|
|
fields = {'a': {'description': 'descr'}}
|
|
|
|
assert Foo.schema(by_alias=True)['properties'] == {
|
|
'a': {'title': 'A', 'description': 'descr', 'foobar': 'hello', 'type': 'integer'},
|
|
}
|
|
|
|
|
|
def test_annotated_alias() -> None:
|
|
# https://github.com/samuelcolvin/pydantic/issues/2971
|
|
|
|
StrAlias = Annotated[str, Field(max_length=3)]
|
|
IntAlias = Annotated[int, Field(default_factory=lambda: 2)]
|
|
|
|
Nested = Annotated[List[StrAlias], Field(description='foo')]
|
|
|
|
class MyModel(BaseModel):
|
|
a: StrAlias = 'abc'
|
|
b: StrAlias
|
|
c: IntAlias
|
|
d: IntAlias
|
|
e: Nested
|
|
|
|
assert MyModel(b='def', e=['xyz']) == MyModel(a='abc', b='def', c=2, d=2, e=['xyz'])
|