Search code examples
pythonwith-statementcontextmanager

manually open context manager


My question is, how can I execute any context manager without using with?


Python has the idea of context managers,

instead of

file = open('some_file', 'w')
try:
    file.write('Hola!')
finally:
    file.close()
# end try

you can write

with open('some_file', 'w') as opened_file:
    opened_file.write('Hola!')
# end with

While in most cases the second one is the golden solution, however for the specific cases of testing in unit tests as well exploring in the interactive console, the first one can be much better used, as you can write it line by line.

>>> file = open('some_file', 'w')
>>> file.write('Hola!')
>>> file.close()

My question is, how can I execute any with context manager like this, best suited for exploring?


My actual use case follows below, but please try to give a answer which is generic and will work for other context managers too.

import flask

app = flask.Flask(__name__)

with app.test_request_context('/?name=Peter'):
    assert flask.request.path == '/'
    assert flask.request.args['name'] == 'Peter'

from flask docs


Solution

  • You can still use with syntax in the interactive console, however a context is based on 2 magic methods __enter__ and __exit__, so you can just use them:

    class MyCtx(object):
      def __init__(self, f):
        self.f = f
    
      def __enter__(self):
        print("Enter")
        return self.f
    
      def __exit__(*args, **kwargs):
        print("Exit")
    
    def foo():
      print("Hello")
    

    usually you do:

    with MyCtx(foo) as f:
      f()
    

    Same as:

    ctx = MyCtx(foo)
    f = ctx.__enter__()
    f() 
    ctx.__exit__()
    

    Here you have the live example

    Remember that contexts __exit__ method are used for managing errors within the context, so most of them have a signature of __exit__(exception_type, exception_value, traceback), if you dont need to handle it for the tests, just give it some None values:

    __exit__(None, None, None)