On Jul 6, 11:15 pm, Scott David Daniels <scott.dani...@acm.org> wrote: > brasse wrote: > > 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. > > ... > > As you can see this is less than straight forward. Is there some kind > > of best practice that I'm not aware of? > > Not so tough. Something like this tweaked version of your example: > > class Foo(object): > def __init__(self, name, fail=False): > self.name = name > if not fail: > print '%s.__init__(%s)' % (type(self).__name__, name) > else: > print '%s.__init__(%s), FAIL' % (type(self).__name__, name) > raise ValueError('Asked to fail: %r' % fail) > > def close(self): > print '%s.close(%s)' % (type(self).__name__, self.name) > > class Bar(object): > def __init__(self): > unwind = [] > try: > self.a = Foo('a') > unwind.append(a) > self.b = Foo('b', fail=True) > unwind.append(b) > ... > except Exception, why: > while unwind): > unwind.pop().close() > raise > > bar = Bar() >
OK. That's another way. I have been thinking about this some more and right now I am experimenting with a decorator that could help me do these kinds of things. This is what I have right now: import functools class Foo(object): def __init__(self, name, fail=False): self.name = name self.closed = False if not fail: print '%s.__init__(%s)' % (self.__class__.__name__, self.name) else: print '%s.__init__(%s), FAIL' % (self.__class__.__name__, self.name) raise Exception() def close(self): print '%s.close(%s)' % (self.__class__.__name__, self.name) def safe(f): @functools.wraps(f) def wrapper(self, *args, **kwargs): variables = [] def recorder(self, name, value): if name != '__class__': variables.append(name) object.__setattr__(self, name, value) class Customized(object): pass setattr(Customized, '__setattr__', recorder) old_class = self.__class__ self.__class__ = Customized try: f(self, *args, **kwargs) self.__class__ = old_class except: # clean up objects created in __init__ (i.e. the call to f ()) for name in reversed(variables): o = getattr(self, name) if hasattr(o, 'close'): o.close() raise return wrapper class Bar(object): @safe def __init__(self): self.a = Foo('a') self.b = Foo('b') self.c = Foo('c', fail=True) bar = Bar() The safe decorator will record all attributes created on self. If an exception is raised during the execution of the decorated constructor it will call close on all the recorded objects. I realize that this decorator still needs a lot of work before it is usable, but I think it illustrates the concept. It might even be possible for the decorator to create a close/cleanup method dynamically. To have a decorator like this that actually worked would be great since it would remove the need to write error prone cleanup code. Any thoughts? :.:: mattias > --Scott David Daniels > scott.dani...@acm.org -- http://mail.python.org/mailman/listinfo/python-list