inspect.signature with PEP 563

what is pep in python
pep 484 docstring
pep 8 type hints
from __future__ import google_type_annotations
python 3 __future__ annotations
python typing circular imports
python 3.8 from __future__ import annotations
python type indicator

The following code:

import inspect
from typing import NamedTuple

class Example(NamedTuple):
    a: str

if __name__== "__main__":
    signature: inspect.Signature = inspect.signature(Example)
    print(signature)

outputs:

(a: str)

However when enabling PEP 563 – Postponed Evaluation of Annotations:

from __future__ import annotations
import inspect
from typing import NamedTuple

class Example(NamedTuple):
    a: str

if __name__== "__main__":
    signature: inspect.Signature = inspect.signature(Example)
    print(signature)

The output is:

(a: 'str')

How can I get the exact same object of type inspect.Signature with PEP 563 like without it?

The point of using PEP 536 is to not evaluate the annotations unless needed. The signature merely reports on the annotations.

If for your purposes you need to have the annotations resolved, you have to do so yourself. PEP 536 tells documents how you do this:

For code that uses type hints, the typing.get_type_hints(obj, globalns=None, localns=None) function correctly evaluates expressions back from its string form.

[...]

For code which uses annotations for other purposes, a regular eval(ann, globals, locals) call is enough to resolve the annotation.

You could even use the typing.get_type_hints() function to assign back to __annotations__ before getting the signature:

import typing

Example.__new__.__annotations__ = typing.get_type_hints(Example.__new__)
signature: inspect.Signature = inspect.signature(Example)

Doing this is safe even if from __future__ import annotations had not been used.

inspect.signature with PEP 563, print(signature). The output is: (a: 'str'). How can I get the exact same object of type inspect.Signature with PEP 563 like without it? How can I get the exact same object of type inspect.Signature with PEP 563 like without it? python python-3.x types python-3.7 static-typing share | improve this question

First, let's run a different example:

signature: inspect.Signature = inspect.signature(Example)
print(signature)
print(Example.__annotations__)

This prints:

(a: str)
OrderedDict([('a', <class 'str'>)])

So far so good, we have or Signature and our __anotations__ as we expected.

Now let's do the same for the second example, it prints:

(a: 'str')
OrderedDict([('a', ForwardRef('str'))])

So you're not getting the same Signature here. One gives you the actual class and the other a typing.ForwardRef to the class.

inspect.signature with PEP 563, inspect.signature with PEP 563. The following code: import inspect from typing import NamedTuple class Example(NamedTuple): a: str if __name__==  Discussion happened largely in two threads, the original announcement and a follow-up called PEP 563 and expensive backwards compatibility. The PEP received rather warm feedback (4 strongly in favor, 2 in favor with concerns, 2 against).

You have to actually use eval to get the same behaviour:

from __future__ import annotations
import inspect
from typing import NamedTuple

class Example(NamedTuple):
    a: str

signature: inspect.Signature = inspect.signature(Example)
print(signature)

# extra bit
globalns = getattr(Example, '__globals__', {})
for param in list(signature.parameters.values()):
  if isinstance(param.annotation, str):
    param._annotation = eval(param.annotation, globalns)

print(signature)

You will get:

(a: 'str')
(a: str)

Alternatively you can modify __annotations__ before calling inspect.signature(obj), but I am finding it too hard, because I need to cover multiple different cases.

Answer by @Martijn Pieters misses one detail about typing.get_type_hints:

if necessary adds Optional[t] if a default value equal to None is set

Example:

# without imporing annotations from __future__
import inspect
import typing

def func(a: str=None): pass
print(inspect.signature(func))
func.__annotations__ = typing.get_type_hints(func)
print(inspect.signature(func))

You will get:

(a: str = None)
(a: Union[str, NoneType] = None)

PEP 563 -- Postponed Evaluation of Annotations, This PEP proposes changing function annotations and variable annotations so that they are no longer evaluated at function definition time. You might want to look into how PEP 563 is implemented, it has a utility to turn an AST back into a string (I assume this is what you want). msg348496 - Author: Eric Snow (eric.snow) * Date: 2019-07-26 15:53 +1 on using a string for Parameter.annotation and Signature.return_annotation. msg348556 - Author: Giovanni Cappellotto (potomak) *

PEP 563 -- Postponed Evaluation of Annotations (https://www , PEP 563 -- Postponed Evaluation of Annotations (https://www.python.org/dev/​peps/pep-0563/). defaults.py from inspect import signature. from functools import  While this is going on, let me continue to emphasize that I plan to accept PEP 563. I don't feel the need to wait until every nit in the code review is addressed, but I do think we need to have agreement on the issue of whether to generate redundant parentheses (e.g. (x + y) + z ).

Feature: Support __future__ annotations (PEP 563) · Issue #181 , Feature: Support __future__ annotations (PEP 563) #181 Uplink currently uses inspect.signature to get the annotations, but that will return all  autodoc does not recognize Cython signature (PEP 563), the signature of especially it was until quite recently that Python switch to call inspect.signature

29.12. inspect, from inspect import signature >>> def foo(a, *, b:int, **kwargs): pass >>> sig = signature(foo) PEP 362 - Function Signature Object. The PEP 563: Postponed evaluation of annotations was introduced an opt-in feature using "from __future__ import annotations". It is scheduled to become the default in Python 4.0. It is scheduled to become the default in Python 4.0.

Comments
  • The whole point of that PEP is to not get the same object... You'll have to evaluate the string using exec or the like.
  • Perfect. Thanks. I'll award the bounty tomorrow, because for some reason stackoverflow does not allow me to do so right now without waiting 3 hours. :rolleyes: