-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Initial Checks
- I have searched GitHub for a duplicate issue and I'm sure this is something new
- I have searched Google & StackOverflow for a solution and couldn't find anything
- I have read and followed the docs and still think this is a bug
- I am confident that the issue is with pydantic (not my code, or another library in the ecosystem like FastAPI or mypy)
Description
When using inheritance with pydantic.dataclasses.dataclass based objects with pydantic>=1.10 I seem to have encountered a bug that calls __post_init_post_parse__ methods multiple times if a __post_init__ method is called further down an inheritance chain.
Running the below code snippet outputs the print statement three times, whereas if __post_init__ is placed in AbstractClass the "post init post parse called" statement is called twice.
The exact same code snippet on pydantic<1.10 works as expected with the print statement only printed the once.
Including an overridden __post_init__ method in ConcreteClass only prints the statement once - but of course if there is more useful logic contained in BaseClass.__post_init__ (as would be the case for this pattern) then this is ignored.
Removing the @dataclass decorators from the base classes works fine however, except if we didn't have access to the code for those underlying base classes.
EDIT: It appears its due to these lines:
https://github.com/pydantic/pydantic/blob/v1.10.2/pydantic/dataclasses.py#L290-L304
Example Code
from pydantic.dataclasses import dataclass
@dataclass
class BaseClass:
def __post_init__(self):
pass
@dataclass
class AbstractClass(BaseClass):
pass
@dataclass
class ConcreteClass(AbstractClass):
def __post_init_post_parse__(self):
print("post init post parse called")
ConcreteClass()
# prints:
# post init post parse called
# post init post parse called
# post init post parse called
# Example of nested dataclasses from docs: https://pydantic-docs.helpmanual.io/usage/dataclasses/#convert-stdlib-dataclasses-into-pydantic-dataclasses
from datetime import datetime
from typing import Optional
@dataclass
class Meta:
modified_date: Optional[datetime]
seen_count: int
def __post_init__(self):
print("POST INIT")
@dataclass
class File(Meta):
filename: str
def __post_init_post_parse__(self):
print("POST INIT POST PARSE")
File(filename="file.txt", modified_date=datetime.now(), seen_count=1)
# prints:
# POST INIT
# POST INIT POST PARSE
# POST INIT POST PARSEPython, Pydantic & OS Version
pydantic version: 1.10.2
pydantic compiled: True
install path: *****
python version: 3.8.13 (default, Mar 28 2022, 06:16:26) [Clang 12.0.0 ]
platform: macOS-10.16-x86_64-i386-64bit
optional deps. installed: ['dotenv', 'typing-extensions']
Affected Components
- Compatibility between releases
- Data validation/parsing
- Data serialization -
.dict()and.json() - JSON Schema
- Dataclasses
- Model Config
- Field Types - adding or changing a particular data type
- Function validation decorator
- Generic Models
- Other Model behaviour -
construct(), pickling, private attributes, ORM mode - Settings Management
- Plugins and integration with other tools - mypy, FastAPI, python-devtools, Hypothesis, VS Code, PyCharm, etc.