[RFC] Add run_cleanup, do_with_cleanup

Andrew Bennetts andrew.bennetts at canonical.com
Thu Sep 24 02:06:37 BST 2009


[Trying an RFC for a branch by cross-posting to both the merge proposal on
Launchpad, and the bazaar@ list.]

Hi all,

Here's the problem.  In this example, Python gives no way for final_func to know
if try_func raised an error or not:

    try:
        try_func()
    finally:
        final_func()

Not even by sys._getframe hacks, AFAICT.  I can go into some gory details about
why not, but more important is what to do about it.

It's problematic because, in general, we don't want an error from final_func to
override an error from try_func.  This means that sometimes users get errors
about, say, abort_write_group failing, when what originally went wrong was
“connection lost”.  The various TooManyConcurrentRequests bugs are the most
common symptom of this.

This brings me to <https://bugs.launchpad.net/bzr/+bug/429747>, and
<https://code.launchpad.net/~spiv/bzr/cleanup-hof/+merge/12318>.

Bug 429747 proposes a higher-order-function that all cleanups should use to at
least give us a common place to control policy about log vs. raise, etc.  That
branch implements that, as a function called run_cleanup (and some variations).
It lives in a new module, bzrlib.cleanups.  I have tried to give the new module
and its contents fairly reasonable docstrings, and clear tests.

The downside of run_cleanup is that it will suppress some exceptions that should
be propagated, but that seems to be unavoidable given the limitations of Python.
(And note that developers can always use -Dcleanup to get UI warnings about
failures in run_cleanup).

The branch also implements something called do_with_cleanups, which can actually
be pretty robust and correct, at the cost of making simple callsites a bit
uglier.  I have a follow-on patch that adjusts bzrlib.commit to use this, which
would fix some outstanding bugs.

So, here's the RFC: I think the policy should be:

 * use run_cleanup if a cleanup failure is likely to be unimportant to a user
   (or at least, the chance of it being important is < the chance of the main
   func raising an error that should not be obscured).
 * consider changing the cleanup function itself to never raise errors, e.g.
   abort_write_group(suppress_error=True).  Then idiomatic try/finally is safe.
   Again, only applicable if failures during cleanup are "uninteresting".
 * use run_cleanups if you are running multiple cleanups; it takes care of
   running them all even if an exception happens.
 * if you want very correct behaviour, use do_with_cleanups.  This tends to make
   simple callsites a bit uglier, but in already complex code (like commit) it
   may be no worse.  

If agreed, I can add this to HACKING.txt.

I don't think we necessarily need to update all existing try/finally blocks at
once, but we should at least fix the cases that have been the source of bug
reports.

-Andrew.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: Digital signature
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20090924/951a46cc/attachment.pgp 


More information about the bazaar mailing list