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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With