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/ // On 1/16/21 11:43 PM, Inada Naoki wrote:
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,