Specify how type checkers should interpret `__exit__` annotations
The typing spec should provide guidance on how type checkers should handle __exit__ annotations. This method is special because __exit__ methods can suppress exceptions, and whether or not they do so affects the control flow through a function.
In code like this:
a: int | None = None
with some_context():
a = 1
reveal_type(a)
What is the type of a? It depends on whether some_context() can suppress exceptions or not. Most context managers don't, but in theory they can. A pedantically correct decision might be to assume that they can always suppress exceptions (so a might still be None in the sample), but that will often lead to a bad user experience.
Mypy currently follows a heuristic I proposed a few years ago. Should we formalize that heuristic into the spec, or something else?
This is important to specify because it affects how library authors should annotate their __exit__ methods. I'll probably pursue getting this into the spec in the next few months.
Ah, that came up in a Discourse thread.
I like the heuristic of None (and maybe Literal[False] for subtyping completeness?) meaning it can't suppress and bool, bool | None (and Literal[True]) meaning it may, it's easy to understand and for library authors to follow.
Although I think it follows that eventually AbstractContextManager should become generic in the return type of __exit__ with an upper bound of bool | None, but that change would be too disruptive without PEP696 defaulting the second parameter to bool | None. I've already prototyped a change just for ExitStack itself based on the discourse discussion, but even that was too disruptive without type var defaults.
Addressed by python/typing-council#27.