I think the problem with that is that the enter_context() calls are dynamic. If the underlying context managers were present in the ExitStack constructor, you could probably do this with typevars. But here we’re calling cm.enter_context(subcm), and we’d want those calls to affect the type of cm. That makes me think of how (in mypy, at least) we have a special case for
xs = []
for ...:
x = ...
xs.append(x)
where the inferred type for x will affect the type inferred for xs – this is hard, and except for the very simplest cases we require you to provide the answer by starting with
xs: list[SomeType] = []
so that the type of x is checked against SomeType instead of changing it.
If the answer was dependent types, since we don’t have those, we’d end up needing my proposed solution anyway.
Also, the enter_context() calls might occur inside some other thing that is called, e.g.
with ExitStack() as cm:
foo(cm)
where foo() is something like this:
def foo(cm: ExitStack):
cm.enter_context(...)
Well, maybe this could be solved by making ExitStack generic:
class ExitStack[T: bool|None]:
...
def __exit__(self, ...) -> T: ...
If this could work we’d be able to say
type NonSuppressingExitStack = ExitStack[None]