Skip to content

Inheritance from base class with property in class makes them non-instantiatable #91393

Open

Description

@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']

Activity

  1. Germandrummer92 commented on Apr 6, 2022

    Germandrummer92mannequin
    MannequinAuthor

    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()
  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    type-bugAn unexpected behavior, bug, or error
    on Apr 6, 2022
  3. ericvsmith commented on Apr 6, 2022

    @ericvsmith
    Member

    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 <module>
        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 <module>
        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
  4. changed the title [-]Inheritance from Protocol with property in dataclass makes them non-instantiatable[/-] [+]Inheritance from Protocol with property in class makes them non-instantiatable[/+] on Apr 6, 2022
  5. changed the title [-]Inheritance from Protocol with property in dataclass makes them non-instantiatable[/-] [+]Inheritance from Protocol with property in class makes them non-instantiatable[/+] on Apr 6, 2022
  6. changed the title [-]Inheritance from Protocol with property in class makes them non-instantiatable[/-] [+]Inheritance from base class with property in class makes them non-instantiatable[/+] on Apr 6, 2022
  7. changed the title [-]Inheritance from Protocol with property in class makes them non-instantiatable[/-] [+]Inheritance from base class with property in class makes them non-instantiatable[/+] on Apr 6, 2022
  8. gvanrossum commented on Apr 6, 2022

    @gvanrossum
    Member

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

  9. JelleZijlstra commented on Apr 6, 2022

    @JelleZijlstra
    Member

    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.

  10. ericvsmith commented on Apr 6, 2022

    @ericvsmith
    Member

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

  11. transferred this issue fromon Apr 10, 2022
  12. Kentzo commented on Oct 22, 2022

    @Kentzo
    Contributor

    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
    
  13. gvanrossum commented on Oct 22, 2022

    @gvanrossum
    Member

    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".

  14. Kentzo commented on Oct 22, 2022

    @Kentzo
    Contributor

    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
  15. vnmabus commented on Apr 2, 2025

    @vnmabus

    Would this work somehow with the ReadOnly extension proposed in PEP 767?

  16. JelleZijlstra commented on Apr 2, 2025

    @JelleZijlstra
    Member

    Yes, ReadOnly should fix this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.10 (EOL)end of lifestdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions