Skip to content

Comments

[ty] Validate signatures of dataclass __post_init__ methods#22730

Merged
AlexWaygood merged 3 commits intomainfrom
alex/dataclass-post-init-validate
Jan 29, 2026
Merged

[ty] Validate signatures of dataclass __post_init__ methods#22730
AlexWaygood merged 3 commits intomainfrom
alex/dataclass-post-init-validate

Conversation

@AlexWaygood
Copy link
Member

@AlexWaygood AlexWaygood commented Jan 19, 2026

Summary

At runtime, if a dataclass has a __post_init__ method then all InitVar fields are passed to that method as positional arguments (in order of the field definitions). Per tests in the typing conformance suite, a type checker should accordingly verify that a __post_init__ method has a compatible signature with the InitVar fields on the class and its dataclass superclasses.

Test Plan

Added mdtests (and updated some broken ones I wrote previously, oops)

@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Jan 19, 2026
@astral-sh-bot
Copy link

astral-sh-bot bot commented Jan 19, 2026

Typing conformance results improved 🎉

The percentage of diagnostics emitted that were expected errors increased from 79.96% to 80.00%. The percentage of expected errors that received a diagnostic increased from 71.23% to 71.42%.

Summary

Metric Old New Diff Outcome
True Positives 770 772 +2 ⏫ (✅)
False Positives 193 193 +0
False Negatives 311 309 -2 ⏬ (✅)
Total Diagnostics 963 965 +2
Precision 79.96% 80.00% +0.04% ⏫ (✅)
Recall 71.23% 71.42% +0.19% ⏫ (✅)

True positives added

Details
Location Name Message
dataclasses_postinit.py:19:9 invalid-dataclass Invalid __post_init__ signature for dataclass DC1
dataclasses_postinit.py:36:9 invalid-dataclass Invalid __post_init__ signature for dataclass DC2

@AlexWaygood
Copy link
Member Author

AlexWaygood commented Jan 19, 2026

uff, all that code for a +0.05% improvement in precision and a +0.18% improvement in recall. Still, every little helps, I guess 😆 And this is fairly low-hanging fruit.

@astral-sh-bot
Copy link

astral-sh-bot bot commented Jan 19, 2026

mypy_primer results

