Inheritance from base class with property in class makes them non-instantiatable · Issue #91393 · python/cpython (original) (raw)

@Germandrummer92

BPO 47237
Nosy @gvanrossum, @ericvsmith, @JelleZijlstra, @Fidget-Spinner, @AlexWaygood, @Germandrummer92

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None closed_at = None created_at = <Date 2022-04-06.08:41:16.184> labels = ['type-bug', 'library', '3.10'] title = 'Inheritance from base class with property in class makes them non-instantiatable' updated_at = <Date 2022-04-06.15:54:34.248> user = 'https://github.com/Germandrummer92'

bugs.python.org fields:

activity = <Date 2022-04-06.15:54:34.248> actor = 'eric.smith' assignee = 'none' closed = False closed_date = None closer = None components = ['Library (Lib)'] creation = <Date 2022-04-06.08:41:16.184> creator = 'Germandrummer92' dependencies = [] files = [] hgrepos = [] issue_num = 47237 keywords = [] message_count = 5.0 messages = ['416846', '416853', '416880', '416881', '416882'] nosy_count = 6.0 nosy_names = ['gvanrossum', 'eric.smith', 'JelleZijlstra', 'kj', 'AlexWaygood', 'Germandrummer92'] pr_nums = [] priority = 'normal' resolution = None stage = None status = 'open' superseder = None type = 'behavior' url = 'https://bugs.python.org/issue47237' versions = ['Python 3.10']

@Germandrummer92

Hi,

According to https://peps.python.org/pep-0544/#explicitly-declaring-implementation it should be possible to explicitly inherit from Protocols. This however breaks the dataclass constructor when using the @Property decorator in the protocol, see this example:

from typing import Protocol from dataclasses import dataclass

class SomeProtocol(Protocol): @property def some_value(self) -> str: ...

@dataclass class SomeDataclasss(SomeProtocol): some_value: str

if name == 'main': a = SomeDataclasss(some_value="value") # this crashes with AttributeError: can't set attribute 'some_value'

The pattern of @Property in the protocol is one taken from the mypy docs (see https://mypy.readthedocs.io/en/stable/protocols.html#recursive-protocols for example).

When removing the explicit inheritiance mypy also correctly typechecks the dataclass implementation when doing something like, only the explicit inheritance seems to fail in python

a: SomeProtocol = SomeDataclass()

@ericvsmith

Here's the error without dataclasses:

------

from typing import Protocol

class SomeProtocol(Protocol): @property def some_value(self) -> str: ...

class SomeClass(SomeProtocol): def init(self, some_value): self.some_value = some_value

if name == 'main': a = SomeClass(some_value="value")


Traceback (most recent call last): File "foo.py", line 12, in a = SomeClass(some_value="value") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "foo.py", line 9, in init self.some_value = some_value ^^^^^^^^^^^^^^^ AttributeError: property 'some_value' of 'SomeClass' object has no setter

And here it is without Protocol:
--------

class SomeProperty: @property def some_value(self) -> str: ...

class SomeClass(SomeProperty): def init(self, some_value): self.some_value = some_value

if name == 'main': a = SomeClass(some_value="value")


Traceback (most recent call last): File "foo.py", line 10, in a = SomeClass(some_value="value") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "foo.py", line 7, in init self.some_value = some_value ^^^^^^^^^^^^^^^ AttributeError: property 'some_value' of 'SomeClass' object has no setter

@ericvsmith ericvsmith changed the titleInheritance from Protocol with property in dataclass makes them non-instantiatable Inheritance from Protocol with property in class makes them non-instantiatable

Apr 6, 2022

@ericvsmith ericvsmith changed the titleInheritance from Protocol with property in dataclass makes them non-instantiatable Inheritance from Protocol with property in class makes them non-instantiatable

Apr 6, 2022

@ericvsmith ericvsmith changed the titleInheritance from Protocol with property in class makes them non-instantiatable Inheritance from base class with property in class makes them non-instantiatable

Apr 6, 2022

@ericvsmith ericvsmith changed the titleInheritance from Protocol with property in class makes them non-instantiatable Inheritance from base class with property in class makes them non-instantiatable

Apr 6, 2022

@gvanrossum

So is the conclusion that this should be closed as "not a bug"?

@JelleZijlstra

I think the behavior with regular classes is expected (that's just how inheritance works), but a case could be made that dataclasses should handle this case specially.

@ericvsmith

What would dataclasses do that's different from a regular class?

@Kentzo

With CPython 3.10.7 and mypy 0.982

import abc import dataclasses as dc from typing import Protocol

class Proto(Protocol): @property @abc.abstractmethod def prop(self) -> int: raise NotImplementedError

@dc.dataclass class Implicit: prop: int

@dc.dataclass class Explicit(Proto): prop: int

x: Proto = Implicit(42) y: Proto = Explicit(42)

The results are:

$ python test.py
Traceback (most recent call last):
  File "/private/tmp/test.py", line 24, in <module>
    y: Proto = Explicit(42)
TypeError: Can't instantiate abstract class Explicit with abstract method prop     

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

@gvanrossum

I see this as a perhaps surprising result of how all these things work, not as a bug. Note that Protocol isn't needed, the same error is given if Proto doesn't inherit from Protocol. A workaround would be to set a default value in the Explicit subclass (prop: int = 0). But mostly my view is "don't do that".

@Kentzo

Another workaround that removes inheritance but preserves explicit type checking at the place of definition:

if TYPE_CHECKING: _: type[Proto] = Explicit

@dc.dataclass class Explicit: prop: int