<br><br><div class="gmail_quote">On Sat, Mar 14, 2009 at 9:58 PM, Nick Coghlan <span dir="ltr"><<a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
I missed the discussion about potentially adding syntactic support for<br>
multiple context managers in the with statement, but figured I should<br>
mention a real limitation of contextlib.nested that *would* be fixed by<br>
adding dedicated syntactic support.<br>
<br>
There's a genuine semantic difference between this:<br>
<br>
  with cmA():<br>
      with cmB():<br>
          # whatever<br>
<br>
and this:<br>
<br>
  with nested(cmA(), cmB()):<br>
      # whatever<br>
<br>
The latter is actually more accurately translated as:<br>
<br>
  mgr1, mgr2 = cmA(), cmB():<br>
  with mgr1:<br>
      with mgr2:<br>
          # whatever<br>
<br>
That is, when using nested() the later context managers are created<br>
outside the scope of the earlier context managers.<br>
<br>
So, to use Christian's example from the previous discussion:<br>
<br>
  with lock:<br>
      with open(infile) as fin:<br>
          with open(outfile, 'w') as fout:<br>
              fout.write(fin.read())<br>
<br>
Using contextlib.nested for that would be outright broken:<br>
<br>
  with nested(lock, open(infile), open(outfile) as (_, fin, fout):<br>
      fout.write(fin.read())<br>
<br>
1. The files are opened without acquiring the lock first<br>
2. If an IOError is raised while opening "outfile", then "infile"<br>
doesn't get closed immediately<br>
<br>
I created issue 5491 [1] to point out that the contextlib.nested docs<br>
could do with being tweaked to make this limitation clearer.<br>
<br>
Dedicated syntax (such as the form that Christian proposed) would fix<br>
this problem:<br>
<br>
  with lock, (open(infile) as fin), (open(outfile, 'w') as fout):<br>
      fout.write(fin.read())<br>
<br>
Of course, a custom context manager doesn't suffer any problems either:<br>
<br>
    @contextmanager<br>
    def synced_io(lock, infile, outfile):<br>
        with lock:<br>
            with open(infile) as fin:<br>
                with open(outfile) as fout:<br>
                    yield fin, fout<br>
<br>
    with synced_io(lock, infile, outfile) as (fin, fout):<br>
       fout.write(fin.read())</blockquote><div><br></div><div>fwiw, I believe you could write a version of nested that generates the above code based on the parameters but I believe it'd be disgustingly slow...</div>
<div><br></div><div><div>@contextmanager<br></div><div>def slow_nested(*args):</div><div>  code_lines = []</div><div>  vars = []</div><div>  code_lines.append('@contextmanager')</div><div>  code_lines.append('def _nested(*args):')</div>
<div>  for idx in xrange(len(args)):</div><div>    vars.append('c%d' % idx)</div><div>    code_lines.append('%swith args[%d] as %s:' % (' '*(idx+1), idx, vars[-1]))</div><div>  code_lines.append('%syield %s' % (' '*(len(args)+1), ','.join(vars)))</div>
<div>  code = '\n'.join(code_lines)</div><div>  print 'CODE:\n', code</div><div>  exec(code)</div><div>  yield _nested(*args)</div><div><br></div></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<br>
<br>
Cheers,<br>
Nick.<br>
<br>
[1] <a href="http://bugs.python.org/issue5491" target="_blank">http://bugs.python.org/issue5491</a><br>
<font color="#888888"><br>
<br>
--<br>
Nick Coghlan   |   <a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>   |   Brisbane, Australia<br>
---------------------------------------------------------------<br>
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br>
<a href="http://mail.python.org/mailman/listinfo/python-ideas" target="_blank">http://mail.python.org/mailman/listinfo/python-ideas</a><br>
</font></blockquote></div><br>