Changes were detected when running on open source projects
prefect (https://github.com/PrefectHQ/prefect)
- src/integrations/prefect-dbt/prefect_dbt/core/settings.py:94:28: error[invalid-assignment] Object of type `dict[Any, Any] | int | dict[str, Any] | ... omitted 4 union elements` is not assignable to `dict[str, Any]`
- src/integrations/prefect-dbt/prefect_dbt/core/settings.py:99:28: error[invalid-assignment] Object of type `int | dict[Any, Any] | float | ... omitted 3 union elements` is not assignable to `dict[str, Any]`
- src/prefect/cli/deploy/_core.py:86:21: error[invalid-assignment] Object of type `dict[Any, Any] | int | dict[str, Any] | ... omitted 4 union elements` is not assignable to `dict[str, Any]`
- src/prefect/cli/deploy/_core.py:87:21: error[invalid-assignment] Object of type `int | dict[Any, Any] | float | ... omitted 3 union elements` is not assignable to `dict[str, Any]`
- src/prefect/deployments/steps/core.py:137:38: error[invalid-argument-type] Argument is incorrect: Argument type `dict[Any, Any] | int | dict[str, Any] | ... omitted 4 union elements` does not satisfy constraints (`str`, `int`, `int | float`, `bool`, `dict[Any, Any]`, `list[Any]`, `None`) of type variable `T`
- src/prefect/utilities/templating.py:320:13: error[invalid-assignment] Invalid subscript assignment with key of type `object` and value of type `Unknown | int | dict[str, Any] | ... omitted 4 union elements` on object of type `dict[str, Any]`
+ src/prefect/utilities/templating.py:320:13: error[invalid-assignment] Invalid subscript assignment with key of type `object` and value of type `Unknown | dict[str, Any]` on object of type `dict[str, Any]`
- src/prefect/utilities/templating.py:323:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_block_document_references | dict[str, Any]`, found `list[Unknown | int | dict[str, Any] | ... omitted 4 union elements]`
+ src/prefect/utilities/templating.py:323:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_block_document_references | dict[str, Any]`, found `list[Unknown | dict[str, Any]]`
- src/prefect/utilities/templating.py:437:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `dict[object, Unknown | int | float | ... omitted 4 union elements]`
+ src/prefect/utilities/templating.py:437:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `dict[object, Unknown]`
- src/prefect/utilities/templating.py:442:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `list[Unknown | int | float | ... omitted 4 union elements]`
+ src/prefect/utilities/templating.py:442:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `list[Unknown]`
+ src/prefect/workers/base.py:232:13: error[invalid-argument-type] Argument is incorrect: Argument type `str | dict[str, Any]` does not satisfy constraints (`str`, `int`, `int | float`, `bool`, `dict[Any, Any]`, `list[Any]`, `None`) of type variable `T`
- src/prefect/workers/base.py:232:13: error[invalid-argument-type] Argument is incorrect: Argument type `str | int | dict[str, Any] | ... omitted 3 union elements` does not satisfy constraints (`str`, `int`, `int | float`, `bool`, `dict[Any, Any]`, `list[Any]`, `None`) of type variable `T`
- src/prefect/workers/base.py:234:20: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `int | Unknown | float | ... omitted 4 union elements`
- Found 5374 diagnostics
+ Found 5368 diagnostics

static-frame (https://github.com/static-frame/static-frame)
+ static_frame/core/bus.py:671:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemLocReduces[Bus[Any], object_]`, found `InterGetItemLocReduces[Bottom[Bus[Any]] | IndexHierarchy | TypeBlocks | ... omitted 7 union elements, object_]`
- static_frame/core/bus.py:675:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Bus[Any], object_]`, found `InterGetItemILocReduces[Self@iloc, Self@iloc]`
+ static_frame/core/bus.py:675:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Bus[Any], object_]`, found `InterGetItemILocReduces[Bottom[Bus[Any]] | IndexHierarchy | TypeBlocks | ... omitted 7 union elements, Self@iloc]`
+ static_frame/core/series.py:772:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Series[Any, Any], TVDtype@Series]`, found `InterGetItemILocReduces[Bottom[Series[Any, Any]] | IndexHierarchy | TypeBlocks | ... omitted 7 union elements, TVDtype@Series]`
+ static_frame/core/series.py:4072:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[SeriesHE[Any, Any], TVDtype@SeriesHE]`, found `InterGetItemILocReduces[Bottom[Series[Any, Any]] | IndexHierarchy | TypeBlocks | ... omitted 7 union elements, TVDtype@SeriesHE]`
+ static_frame/core/yarn.py:418:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Yarn[Any], object_]`, found `InterGetItemILocReduces[Bottom[Yarn[Any]] | IndexHierarchy | TypeBlocks | ... omitted 7 union elements, object_]`
- Found 1825 diagnostics
+ Found 1829 diagnostics

rotki (https://github.com/rotki/rotki)
- rotkehlchen/chain/decoding/tools.py:96:44: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- rotkehlchen/chain/decoding/tools.py:99:13: error[invalid-argument-type] Argument to function `decode_transfer_direction` is incorrect: Expected `Sequence[A@BaseDecoderTools]`, found `Unknown | tuple[BTCAddress, ...] | tuple[ChecksumAddress, ...] | tuple[SubstrateAddress, ...] | tuple[SolanaAddress, ...]`
- rotkehlchen/chain/decoding/tools.py:100:62: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ rotkehlchen/chain/decoding/tools.py:97:13: error[invalid-argument-type] Argument to function `decode_transfer_direction` is incorrect: Expected `BTCAddress | ChecksumAddress | SubstrateAddress | SolanaAddress`, found `A@BaseDecoderTools`
+ rotkehlchen/chain/decoding/tools.py:98:13: error[invalid-argument-type] Argument to function `decode_transfer_direction` is incorrect: Expected `BTCAddress | ChecksumAddress | SubstrateAddress | SolanaAddress | None`, found `A@BaseDecoderTools | None`
- Found 2054 diagnostics
+ Found 2053 diagnostics

No memory usage changes detected ✅

@AlexWaygood AlexWaygood marked this pull request as ready for review January 19, 2026 19:04
@AlexWaygood AlexWaygood force-pushed the alex/dataclass-post-init-validate branch from a1bc0b3 to a33e8f8 Compare January 23, 2026 18:44
);

let expected_signature =
CallableType::single(db, Signature::new(parameters, Type::object()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type::object here is to support the "any return type is allowed" part of your test suite, right? If so, I think Type::any() better models that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type::object here is to support the "any return type is allowed" part of your test suite, right?

yup, that's correct!

If so, I think Type::any() better models that.

hmm... why do you say that? I'd prefer to use fully static types where they express the semantics just as well. In this case, the returned value from __post_init__ is just discarded at runtime -- any object can be returned, so the return type of object feels like it expresses that very well!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seemed more about the absence of information to me. object suggests "I want an object", whereas Any suggests "I don't care". It's a subtle difference, definitely not something I'd push too hard on.

@AlexWaygood AlexWaygood force-pushed the alex/dataclass-post-init-validate branch from a33e8f8 to 3ad7fac Compare January 29, 2026 14:31
@AlexWaygood AlexWaygood enabled auto-merge (squash) January 29, 2026 14:32
@AlexWaygood AlexWaygood merged commit 6b1a872 into main Jan 29, 2026
48 checks passed
@AlexWaygood AlexWaygood deleted the alex/dataclass-post-init-validate branch January 29, 2026 14:35
carljm added a commit that referenced this pull request Jan 30, 2026
* main: (76 commits)
  [ty] Improve the check for `NewType`s with generic bases (#22961)
  [ty] Ban legacy `TypeVar` bounds or constraints from containing type variables (#22949)
  Bump the typing conformance suite pin (#22960)
  [ty] Emit an error if a TypeVarTuple is used to subscript `Generic` or `Protocol` without being unpacked (#22952)
  [ty] Reduce false positives when subscripting classes generic over `TypeVarTuple`s (#22950)
  [ty] Detect invalid attempts to subclass `Protocol[]` and `Generic[]` simultaneously (#22948)
  Fix suppression indentation matching (#22903)
  Remove hidden `--output-format` warning (#22944)
  [ty] Validate signatures of dataclass `__post_init__` methods (#22730)
  [ty] extend special-cased `numbers` diagnostic to `invalid-argument-type` errors (#22938)
  [ty] Avoid false positive for `not-iterable` with no-positive intersection types (#22089)
  [ty] Preserve pure negation types in descriptor protocol (#22907)
  [ty] add special-case diagnostic for `numbers` module (#22931)
  [ty] Move the location of more `invalid-overload` diagnostics (#22933)
  [ty] Fix unary and comparison operators for TypeVars with union bounds (#22925)
  [ty] Rule Selection: ignore/warn/select all rules (unless subsequently overriden) (#22832)
  [ty] Fix TypedDict construction from existing TypedDict values (#22904)
  [ty] fix bug in string annotations and clean up diagnostics (#22913)
  [ty] Improve support for goto-type, goto-declaration, hover, and highlighting of string annotations (#22878)
  [ty] Rename old typing imports to new on `unresolved-reference`. (#22827)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants