[Web-SIG] a convenient way to deeply nest try/finally statements

Shannon -jj Behrens jjinux at gmail.com
Tue Oct 11 19:01:33 CEST 2005


I'm posting this in the hope someone else will find it useful.  If you
want it, consider it in the public domain.


"""This is a convenient way to deeply nest try/finally statements."""

__docformat__ = "restructuredtext"

# Created: Tue Oct 11 08:42:23 PDT 2005
# Author: Shannon -jj Behrens
# Email: jjinux at users.sourceforge.net
#
# Copyright (c) Shannon -jj Behrens.  All rights reserved.


def tryFinally(lst):

    """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::

        lst = [
            ((f_enter_0, f_enter_0_args, f_enter_0_kargs),
             (f_exit_0, f_exit_0_args, f_exit_0_kargs)),

            ((f_enter_1, f_enter_1_args, f_enter_1_kargs),
             (f_exit_1, f_exit_1_args, f_exit_1_kargs)),

            ((f_enter_2, f_enter_2_args, f_enter_2_kargs),
             (f_exit_2, f_exit_2_args, f_exit_2_kargs))
        ]

    Execute::

        f_enter_0(*f_enter_0_args, **f_enter_0_kargs)
        try:

            f_enter_1(*f_enter_1_args, **f_enter_1_kargs)
            try:

                f_enter_2(*f_enter_2_args, **f_enter_2_kargs)
                try:
                    pass
                finally:
                     f_exit_2(*f_exit_2_args, **f_exit_2_kargs)

            finally:
                 f_exit_1(*f_exit_1_args, **f_exit_1_kargs)

        finally:
             f_exit_0(*f_exit_0_args, **f_exit_0_kargs)

    lst
        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, f_enter_2_args, f_enter_2_kargs),)

            # Leave out args or args and kargs.
            ((f_enter_2,),
             (f_exit_2, f_exit_2_args))

    """

    def castTwoParts(first):
        lenFirst = len(first)
        default = ((), ())
        max = len(default)
        if lenFirst > max:
            raise ValueError("""\
The lst 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 the lst is a pair of tuples that look like (f, args, kargs).""",
                             fTuple)
        return fTuple + default[lenFTuple:]

    if not len(lst):
        return
    first, others = lst[0], lst[1:]
    first = castTwoParts(first)
    first = (castFunctionArgsKargs(first[0]),
             castFunctionArgsKargs(first[1]))
    ((fEnter, fEnterArgs, fEnterKargs),
     (fExit, fExitArgs, fExitKargs)) = first

    fEnter(*fEnterArgs, **fEnterKargs)
    try:
        tryFinally(others)
    finally:
        fExit(*fExitArgs, **fExitKargs)


if __name__ == '__main__':

    from cStringIO import StringIO

    def printEverything(*args, **kargs): print >>buf, `args`, `kargs`
    def refuseArgs(): print >>buf, "refused args"

    lst = [
        ((printEverything, ("f_enter_0_args",), {"in": "in"}),
         (printEverything, ("f_exit_0_args",))),

        ((printEverything,),
         ()),

        ((refuseArgs,),)
    ]

    result = """\
('f_enter_0_args',) {'in': 'in'}
() {}
refused args
('f_exit_0_args',) {}
"""

    buf = StringIO()
    tryFinally(lst)
    assert buf.getvalue() == result


More information about the Web-SIG mailing list