This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: dataclasses that inherit from Protocol subclasses have wrong __init__
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.11, Python 3.10, Python 3.9
process
Status: closed Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: edgarrmondragon, eric.smith, icgood, julianfortune, kj, lukasz.langa, miss-islington, serhiy.storchaka, shrik, uriyyo
Priority: normal Keywords: patch

Created on 2021-09-01 21:06 by julianfortune, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
test.py icgood, 2021-09-11 21:01 Example of another issue with Protocol subclassing
Pull Requests
URL Status Linked Edit
PR 28121 merged uriyyo, 2021-09-02 09:45
PR 28131 merged miss-islington, 2021-09-02 16:17
PR 28132 merged miss-islington, 2021-09-02 16:17
Messages (7)
msg400868 - (view) Author: Julian Fortune (julianfortune) Date: 2021-09-01 21:06
I believe [`bpo-44806: Fix __init__ in subclasses of protocols`](https://github.com/python/cpython/pull/27545) has caused a regression when using a Dataclass.

In Python `3.9.7`, a `dataclass` that inherits from a subclass of `typing.Protocol` (i.e., a user-defined protocol), does not have the correct `__init__`.

### Demonstration

```python
from dataclasses import dataclass
from typing import Protocol

class P(Protocol):
    pass

@dataclass
class B(P):
    value: str

print(B("test"))
```
In `3.9.7`:
```shell
Traceback (most recent call last):
  File "test.py", line 11, in <module>
    print(B("test"))
TypeError: B() takes no arguments
```
In `3.9.6`:
```shell
B(value='test')
```

### Affected Projects

- [dbt](https://github.com/dbt-labs/dbt/issues/3843)
msg400936 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2021-09-02 16:17
New changeset 0635e201beaf52373f776ff32702795e38f43ae3 by Yurii Karabas in branch 'main':
bpo-45081: Fix __init__ method generation when inheriting from Protocol (GH-28121)
https://github.com/python/cpython/commit/0635e201beaf52373f776ff32702795e38f43ae3
msg400939 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2021-09-02 17:01
New changeset 98eb40828af97760badfa7b8ff84bd4f7a079839 by Miss Islington (bot) in branch '3.9':
bpo-45081: Fix __init__ method generation when inheriting from Protocol (GH-28121) (GH-28132)
https://github.com/python/cpython/commit/98eb40828af97760badfa7b8ff84bd4f7a079839
msg400979 - (view) Author: miss-islington (miss-islington) Date: 2021-09-03 06:27
New changeset 79e9f5a58427c73dc546cb571819d50defe2e14f by Miss Islington (bot) in branch '3.10':
bpo-45081: Fix __init__ method generation when inheriting from Protocol (GH-28121)
https://github.com/python/cpython/commit/79e9f5a58427c73dc546cb571819d50defe2e14f
msg401647 - (view) Author: Ian Good (icgood) * Date: 2021-09-11 21:01
I believe this was a deeper issue that affected all classes inheriting Protocol, causing a TypeError on even the most basic case (see attached):

Traceback (most recent call last):
  File "/.../test.py", line 14, in <module>
    MyClass()
  File "/.../test.py", line 11, in __init__
    super().__init__()
  File "/usr/local/Cellar/python@3.9/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python3.9/typing.py", line 1083, in _no_init
    raise TypeError('Protocols cannot be instantiated')
TypeError: Protocols cannot be instantiated


This was a new regression in 3.9.7 and seems to be resolved by this fix. The desired behavior should be supported according to PEP 544: https://www.python.org/dev/peps/pep-0544/#explicitly-declaring-implementation
msg401671 - (view) Author: Julian Fortune (julianfortune) Date: 2021-09-12 20:42
Ian,

`MyProtocol` does not provide an `__init__()`, and thus
```
super().__init__()
```
is calling the `__init__()` from `Protocol`. This results in the `TypeError`.

Simply remove `super().__init__()` to resolve your issue.

This behavior was changed in https://github.com/python/cpython/pull/27545 (see `Lib/typing.py:1384`); I don't see what you are reporting as a regression, I see it as correct behavior that I would expect. Apologies if you feel differently.

Cheers,
Julian
msg401672 - (view) Author: Ian Good (icgood) * Date: 2021-09-12 21:38
Julian,

That is certainly a workaround, however the behavior you are describing is inconsistent with PEP-544 in both word and intention. From the PEP:

> To explicitly declare that a certain class implements a given protocol, it can be used as a regular base class.

It further describes the semantics of inheriting as "unchanged" from a "regular base class". If the semantics are "unchanged" then it should follow that super().__init__() would pass through the protocol to the object.__init__, just like a "regular base class" would if it does not override __init__.

Furthermore, the intention of inheriting a Protocol as described in the PEP:

> Static analysis tools are expected to automatically detect that a class implements a given protocol. So while it's possible to subclass a protocol explicitly, it's not necessary to do so for the sake of type-checking.

The purpose of adding a Protocol sub-class as an explicit base class is thus only to improve static analysis, it should *not* to modify the runtime semantics.

Consider the case where a package maintainer wants to enhance the flexibility of their types by transitioning from using an ABC to using structural sub-typing. That simple typing change would be a breaking change to the package consumers, who must now remove a super().__init__() call.

Ian
History
Date User Action Args
2022-04-11 14:59:49adminsetgithub: 89244
2021-09-12 21:38:03icgoodsetmessages: + msg401672
2021-09-12 20:42:53julianfortunesetmessages: + msg401671
2021-09-11 21:01:11icgoodsetfiles: + test.py
nosy: + icgood
messages: + msg401647

2021-09-03 06:28:16lukasz.langasetversions: + Python 3.10, Python 3.11
2021-09-03 06:27:22miss-islingtonsetmessages: + msg400979
2021-09-02 18:46:32julianfortunesetstatus: open -> closed
stage: patch review -> resolved
2021-09-02 17:01:40lukasz.langasetmessages: + msg400939
2021-09-02 16:17:27miss-islingtonsetpull_requests: + pull_request26570
2021-09-02 16:17:23miss-islingtonsetnosy: + miss-islington
pull_requests: + pull_request26569
2021-09-02 16:17:21lukasz.langasetmessages: + msg400936
2021-09-02 11:26:13kjsetnosy: + lukasz.langa, serhiy.storchaka, kj
2021-09-02 09:45:45uriyyosetkeywords: + patch
nosy: + uriyyo

pull_requests: + pull_request26562
stage: patch review
2021-09-02 06:17:56shriksetnosy: + shrik
2021-09-01 23:46:54edgarrmondragonsetnosy: + edgarrmondragon
2021-09-01 22:46:46eric.smithsetnosy: + eric.smith
2021-09-01 21:06:11julianfortunecreate