datamodel-code-generator
datamodel-code-generator copied to clipboard
mypy errors due to the way classes are generated with pydantic_v2.BaseModel and --use-annotated
Describe the bug Mypy (and pyright) are not happy about the generated classes
See https://github.com/pydantic/pydantic/issues/6713
To Reproduce
- Generate a model with an Optional field and
--use-annotated
- Use it
- Watch mypy complain with
Missing named argument
error
Expected behavior
- We can use the generated classes without a type checker error
- Classes are generated according to the workaround described in the issue
Version:
- OS: MacOS
- Python version: 3.11.5
- datamodel-code-generator version: 0.25.4
Here is some code that reproduce the issue with --use-annotated
kind of classes
class SomeModel(BaseModel):
some_field: Annotated[Optional[str], Field(None)]
class OtherModel(BaseModel):
other_field: Annotated[Optional[str], Field(default=None)]
some = SomeModel()
other = OtherModel()
🔴 pyright complains about missing parameters for both classes 🔴 mypy complains about missing parameters for both classes
There is a different issue without --use-annotated
kind of classes
class SomeModel(BaseModel):
some_field: Optional[str] = Field(None)
class OtherModel(BaseModel):
other_field: Optional[str] = Field(default=None)
some = SomeModel()
other = OtherModel()
🔴 pyright says Argument missing for parameter "some_field"
but it passes for OtherModel
🤷
🟠 mypy passes only with pydantic.mypy plugin but otherwise same error as pyright
Working version (pydantic v1-like)
from pydantic import BaseModel, Field
from typing import Annotated, Optional
class SomeModel(BaseModel):
some_field: Annotated[Optional[str], Field(None)] = None
class OtherModel(BaseModel):
other_field: Annotated[Optional[str], Field(default=None)] = None
some = SomeModel()
other = OtherModel()
🟢 pyright passes 🟢 mypy passes
I'm seeing this too. I'm a first time mypy
and pydantic
user, so this was a bit confusing 😕
The failure is when instantiating a class Settings
:
settings = Settings()
The checks pass when Settings
is defined as:
class Settings(BaseSettings):
debug: bool = False
prefix_path: Optional[DirectoryPath] = None
but fails when using a `Field:
class Settings(BaseSettings):
debug: bool = False
prefix_path: Optional[DirectoryPath] = Field(None)
The workaround for now is to suppress the error
settings = Settings() # type: ignore[call-arg]
This change was introduced by https://github.com/koxudaxi/datamodel-code-generator/pull/1498.
@i404788 Could you shed some light into why the change was necessary? I've parsed the pydantic documentation and GitHub issues, and couldn't find where pydantic v2 "assign-style" defaults don't work well with Annotated fields.
Could this change maybe be behind a configuration value, like --set-defaults-in-field
?
@tcrasset It's a long time ago so I don't remember the exact case; I believe something like this didn't work at all with pydantic v2:
from pydantic import BaseModel, Field
from typing import Annotated, Optional
class SomeModel(BaseModel):
some_field: Annotated[Optional[str], Field(description="")] = None
which was the default for datamodel-code-generator. I don't think I found anyone else with this issue at the time either (cause v2 was new, and the model would be trivially re-writable if done manually).
Also running into this. Pyright can't type check when we use Annotated
with an optional value; it does not generate the = None
to populate the default even for optional values. Using --output-model-type pydantic.BaseModel
instead of --output-model-type pydantic_v2.BaseModel
seems to fix.