This is a convenient way to deeply nest try/finally statements.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | """It's a convenient way to deeply nest try/finally statements."""
__docformat__ = "restructuredtext"
from traceback import print_exc
def tryFinally(tasks, handleFinallyException=None):
"""This is a convenient way to deeply nest try/finally statements.
It is appropriate for complicated resource initialization and destruction.
For instance, if you have a list of 50 things that need to get intialized
and later destructed via using try/finally (especially if you need to
create the list dynamically) this function is appropriate.
Given::
tasks = [
((f_enter_0, enter_0_args, enter_0_kargs),
(f_exit_0, exit_0_args, exit_0_kargs)),
((f_enter_1, enter_1_args, enter_1_kargs),
(f_exit_1, exit_1_args, exit_1_kargs)),
((f_enter_2, enter_2_args, enter_2_kargs),
(f_exit_2, exit_2_args, exit_2_kargs))
]
Execute::
f_enter_0(*enter_0_args, **enter_0_kargs)
try:
f_enter_1(*enter_1_args, **enter_1_kargs)
try:
f_enter_2(*enter_2_args, **enter_2_kargs)
try:
pass
finally:
try:
f_exit_2(*exit_2_args, **exit_2_kargs)
except Exception, e:
handleFinallyException(e)
finally:
try:
f_exit_1(*exit_1_args, **exit_1_kargs)
except Exception, e:
handleFinallyException(e)
finally:
try:
f_exit_0(*exit_0_args, **exit_0_kargs)
except Exception, e:
handleFinallyException(e)
tasks
See the example above. Note that you can leave out parts of the tuples
by passing shorter tuples. For instance, here are two examples::
# Second tuple missing.
((f_enter_2, enter_2_args, enter_2_kargs),)
# Leave out args or args and kargs.
((f_enter_2,),
(f_exit_2, exit_2_args))
Don't forget that a tuple of 1 item is written ``(item,)``. This is an
amazingly easy thing to do.
handleFinallyException(e)
This is a callback that gets called if an exception, ``e``, is raised
in a finally block. By default, traceback.print_exc is called.
"""
def defaultFinallyExceptionHandler(e):
print_exc()
def castTwoParts(first):
lenFirst = len(first)
default = ((), ())
max = len(default)
if lenFirst > max:
raise ValueError("""\
tasks must be a list of tuples of the form (enterTuple, exitTuple).""", first)
return first + default[lenFirst:]
def doNothing(*args, **kargs):
pass
def castFunctionArgsKargs(fTuple):
lenFTuple = len(fTuple)
default = (doNothing, (), {})
max = len(default)
if lenFTuple > max:
raise ValueError("""\
Each tuple in tasks is a pair of tuples that look like (f, args, kargs).""",
fTuple)
return fTuple + default[lenFTuple:]
if not len(tasks):
return
if not handleFinallyException:
handleFinallyException = defaultFinallyExceptionHandler
first, others = tasks[0], tasks[1:]
first = castTwoParts(first)
first = (castFunctionArgsKargs(first[0]),
castFunctionArgsKargs(first[1]))
((fEnter, fEnterArgs, fEnterKargs),
(fExit, fExitArgs, fExitKargs)) = first
fEnter(*fEnterArgs, **fEnterKargs)
try:
tryFinally(others, handleFinallyException)
finally:
try:
fExit(*fExitArgs, **fExitKargs)
except Exception, e:
handleFinallyException(e)
if __name__ == '__main__':
from cStringIO import StringIO
def printEverything(*args, **kargs): print >>buf, `args`, `kargs`
def refuseArgs(): print >>buf, "refused args"
def raiseValueError(): raise ValueError
def finallyExceptionHandler(e): print >>buf, "caught exception in finally"
tasks = [
((printEverything, ("enter_0_args",), {"in": "in"}),
(printEverything, ("exit_0_args",))),
((printEverything,),
(raiseValueError,)),
((refuseArgs,),)
]
result = """\
('enter_0_args',) {'in': 'in'}
() {}
refused args
caught exception in finally
('exit_0_args',) {}
"""
buf = StringIO()
tryFinally(tasks, finallyExceptionHandler)
assert buf.getvalue() == result
|
Many people have suggested that newer versions of Python will have "with" statements that will alleviate the need for my library. However, my library is appropriate if you have a long list of initialization tasks that may need to be setup dynamically using input from a subclass. Perhaps I could create a new library based on the "with" statement, but the ability to control a complete list of init/destruct functions is still necessary.
Wouldn't a structure like this work?
oops -- add to top of final loop. Oops, missing line (pasting in parts):
The final loop should read: