Context manager protocol extension (2nd attempt)

Yeah, as David said, there is a way to do the thing without interpreter support but it is pretty ugly. The thing is that for classes we most likely don’t want wrapped class be converted in something else, which @contextmanager does (it returns a wrapper function that returns _GeneratorContextManager), so it will be a function that modify the class itself to add __enter__/__exit__, a la

Code that does a thing
def __with__manager[T](typ: type[T]) -> type[T]:
    generators: dict[int, Generator] = {}

    def __enter__(self):
        generators[id(self)] = gen = typ.__with__(self)
        try:
            result = next(gen)
        except StopIteration:
            raise RuntimeError("generator does not yielded.")

        return result

    def __exit__(self, typ, value, tb):
        try:
            gen = generators.pop(id(self))
        except KeyError:
            raise RuntimeError("__exit__ called before __enter__")

        if typ is None:
            try:
                next(gen)
            except StopIteration:
                return False
            raise RuntimeError("generator didn't stop")
        else:
            try:
                gen.throw(value)
            except StopIteration:
                return True
            except BaseException as e:
                if e is not value:
                    raise
                return False
            raise RuntimeError("generator didn't stop after throw()")

    typ.__enter__ = __enter__
    typ.__exit__ = __exit__
    return typ

and even if we teach type checkers to recognize it as a context manager I proposed this feature mostly because of other reasons.


To add more context, In 3.11 and 3.12, Irit Katriel (and others) did a great job to reduce the amount of exc_info triplets replacing it with single exception instance, which eventually made it possible to store exceptions as single object in the thread state in gh-30122, so actually now WITH_EXCEPT_START opcode does __exit__(type(value), value, value.__traceback__), so here C side is simpler than Python side.

After that she proposed PEP 707, which SC rejected as too magic.
But in the PEP discussion some core devs voiced (e.g. Serhiy) their like for better context manager protocol which I proposed in previos discussion, but it faded out because I think it had too wide scope.

So, this idea is not because there is no way to achieve this now, but more as a way to push that work futher, removing I think the only remaining Python-side exc_info triplet.