8000 Merge branch 'main' into add-len-baseurl · pydantic/pydantic@6f7ca19 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6f7ca19

Browse files
authored
Merge branch 'main' into add-len-baseurl
2 parents 6149769 + acc5902 commit 6f7ca19

File tree

7 files changed

+66
-12
lines changed

7 files changed

+66
-12
lines changed

docs/concepts/validation_decorator.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,12 @@ using all possible [parameter configurations][parameter] and all possible combin
191191
## Using the [`Field()`][pydantic.Field] function to describe function parameters
192192

193193
The [`Field()` function](fields.md) can also be used with the decorator to provide extra information about
194-
the field and validations. In general it should be used in a type hint with [Annotated](types.md#using-the-annotated-pattern),
195-
unless `default_factory` is specified, in which case it should be used as the default value of the field:
194+
the field and validations. If you don't make use of the `default` or `default_factory` parameter, it is
195+
recommended to use the [annotated pattern](./fields.md#the-annotated-pattern) (so that type checkers
196+
infer the parameter as being required). Otherwise, the [`Field()`][pydantic.Field] function can be used
197+
as a default value (again, to trick type checkers into thinking a default value is provided for the parameter).
196198

197199
```python
198-
from datetime import datetime
199-
200200
from typing_extensions import Annotated
201201

202202
from pydantic import Field, ValidationError, validate_call
@@ -219,12 +219,12 @@ except ValidationError as e:
219219

220220

221221
@validate_call
222-
def when(dt: datetime = Field(default_factory=datetime.now)):
223-
return dt
222+
def return_value(value: str = Field(default='default value')):
223+
return value
224224

225225

226-
print(type(when()))
227-
#> <class 'datetime.datetime'>
226+
print(return_value())
227+
#> default value
228228
```
229229

230230
[Aliases](fields.md#field-aliases) can be used with the decorator as normal:

pydantic/_internal/_typing_extra.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,27 @@ def is_literal(tp: Any, /) -> bool:
8585
return _is_typing_name(get_origin(tp), name='Literal')
8686

8787

88-
# TODO remove and replace with `get_args` when we drop support for Python 3.8
89-
# (see https://docs.python.org/3/whatsnew/3.9.html#id4).
9088
def literal_values(tp: Any, /) -> list[Any]:
91-
"""Return the values contained in the provided `Literal` special form."""
89+
"""Return the values contained in the provided `Literal` special form.
90+
91+
If one of the literal values is a PEP 695 type alias, recursively parse
92+
the type alias' `__value__` to unpack literal values as well. This function
93+
*doesn't* check that the type alias is referencing a `Literal` special form,
94+
so unexpected values could be unpacked.
95+
"""
96+
# TODO When we drop support for Python 3.8, there's no need to check of `is_literal`
97+
# here, as Python unpacks nested `Literal` forms in 3.9+.
98+
# (see https://docs.python.org/3/whatsnew/3.9.html#id4).
9299
if not is_literal(tp):
100+
# Note: we could also check for generic aliases with a type alias as an origin.
101+
# However, it is very unlikely that this happens as type variables can't appear in
102+
# `Literal` forms, so the only valid (but unnecessary) use case would be something like:
103+
# `type Test[T] = Literal['whatever']` (and then use `Test[SomeType]`).
104+
if is_type_alias_type(tp):
105+
# Note: accessing `__value__` could raise a `NameError`, but we just let
106+
# the exception be raised as there's not much we can do if this happens.
107+
return literal_values(tp.__value__< 8000 /span>)
108+
93109
return [tp]
94110

95111
values = get_args(tp)

pydantic/mypy.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,10 @@ def collect_field_or_class_var_from_stmt( # noqa C901
761761
)
762762
node.type = AnyType(TypeOfAny.from_error)
763763

764+
if node.is_final and has_default:
765+
# TODO this path should be removed (see https://github.com/pydantic/pydantic/issues/11119)
766+
return PydanticModelClassVar(lhs.name)
767+
764768
alias, has_dynamic_alias = self.get_alias_info(stmt)
765769
if has_dynamic_alias and not model_config.populate_by_name and self.plugin_config.warn_required_dynamic_aliases:
766770
error_required_dynamic_aliases(self._api, stmt)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# To be changed in V3 (see https://github.com/pydantic/pydantic/issues/11119)
2+
from typing import Final
3+
4+
from pydantic import BaseModel
5+
6+
7+
class Model(BaseModel):
8+
f: Final[int] = 1
9+
10+
11+
Model()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# To be changed in V3 (see https://github.com/pydantic/pydantic/issues/11119)
2+
from typing import Final
3+
4+
from pydantic import BaseModel
5+
6+
7+
class Model(BaseModel):
8+
f: Final[int] = 1
9+
10+
11+
Model()

tests/mypy/test_mypy.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def build_cases(
111111
('mypy-plugin.ini', 'generics.py'),
112112
('mypy-plugin.ini', 'root_models.py'),
113113
('mypy-plugin.ini', 'plugin_strict_fields.py'),
114+
('mypy-plugin.ini', 'final_with_default.py'),
114115
('mypy-plugin-strict-no-any.ini', 'dataclass_no_any.py'),
115116
('mypy-plugin-very-strict.ini', 'metaclass_args.py'),
116117
('pyproject-plugin-no-strict-optional.toml', 'no_strict_optional.py'),

tests/test_json_schema.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from packaging.version import Version
3838
from pydantic_core import CoreSchema, SchemaValidator, core_schema, to_jsonable_python
3939
from pydantic_core.core_schema import ValidatorFunctionWrapHandler
40-
from typing_extensions import Annotated, Literal, Self, TypedDict, deprecated
40+
from typing_extensions import Annotated, Literal, Self, TypeAliasType, TypedDict, deprecated
4141

4242
import pydantic
4343
from pydantic import (
@@ -2378,6 +2378,17 @@ class Model(BaseModel):
23782378
}
23792379

23802380

2381+
def test_literal_schema_type_aliases() -> None:
2382+
TestType0 = TypeAliasType('TestType0', Literal['a'])
2383+
TestType1 = TypeAliasType('TestType1', Literal[TestType0, 'b'])
2384+
TestType2 = TypeAliasType('TestType2', Literal[TestType1, 'c'])
2385+
2386+
assert TypeAdapter(TestType2).json_schema() == {
2387+
'enum': ['a', 'b', 'c'],
2388+
'type': 'string',
2389+
}
2390+
2391+
23812392
def test_literal_enum():
23822393
class MyEnum(str, Enum):
23832394
FOO = 'foo'

0 commit comments

Comments
 (0)
0