python-asynciopython-3.6

Extracting the function and arguments from a coroutine


Is it possible to extract the function and arguments of a coroutine object in python3.6?

Context: currently I have something like this:

async def func(*args):
    ...

ret = await remotely(func, x, y)

Under the hood, remotely pickles func, x, and y, scp's that to a different server, where it unpickles them, executes func(x,y), pickles the result, scp's that back, and finally unpickles it into ret.

This API feels distasteful to me, I'd prefer to have:

ret = await remotely(func(x, y))

I could do this if I could pickle the coroutine object represented by func(x, y), but when I tried that, I get:

TypeError: can't pickle coroutine objects

So my alternate hope is that I can extract f, x, and y from f(x, y), hence this question.


Solution

  • So when you do ret = await remotely(func(x, y)), you actually construct the coroutine object for func. Fortunately, you can extract the information you need from coroutine objects, which you can send over for remote execution.

    So first of all you can get the function name using the __qualname__ attribute. This will give you the fully qualified name, i.e. if the coroutine is nested, it will get you the full path to your function.

    Next, you can extract the argument values from the frame object of the coroutine.

    So this is how your remote function would look like

    async def remote(cr):
        # Get the function name
        fname = cr.__qualname__
    
        # Get the argument values
        frame = cr.cr_frame
        args = frame.f_locals  # dict object
    
        result = await ...  # your scp stuff
    
        return result
    

    There is just one caveat. You should indicate that the function should be only used the way that you have posted, i.e.

    ret = await remotely(func(x, y))
    

    ...in other words, the coroutine should be "fresh", and not half-way executed (which is almost not possible if you initiate it right before passing it to remote). Otherwise, the f_locals value might include any other local variable that is defined before any awaits.