Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions crates/ty_python_semantic/resources/mdtest/dataclasses/post_init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Tests for the `__post_init__` method

At runtime, if a dataclass has a `__post_init__` method then all `InitVar`-annotated fields will be
passed as positional arguments to that method as the final statement in the dataclass's generated
`__init__` method. The `__post_init__` signature must therefore be compatible with the fields of the
dataclass:

```py
from dataclasses import dataclass, InitVar

@dataclass
class Empty1:
def __post_init__(self): ... # fine

@dataclass
class Empty2:
def __post_init__(self) -> None: ... # fine

@dataclass
class Empty3:
# The returned value is discarded,
# so arbitrary return annotations are allowed
def __post_init__(self) -> int:
return 42

@dataclass
class Empty4:
def __post_init__(self, *args): ... # fine

@dataclass
class Empty5:
def __post_init__(self, **kwargs): ... # fine

@dataclass
class Empty6:
def __post_init__(self, *args, **kargs): ... # fine

@dataclass
class Empty7:
# no arguments will be passed to this method at runtime,
# because there are no `InitVar` fields on the class,
# so this is an error:
#
# error: [invalid-dataclass]
def __post_init__(self, required_argument: int): ...

@dataclass
class Empty8:
# error: [invalid-dataclass]
def __post_init__(self, *, required_argument): ...

@dataclass
class Empty9:
# error: [invalid-dataclass]
def __post_init__(self, required_argument, /): ...

@dataclass
class SingleField:
x: int

# `x` will not be passed to `__post_init__`,
# because it is not an `InitVar`, so this is an
#
# error: [invalid-dataclass]
def __post_init__(self, x: int) -> None: ...

@dataclass
class SingleFieldGood:
x: int

# this is fine!
def __post_init__(self) -> None: ...

@dataclass
class HasInitVarNoParameter:
x: InitVar[int]

# error: [invalid-dataclass]
def __post_init__(self) -> None: ...

@dataclass
class HasInitVarDifferentParameterName:
x: InitVar[int]

# because arguments are always passed in positionally
# to `__post_init__` methods, we allow a parameter to
# have an arbitrary name as long as it is inferred has
# having a compatible type. So this is fine:
def __post_init__(self, xx) -> None: ...

@dataclass
class HasInitVarBadParameterType:
x: InitVar[int]

# error: [invalid-dataclass]
def __post_init__(self, x: str) -> None: ...

@dataclass
class HasInitVarBadParameterKind:
x: InitVar[int]

# error: [invalid-dataclass]
def __post_init__(self, *, x: int) -> None: ...

@dataclass
class HasInitVarGood:
x: InitVar[int]

def __post_init__(self, x: int) -> None: ...

@dataclass
class HasInitVarGoodPositionalOnly:
x: InitVar[int]

# arguments are always passed to `__post_init__` positionally at runtime,
# so this is fine
def __post_init__(self, x: int, /) -> None: ...

@dataclass
class LotsOfInitVarsBad:
a: int
b: InitVar[str]
c: InitVar[bytes]
d: int
e: int
f: InitVar[range]
g: int

# Only `InitVar` fields are passed in at runtime, so this is an
# error: [invalid-dataclass]
def __post_init__(self, a: int, b: str, c: bytes, d: int, e: int, f: range, g: int): ...

@dataclass
class LotsOfInitVarsOutOfOrder:
a: int
b: InitVar[str]
c: InitVar[bytes]
d: int
e: int
f: InitVar[range]
g: int

# the parameters are in the wrong order, so this is an
# error: [invalid-dataclass]
def __post_init__(self, c: bytes, b: str, f: range): ...

@dataclass
class LotsOfInitVarsGood:
a: int
b: InitVar[str]
c: InitVar[bytes]
d: int
e: int
f: InitVar[range]
g: int

def __post_init__(self, b: str, c: bytes, f: range): ...

@dataclass
class InitVarSubclassGood(LotsOfInitVarsGood):
h: InitVar[list[int]]
i: str
j: InitVar[bool]

def __post_init__(self, b: str, c: bytes, f: range, h: list[int], j: bool): ...

@dataclass
class InitVarSubclassBad(LotsOfInitVarsGood):
h: InitVar[list[int]]
i: str
j: InitVar[bool]

# error: [invalid-dataclass]
def __post_init__(self, h: list[int], j: bool, b: str, c: bytes, f: range): ...
```
6 changes: 3 additions & 3 deletions crates/ty_python_semantic/resources/mdtest/liskov.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ python-version = "3.13"
```

```pyi
from dataclasses import dataclass
from dataclasses import dataclass, InitVar
from typing_extensions import Self

class Grandparent: ...
Expand All @@ -425,14 +425,14 @@ class Child(Parent):

@dataclass(init=False)
class DataSuper:
x: int
x: InitVar[int]

def __post_init__(self, x: int) -> None:
self.x = x

@dataclass(init=False)
class DataSub(DataSuper):
y: str
y: InitVar[str]

def __post_init__(self, x: int, y: str) -> None:
self.y = y
Expand Down
Loading
Loading