On 2010-03-03 11:18 AM, Alf P. Steinbach wrote:
* Robert Kern:
On 2010-03-03 09:56 AM, Alf P. Steinbach wrote:
* Mike Kent:
What's the compelling use case for this vs. a simple try/finally?

if you thought about it you would mean a simple "try/else". "finally" is
always executed. which is incorrect for cleanup

Eh? Failed execution doesn't require cleanup? The example you gave is
definitely equivalent to the try: finally: that Mike posted.

Sorry, that's incorrect: it's not.

With correct code (mine) cleanup for action A is only performed when
action A succeeds.

With incorrect code cleanup for action A is performed when A fails.

Oh?

$ cat cleanup.py

class Cleanup:
    def __init__( self ):
        self._actions = []

    def call( self, action ):
        assert( callable( action ) )
        self._actions.append( action )

    def __enter__( self ):
        return self

    def __exit__( self, x_type, x_value, x_traceback ):
        while( len( self._actions ) != 0 ):
            try:
                self._actions.pop()()
            except BaseException as x:
                raise AssertionError( "Cleanup: exception during cleanup" )

def print_(x):
    print x

with Cleanup() as at_cleanup:
    at_cleanup.call(lambda: print_("Cleanup executed without an exception."))

with Cleanup() as at_cleanup:
    at_cleanup.call(lambda: print_("Cleanup execute with an exception."))
    raise RuntimeError()

$ python cleanup.py
Cleanup executed without an exception.
Cleanup execute with an exception.
Traceback (most recent call last):
  File "cleanup.py", line 28, in <module>
    raise RuntimeError()
RuntimeError

The actions are always executed in your example,

Sorry, that's incorrect.

Looks like it to me.

From your post, the scope guard technique is used "to ensure some
desired cleanup at the end of a scope, even when the scope is exited
via an exception." This is precisely what the try: finally: syntax is
for.

You'd have to nest it. That's ugly. And more importantly, now two people
in this thread (namely you and Mike) have demonstrated that they do not
grok the try functionality and manage to write incorrect code, even
arguing that it's correct when informed that it's not, so it's a pretty
fragile construct, like goto.

Uh-huh.

The with statement allows you to encapsulate repetitive boilerplate
into context managers, but a general purpose context manager like your
Cleanup class doesn't take advantage of this.

I'm sorry but that's pretty meaningless. It's like: "A house allows you
to encapsulate a lot of stinking garbage, but your house doesn't take
advantage of that, it's disgustingly clean". Hello.

No, I'm saying that your Cleanup class is about as ugly as the try: finally:. It just shifts the ugliness around. There is a way to use the with statement to make things look better and more readable in certain situations, namely where there is some boilerplate that you would otherwise repeat in many places using try: finally:. You can encapsulate that repetitive code into a class or a @contextmanager generator and just call the contextmanager. A generic context manager where you register callables doesn't replace any boilerplate. You still repeat all of the cleanup code everywhere. What's more, because you have to shove everything into a callable, you have significantly less flexibility than the try: finally:.

I will admit that you can put the cleanup code closer to the code that needs to get cleaned up, but you pay a price for that.

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
 that is made terrible by our own mad attempt to interpret it as though it had
 an underlying truth."
  -- Umberto Eco

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to