Question

I have been using a lot of context managers as a clean way of composing various setup/teardown situations. Since my deployments target Python 2.6, this means using contextlib.nested.

Lately I've been interested in supporting both Python 2.x and Python 3 with the same code base. This has been possible with some projects, but I'm running into trouble in the case of context managers because:

  1. contextlib.nested isn't supported in Python 3
  2. Python-3 style nested context managers (e.g., with aa() as a, bb() as b: ...) aren't supported in 2.6.

There is a basic syntactic incompatibility here. For various reasons beyond my control, 2.7 may be difficult to get into production for now, but I'd like to future-proof the code as much as possible, hence the Python 3 interest.

Can anyone suggest a workaround for supporting nested context managers in the same code base for 2.6 and 3.x? Or is this a lost cause?

Was it helpful?

Solution

From the docs:

This function has two major quirks that have led to it being deprecated. Firstly, as the context managers are all constructed before the function is invoked, the __new__() and __init__() methods of the inner context managers are not actually covered by the scope of the outer context managers. That means, for example, that using nested() to open two files is a programming error as the first file will not be closed promptly if an exception is thrown when opening the second file.

Secondly, if the__enter__() method of one of the inner context managers raises an exception that is caught and suppressed by the __exit__() method of one of the outer context managers, this construct will raise RuntimeError rather than skipping the body of the with statement.

Thus in almost all cases the correct answer is JBernardo's. It's a bit more indenting but it's a bit less buggy, too.

OTHER TIPS

Just nest them

with aa() as a:
    with bb() as b:
        #some code here

If the quirks of nested that Veedrac mentioned aren't an issue for you, you can just copy the code from the Python standard library.

If they do bother you, then your only choices are to manually nest them, or to drop Python 2.6 support. It really doesn't matter if you use two code-bases or one for this. If this is the case, then the only way it will work in Python 2.6 would be to nest them. I guess you could play with writing some kind of custom 2to3 fixer that translates your unnested 2.7 code into nested 2.6 code. But honestly, it will be less painful to just use a single code-base with nested managers until you can drop 2.6 support.

You can always reimplement nested on your own and keep it in a compatibility.py file within the project. This is often what is done to cross versions.

Edit: I see that @JBernardo already mentioned this solution in a comment.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top