Some context managers are designed to produce a value on exit rather than provide one on entry, for example a stopwatch that measures duration, or a builder that yields a constructed object. The current protocol has no way to express this, leaving authors with an awkward choice:
# The as-binding happens at __enter__, so sw is accessible inside the wrapped block
with stopwatch() as sw:
do_work()
sw.duration # but duration isn't ready yet
The usual workarounds are a result-container (sw.duration starts as None and is set on exit) or a runtime guard that raises if accessed early. Both work, but neither lets a type checker enforce the constraint statically.
What do you think about introducing a typing-only convention (so no runtime or grammar changes) where a context manager can opt into “post-exit binding” semantics by annotating __enter__ as returning a special sentinel type (e.g. typing.Deferred[T]). Type checkers would then:
- Treat the
as-bound variable asDeferred[T](effectively unusable) inside thewithblock body. - Narrow it to
Tin the scope after the block completes.
IIUC this maps somewhat naturally onto existing control-flow narrowing - it would be the same mechanism used after an assert or isinstance check, just triggered by block exit rather than a boolean predicate.