• Glyph [2025-01-22 18:25]:
In general, if you have to call reactor.stop() manually like this in a
script, you're probably doing too much manual fiddling with its
lifecycle. I have not fully debugged this example, but I suspect it's
just because the error is synchronous before the reactor starts.
The provided scripts are just minimal examples which illustrate the
problem I am having in a bigger piece of a daemon, which sets up a
number of listeners on multiple interfaces and ports: if it is not able
to bind to any of those, reactor should stop. It it does not stop by
calling reactor.stop(), only by wrapping it in callWhenRunning or
callLater. At first I just thought that it's just the way it is, but
after I started the question WHY, it bothers me a little bit.
However, rather than carefully managing the reactor's startup state like
this where half your code runs synchronously before reactor startup,
There is no synchronous code in the examples though, apart from setting
up some things which would be called once reactor is running. E.g. the
first two defs set up callback and erroback functions -- isn't
reactor.running supposed to be True when callbacks/errorbacks attached
to a deferred are called?
as an experiment, here's example which uses react():
#!/usr/local/bin/python3
from twisted.internet import reactor, defer, protocol
from twisted.internet.protocol import Factory
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.internet.task import react
def _port(port):
print('got', port)
def _error(err):
print('got err', err)
print('is reactor running in error?', reactor.running)
print('is reactor running in lambda?', (lambda: reactor.running)())
reactor.stop() # <- this fails
# reactor.callWhenRunning(reactor.stop) # <- this works
async def main(reactor):
print('running in main, huh?', reactor.running)
d = TCP4ServerEndpoint(reactor, 123).listen(Factory())
d.addCallback(_port)
d.addErrback(_error)
d.addErrback(print)
react(main)
it outputs this:
running in main, huh? False
got err [Failure instance: Traceback: <class
'twisted.internet.error.CannotListenError'>: Couldn't listen on any:123:
[Errno 13] Permission denied.
/usr/local/lib/python3.12/site-packages/twisted/internet/defer.py:155:execute
/usr/local/lib/python3.12/site-packages/twisted/internet/posixbase.py:366:listenTCP
/usr/local/lib/python3.12/site-packages/twisted/internet/tcp.py:1360:startListening
]
is reactor running in error? False
is reactor running in lambda? False
[Failure instance: Traceback: <class
'twisted.internet.error.ReactorNotRunning'>: Can't stop reactor that
isn't running.
/usr/local/lib/python3.12/site-packages/twisted/internet/defer.py:1088:_runCallbacks
/home/km/./foo:15:_error
/usr/local/lib/python3.12/site-packages/twisted/internet/base.py:790:stop
]
consider using <https://docs.twistedmatrix.com/en/stable/api/
twisted.internet.task.html#react <https://docs.twistedmatrix.com/en/
stable/api/twisted.internet.task.html#react>> instead and putting all
your code (other than your imports) into your main function. This will
result in far fewer confusing scenarios. Don't stop the reactor, just
complete your coroutine and let `react()` shut it down for you when
you're done.
All that said: there /are/ probably a bunch of improvements to the API
and documentation that could make this easier, and the reactor's
starting/stopping state shouldn't be such an opaque mess. It might be
nice to eventually put it into an Automat state machine, for example,
with some more explicit queries and fewer flags. So it's not like
there's no work to be done here. But even if we did all that, the right
way to write 99% of applications would still be to use something like a
`twist` plugin, or a callable passed to `react`.
Hope this helps,
-g
On Jan 22, 2025, at 1:45 AM, Kirill Miazine <k...@krot.org> wrote:
This has been driving me crazy for a while -- for some reason
reactor.stop() in the _error errback in example below raises
error.ReactorNotRunning. In order to stop the reactor, I have to do
reactor.callWhenRunning(reactor.stop) (or I did reactor.callLater(0,
...) until I discovered callWhenRunning).
In the example, I bind to a low port to make sure error is triggered.
#!/usr/local/bin/python3
from twisted.internet import reactor, defer, protocol
from twisted.internet.protocol import Factory
from twisted.internet.endpoints import TCP4ServerEndpoint
def _port(port):
print('got', port)
def _error(err):
print('got err', err)
print('is reactor running?', reactor.running)
print('is reactor running?', (lambda: reactor.running)())
reactor.stop()
# reactor.callWhenRunning(reactor.stop)
d = TCP4ServerEndpoint(reactor, 123).listen(Factory())
d.addCallback(_port)
d.addErrback(_error)
d.addErrback(print)
reactor.run()
OTOH, in the following example reactor.stop() is stoppig the reactor
properly:
#!/usr/local/bin/python3
from twisted.internet import reactor, defer
def cb(res):
print('running?', reactor.running)
if res == 'bar':
raise Exception()
reactor.stop()
def eb(err):
print('running?', reactor.running)
print(err)
reactor.stop()
d = defer.Deferred()
d.addCallback(cb)
d.addErrback(eb)
#reactor.callWhenRunning(d.callback, 'foo')
reactor.callWhenRunning(d.callback, 'bar')
reactor.run()
Any ideas?
_______________________________________________
Twisted mailing list -- twisted@python.org
To unsubscribe send an email to twisted-le...@python.org
https://mail.python.org/mailman3/lists/twisted.python.org/
Message archived at
https://mail.python.org/archives/list/twisted@python.org/message/NZD2X6F5UI2NM4X2RAYASV3RWJU6JDSX/
Code of Conduct: https://twisted.org/conduct