Hi all,

This might be interesting to anyone who uses deferred.defer in python
on appengine.

I've just written this code that expands what you can do with defer.
You can defer a lambda, and you can defer (functions with) closures.
It's implemented using the original defer, but marshals a function's
code across the defer boundary (using the marshal library and a
wrapper function) and keeps the function's closure information intact.

Note that this wont take an arbitrary callable, you need to be passing
a function.

I'd love any feedback; am I doing anything disastrous? Could anything
be done a better way?

import marshal, types, sys
from google.appengine.ext import deferred

# defer any function object to be run in a background task
def Defer(aFunction, *args, **kwargs):

    if not aFunction or not hasattr(aFunction, "func_code"):
        raise Exception ("First argument required, must be a function")

    # recursively turn a function's "closure" info into something marshallable
    def MarshalClosureValues(aClosure):
        logging.debug(aClosure)
        lmarshalledClosureValues = []
        if aClosure:
            lclosureValues = [lcell.cell_contents for lcell in aClosure]
            logging.debug("lclosureValues: %s" % lclosureValues)
            lmarshalledClosureValues = [
                [marshal.dumps(litem.func_code),
MarshalClosureValues(litem.func_closure)] if hasattr(litem,
"func_code")
                else [marshal.dumps(litem)]
                for litem in lclosureValues
            ]
        return lmarshalledClosureValues

    # marshall the function's code
    lmarshalledFunc = marshal.dumps(aFunction.func_code)

    # marshall the function's closure info
    lmarshalledClosureValues = MarshalClosureValues(aFunction.func_closure)

    # also grab the function's module (for restoring appropriate "globals")
    lmoduleName = aFunction.__module__

    # call "defer" on our wrapper function, with all this info;
    # it will reverse the above and call the origin function
    deferred.defer(WrapDeferred, lmarshalledFunc,
lmarshalledClosureValues, lmoduleName, *args, **kwargs)


# Reverse the marshalling process and call reconstituted function
def WrapDeferred(aMarshalledFunc, aMarshalledClosureValues,
aModuleName, *args, **kwargs):

    # get appropriate globals. These are being used for functions
inside closure info
    # as well as top level function; is this dangerous?
    lglobals = sys.modules[aModuleName].__dict__

    # Creates a "cell" for a value by wrapping it in a function and
then pulling it out
    # of the closure info
    def make_cell(value):
        return (lambda x: lambda: x)(value).func_closure[0]

    # Reverse marshalling process on closure info
    def UnmarshalClosureValues(aMarshalledClosureValues):
        lclosure = None
        if aMarshalledClosureValues:
            lclosureValues = [
                    marshal.loads(item[0]) if len(item) == 1
                    else types.FunctionType(marshal.loads(item[0]),
lglobals, closure=UnmarshalClosureValues(item[1]))
                    for item in aMarshalledClosureValues if len(item)
>= 1 and len(item) <= 2
                ]
            lclosure = tuple([make_cell(lvalue) for lvalue in lclosureValues])
        return lclosure

    # unmarshal the function code
    lfunctionCode = marshal.loads(aMarshalledFunc)

    # unmarshal the closure info
    lclosure = UnmarshalClosureValues(aMarshalledClosureValues)

    # create a new function using the unmarshalled code, closure info,
and globals
    lfunction = types.FunctionType(lfunctionCode, lglobals, closure=lclosure)

    # call the function!
    lfunction(*args, **kwargs)



-- 
Emlyn

http://point7.wordpress.com - My blog
https://plus.google.com/u/0/100281903174934656260 - Google+

-- 
You received this message because you are subscribed to the Google Groups 
"Google App Engine" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-appengine+unsubscr...@googlegroups.com.
To post to this group, send email to google-appengine@googlegroups.com.
Visit this group at http://groups.google.com/group/google-appengine.
For more options, visit https://groups.google.com/d/optout.

Reply via email to