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
We have a pytest that boils down to the following:
#from __future__ import annotations
from inspect import Parameter, signature
def foo(x: str) -> str:
return x + x
def test_foo():
expected = (
Parameter("x", Parameter.POSITIONAL_OR_KEYWORD, annotation=str),
)
actual = tuple(signature(foo).parameters.values())
assert expected == actual
(execute with pip install pytest && pytest -vv test_foo.py)
I tried importing 3.10 annotations (so that we can get rid of quotes around the class containing foo(), which is omitted here because it isn't necessary to reproduce the bug), but doing so changes the output of inspect.signature() but not the output inspect.Parameter(), causing a mismatch between the two that breaks the test.
The above passes on 3.7.9, 3.8.7 & 3.9.1, and if I uncomment the first line, it fails on those same versions.
As can be expected, the annotations import is a no-op on 3.10.0a5 and the test passes either way.
I expect inspect might have not been correctly updated to support postponed annotations, but I haven't looked at the implementation (I'm not familiar with the CPython codebase at all) so it's just a guess.
Can you show the values of “expected” and “actual” for both the failures and successes?
1ace commented on Mar 3, 2021
1acemannequin
MannequinAuthor
More actions
Sure thing.
Putting the reproducing code in test_foo.py and running docker run --rm -it -v $PWD:/code python sh -c 'pip install pytest && pytest -vvv /code/test_foo.py' yields:
@1ace, a possible workaround if you want full compatibility regardless of the future import is to use typing.get_type_hints:
(The result doesn't change even with from __future__ import annotations. This also works from 3.7-3.10)
>>> from typing import get_type_hints
>>> get_type_hints(foo)
{'x': <class 'str'>, 'return': <class 'str'>}
1ace commented on Mar 3, 2021
1acemannequin
MannequinAuthor
More actions
Sorry, I just realized you asked for the "success" value as well.
For python 3.7-3.9 without the import annotations, and for python 3.10, the type annotation is a class, ie. <Parameter "x: str">.
1ace commented on Mar 3, 2021
1acemannequin
MannequinAuthor
More actions
(... and I had the window open for too long and didn't notice Ken's reply before mine ^^')
@kj: thanks for the workaround! it's good to be able to change our code and not have to depend on a yet-to-be-released patch on each python version :)
That said, typing.get_type_hints() only returns the type hint, whereas our current test checks for other things (like the default value) which need to match (I think) for cython's use of __signature__? (I inherited all this code, so I'm not familiar with it and its history, and I'm beginning to question this test's necessity.)
I think this is all about: should inspect.signature() resolve string annotations into actual types (via get_type_hints, or whatever)? I don't use inspect much, so I can't offer an opinion there.
Python 3.10 will add an eval_str= argument to inspect.signature() that lets you evaluate string annotations. I don't think it makes sense to change anything in the bugfix branches, so I propose that this issue be closed.
Activity
1ace commentedon Mar 1, 2021
We have a pytest that boils down to the following:
(execute with
pip install pytest && pytest -vv test_foo.py)I tried importing 3.10 annotations (so that we can get rid of quotes around the class containing
foo(), which is omitted here because it isn't necessary to reproduce the bug), but doing so changes the output ofinspect.signature()but not the outputinspect.Parameter(), causing a mismatch between the two that breaks the test.The above passes on 3.7.9, 3.8.7 & 3.9.1, and if I uncomment the first line, it fails on those same versions.
As can be expected, the annotations import is a no-op on 3.10.0a5 and the test passes either way.
I expect
inspectmight have not been correctly updated to support postponed annotations, but I haven't looked at the implementation (I'm not familiar with the CPython codebase at all) so it's just a guess.ericvsmith commentedon Mar 2, 2021
Can you show the values of “expected” and “actual” for both the failures and successes?
1ace commentedon Mar 3, 2021
Sure thing.
Putting the reproducing code in
test_foo.pyand runningdocker run --rm -it -v $PWD:/code python sh -c 'pip install pytest && pytest -vvv /code/test_foo.py'yields:Fidget-Spinner commentedon Mar 3, 2021
@eric.smith, here's a summarized version. I hope it helps:
In Python 3.7 - 3.9 with from __future__ import annotations, inspect.signature sees foo's parameter as:
<Parameter "x: 'str'">
Without the future import (and also in Python 3.10):
<Parameter "x: str">
The reason for the difference in 3.10 (IIRC) is that inspect.signature auto converts all strings to typing.ForwardRef internally and then resolves them. This is a result of PEP-563 becoming default (also mentioned here https://docs.python.org/3.10/whatsnew/3.10.html#pep-563-postponed-evaluation-of-annotations-becomes-default )
Prior to 3.10, the future import treats function annotations as strings and inspect.signature _doesn't_ convert them. I don't know of this is a bug or intentional. Especially considering what PEP-563 has to say: https://www.python.org/dev/peps/pep-0563/#resolving-type-hints-at-runtime
@1ace, a possible workaround if you want full compatibility regardless of the future import is to use typing.get_type_hints:
1ace commentedon Mar 3, 2021
Sorry, I just realized you asked for the "success" value as well.
For python 3.7-3.9 without the
import annotations, and for python 3.10, the type annotation is a class, ie.<Parameter "x: str">.1ace commentedon Mar 3, 2021
(... and I had the window open for too long and didn't notice Ken's reply before mine ^^')
@kj: thanks for the workaround! it's good to be able to change our code and not have to depend on a yet-to-be-released patch on each python version :)
That said, typing.get_type_hints() only returns the type hint, whereas our current test checks for other things (like the default value) which need to match (I think) for cython's use of
__signature__? (I inherited all this code, so I'm not familiar with it and its history, and I'm beginning to question this test's necessity.)ericvsmith commentedon Mar 3, 2021
I think this is all about: should inspect.signature() resolve string annotations into actual types (via get_type_hints, or whatever)? I don't use inspect much, so I can't offer an opinion there.
terryjreedy commentedon Mar 5, 2021
3.7 only gets security fixes
JelleZijlstra commentedon May 4, 2021
Python 3.10 will add an eval_str= argument to inspect.signature() that lets you evaluate string annotations. I don't think it makes sense to change anything in the bugfix branches, so I propose that this issue be closed.