__post_init__ not called from replace
Description
When using msgspec.structs.replace to create a new object, the __post_init__ method is not called (unlike with dataclass.replace).
from dataclasses import dataclass, replace
import msgspec
@dataclass
class D:
x: int
def __post_init__(self):
print("dataclass: post init called")
class M(msgspec.Struct):
x: int
def __post_init__(self):
print("msgspec: post init called")
d = D(1)
m = M(1)
d2 = replace(d, x=2)
m2 = msgspec.structs.replace(m, x=2)
# Output:
# dataclass: post init called
# msgspec: post init called
# dataclass: post init called
Tested with python 3.13.5.
Looking at the PR that introduced this, it seems like __post_init__ is intentionally not called when doing copy or replace operations. I am not entirely sure why that is, but it should probably at least be mentioned in the documentation, as this behaviour differs from e.g. dataclasses.
Thanks for looking into this.
But I think msgspec should really be calling __post_init__ to behave like dataclasses. Also when using copy.replace currently __post_init__ is not called unlike dataclasses does. This is a very unexpected behaviour for devs using msgspec as a dataclass replacement and only mentioning this in the documentation is in my opinion not really the right thing to do.
Yeah, I guess it makes sense to keep the behaviour the same. I've created #933 with a fix and some open questions.