2015-03-15 24 views
6

Mam problem z zawijaniem głowy wokół tego, jak połączone są łańcuchy. W nieco mniej trywialny przykład niż Hello World czy silni, chciałbym mieć pętlę, która stale zegarki czasy modyfikacji pliku, a następnie drukuje się czas, kiedy plik został dotknięty:Łączenie łańcuchów w asyncio (i wzorzec obserwatora)

#!/usr/bin/env python3 
import os 
import asyncio 

@asyncio.coroutine 
def pathmonitor(path): 
    modtime = os.path.getmtime(path) 
    while True: 
     new_time = os.path.getmtime(path) 
     if new_time != modtime: 
      modtime = new_time 
      yield modtime 
     yield from asyncio.sleep(1) 

@asyncio.coroutine 
def printer(): 
    while True: 
     modtime = yield from pathmonitor('/home/users/gnr/tempfile') 
     print(modtime) 

loop = asyncio.get_event_loop() 
loop.run_until_complete(printer()) 
loop.run_forever() 

spodziewałbym to do pracy - jednak po uruchomieniu otrzymuję:

RuntimeError: Task got bad yield: 1426449327.2590399 

Co ja tu robię źle?

AKTUALIZACJA: zapoznaj się z poniższą odpowiedzią, aby zobaczyć przykład wzorca obserwatora (tzn. Sprawnie umożliwić wielu rejestrującym pobieranie aktualizacji po dotknięciu pliku) bez korzystania z wywołań zwrotnych (musisz użyć Zadania).

Update2: jest lepiej fix do tego: 3.5 za async for (Asynchronous iteratory): https://www.python.org/dev/peps/pep-0492/

Odpowiedz

4

Mam swój kod działa przy użyciu return zamiast yield w przykuty współprogram, podobnie jak chained coroutines example:

#!/usr/bin/env python3 
import os 
import asyncio2 

@asyncio.coroutine 
def pathmonitor(path): 
    modtime = os.path.getmtime(path) 
    while True: 
     new_time = os.path.getmtime(path) 
     if new_time != modtime: 
      modtime = new_time 
      return modtime 
     yield from asyncio.sleep(1) 


@asyncio.coroutine 
def printer(): 
    while True: 
     modtime = yield from pathmonitor('/tmp/foo.txt') 
     print(modtime) 


loop = asyncio.get_event_loop() 
loop.run_until_complete(printer()) 
loop.run_forever() 

Zauważ, że pętla printer() „s stworzy nowy pathmonitor generator dla każdej iteracji. Nie jestem pewien, czy to właśnie miałeś na myśli, ale może to być początek.

Uważam, że interfejs API i składnia są nieco mylące. Oto niektóre czytanie że znalazłem pomocne:

+1

Dzięki za pomocny answer- po patrząc na docs nieco więcej zauważyłem, że stwierdzają oni, że współprogram może jedynie zwrócić lub wydajność od - prosty wydajność nie jest dozwolone. Zastanawiam się, dlaczego coroutines nie są w stanie wykorzystać wydajności jak normalnego generatora. – gnr

+0

Tak, to ciekawe. Możesz przepisać 'pathmonitor' jako zwykły generator i przesunąć uśpienie do' drukarki'. Ale myślę, że chciałaś spętanego konwoju. –

+1

Ach, to ciekawy pomysł - trochę się pobawię, żeby zobaczyć, co potrafię wymyślić. Mam kod biblioteki, który używa wywołań zwrotnych, aby powiadomić, kiedy plik zostanie dotknięty i chciałbym zobaczyć, jak sprawić, by działał on z coroutines (lub bez wywołań zwrotnych). Chciałbym mieć funkcję drukarki, funkcję rejestratora lub funkcję gniazda, która wszystko może zrobić inaczej, gdy zostanie dotknięty plik. – gnr

0

Jak inni wskazał, moim błędem było to, że starałem się używać współprogram jak generator. Zamiast polegać na generatorze do iteracji, potrzebowałem stworzyć wiele coroutines. Musiałem także użyć zadań do implementacji wzorca obserwatora bez wywołań zwrotnych, ponieważ wielu rejestrujących może yield from wykonać to samo zadanie. Moja pathmonitor wygląda mniej więcej tak:

import os 
import asyncio 

class PathInfo: 

    def __init__(self, path): 
     self.path = path 
     self.modtime = os.path.getmtime(path) 
     self.startTask() 

    def startTask(self): 
     self.task = asyncio.async(self._checkIfTouched()) 

    def _checkIfTouched(self): 
     while True: 
      yield from asyncio.sleep(1) 
      newtime = os.path.getmtime(self.path) 
      if self.modtime != newtime: 
       self.modtime = newtime 
       return newtime 

class PathMonitor: 

    def __init__(self): 
     self._info = {} 

    @asyncio.coroutine 
    def wasTouched(self, path): 
     try: 
      info = self._info[path] 
     except KeyError: 
      self._info[path] = info = PathInfo(path) 
     if info.task.done(): 
      info.startTask() 
     modtime = yield from info.task 
     return modtime 

def printer(): 
    while True: 
     modtime = yield from mon.wasTouched('/tmp/myfile') 
     print(modtime) 

mon = PathMonitor() 

loop = asyncio.get_event_loop() 
asyncio.async(printer()) 
loop.run_forever() 
Powiązane problemy