File: tortoise_and_the_hare.rst

package info (click to toggle)
python-stem 1.8.0-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 7,892 kB
  • sloc: python: 33,713; java: 312; makefile: 131; sh: 27
file content (77 lines) | stat: -rw-r--r-- 2,736 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
Tortoise and the Hare
=====================

Controllers have two methods of talking with Tor...

* **Synchronous** - Most commonly you make a request to Tor then receive its
  reply. The :func:`~stem.control.Controller.get_info` calls in the `first
  tutorial <the_little_relay_that_could.html>`_ are an example of this.

* **Asynchronous** - Controllers can subscribe to be notified when various
  kinds of events occur within Tor (see the :data:`~stem.control.EventType`).
  Stem's users provide a callback function to
  :func:`~stem.control.Controller.add_event_listener` which is then notified
  when the event occurs.

Try to avoid lengthy operations within event callbacks. They're notified by a
single dedicated event thread, and blocking this thread will prevent the
delivery of further events.

With that out of the way lets see an example. The following is a `curses
<https://docs.python.org/2/howto/curses.html>`_ application that graphs the
bandwidth usage of Tor...

.. image:: /_static/bandwidth_graph_output.png

To do this it listens to **BW events**
(the class for which is a :class:`~stem.response.events.BandwidthEvent`). These
are events that Tor emits each second saying the number of bytes downloaded and
uploaded.

.. literalinclude:: /_static/example/event_listening.py
   :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