2016-10-28 1 views
3

Ich habe einen Decorator, der eine Coroutine-Funktion dekoriert und den von der Coroutine zurückgegebenen Wert einer zukünftigen Instanz zuweist.Rückgabe eines zukünftigen Ergebnisses unter Verwendung einer einzelnen Ereignisschleife mit dekorierter Coroutine

import asyncio 
import functools 


def ensure_prepare(future): 
    async def decorator(asyncfunc): 
     @functools.wraps(asyncfunc) 
     async def wrapper(*args, **kwargs): 
      future.set_result(await asyncfunc(*args, **kwargs)) 
      return future 
     return wrapper 
    return decorator 

Demo:

>>> future = asyncio.Future() 
>>> 
>>> @ensure_prepare(future) 
... async def check_sanity(): 
...  return 9 
... 
>>> loop = asyncio.get_event_loop() 
>>> loop.run_until_complete(check_sanity) 
<function check_sanity at 0x7f935300a158> 
>>> _() 
<coroutine object check_sanity at 0x7f934f78a728> 
>>> loop.run_until_complete(_) 
<Future finished result=9> 
>>> _.result() 
9 

Wie Sie sehen, ich brauche zwei Mal die Ereignisschleife laufen, um die Zukunft Ergebnis zu erhalten. Gibt es eine Möglichkeit, die Ereignisschleife den Wert nach dem ersten Lauf zurückzugeben? Ich möchte in meinem Code nicht erwarten und das Ergebnis (Funktion) einem Namen zuweisen.

+1

um ... warum definieren Sie Ihren 'Dekorateur' als Async? Ich bin ziemlich sicher, wenn Sie 'run_until_complete (chech_sanity)' zum ersten Mal ausführen, wird nur der Decorator ausgeführt, um den Wrapper zu erstellen. Wenn Sie die 'async' aus der' def decorator' Zeile entfernen, löst sie dann Ihr Problem? –

+0

Das ist eine interessante Frage. Sie versuchen, eine Coroutine in eine Decorator-Funktion einzubinden. Eine Coroutine muss in ihnen warten, also bin ich mir nicht sicher, was du meinst, wenn du sagst, dass du das nicht willst. Können Sie erklären, was Sie zu tun versuchen? – Keith

+0

@Keith Ich mache einige IO-Sachen mit Nebenläufigkeit und muss irgendwann auf alle Aufgaben warten, bevor ich zu etwas anderem übergehe. Dazu benutze ich ein zukünftiges Objekt als Sentinel (ich bin mir nicht sicher, ob das eine gute Idee ist) und habe die innere 'Decorator'-Funktion als Coroutine definiert, was hier der Täter ist. Dies ist wahrscheinlich ein Zeichen, dass ich solche Dinge am Freitag nach den Arbeitszeiten tun sollte :) – styvane

Antwort

1

in Ihrem Code Sie haben Ihre decorator Wrapper gemacht async zu sein, das ist nicht das, was man wollte, es bedeutet, dass jedes Mal, wenn Sie Ihre Wrapper verwenden sie ein Koroutine Objekt zurückgibt, die die verpackte Funktion generieren:

>>> future = asyncio.Future() 
>>> @ensure_prepare(future) 
async def chech_sanity(): 
    return 9 

>>> check_sanity 
<coroutine object ensure_prepare.<locals>.decorator at 0x10572f4c0> 

>>> check_sanity.send(None) #advance coroutine 
Traceback (most recent call last): 
    File "<pyshell#7>", line 1, in <module> 
    check_sanity.send(None) #advance coroutine 
StopIteration: <function check_sanity at 0x105096a60> 

       #^the function is the result of the coroutine 

so einfach async in der Zeile entfernen

async def decorator(asyncfunc): 

Und Ihr Problem gelöst werden:

def ensure_prepare(future): 
    def decorator(asyncfunc): 
     @functools.wraps(asyncfunc) 
     async def wrapper(*args, **kwargs): 
      future.set_result(await asyncfunc(*args, **kwargs)) 
      return future 
     return wrapper 
    return decorator 


>>> future = asyncio.Future() 
>>> @ensure_prepare(future) 
async def check_sanity(): 
    return 9 

>>> chech_sanity 
<function check_sanity at 0x105784a60> 
>>> loop = asyncio.get_event_loop() 
>>> loop.run_until_complete(check_sanity()) #remember to call check_sanity! 
<Future finished result=9> 
Verwandte Themen