Skip to content

Dataclass post_init_post_parse called multiple times. #4487

@Danosaurus-rex

Description

@Danosaurus-rex

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 PARSE

Python, 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

Metadata

Metadata

Assignees

Labels

bug V1Bug related to Pydantic V1.X

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions