You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Inheriting from class that defines __new__ causes inspect.signature to always return (*args, **kwargs) for constructor #85074
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=Noneclosed_at=<Date2021-07-17.08:37:04.648>created_at=<Date2020-06-07.03:37:38.227>labels= ['type-bug', 'library', '3.9', '3.10', '3.11']
title='Inheriting from class that defines __new__ causes inspect.signature to always return (*args, **kwargs) for constructor'updated_at=<Date2021-07-17.08:37:04.647>user='https://github.com/ezyang'
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.
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().
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.
changed the 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[/+]on Jun 8, 2020
changed the 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[/+]on Jun 8, 2020
JonathanSlenders commented on Feb 17, 2021
JonathanSlendersmannequin
Mannequin
More actions
The following patch to inspect.py solves the issue that inspect.signature() returns the wrong signature on classes that inherit from Generic. Not 100% sure though if this implementation is the cleanest way possible. I've been looking into attaching a __wrapped__ to Generic as well, without success. I'm not very familiar with the inspect code.
To me, this fix is pretty important. ptpython, a Python REPL, has the ability to show the function signature of what the user is currently typing, and with codebases that have lots of generics, there's nothing really useful we can show.
importtokenizeimporttokenimporttypes+importtypingimportwarningsimportfunctoolsimportbuiltins***************def_signature_get_user_defined_method(c***1715,1720****---1716,1725----exceptAttributeError:
returnelse:
+ifmethin (typing.Generic.__new__, typing.Protocol.__new__):
+# Exclude methods from the typing module.+return+ifnotisinstance(meth, _NonUserDefinedCallables):
# Once '__signature__' will be added to 'C'-level# callables, this check won't be necessary
For those interested, the following monkey-patch has the same effect:
defmonkey_patch_typing() ->None:
importinspect, typingdef_signature_get_user_defined_method(cls, method_name):
try:
meth=getattr(cls, method_name)
exceptAttributeError:
returnelse:
ifmethin (typing.Generic.__new__, typing.Protocol.__new__):
# Exclude methods from the typing module.returnifnotisinstance(meth, inspect._NonUserDefinedCallables):
# Once '__signature__' will be added to 'C'-level# callables, this check won't be necessaryreturnmethinspect._signature_get_user_defined_method=_signature_get_user_defined_methodmonkey_patch_typing()
I doubt that solution is correct, given that we already established that the problem is *not* specific to Generic.
2 remaining items
hongweipeng commented on Jul 16, 2021
hongweipengmannequin
Mannequin
More actions
>>> from datetime import timedelta as a
>>> from _datetime import timedelta as b
>>> a is b
True
>>>
timedelta is a C-level class, so inspect.signature(timedelta) is the same with inspect.signature(int).
But signature allow C-level function such as inspect.signature(len), I think one way to improve is to use the C-level function when the python-level function cannot be found.
New changeset 6aab5f9 by Weipeng Hong in branch 'main': bpo-40897:Give priority to using the current class constructor in inspect.signature (bpo-27177) 6aab5f9
New changeset 948e39a by Miss Islington (bot) in branch '3.10': bpo-40897:Give priority to using the current class constructor in inspect.signature (GH-27177) (bpo-27189) 948e39a
On second thought it's a bummer not to fix this in 3.9.x that will still be the only stable version until October. I'll refactor the relevant part of inspect.py in 3.9 to make the backport applicable.
New changeset ed2db9b by Łukasz Langa in branch '3.9': bpo-40897: Partially backport #66773's refactor of inspect.py to allow bugfix backports (bpo-27193) ed2db9b
New changeset df7c629 by Miss Islington (bot) in branch '3.9': bpo-40897:Give priority to using the current class constructor in inspect.signature (GH-27177) (GH-27209) df7c629
Activity
ezyang commentedon Jun 7, 2020
Consider the following program:
I expect inspect.signature to return () as the signature of the constructor of this function. However, I get this:
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.
serhiy-storchaka commentedon Jun 7, 2020
It is not special for Generic, but happens with every type implementing __new__.
The above example prints "(a=1, *args, **kwargs)" instead of "(b)".
gvanrossum commentedon Jun 8, 2020
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().
serhiy-storchaka commentedon Jun 8, 2020
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.
gvanrossum commentedon Jun 8, 2020
Changing the topic to not point fingers at Generic.
[-]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[/+][-]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[/+]JonathanSlenders commentedon Feb 17, 2021
The following patch to inspect.py solves the issue that inspect.signature() returns the wrong signature on classes that inherit from Generic. Not 100% sure though if this implementation is the cleanest way possible. I've been looking into attaching a __wrapped__ to Generic as well, without success. I'm not very familiar with the inspect code.
To me, this fix is pretty important. ptpython, a Python REPL, has the ability to show the function signature of what the user is currently typing, and with codebases that have lots of generics, there's nothing really useful we can show.
For those interested, the following monkey-patch has the same effect:
gvanrossum commentedon Feb 17, 2021
I doubt that solution is correct, given that we already established that the problem is *not* specific to Generic.
2 remaining items
hongweipeng commentedon Jul 16, 2021
timedeltais a C-level class, so inspect.signature(timedelta) is the same with inspect.signature(int).But
signatureallow C-level function such asinspect.signature(len), I think one way to improve is to use the C-level function when the python-level function cannot be found.ambv commentedon Jul 16, 2021
New changeset 6aab5f9 by Weipeng Hong in branch 'main':
bpo-40897:Give priority to using the current class constructor in
inspect.signature(bpo-27177)6aab5f9
ambv commentedon Jul 16, 2021
We won't be backporting this fix to 3.9 due to larger changes between versions.
ambv commentedon Jul 16, 2021
New changeset 948e39a by Miss Islington (bot) in branch '3.10':
bpo-40897:Give priority to using the current class constructor in
inspect.signature(GH-27177) (bpo-27189)948e39a
ambv commentedon Jul 16, 2021
On second thought it's a bummer not to fix this in 3.9.x that will still be the only stable version until October. I'll refactor the relevant part of inspect.py in 3.9 to make the backport applicable.
ambv commentedon Jul 17, 2021
New changeset ed2db9b by Łukasz Langa in branch '3.9':
bpo-40897: Partially backport #66773's refactor of inspect.py to allow bugfix backports (bpo-27193)
ed2db9b
ambv commentedon Jul 17, 2021
New changeset df7c629 by Miss Islington (bot) in branch '3.9':
bpo-40897:Give priority to using the current class constructor in
inspect.signature(GH-27177) (GH-27209)df7c629
ambv commentedon Jul 17, 2021
Thanks! ✨ 🍰 ✨