Skip to content

Cannot inherit frozen dataclass from a non-frozen one error when using pydantic frozen=True #14857

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
johnnyleitrim opened this issue Mar 8, 2023 · 2 comments · Fixed by #14878
Labels
bug mypy got something wrong topic-dataclass-transform PEP 681

Comments

@johnnyleitrim
Copy link

Bug Report

Specifying frozen=True as model class kwargs for pydantic models causes the following error to be emitted from mypy:

Cannot inherit frozen dataclass from a non-frozen one

To Reproduce

  1. Install pydantic and mypy: pip install pydantic mypy
  2. Create test.py with the following content:
from pydantic import BaseModel

class MyModel(BaseModel, frozen=True):
    my_val: int
  1. Run mypy on the test file: mypy test.py

Expected Behavior
No errors reported (as was the case with mypy 1.0.1):

> mypy test.py
Success: no issues found in 1 source file

Actual Behavior
Errors reported:

> mypy test.py
test.py:3: error: Cannot inherit frozen dataclass from a non-frozen one  [misc]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.1.1
  • Python version used: 3.10
@johnnyleitrim johnnyleitrim added the bug mypy got something wrong label Mar 8, 2023
@wesleywright
Copy link
Collaborator

Similar to in #14850, I believe this is actually an issue with the Pydantic library's use of dataclass_transform. From looking at the code, it seems that BaseModel is itself using the transform and should be treated as a dataclass, so per the standard, frozen dataclasses cannot subclass it.

You may be able to use the same workaround as suggested in #14850 (using the latest version of the Pydantic plugin).

@wesleywright
Copy link
Collaborator

After double checking the spec (whoops), there is actually a provision for this:

Frozen dataclasses cannot inherit from non-frozen dataclasses. A class that has been decorated with dataclass_transform is considered neither frozen nor non-frozen, thus allowing frozen classes to inherit from it. Similarly, a class that directly specifies a metaclass that is decorated with dataclass_transform is considered neither frozen nor non-frozen.

This seems to be the only way to declare a class that has dataclass semantics but which is treated as neither frozen nor non-frozen. This is definitely a bug in MyPy; I will try to create a PR to fix it.

JukkaL pushed a commit that referenced this issue Mar 15, 2023
…t metaclasses (#14878)

Fixes #14857. The initial implementation overlooked this statement in
[PEP 681](https://peps.python.org/pep-0681/#dataclass-semantics):

> Similarly, a class that directly specifies a metaclass that is
decorated with dataclass_transform is considered neither frozen nor
non-frozen.

As far as I can tell, this is a special case that _only_ applies to
classes that directly specify a `dataclass_transform` metaclass. This is
import for projects like Pydantic, which requires clients to use a base
class but still supports a `frozen` parameter.

Note that this allows mixed frozen and non-frozen behavior if the base
class _does_ have fields:

```
@dataclass_transform()
class Meta(type): ...
class Base(metaclass=Meta, frozen=False):
    base: int = 0
class Foo(Base, frozen=True):
    foo: int = 0

foo = Foo()
foo.foo = 1 # an error because Foo is frozen
foo.base = 1 # NOT an error — Base is neither frozen nor non-frozen
```

While this is probably surprising behavior, it seems to match the text
of the PEP as well as the behavior of Pyright.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-dataclass-transform PEP 681
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants