If you never examine __annotations__, then you can refer to
symbols that are never defined and nothing bad happens. It's just
like writing a function that refers to undefined symbols, then
never calling that function.
If you examine __annotations__, and the annotations refer to
values that aren't defined, the evaluation fails. This too works
like you'd expect: the __co_annotation__ function raises
NameError. So your IPython use case would raise a NameError.
Note that the code is deliberately written to allow you to fix
the name errors and try again. (The __co_annotations__ attribute
is only cleared if calling it succeeds and it returns a legal
value.) So, if you examine an annotation in IPython, and it fails
with a NameError, you could import the missing module--or
otherwise do what is needed to fix the problem--and try again.
If your imports are complicated, you could always hide them in a function. I just tried this and it seems to work fine:
def my_imports():
global other_mod
import other_mod
So, you could put all your imports in such a function, run it
from inside a "if typing.TYPE_CHECKING" block, and you'd have a
convenient way of doing all your imports from inside IPython too.
One final note: with PEP 649, you can still use strings as
annotations if you prefer. You just have to write them as strings
manually. So the IPython use case could continue to work
correctly that way. I realize that this itself causes minor
problems--it means no syntax checking is done on the annotation,
and it causes a little extra work for the user--but I assume this
is a rare use case and most users won't need to bother.
Cheers,
/arry
This PEP doesn't cover about what happened when __co_annotation__() failed (e.g. NameError). Forward reference is a major reason, but not a only reason for using string annotation. There are two other reasons: * Avoid importing heavy module. * Avoid circular imports. In these cases, this pattern is used: ``` from __future__ import annotations import typing from dataclasses import dataclass if typing.TYPE_CHECKING: import other_mod # do not want to import actually @dataclass class Foo: a: other_mod.spam b: other_mod.ham def fun(a: other_mod.spam, b: other_mod.ham) -> None: ... ``` Of course, mypy works well with string annotation because it is static checker. IPython shows signature well too: ``` In [3]: sample.Foo? Init signature: sample.Foo(a: 'other_mod.spam', b: 'other_mod.ham') -> None Docstring: Foo(a: 'other_mod.spam', b: 'other_mod.ham') ``` PEP 563 works fine in this scenario. How PEP 649 works? Regards,