Pyjo.Reactor.Twisted - Low-level event reactor with Twisted support¶
from Pyjo.Reactor.Twisted import Twisted
# Watch if handle becomes readable or writable
reactor = Twisted()
reactor.io(first, lambda reactor, writable: print(
'First handle is writable' if writable else 'First handle is readable'
))
# Or import the module, as in Google Python Style Guide
import Pyjo.Reactor.Twisted
# Watch if handle becomes readable or writable
reactor = Pyjo.Reactor.Twisted.new()
reactor.io(first, lambda reactor, writable: print(
'First handle is writable' if writable else 'First handle is readable'
))
# Change to watching only if handle becomes writable
reactor.watch(first, False, True)
# Watch a file descriptor if it becomes readable
reactor.io(fd, lambda reactor, writable: print(
'Second handle is writable' if writable else 'Second handle is readable'
)).watch(fd, True, False)
# Add a timer
def timeout(reactor):
reactor.remove(first)
reactor.remove(fd)
print('Timeout!')
reactor.timer(15, timeout)
# Start reactor if necessary
if not reactor.is_running:
reactor.start()
Pyjo.Reactor.Twisted is a low-level event reactor based on the reactor
of Twisted, the one that the application installed
before, or else the reactor of poll of Twisted, or of select where there is no
poll or it does not work well, as on macOS. Its default on Linux, the reactor
of epoll, is not installed, as epoll keeps its state in the kernel: a socket
that is closed before it is removed leaves it, which Pyjoyment does, and the
state is shared with the child of a fork, as a worker of
Pyjo.Server.Prefork, so an application that installs it has neither.
An application of Twisted that imports twisted.internet.reactor itself
calls install() before, which installs the one of poll or select.
PYJO_REACTOR selects it for Pyjo.IOLoop:
PYJO_REACTOR=Pyjo.Reactor.Twisted
The reactor of Twisted is unique in a process, so only one object of this
class uses it at a time, and new() returns a
Pyjo.Reactor.Poll object while it lives, as the constructor of the
class does. It runs in the thread that
made it, so the threads of Pyjo.Server.Prethread get
Pyjo.Reactor.Poll objects. When the reactor of Twisted is running
already, as under twistd or Scrapy, start()
and one_tick() return right away, and the events
are handled by the reactor of Twisted that runs.
Events¶
Pyjo.Reactor.Twisted inherits all events from Pyjo.Reactor.Poll.
See also¶
Pyjo.Reactor.Poll, Pyjoyment, https://github.com/pyjoyment/Pyjoyment.
Classes¶
- class Pyjo.Reactor.Twisted.Pyjo_Reactor_Twisted(*args: Any, **kwargs: Any)[source]¶
Pyjo.Reactor.Twistedinherits all methods fromPyjo.Reactor.Polland implements the following new ones.- again(id: str, after: float | None = None) None[source]¶
reactor.again(id) reactor.again(id, 0.5)
Restart timer and optionally change the invocation time. Note that this method requires an active timer.
- io(handle: Handle, cb: IOCallback) Self[source]¶
reactor = reactor.io(handle, cb)
Watch handle for I/O events, invoking the callback whenever handle becomes readable or writable.
- classmethod new(*args: Any, **kwargs: Any) Any[source]¶
reactor = Pyjo.Reactor.Twisted.new()
Construct a new
Pyjo.Reactor.Twistedobject, or aPyjo.Reactor.Pollobject while another one uses the reactor of Twisted.
- one_tick() None[source]¶
reactor.one_tick()
Run reactor until an event occurs or no events are being watched anymore, or return right away while the reactor of Twisted runs.
- recurring(after: float, cb: TimerCallback) str[source]¶
id = reactor.recurring(0.25, cb)
Create a new recurring timer, invoking the callback repeatedly after a given amount of time in seconds.
- remove(remove: Handle | str) bool[source]¶
boolean = reactor.remove(handle) boolean = reactor.remove(id)
Remove handle or timer.
- start() None[source]¶
reactor.start()
Start watching for I/O and timer events, this will block until
stop()is called or no events are being watched anymore, or return right away while the reactor of Twisted runs.
- Pyjo.Reactor.Twisted.install() None[source]¶
Pyjo.Reactor.Twisted.install()
Install the reactor of Twisted that
Pyjo.Reactor.Twistedprefers, of poll, or of select where there is no poll or it does not work well, as on macOS, in the place of the default of Twisted, epoll on Linux. An application of Twisted calls it before it importstwisted.internet.reactor, which installs the default. It does nothing where a reactor of Twisted is installed already.import Pyjo.Reactor.Twisted Pyjo.Reactor.Twisted.install() from twisted.internet import reactor
Examples
examples/hello_world_twisted.py runs Pyjo.HelloWorld on the server of
Pyjoyment in the event loop that Twisted runs, with reactor.run(), in a
with block, which closes the listen sockets of the server at its end:
import os # The reactor of Pyjoyment on the reactor of Twisted, selected before Pyjo.IOLoop is loaded os.environ['PYJO_REACTOR'] = 'Pyjo.Reactor.Twisted' import Pyjo.Reactor.Twisted # noqa: E402 # The reactor of Twisted of poll or select, before Twisted is imported, which would install its default, epoll on Linux Pyjo.Reactor.Twisted.install() import Pyjo.HelloWorld # noqa: E402 import Pyjo.Server.Daemon # noqa: E402 from twisted.internet import reactor # noqa: E402 # The server of Pyjoyment, which closes its listen sockets at the end of the block with Pyjo.Server.Daemon.new(app=Pyjo.HelloWorld.new(), listen=['http://*:3000']) as daemon: daemon.start() # Twisted runs the event loop, with the server of Pyjoyment in it reactor.run()
$ python examples/hello_world_twisted.py
Web application available at http://127.0.0.1:3000
$ curl http://127.0.0.1:3000/
Your Pyjo is working!
examples/user_agent_twisted.py is an application of Twisted, a server of
twisted.web, whose timer requests it every second with the user agent of
Pyjoyment, without blocking the reactor of Twisted. The user agent is in a
with block, which closes its connections at its end, so the callback shows
the error of a request that it interrupts:
import os # The reactor of Pyjoyment on the reactor of Twisted, selected before Pyjo.IOLoop is loaded os.environ['PYJO_REACTOR'] = 'Pyjo.Reactor.Twisted' import Pyjo.Reactor.Twisted # noqa: E402 # The reactor of Twisted of poll or select, before Twisted is imported, which would install its default, epoll on Linux Pyjo.Reactor.Twisted.install() import Pyjo.UserAgent # noqa: E402 from twisted.internet import reactor, task # noqa: E402 from twisted.web import resource, server # noqa: E402 # An application of Twisted class Hello(resource.Resource): isLeaf = True def render_GET(self, request): return b'Hello from Twisted!' reactor.listenTCP(3000, server.Site(Hello())) # The user agent of Pyjoyment, which closes its connections at the end of the block with Pyjo.UserAgent.new() as ua: # A request every second, which does not block the reactor of Twisted; a request that the end of the block # interrupts gets an error def fetch(): def done(ua, tx): print(tx.error['message'] if tx.error else f'{tx.res.code} {tx.res.text}') ua.get('http://127.0.0.1:3000/', done) task.LoopingCall(fetch).start(1) reactor.run()
$ python examples/user_agent_twisted.py
200 Hello from Twisted!
200 Hello from Twisted!
200 Hello from Twisted!
examples/loopback_twisted.py is a server of Pyjoyment and its user agent on
the reactor of Twisted, a blocking request, which runs the event loop until
the response, and the class of the reactor:
import os # The reactor of Pyjoyment on the reactor of Twisted, selected before Pyjo.IOLoop is loaded os.environ['PYJO_REACTOR'] = 'Pyjo.Reactor.Twisted' import Pyjo.HelloWorld # noqa: E402 import Pyjo.IOLoop # noqa: E402 import Pyjo.Server.Daemon # noqa: E402 import Pyjo.UserAgent # noqa: E402 # The server, on a port that the system picks, and the user agent, in the same event loop, which close their sockets at # the end of the block with ( Pyjo.Server.Daemon.new(app=Pyjo.HelloWorld.new(), listen=['http://127.0.0.1'], silent=True) as daemon, Pyjo.UserAgent.new(ioloop=Pyjo.IOLoop.singleton) as ua, ): # A blocking request, which runs the event loop of the server on the reactor of Twisted until the response tx = ua.get(f'http://127.0.0.1:{daemon.start().ports()[0]}/') print(type(Pyjo.IOLoop.singleton.reactor).__name__) print(tx.result.code, tx.result.text)
$ python examples/loopback_twisted.py
Pyjo_Reactor_Twisted
200 Your Pyjo is working!
The first two call install() before they import
Twisted, which would install its default, epoll on Linux, in the place of poll
or select; the third does not import Twisted, so the reactor installs it.