Skip to content

[ty] Emit a diagnostic when frozen dataclass inherits a non-frozen dataclass and the other way around#21962

Merged
AlexWaygood merged 5 commits intoastral-sh:mainfrom
silamon:frozen-non-frozen-diagnostics
Dec 13, 2025
Merged

[ty] Emit a diagnostic when frozen dataclass inherits a non-frozen dataclass and the other way around#21962
AlexWaygood merged 5 commits intoastral-sh:mainfrom
silamon:frozen-non-frozen-diagnostics

Conversation

@silamon
Copy link
Contributor

@silamon silamon commented Dec 13, 2025

Summary

Introduce a new diagnostic that is raised in the following situations:

  • Checks for non-frozen dataclasses that inherit from frozen dataclasses.
  • Checks for frozen dataclasses that inherit from non-frozen dataclasses.

Test Plan

Tests have been added.
The diagnostics include example codes to test.

@AlexWaygood AlexWaygood changed the title Raise a diagnostic when frozen dataclass inherits a non-frozen dataclass and the other way around [ty] Emit a diagnostic when frozen dataclass inherits a non-frozen dataclass and the other way around Dec 13, 2025
@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Dec 13, 2025
@astral-sh-bot
Copy link

astral-sh-bot bot commented Dec 13, 2025

Diagnostic diff on typing conformance tests

Changes were detected when running ty on typing conformance tests
--- old-output.txt	2025-12-13 20:51:37.689793991 +0000
+++ new-output.txt	2025-12-13 20:51:41.343814255 +0000
@@ -282,6 +282,8 @@
 dataclasses_final.py:38:1: error[invalid-assignment] Cannot assign to final attribute `final_with_default` on type `<class 'D'>`
 dataclasses_frozen.py:16:1: error[invalid-assignment] Property `a` defined in `DC1` is read-only
 dataclasses_frozen.py:17:1: error[invalid-assignment] Property `b` defined in `DC1` is read-only
+dataclasses_frozen.py:23:7: error[invalid-frozen-dataclass-subclass] Non-frozen dataclass `DC2` cannot inherit from frozen dataclass `DC1`
+dataclasses_frozen.py:33:7: error[invalid-frozen-dataclass-subclass] Frozen dataclass `DC4` cannot inherit from non-frozen dataclass `DC3`
 dataclasses_kwonly.py:23:11: error[too-many-positional-arguments] Too many positional arguments: expected 1, got 2
 dataclasses_kwonly.py:38:11: error[too-many-positional-arguments] Too many positional arguments: expected 1, got 2
 dataclasses_kwonly.py:53:11: error[too-many-positional-arguments] Too many positional arguments: expected 1, got 2
@@ -342,6 +344,7 @@
 dataclasses_transform_func.py:70:8: error[missing-argument] No arguments provided for required parameters `id`, `name`
 dataclasses_transform_func.py:70:18: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 2
 dataclasses_transform_func.py:76:36: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `T@create_model_frozen`
+dataclasses_transform_func.py:89:7: error[invalid-frozen-dataclass-subclass] Non-frozen dataclass `Customer3Subclass` cannot inherit from frozen dataclass `Customer3`
 dataclasses_transform_func.py:96:1: error[invalid-assignment] Property `id` defined in `Customer3` is read-only
 dataclasses_transform_meta.py:63:1: error[invalid-assignment] Property `id` defined in `Customer1` is read-only
 dataclasses_transform_meta.py:66:8: error[missing-argument] No arguments provided for required parameters `id`, `name`
@@ -1022,4 +1025,4 @@
 typeddicts_usage.py:28:17: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor
 typeddicts_usage.py:28:18: error[invalid-key] Unknown key "title" for TypedDict `Movie`: Unknown key "title"
 typeddicts_usage.py:40:24: error[invalid-type-form] The special form `typing.TypedDict` is not allowed in type expressions
-Found 1024 diagnostics
+Found 1027 diagnostics

@astral-sh-bot
Copy link

astral-sh-bot bot commented Dec 13, 2025

mypy_primer results

Changes were detected when running on open source projects
attrs (https://github.com/python-attrs/attrs)
+ tests/test_functional.py:84:7: error[invalid-frozen-dataclass-subclass] Non-frozen dataclass `SubFrozen` cannot inherit from frozen dataclass `Frozen`
+ tests/test_next_gen.py:252:15: error[invalid-frozen-dataclass-subclass] Non-frozen dataclass `C` cannot inherit from frozen dataclass `B`
+ tests/test_next_gen.py:297:19: error[invalid-frozen-dataclass-subclass] Non-frozen dataclass `C` cannot inherit from frozen dataclass `A`
- Found 605 diagnostics
+ Found 608 diagnostics

scikit-build-core (https://github.com/scikit-build/scikit-build-core)
- src/scikit_build_core/build/wheel.py:98:20: error[no-matching-overload] No overload of bound method `__init__` matches arguments
- Found 42 diagnostics
+ Found 41 diagnostics

pandas-stubs (https://github.com/pandas-dev/pandas-stubs)
+ pandas-stubs/_typing.pyi:1218:16: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
- Found 5136 diagnostics
+ Found 5137 diagnostics

No memory usage changes detected ✅

Copy link
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

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

Nice, we should definitely emit these diagnostics! These always raise exceptions at runtime, so it's a great thing for a type checker to catch.

But let's improve the documentation and add some tests.

@AlexWaygood
Copy link
Member

The typing conformance suite results are great! They show us now emitting diagnostics on several places where we are meant to, but where we previously didn't.

The mypy_primer results show us now emitting three false-positive diagnostics on attrs. attrs has slightly different semantics to the stdlib dataclasses module here, apparently. For the stdlib dataclasses module, you have to explicitly pass frozen=True for a @dataclass subclass of a frozen=True @dataclass superclass. But for attrs, any subclass of a frozen=True @attr.s superclass is implicitly frozen.

The fact that we now emit a few false positives on attrs is a bit unfortunate, but I think it's fine. I don't think you need to worry about it here. We might add some special-cased support for attrs in the future, but until that point it's the right thing for us to just assume that attrs classes have the exact same semantics as stdlib dataclasses.

@AlexWaygood
Copy link
Member

AlexWaygood commented Dec 13, 2025

Maybe these should just be one error code? invalid-frozen-dataclass-subclass?

@silamon silamon requested a review from MichaReiser as a code owner December 13, 2025 18:47
Copy link
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

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

Thanks!

@AlexWaygood AlexWaygood merged commit a544c59 into astral-sh:main Dec 13, 2025
39 of 41 checks passed
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