Search code examples
pythonwith-statementmagic-methodstry-except

Convert "with" statement to "try" statement


I wonder how with statements work. I am trying to convert the following:

with obj() as o:
    do_something()

into this:

o = obj.__enter__()
try:
    do_something()
except Exception as e:
    obj.__exit__(type(e),e, **I don't know what should be here**)
else:
    obj.__exit__(None, None , None)

So how it will be? If I am wrong in anywhere, please correct me. I want to know what to replace the
**I don't know what should be here** with.


Solution

  • According to PEP-343, which introduced context managers, the code

    with obj() as o:
        do_something()
    

    is equivalent to

    mgr = obj()
    exit = type(mgr).__exit__
    value = type(mgr).__enter__(mgr)
    exc = True
    
    try:
        try:
            o = value
            do_something()
        except:
            exc = False
            if not exit(mgr, *sys.exc_info()):
                raise
    finally:
        if exc:
            exit(mgr, None, None, None)
    

    Some notes:

    1. We don't write o = type(mgr).__enter__(mgr) because the name o is only defined if __enter__ does not raise an exception, allowing us to enter the try statement at all. (There are other ways to handle this, but this is how I interpret PEP-343's translation.)
    2. __exit__ can be called in two different places. If we catch an exception, we pass information about that to __exit__, which can prevent the calling code from seeing it if it returns True.
    3. The finally block ensures that __exit__ is called exactly once. Namely, we want to call it if no exceptions were raised, but not again if the first call swallowed an exception by returning True or raised an exception itself.