[Python-ideas] Avoiding nested for try..finally: atexit for functions?
Nikolaus Rath
Nikolaus at rath.org
Thu Oct 20 04:19:10 CEST 2011
Jan Kaliszewski <zuo-WMSfXiZwWcGxgjU+5Knr6g at public.gmane.org> writes:
> Nikolaus Rath dixit (2011-10-19, 10:43):
>
>> >> with CleanupManager() as mngr:
>> >> allocate_res1()
>> >> mngr.register(cleanup_res1)
>> >> # do stuff
>> >> allocate_res2()
>> >> mngr.register(cleanup_res2)
>> >> # do stuff
>> >> allocate_res3()
>> >> mngr.register(cleanup_res3)
>> >> # do stuff
>> >>
>> >> The mngr object would just run all the registered functions when the
>> >> block is exited.
> [snip]
>> What would be the best way to handle errors during cleanup? Personally I
>> would log them with logging.exception and discard them, but using the
>> logging module is probably not a good option for contextlib, or is it?
>
> I'd suggest something like the following:
>
> class CleanupManager:
>
> _default_error_handler = lambda exc_type, exc_value, tb: False
>
> def __init__(self, error_handler=_default_error_handler):
> self.error_handler = error_handler
> self.cleanup_callbacks = []
>
> def register(self, callback):
> self.cleanup_callbacks.append(callback)
>
> def __enter__(self):
> return self
>
> def __exit__(self, exc_type, exc_value, tb):
> try:
> if exc_value is not None:
> # if returns True, exception will be suppressed...
> return self.error_handler(exc_type, exc_value, tb)
> finally:
> # ...except something wrong happen when using callbacks
> self._next_callback()
>
> def _next_callback(self):
> if self.cleanup_callbacks:
> callback = self.cleanup_callbacks.pop()
> try:
> callback()
> finally:
> # all callbacks to be used + all errors to be reported
> self._next_callback()
>
[...]
>
> Please also note that all cleanup callbacks will be used and, at the same time,
> no exception will remain unnoticed -- that within the with-block (handled with
> the error handler), but also that from the error handler as well as those from
> all cleanup handlers (as long as we talk about Py3.x, with its cool exception
> chaining feature):
Wow, that's really neat! I was in Python 2.x mode and would have tried
to iterate over the callbacks, not knowing what to do with any
exceptions from them.
That said, do you have a suggestion for Python 2.7 as well? Maybe
something like a cleanup error handler?
class CleanupManager:
_default_error_handler = lambda exc_type, exc_value, tb: False
_default_cleanup_error_handler = lambda exc_type, exc_value, tb: True
def __init__(self, error_handler=_default_error_handler,
cleanup_error_handler=_default_cleanup_error_handler):
self.error_handler = error_handler
self.cleanup_error_handler = cleanup_error_handler
self.cleanup_callbacks = []
def register(self, callback):
self.cleanup_callbacks.append(callback)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
try:
if exc_value is not None:
# if returns True, exception will be suppressed...
return self.error_handler(exc_type, exc_value, tb)
finally:
# Saves the exception that we are going to raise at the
# end (if any)
exc_info = None
for cb in self.cleanup_callbacks:
try:
cb()
except:
# If returns true, ignore exceptions during cleanup
if self.cleanup_error_handler(*sys.exc_info()):
pass
else:
# Only first exception gets propagated
if not exc_info:
exc_info = sys.exc_info()
if exc_info:
raise exc_info[0], exc_info[1], exc_info[2]
Best,
-Nikolaus
--
»Time flies like an arrow, fruit flies like a Banana.«
PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C
More information about the Python-ideas
mailing list