I don't understand why the code:
from typing import Optional
from pydantic import Field
from pydantic.dataclasses import dataclass
@dataclass
class Klass:
field1: str = Field(min_length=1)
field2: str = Field(min_length=1)
field3: Optional[str]
throws the error:
TypeError: non-default argument 'field3' follows default argument
if by default Field
default
kwarg is PydanticUndefined
. Why are field1
and field2
default arguments?
I'm using python 3.8 and pydantic 2.6
I tried field3: Optional[str] = Field(...)
and it works. I expected the code block above to work because all fields are required and none has default values.
TL;DR
field3
is a required argument with type Optional[str]
, not an optional argument, because you didn't assign anything to field3
in the class definition.
field1
and field2
are technically optional, because the Field
object you assign to each provides a default of PydanticUndefined
. That value, though,
causes a validation error at runtime if you don't supply another argument in its place.
The dataclass
decorator is constructing a def
statement to define your class's __init__
method that looks something like
def __init__(self, field1=PydanticUndefined, field2=PydanticUndefined, field3):
...
The constructed statement is then exec
ed, which is why you get the error about a non-default argument when the class is defined, rather than when you try to instantiate the class.
To make field3
optional, you have to provide a default value.
field3: Optional[str] = None
This makes the defined statement something like
def __init__(self, field1=PydanticUndefined, field2=PydanticUndefined, field3=None):
...
You can't (as far as I know) make field1
or field2
truly required; the PydanticUndefined
value just causes __init__
to raise a ValidationError
rather than a TypeError
if no explicit
argument is passed.
>>> Klass()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/chepner/py311/lib/python3.11/site-packages/pydantic/_internal/_dataclasses.py", line 134, in __init__
s.__pydantic_validator__.validate_python(ArgsKwargs(args, kwargs), self_instance=s)
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Klass
field1
Field required [type=missing, input_value=ArgsKwargs(()), input_type=ArgsKwargs]
For further information visit https://errors.pydantic.dev/2.5/v/missing
field2
Field required [type=missing, input_value=ArgsKwargs(()), input_type=ArgsKwargs]
For further information visit https://errors.pydantic.dev/2.5/v/missing
I haven't dug into the source to see exactly how that happens, but I assume it's something resembling
def __init__(self, field1=PydanticUndefined, ...):
if field1 is PydanticUndefined:
# prepare ValidationError exception
if field2 is PydanticUndefined:
# prepare ValidationError exception
if <ValidationError needs to be raised>:
raise ValidationError(...)
If desired, you can provide "real" default values for field1
and field2
by adding the default
keyword argument to Field
.