Skip to content
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

Dataclass doesn't match a protocol if a field is Callable (expected settable variable, got read-only attribute) #18179

Open
wodny opened this issue Nov 23, 2024 · 0 comments
Labels
bug mypy got something wrong

Comments

@wodny
Copy link

wodny commented Nov 23, 2024

Bug Report

A dataclass containing a Callable field doesn't match the protocol. On the other hand if the field is defined using a callback protocol (another protocol with __call__) or the class is not a dataclass, no error is reported.

test.py:26: error: Incompatible types in assignment (expression has type "FooDC", variable has type "FooProto")  [assignment]
test.py:26: note: Protocol member FooProto.f expected settable variable, got read-only attribute

The mypy's subtypes.py:get_member_flags() function contains:

if v.is_property:
    return {IS_VAR}

which doesn't take the v.is_settable_property into account while it is set for Callables in plugins/dataclasses.py:_propertize_callables().

To Reproduce

https://mypy-play.net/?mypy=latest&python=3.12&gist=5c77ed1a30b03cba5cf0ba8e710a4214

import typing
import collections.abc
import dataclasses



CAC = collections.abc.Callable[..., None]

class FooProto(typing.Protocol):
    f: CAC

class Foo:
    f: CAC

    def __init__(self, f: CAC):
        self.f = f

@dataclasses.dataclass(frozen=False)
class FooDC:
    f: CAC

foo: FooProto = Foo(lambda: None)
# only foo_dc fails:
# test.py:23: error: Incompatible types in assignment (expression has type "FooDC", variable has type "FooProto")  [assignment]
# test.py:23: note: Protocol member FooProto.f expected settable variable, got read-only attribute
foo_dc: FooProto = FooDC(lambda: None)


class CallbackProto(typing.Protocol):
    def __call__(self, *args, **kwargs) -> None: ...

class FooProtoCallback(typing.Protocol):
    f: CallbackProto

class FooCallback:
    f: CallbackProto

    def __init__(self, f: CAC):
        self.f = f

@dataclasses.dataclass(frozen=False)
class FooDCCallback:
    f: CallbackProto

foo_callback: FooProtoCallback = FooCallback(lambda: None)
foo_dc_callback: FooProtoCallback = FooDCCallback(lambda: None)

Expected Behavior

I expect a dataclass with a Callable to match the protocol.

Actual Behavior

The dataclass doesn't match the protocol.

Your Environment

  • Mypy version used: 1.13.0
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.11.2
@wodny wodny added the bug mypy got something wrong label Nov 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

1 participant