Expand event listener tutorial

George had a great question today about catching event listener exceptions...

  https://lists.torproject.org/pipermail/tor-dev/2019-November/014092.html

Expanding our event listener tutorial to dive a bit deeper into this topic.
This commit is contained in:
Damian Johnson 2019-11-27 16:39:14 -08:00
parent bd5c8aaaa9
commit 9b8d769260
4 changed files with 91 additions and 0 deletions

15
docs/_static/example/broken_listener.py vendored Normal file
View File

@ -0,0 +1,15 @@
import time
from stem.control import EventType, Controller
def broken_handler(event):
print('start of broken_handler')
raise ValueError('boom')
print('end of broken_handler')
with Controller.from_port() as controller:
controller.authenticate()
controller.add_event_listener(broken_handler, EventType.BW)
time.sleep(2)

17
docs/_static/example/queue_listener.py vendored Normal file
View File

@ -0,0 +1,17 @@
import queue
import time
from stem.control import EventType, Controller
with Controller.from_port() as controller:
controller.authenticate()
start_time = time.time()
event_queue = queue.Queue()
controller.add_event_listener(lambda event: event_queue.put(event), EventType.BW)
while time.time() - start_time < 2:
event = event_queue.get()
print('I got a BW event for %i bytes downloaded and %i bytes uploaded' % (event.read, event.written))

16
docs/_static/example/slow_listener.py vendored Normal file
View File

@ -0,0 +1,16 @@
import time
from stem.control import EventType, Controller
with Controller.from_port() as controller:
def slow_handler(event):
age = time.time() - event.arrived_at
unprocessed_count = controller._event_queue.qsize()
print("processing a BW event that's %0.1f seconds old (%i more events are waiting)" % (age, unprocessed_count))
time.sleep(5)
controller.authenticate()
controller.add_event_listener(slow_handler, EventType.BW)
time.sleep(10)

View File

@ -32,3 +32,46 @@ uploaded.
:emphasize-lines: 53-55,62-67
:language: python
Advanced Listeners
------------------
When you attach a listener to a :class:`~stem.control.Controller` events are
processed within a dedicated thread. This is convenient for simple uses, but
can make troubleshooting your code confusing. For example, exceptions have
nowhere to propagate...
.. literalinclude:: /_static/example/broken_listener.py
:language: python
::
% python demo.py
start of broken_handler
start of broken_handler
start of broken_handler
... and processing events slower than they're received will make your listener
fall behind. This can result in a memory leak for long running processes...
.. literalinclude:: /_static/example/slow_listener.py
:language: python
::
% python demo.py
processing a BW event that's 0.9 seconds old (0 more events are waiting)
processing a BW event that's 4.9 seconds old (3 more events are waiting)
processing a BW event that's 8.9 seconds old (7 more events are waiting)
Avoid performing heavy business logic directly within listeners. For example, a
producer/consumer pattern sidesteps these issues...
.. literalinclude:: /_static/example/queue_listener.py
:language: python
::
% python demo.py
I got a BW event for 20634 bytes downloaded and 2686 bytes uploaded
I got a BW event for 0 bytes downloaded and 0 bytes uploaded
I got a BW event for 0 bytes downloaded and 0 bytes uploaded