Форум сайта python.su
Добрый день. Принялся изучать twisted, но к сожалению слаб в техническом английском, поэтому много понять не могу . Вопрос в следующем, почему при попытке остановить реактор, выдает исключение (смотреть после кода) и как выстроить архитектуру приложения что бы такого не происходило.Цель с помощью callLater выставить timeout после которого Reactor будет остановлен, если данные с сервера не были получены за этот промежуток.
import optparse, sys from twisted.internet.protocol import Protocol, ClientFactory def parse_args(): usage = """usage: %prog [options] [hostname]:port ... This is the Get Poetry Now! client, Twisted version 3.1 Run it like this: python get-poetry-1.py port1 port2 port3 ... If you are in the base directory of the twisted-intro package, you could run it like this: python twisted-client-3/get-poetry-1.py 10001 10002 10003 to grab poetry from servers on ports 10001, 10002, and 10003. Of course, there need to be servers listening on those ports for that to work. """ parser = optparse.OptionParser(usage) _, addresses = parser.parse_args() if not addresses: print parser.format_help() parser.exit() def parse_address(addr): if ':' not in addr: host = '127.0.0.1' port = addr else: host, port = addr.split(':', 1) if not port.isdigit(): parser.error('Ports must be integers.') return host, int(port) return map(parse_address, addresses) class PoetryProtocol(Protocol): poem = '' def dataReceived(self, data): self.poem += data def connectionLost(self, reason): self.poemReceived(self.poem) def poemReceived(self, poem): self.factory.poem_finished(poem) class PoetryClientFactory(ClientFactory): protocol = PoetryProtocol def __init__(self, callback, errback): self.callback = callback self.errback = errback def poem_finished(self, poem): self.callback(poem) def clientConnectionFailed(self, connector, reason): print 'Connection Failed' self.errback(reason) def get_poetry(host, port, callback, errback): """ Download a poem from the given host and port and invoke callback(poem) when the poem is complete. If there is a failure, invoke: errback(err) instead, where err is a twisted.python.failure.Failure instance. """ from twisted.internet import reactor factory = PoetryClientFactory(callback, errback) reactor.connectTCP(host, port, factory) def poetry_main(): addresses = parse_args() from twisted.internet import reactor poems = [] errors = [] def got_poem(poem): poems.append(poem) poem_done() def poem_failed(err): print >>sys.stderr, 'Poem failed:', err errors.append(err) poem_done() def poem_done(): if len(poems) + len(errors) == len(addresses): reactor.stop() for address in addresses: host, port = address get_poetry(host, port, got_poem, poem_failed) reactor.run() for poem in poems: print poem def go(): from twisted.internet import reactor reactor.callLater(0.000001,poetry_main) reactor.callLater(2,stop) def stop(): from twisted.internet import reactor print 'Exceeded the threshold of response' reactor.stop() if __name__ == '__main__': from twisted.internet import reactor reactor.callWhenRunning(go) reactor.run()
Unhandled Error Traceback (most recent call last): File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 144, in <module> reactor.run() File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 1169, in run self.mainLoop() File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 1178, in mainLoop self.runUntilCurrent() --- <exception caught here> --- File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 800, in runUntilCurrent call.func(*call.args, **call.kw) File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 124, in poetry_main reactor.run() File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 1168, in run self.startRunning(installSignalHandlers=installSignalHandlers) File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 1148, in startRunning ReactorBase.startRunning(self) File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 678, in startRunning raise error.ReactorAlreadyRunning() twisted.internet.error.ReactorAlreadyRunning: Exceeded the threshold of response Unhandled Error Traceback (most recent call last): File "C:\Python27\lib\site-packages\twisted\internet\defer.py", line 290, in addCallbacks self._runCallbacks() File "C:\Python27\lib\site-packages\twisted\internet\defer.py", line 551, in _runCallbacks current.result = callback(current.result, *args, **kw) File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 426, in _continueFiring callable(*args, **kwargs) File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 621, in disconnectAll failure.Failure(main.CONNECTION_LOST)) --- <exception caught here> --- File "C:\Python27\lib\site-packages\twisted\python\log.py", line 84, in callWithLogger return callWithContext({"system": lp}, func, *args, **kw) File "C:\Python27\lib\site-packages\twisted\python\log.py", line 69, in callWithContext return context.call({ILogContext: newCtx}, func, *args, **kw) File "C:\Python27\lib\site-packages\twisted\python\context.py", line 118, in callWithContext return self.currentContext().callWithContext(ctx, func, *args, **kw) File "C:\Python27\lib\site-packages\twisted\python\context.py", line 81, in callWithContext return func(*args,**kw) File "C:\Python27\lib\site-packages\twisted\internet\tcp.py", line 473, in connectionLost self._commonConnection.connectionLost(self, reason) File "C:\Python27\lib\site-packages\twisted\internet\tcp.py", line 287, in connectionLost protocol.connectionLost(reason) File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 60, in connectionLost self.poemReceived(self.poem) File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 63, in poemReceived self.factory.poem_finished(poem) File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 75, in poem_finished self.callback(poem) File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 108, in got_poem poem_done() File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 117, in poem_done reactor.stop() File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 577, in stop "Can't stop reactor that isn't running.") twisted.internet.error.ReactorNotRunning: Can't stop reactor that isn't running.
Отредактировано slav0nic (Ноя. 1, 2012 10:28:45)
Офлайн
Уточню. То что ошибка указывает на невозможность остановки реактора, потому как он запущен и выполняет процессы я понимаю. Как мне избежать данного момента и остановить реактор не дожидаясь полного скачивания данных? Может вместо остановки реактора мне использовать raise connectionLost ? ну тогда скорей всего произойдет ошибка из-за вызова одного и того же callback'a одновременно, скорей всего. Возможно я не совсем правильно излагаю свои догадки, поэтому готов ответить на уточняющие вопросы и жду вашей помощи .
Офлайн
Ты не прав. Проблема в том что ты его дважды останавливаешь, собственно в сообщении об ошибке это и написано: Низя остановить _НЕ_ запущенный реактор.
Следующая проблема – это два run'а, избавься от них, или они избавятся от тебя :-).
Если у тебя несколько точек запуска реактора и несколько точек останова, то что-то не так, т.е. такое возможно, но так луше не делать. Постарайся построить архитектуру так, что бы `reactor.run()` и `reactor.stop()` присутствовали в коде в единственном экземпляре. Так же, если по какой-то причине `reactor.stop()` вызывается дважды, значит что-то не так, хотя повторный вызов stop'а можно избежать проверкой свойства `reactor.running`, но по мне, это признак плохого дизайна, который безусловно аукнется по тебе.
..bw
Офлайн
Да я понимаю, что архитектура страдает, нет опыта разработки приложений. Второй run закаментен, тут просто не отображено, извиняюсь. Избавиться от второго stop'a не знаю как, по идеи после таймаута callLetar он должен остановить связь с сервером, поэтому предположил что лучше будет использовать raise функции connectionLost, например. Но тогда может произойти вызов двух callback одновременно, что опять же вызовет ошибку.
def connectionLost(self, reason): self.poemReceived(self.poem)
Отредактировано lat (Ноя. 1, 2012 11:54:51)
Офлайн
Понятно. Более правильным решением будет принудительно завершить все “работы”, что приведёт к вызову каждой из них метода `poem_done` и в конечном итоге остановке реактора. А явный вызов `stop` я бы убрал. Хотя можно по прежнему использовать `stop` явно, а в `poem_done` добавить проверку того работает ли реактор или нет, как писал выше. Есть и третий вариант, вместо `reactor.stop()` в `poem_done` выставлять где-нибудь флаг, что мы тут закончили, а в отдельном “потоке” (например используя `LoopingCall`) мониторить это дело и вызывать `stop`. Для данного примера я бы использовал первый вариант.
Для первого случая. Пишу по памяти :-). Уже больше двух лет не пользовал Twisted, так что могу ошибаться. Тут должен помочь вызов `transport.loseConnection` в каждом из протоколов, а как до них докопаться не скажу, а то будет не интересно :-). Могу только сказать, что не хорошо будет включать логику по прирыванию соединения в сами протоколы, она должна действовать извне.
..bw
Офлайн
Решил проблему обрубив все соединения. Хоть и не совсем эстетично, но в данном случаи как раз это и надо было. Воспользовался, кому интересно, reactor.disconnectAll().
bw Спасибо за советы и удачи!
Офлайн