classification
Title: Inheriting from class that defines __new__ causes inspect.signature to always return (*args, **kwargs) for constructor
Type: behavior Stage: patch review
Components: Library (Lib) Versions: Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: FFY00, ezyang, gvanrossum, hongweipeng, ralf.gommers, serhiy.storchaka, yselivanov
Priority: normal Keywords: patch

Created on 2020-06-07 03:37 by ezyang, last changed 2020-11-17 08:46 by hongweipeng.

Pull Requests
URL Status Linked Edit
PR 23336 open hongweipeng, 2020-11-17 08:46
Messages (5)
msg370870 - (view) Author: Edward Yang (ezyang) Date: 2020-06-07 03:37
Consider the following program:

```
import inspect
from typing import Generic, TypeVar

T = TypeVar('T')

class A(Generic[T]):
    def __init__(self) -> None:
        pass

print(inspect.signature(A))
```

I expect inspect.signature to return () as the signature of the constructor of this function. However, I get this:

```
$ python3 foo.py
(*args, **kwds)
```

Although it is true that one cannot generally rely on inspect.signature to always give the most accurate signature (because there may always be decorator or metaclass shenanigans getting in the way), in this particular case it seems especially undesirable because Python type annotations are supposed to be erased at runtime, and yet here inheriting from Generic (simply to add type annotations) causes a very clear change in runtime behavior.
msg370888 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2020-06-07 11:11
It is not special for Generic, but happens with every type implementing __new__.

class A:
    def __new__(cls, a=1, *args, **kwargs):
        return object.__new__(cls)

class B(A):
    def __init__(self, b):
        pass

import inspect
print(inspect.signature(B))

The above example prints "(a=1, *args, **kwargs)" instead of "(b)".
msg370958 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2020-06-08 04:58
So does that make this "not a bug"? Or is there something to document? For technical reasons we can't just add a __init__ method to Generic, and I doubt that it's feasible to change inspect.signature().
msg370970 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2020-06-08 09:25
I think that inspect.signature() could be made more smart. It should take into account signatures of both __new__ and __init__ and return the most specific compatible signature.
msg371023 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2020-06-08 17:16
Changing the topic to not point fingers at Generic.
History
Date User Action Args
2020-11-17 08:46:55hongweipengsetkeywords: + patch
nosy: + hongweipeng

pull_requests: + pull_request22225
stage: patch review
2020-06-08 17:16:39gvanrossumsetnosy: - levkivskyi

messages: + msg371023
title: Inheriting from Generic causes inspect.signature to always return (*args, **kwargs) for constructor (and all subclasses) -> Inheriting from class that defines __new__ causes inspect.signature to always return (*args, **kwargs) for constructor
2020-06-08 17:12:47FFY00setnosy: + FFY00
2020-06-08 09:25:51serhiy.storchakasetmessages: + msg370970
2020-06-08 04:58:18gvanrossumsetmessages: + msg370958
2020-06-07 18:59:17ralf.gommerssetnosy: + ralf.gommers
2020-06-07 11:11:40serhiy.storchakasetnosy: + serhiy.storchaka, yselivanov
messages: + msg370888
2020-06-07 04:17:26xtreaksetnosy: + gvanrossum, levkivskyi
2020-06-07 03:37:38ezyangcreate