Python nested context manager on multiple lines

14,016

Solution 1

Backslash characters

Two or more physical lines may be joined into logical lines using backslash characters (\)

(citing the Explicit line joining section)

If you want put context managers on different lines, you can make that work by ending lines with backslashes:

with context1 as a,\
     context2 as b:
    pass

contextlib.ExitStack

contextlib.ExitStack is a

context manager that is designed to make it easy to programmatically combine other context managers and cleanup functions, especially those that are optional or otherwise driven by input data.

It's available in Python 3.3 and newer, and allows to enter a variable number of context managers easily. For just two context managers the usage looks like this:

from contextlib import ExitStack

with ExitStack() as es:
    a = es.enter_context(context1)
    b = es.enter_context(context2)

Nesting

It's possible to split a context expression across several nested with statements:

With more than one item, the context managers are processed as if multiple with statements were nested:

with A() as a, B() as b:

suite is equivalent to

with A() as a:
    with B() as b:
        suite

(from The with statement)

Solution 2

There is a way to creatively use the parentheses and avoid the backslash: parenthesize the expression before as. Not very sightly, though:

with (
  open('/etc/passwd')) as foo, (
  open('/tmp/bar')) as bar:
  pass  # this parses correctly

It's easy to nest more and more if needed.

Solution 3

with context1 as a, \
context2 as b:
pass

Like any line-break, backslash provides the solution

Solution 4

Your example is equivalent to:

with context1 as a:
    with context2 as b:
        pass

which looks nice on two lines.

Reference: https://docs.python.org/2.7/reference/compound_stmts.html#the-with-statement

Share:
14,016
Simon Boudrias
Author by

Simon Boudrias

Originally from Montréal, but explored the world through different long term work opportunities: San Francisco 🚆Beijing 🚆Vancouver. Main contributor on Yeoman project and original author of Inquirer. I also manage open sourced many other semi-popular projects. Checkout https://github.com/SBoudrias to find out more.

Updated on June 15, 2022

Comments

  • Simon Boudrias
    Simon Boudrias almost 2 years

    In Python 2.6, we used to format our nested context manager that way:

    with nested(
        context1,
        context2
    ) as a, b:
        pass
    

    From Python 2.7 and on, nested is deprecated. I've seen lots of example of multiple context manager on a single line, but I can't find a syntax that allow them on multiple lines. How would you do this?

    # That's working fine
    with context1 as a, context2 as b:
        pass
    
    # But how do we make it multine?
    # These are not working
    with (
        context1,
        context2
    ) as a, b:
        pass
    
    with context1 as a,
        context2 as b:
        pass
    
  • Simon Boudrias
    Simon Boudrias almost 9 years
    Is this the only syntax - because it's very ugly... :(
  • Joseph Garvin
    Joseph Garvin over 7 years
    Can't believe I'm saying it but in this instance python syntax is terrible compared to lisp. Multiple assignments in a (let) or other binding form looks completely natural... but this is awkward.
  • tebanep
    tebanep almost 7 years
    This is the prettiest solution. Thanks!
  • isarandi
    isarandi almost 7 years
    Why would you want to avoid backslashes in this case though? The open parens play essentially the same role here, but it also becomes more cryptic.
  • 9000
    9000 almost 7 years
    @isarandi: A backslash as line continuation is easier to break when editing than some other things. PEP-8 recommends the use of parens in favor of backslash continuation. BTW the same PEP-8 suggests to use a \ exactly with multiple with statements. I personally prefer nesting of with, but added this answer for completeness.
  • CMCDragonkai
    CMCDragonkai about 4 years
    What if B() requires a? Is there a way to pass it in?