Cleaning up after failing to contructing objects

Mattias Brändström thebrasse at gmail.com
Thu Jul 9 17:10:02 EDT 2009


On Jul 9, 7:30 pm, Lie Ryan <lie.1... at gmail.com> wrote:
> brasse wrote:
> > Hello!
> > I have been thinking about how write exception safe constructors in
> > Python. By exception safe I mean a constructor that does not leak
> > resources when an exception is raised within it. The following is an
> > example of one possible way to do it:
>
> First, your automatic cleaning Bar() silently pass an unitialized Bar()
> into the calling code. When a code fails, it should fail as loud as
> possible. Bar() should raises an Exception and the calling code should
> turn into something like:
>
> try:
>     bar = Bar()
> except Exception, e:
>     print 'bar failed to construct'
>

I know, I missed that little detail. :-)

> And about the cleaning up, how about:
>
> class CleanWrap(object):
>     def __init__(self, cls, cleanup):
>         self.cls = cls
>         self.cleanup = cleanup
>     def __call__(self, *args, **kargs):
>         try:
>             return self.cls(*args, **kargs)
>         except:
>             self.cleanup()
>             raise
>
> class Bar(object):
>     def __init__(self):
>         CleanWrappedFoo = CleanWrap(Foo, self.close)
>         self.a = CleanWrappedFoo('a')
>         self.b = CleanWrappedFoo('b', fail=True)
>     def close(self):
>         if hasattr(self, 'a'):
>             self.a.close()
>         if hasattr(self, 'b'):
>             self.b.close()
>

I think this example adds about as much overhead as my original
example with the try block. Ideally I would like to be able to write
my classes in the most straight forward manner possible. In my mind
that would be something like a naive constructor and a close/dispose
method:

def __init__(self):
    self.a = Foo()
    self.b = Bar()

def close(self):
    self.a.close()
    self.b.close()

See my other post (the one with the decorator named safe) in this
thread for an experiment I thought was promising for a while. However
there are some some use cases it has to cover to be useful:

(1) References to objects owned by someone else
def __init__(self, some_resource):
    self.not_my_responsibility = some_resource
    self.a = Foo()
    # If Foo.__init__ raises an exception self.not_my responsibility
should not be closed.

(2) Local objects
def __init__(self):
    x = Bar()
    self.a = Foo(x)
    # If Foo.__init__ raises an exception x should also be closed.

So far I have not been able to find any good solutions for (at least)
case 1. I'll give it a few more days, but I'm not  that hopeful. I
think that I will end up writing my classes very much like Bar in my
original post. If someone has any ideas for how to solve my two use
cases above with my decorator based approach I would really like to
hear them!

:.:: mattias



More information about the Python-list mailing list