John A Meinel wrote:
[...]
> Is it just me, or is "_run_cleanup_reporting_errors" doing exactly the same
> thing as _run_cleanup, only it isn't calling out to a helper to log the error.
> As such, I'm thinking one of them should be removed. Which should also simplify
> the _run_cleanups() function.
>
> I assume at one time one of them might have propagated the exception?
Yeah, I think so. Also note that _run_cleanup_reporting_errors unconditionally
warns rather than checking for -Dcleanup, but I don't think that's particularly
valuable.
> 124 + def add_cleanup(self, cleanup_func):
> 125 + """Add a cleanup to run. Cleanups will be executed in LIFO order."""
> 126 + self.cleanups.insert(0, cleanup_func)
>
> ^- insert(0,) has to move all items in the list each time. Why not just do:
>
> self.cleanups.append(cleanup_func)
>
> and then
>
> 128 + def run(self, *args, **kwargs):
> 129 + func = lambda: self.func(self, *args, **kwargs)
> 130 + return do_with_cleanups(func, reversed(self.cleanups))
The func passed to do_with_cleanups here is expected to mutate self.cleanups
(via calling add_cleanup). So passing in reversed(self.cleanups) here would
mean no cleanups get run!
I'd be happy to make it a deque for clarity, but as the longest cleanup chain we
have so far is 4 elements I didn't think it really mattered much in terms of
performance :)
> Or alternatively, I think the standard idiom would be to build up state in
> forward order and then walk it reversed. so I would think it reasonable to
> change '_run_cleanups' from:
> for func in funcs:
> to
> for func in reversed(funcs):
Hmm. There's the competing concern that when calling do_with_cleanups directly
with a pre-built literal list I think it may be clearer to have the cleanups
forwards, i.e.:
But you're right that it might be a net win to bias towards the other way. I'll
try it out and see what I think.
> I'm pretty sure you always want to run cleanups in LIFO order, so it would make
> sense to bias the apis to do that easily.
>
> Also, if we are going to have an "add_cleanup()" helper, it seems like it would
> be good to make it the twin of TestCase.addCleanup(). Most notably, that api
> (now) allows you to pass in arguments to the cleanup functions.
Ok, if you like. It held off doing that simply on the grounds that I hadn't
had use for it. It's easy to add.
> I also think that would make for a cleaner api for 'do_with_cleanups', since
> otherwise you have this:
> func = lambda: self.func(self, *args, **kwargs)
>
> which works, but means that the api isn't very nice to use.
I don't follow... what signature are you proposing do_with_cleanups should have
instead? You need some way to keep the cleanups list separate from the *args
and **kwargs, but that means either putting the cleanups list as the first arg,
or as a positional arg between the function and its *args. Both options feel
ugly to me.
> 695 +#class TestRunCleanupReportingErrors(CleanupsTestCase):
> 696 +#
> 697 +# def test_cleanup_error_reported(self):
> 698 +# xxx
>
>
> ^- I'm assuming you just want to nuke this when you nuke
> _run_cleanup_reporting_exceptions.
Yeah.
> I do think commit.py is nicer with an object managing the state of things that
> needs to be cleaned up. And I'm flexible about api decisions. I figured I could
> state my opinion, though.
John A Meinel wrote: reporting_ errors" doing exactly the same
[...]
> Is it just me, or is "_run_cleanup_
> thing as _run_cleanup, only it isn't calling out to a helper to log the error.
> As such, I'm thinking one of them should be removed. Which should also simplify
> the _run_cleanups() function.
>
> I assume at one time one of them might have propagated the exception?
Yeah, I think so. Also note that _run_cleanup_ reporting_ errors unconditionally
warns rather than checking for -Dcleanup, but I don't think that's particularly
valuable.
> 124 + def add_cleanup(self, cleanup_func): insert( 0, cleanup_func) append( cleanup_ func) cleanups( func, reversed( self.cleanups) )
> 125 + """Add a cleanup to run. Cleanups will be executed in LIFO order."""
> 126 + self.cleanups.
>
> ^- insert(0,) has to move all items in the list each time. Why not just do:
>
> self.cleanups.
>
> and then
>
> 128 + def run(self, *args, **kwargs):
> 129 + func = lambda: self.func(self, *args, **kwargs)
> 130 + return do_with_
The func passed to do_with_cleanups here is expected to mutate self.cleanups self.cleanups) here would
(via calling add_cleanup). So passing in reversed(
mean no cleanups get run!
I'd be happy to make it a deque for clarity, but as the longest cleanup chain we
have so far is 4 elements I didn't think it really mattered much in terms of
performance :)
> Or alternatively, I think the standard idiom would be to build up state in
> forward order and then walk it reversed. so I would think it reasonable to
> change '_run_cleanups' from:
> for func in funcs:
> to
> for func in reversed(funcs):
Hmm. There's the competing concern that when calling do_with_cleanups directly
with a pre-built literal list I think it may be clearer to have the cleanups
forwards, i.e.:
do_ with_cleanup( foo, [first_cleanup, second_cleanup])
But you're right that it might be a net win to bias towards the other way. I'll
try it out and see what I think.
> I'm pretty sure you always want to run cleanups in LIFO order, so it would make addCleanup( ). Most notably, that api
> sense to bias the apis to do that easily.
>
> Also, if we are going to have an "add_cleanup()" helper, it seems like it would
> be good to make it the twin of TestCase.
> (now) allows you to pass in arguments to the cleanup functions.
Ok, if you like. It held off doing that simply on the grounds that I hadn't
had use for it. It's easy to add.
> I also think that would make for a cleaner api for 'do_with_cleanups', since
> otherwise you have this:
> func = lambda: self.func(self, *args, **kwargs)
>
> which works, but means that the api isn't very nice to use.
I don't follow... what signature are you proposing do_with_cleanups should have
instead? You need some way to keep the cleanups list separate from the *args
and **kwargs, but that means either putting the cleanups list as the first arg,
or as a positional arg between the function and its *args. Both options feel
ugly to me.
> 695 +#class TestRunCleanupR eportingErrors( CleanupsTestCas e): error_reported( self): reporting_ exceptions.
> 696 +#
> 697 +# def test_cleanup_
> 698 +# xxx
>
>
> ^- I'm assuming you just want to nuke this when you nuke
> _run_cleanup_
Yeah.
> I do think commit.py is nicer with an object managing the state of things that
> needs to be cleaned up. And I'm flexible about api decisions. I figured I could
> state my opinion, though.
Thanks for doing so :)
-Andrew.