[RFC] Add run_cleanup, do_with_cleanup
Andrew Bennetts
andrew.bennetts at canonical.com
Thu Sep 24 08:40:00 BST 2009
Martin Pool wrote:
[...]
> > I don't particularly like checking return values.
>
> OK, but why? Obviously in general in Python one should be using
> exceptions rather than return code as the normal convention, but are
> there any other drawbacks? The other main reason is that return codes
> can easily be ignored but that is (perhaps) just what we want here.
It's just the “easy to forget to check them” issue that concerns me.
> One problem with return codes is that, if you return a boolean, you
> don't get any explanation of what went wrong. We could potentially
> return an Exception object, but that might be getting too weird.
>
> > I think it might be nice
> > to be able to say "foo.unlock(on_error='warn')", or
> > on_error='log'/'ignore'/'raise'. I'm not sure which we'd like to have as
> > the default, and it's also questionable about whether we really want to
> > burden all unlock implementations with this, but it's an interesting
> > strawman API.
>
> That seems ok, at least assuming that you expect different callers to
> prefer to get errors, warnings, or nothing. But aside from tests
> specifically for locking, I'm not sure that they will.
Yeah, I'm not sure exactly how featureful we need to be. It could well be a
YAGNI, although testability is important.
> > My thinking here is that *usually* an unlock failure is probably not
> > something to report to the user, but there are times when we definitely want
> > errors to propagate directly (e.g. an explicit invocation of break-lock, and
> > probably during the test suite).
>
> I don't think break-lock calls unlock, it calls break_lock, which
> presumably would always error.
Oh, right, good point. I feel like there's probably other examples, but
perhaps not...
> > So there's a mix of inputs here. I'm having trouble articulating this
> > clearly, but roughly I feel that what's desired in any individual case may
> > depend on a combination of:
> >
> > - the cleanup operation itself;
> > - what the callsite intends (some places may explicitly expect and suppress
> > particular errors, so emitting something to the UI instead of raising
> > something the callsite can catch may be a bad thing);
> > - any global policy, like debug flags;
> > - and maybe also the specific error (e.g. KeyboardInterrupt vs. connection
> > lost vs. an assertion about unfinished progress bars)?
> >
> > The challenge seems to be balancing these desires while keeping things
> > reasonable clean and simple.
>
> My idea was that we should try, at least in a branch, just changing
> the policy, and see what happens in several dimensions: how that
> changes the look & feel of the code as you change it, what tests fail
> if any and how easy & clean it is to fix them, and what positive or
> negative effects this has on users. Then we'll have more data on the
> balance.
Ok, trying it in practice makes good sense. I might aim for trying to
update (or at least look at) every try-finally in bzrdir.py, repository.py
and branch.py and see how it goes.
[...]
> I think that policy sounds ok, though maybe we need different
> functions for important and unimportant cleanups. I suggest taking
> the implementation all the way through with say unlock so that we can
> see how it actually behaves.
That sounds like a good experiment, although I dread the effort to actually
update all the implementations of unlock...
(By “all the way through” you mean extending the API of unlock, right?)
> === added file 'bzrlib/cleanup.py'
> --- bzrlib/cleanup.py 1970-01-01 00:00:00 +0000
> +++ bzrlib/cleanup.py 2009-09-24 01:05:22 +0000
>
> +"""Helpers for managing cleanup functions and the errors they might raise.
> +
> +Generally, code that wants to perform some cleanup at the end of an action will
> +look like this::
> +
> + from bzrlib.cleanups import run_cleanup
> + try:
> + do_something()
> + finally:
> + run_cleanup(cleanup_something)
> +
> +Any errors from `cleanup_something` will be logged, but not raised.
> +Importantly, any errors from do_something will be propagated.
>
> I wonder if generally this should be going inside the function run
> from cleanups - but we can change that later.
Yeah, me too...
[...]
>
> Nice docstring.
Thanks!
I found writing it really helped me decide what code I was trying to write.
> These could perhaps become with blocks when we require python2.5 (or 2.6?).
Yeah, probably. I haven't thought about that too hard.
[...]
> Do you need to handle KeyboardInterrupt specially? Isn't it outside
> of Exception, or is that only from 2.5 on?
Only from 2.5 IIRC. I'll double-check.
[...]
> Ok, I can see this is a bit different but it probably wants a
> docstring to say so.
Oops, yes.
[...]
> + by func or any of the cleanup_funcs is the one that will be propagted by
> + this function (subsequent errors are caught and logged).
>
> typo
Ta.
[...]
> OK, so +1 from me, but the proof will be in how it would handle the
> bugs reported as TooMany*. It might be worth grepping for them and
> looking at their call stacks.
My browser is filled with tabs of those bugs as I write...
Thanks for the review!
-Andrew.
More information about the bazaar
mailing list