[tor-commits] [stem/master] Added downloadable tutorial for tutorial Tortoise and the Hare
atagar at torproject.org
atagar at torproject.org
Mon May 25 17:15:20 UTC 2015
commit a20733062c11b5d68613ec0c17518fb16229bd0f
Author: Sambuddha Basu <sambuddhabasu1 at gmail.com>
Date: Mon May 25 07:34:11 2015 +0400
Added downloadable tutorial for tutorial Tortoise and the Hare
---
docs/_static/example/event_listening.py | 177 ++++++++++++++++++++++++++++
docs/tutorials/tortoise_and_the_hare.rst | 184 +-----------------------------
2 files changed, 181 insertions(+), 180 deletions(-)
diff --git a/docs/_static/example/event_listening.py b/docs/_static/example/event_listening.py
new file mode 100644
index 0000000..2e6f026
--- /dev/null
+++ b/docs/_static/example/event_listening.py
@@ -0,0 +1,177 @@
+import curses
+import functools
+
+from stem.control import EventType, Controller
+from stem.util import str_tools
+
+# colors that curses can handle
+
+COLOR_LIST = {
+ "red": curses.COLOR_RED,
+ "green": curses.COLOR_GREEN,
+ "yellow": curses.COLOR_YELLOW,
+ "blue": curses.COLOR_BLUE,
+ "cyan": curses.COLOR_CYAN,
+ "magenta": curses.COLOR_MAGENTA,
+ "black": curses.COLOR_BLACK,
+ "white": curses.COLOR_WHITE,
+}
+
+GRAPH_WIDTH = 40
+GRAPH_HEIGHT = 8
+
+DOWNLOAD_COLOR = "green"
+UPLOAD_COLOR = "blue"
+
+def main():
+ with Controller.from_port(port = 9051) as controller:
+ controller.authenticate()
+
+ try:
+ # This makes curses initialize and call draw_bandwidth_graph() with a
+ # reference to the screen, followed by additional arguments (in this
+ # case just the controller).
+
+ curses.wrapper(draw_bandwidth_graph, controller)
+ except KeyboardInterrupt:
+ pass # the user hit ctrl+c
+
+def draw_bandwidth_graph(stdscr, controller):
+ window = Window(stdscr)
+
+ # (downloaded, uploaded) tuples for the last 40 seconds
+
+ bandwidth_rates = [(0, 0)] * GRAPH_WIDTH
+
+ # Making a partial that wraps the window and bandwidth_rates with a function
+ # for Tor to call when it gets a BW event. This causes the 'window' and
+ # 'bandwidth_rates' to be provided as the first two arguments whenever
+ # 'bw_event_handler()' is called.
+
+ bw_event_handler = functools.partial(_handle_bandwidth_event, window, bandwidth_rates)
+
+ # Registering this listener with Tor. Tor reports a BW event each second.
+
+ controller.add_event_listener(bw_event_handler, EventType.BW)
+
+ # Pause the main thread until the user hits any key... and no, don't you dare
+ # ask where the 'any' key is. :P
+
+ stdscr.getch()
+
+def _handle_bandwidth_event(window, bandwidth_rates, event):
+ # callback for when tor provides us with a BW event
+
+ bandwidth_rates.insert(0, (event.read, event.written))
+ bandwidth_rates = bandwidth_rates[:GRAPH_WIDTH] # truncate old values
+ _render_graph(window, bandwidth_rates)
+
+def _render_graph(window, bandwidth_rates):
+ window.erase()
+
+ download_rates = [entry[0] for entry in bandwidth_rates]
+ upload_rates = [entry[1] for entry in bandwidth_rates]
+
+ # show the latest values at the top
+
+ label = "Downloaded (%s/s):" % str_tools.size_label(download_rates[0], 1)
+ window.addstr(0, 1, label, DOWNLOAD_COLOR, curses.A_BOLD)
+
+ label = "Uploaded (%s/s):" % str_tools.size_label(upload_rates[0], 1)
+ window.addstr(0, GRAPH_WIDTH + 7, label, UPLOAD_COLOR, curses.A_BOLD)
+
+ # draw the graph bounds in KB
+
+ max_download_rate = max(download_rates)
+ max_upload_rate = max(upload_rates)
+
+ window.addstr(1, 1, "%4i" % (max_download_rate / 1024), DOWNLOAD_COLOR)
+ window.addstr(GRAPH_HEIGHT, 1, " 0", DOWNLOAD_COLOR)
+
+ window.addstr(1, GRAPH_WIDTH + 7, "%4i" % (max_upload_rate / 1024), UPLOAD_COLOR)
+ window.addstr(GRAPH_HEIGHT, GRAPH_WIDTH + 7, " 0", UPLOAD_COLOR)
+
+ # draw the graph
+
+ for col in xrange(GRAPH_WIDTH):
+ col_height = GRAPH_HEIGHT * download_rates[col] / max(max_download_rate, 1)
+
+ for row in xrange(col_height):
+ window.addstr(GRAPH_HEIGHT - row, col + 6, " ", DOWNLOAD_COLOR, curses.A_STANDOUT)
+
+ col_height = GRAPH_HEIGHT * upload_rates[col] / max(max_upload_rate, 1)
+
+ for row in xrange(col_height):
+ window.addstr(GRAPH_HEIGHT - row, col + GRAPH_WIDTH + 12, " ", UPLOAD_COLOR, curses.A_STANDOUT)
+
+ window.refresh()
+
+class Window(object):
+ """
+ Simple wrapper for the curses standard screen object.
+ """
+
+ def __init__(self, stdscr):
+ self._stdscr = stdscr
+
+ # Mappings of names to the curses color attribute. Initially these all
+ # reference black text, but if the terminal can handle color then
+ # they're set with that foreground color.
+
+ self._colors = dict([(color, 0) for color in COLOR_LIST])
+
+ # allows for background transparency
+
+ try:
+ curses.use_default_colors()
+ except curses.error:
+ pass
+
+ # makes the cursor invisible
+
+ try:
+ curses.curs_set(0)
+ except curses.error:
+ pass
+
+ # initializes colors if the terminal can handle them
+
+ try:
+ if curses.has_colors():
+ color_pair = 1
+
+ for name, foreground in COLOR_LIST.items():
+ background = -1 # allows for default (possibly transparent) background
+ curses.init_pair(color_pair, foreground, background)
+ self._colors[name] = curses.color_pair(color_pair)
+ color_pair += 1
+ except curses.error:
+ pass
+
+ def addstr(self, y, x, msg, color = None, attr = curses.A_NORMAL):
+ # Curses throws an error if we try to draw a message that spans out of the
+ # window's bounds (... seriously?), so doing our best to avoid that.
+
+ if color is not None:
+ if color not in self._colors:
+ recognized_colors = ", ".join(self._colors.keys())
+ raise ValueError("The '%s' color isn't recognized: %s" % (color, recognized_colors))
+
+ attr |= self._colors[color]
+
+ max_y, max_x = self._stdscr.getmaxyx()
+
+ if max_x > x and max_y > y:
+ try:
+ self._stdscr.addstr(y, x, msg[:max_x - x], attr)
+ except:
+ pass # maybe an edge case while resizing the window
+
+ def erase(self):
+ self._stdscr.erase()
+
+ def refresh(self):
+ self._stdscr.refresh()
+
+if __name__ == '__main__':
+ main()
diff --git a/docs/tutorials/tortoise_and_the_hare.rst b/docs/tutorials/tortoise_and_the_hare.rst
index d3f759e..413e149 100644
--- a/docs/tutorials/tortoise_and_the_hare.rst
+++ b/docs/tutorials/tortoise_and_the_hare.rst
@@ -28,184 +28,8 @@ To do this it listens to **BW events**
are events that Tor emits each second saying the number of bytes downloaded and
uploaded.
-.. code-block:: python
- :emphasize-lines: 53-55,62-67
-
- import curses
- import functools
-
- from stem.control import EventType, Controller
- from stem.util import str_tools
-
- # colors that curses can handle
-
- COLOR_LIST = {
- "red": curses.COLOR_RED,
- "green": curses.COLOR_GREEN,
- "yellow": curses.COLOR_YELLOW,
- "blue": curses.COLOR_BLUE,
- "cyan": curses.COLOR_CYAN,
- "magenta": curses.COLOR_MAGENTA,
- "black": curses.COLOR_BLACK,
- "white": curses.COLOR_WHITE,
- }
-
- GRAPH_WIDTH = 40
- GRAPH_HEIGHT = 8
-
- DOWNLOAD_COLOR = "green"
- UPLOAD_COLOR = "blue"
-
- def main():
- with Controller.from_port(port = 9051) as controller:
- controller.authenticate()
-
- try:
- # This makes curses initialize and call draw_bandwidth_graph() with a
- # reference to the screen, followed by additional arguments (in this
- # case just the controller).
-
- curses.wrapper(draw_bandwidth_graph, controller)
- except KeyboardInterrupt:
- pass # the user hit ctrl+c
-
- def draw_bandwidth_graph(stdscr, controller):
- window = Window(stdscr)
-
- # (downloaded, uploaded) tuples for the last 40 seconds
-
- bandwidth_rates = [(0, 0)] * GRAPH_WIDTH
-
- # Making a partial that wraps the window and bandwidth_rates with a function
- # for Tor to call when it gets a BW event. This causes the 'window' and
- # 'bandwidth_rates' to be provided as the first two arguments whenever
- # 'bw_event_handler()' is called.
-
- bw_event_handler = functools.partial(_handle_bandwidth_event, window, bandwidth_rates)
-
- # Registering this listener with Tor. Tor reports a BW event each second.
-
- controller.add_event_listener(bw_event_handler, EventType.BW)
-
- # Pause the main thread until the user hits any key... and no, don't you dare
- # ask where the 'any' key is. :P
-
- stdscr.getch()
-
- def _handle_bandwidth_event(window, bandwidth_rates, event):
- # callback for when tor provides us with a BW event
-
- bandwidth_rates.insert(0, (event.read, event.written))
- bandwidth_rates = bandwidth_rates[:GRAPH_WIDTH] # truncate old values
- _render_graph(window, bandwidth_rates)
-
- def _render_graph(window, bandwidth_rates):
- window.erase()
-
- download_rates = [entry[0] for entry in bandwidth_rates]
- upload_rates = [entry[1] for entry in bandwidth_rates]
-
- # show the latest values at the top
-
- label = "Downloaded (%s/s):" % str_tools.size_label(download_rates[0], 1)
- window.addstr(0, 1, label, DOWNLOAD_COLOR, curses.A_BOLD)
-
- label = "Uploaded (%s/s):" % str_tools.size_label(upload_rates[0], 1)
- window.addstr(0, GRAPH_WIDTH + 7, label, UPLOAD_COLOR, curses.A_BOLD)
-
- # draw the graph bounds in KB
-
- max_download_rate = max(download_rates)
- max_upload_rate = max(upload_rates)
-
- window.addstr(1, 1, "%4i" % (max_download_rate / 1024), DOWNLOAD_COLOR)
- window.addstr(GRAPH_HEIGHT, 1, " 0", DOWNLOAD_COLOR)
-
- window.addstr(1, GRAPH_WIDTH + 7, "%4i" % (max_upload_rate / 1024), UPLOAD_COLOR)
- window.addstr(GRAPH_HEIGHT, GRAPH_WIDTH + 7, " 0", UPLOAD_COLOR)
-
- # draw the graph
-
- for col in xrange(GRAPH_WIDTH):
- col_height = GRAPH_HEIGHT * download_rates[col] / max(max_download_rate, 1)
-
- for row in xrange(col_height):
- window.addstr(GRAPH_HEIGHT - row, col + 6, " ", DOWNLOAD_COLOR, curses.A_STANDOUT)
-
- col_height = GRAPH_HEIGHT * upload_rates[col] / max(max_upload_rate, 1)
-
- for row in xrange(col_height):
- window.addstr(GRAPH_HEIGHT - row, col + GRAPH_WIDTH + 12, " ", UPLOAD_COLOR, curses.A_STANDOUT)
-
- window.refresh()
-
- class Window(object):
- """
- Simple wrapper for the curses standard screen object.
- """
-
- def __init__(self, stdscr):
- self._stdscr = stdscr
-
- # Mappings of names to the curses color attribute. Initially these all
- # reference black text, but if the terminal can handle color then
- # they're set with that foreground color.
-
- self._colors = dict([(color, 0) for color in COLOR_LIST])
-
- # allows for background transparency
-
- try:
- curses.use_default_colors()
- except curses.error:
- pass
-
- # makes the cursor invisible
-
- try:
- curses.curs_set(0)
- except curses.error:
- pass
-
- # initializes colors if the terminal can handle them
-
- try:
- if curses.has_colors():
- color_pair = 1
-
- for name, foreground in COLOR_LIST.items():
- background = -1 # allows for default (possibly transparent) background
- curses.init_pair(color_pair, foreground, background)
- self._colors[name] = curses.color_pair(color_pair)
- color_pair += 1
- except curses.error:
- pass
-
- def addstr(self, y, x, msg, color = None, attr = curses.A_NORMAL):
- # Curses throws an error if we try to draw a message that spans out of the
- # window's bounds (... seriously?), so doing our best to avoid that.
-
- if color is not None:
- if color not in self._colors:
- recognized_colors = ", ".join(self._colors.keys())
- raise ValueError("The '%s' color isn't recognized: %s" % (color, recognized_colors))
-
- attr |= self._colors[color]
-
- max_y, max_x = self._stdscr.getmaxyx()
-
- if max_x > x and max_y > y:
- try:
- self._stdscr.addstr(y, x, msg[:max_x - x], attr)
- except:
- pass # maybe an edge case while resizing the window
-
- def erase(self):
- self._stdscr.erase()
-
- def refresh(self):
- self._stdscr.refresh()
-
- if __name__ == '__main__':
- main()
+.. literalinclude:: /_static/example/event_listening.py
+ :caption: `[Download] <../_static/example/event_listening.py>`__
+ :emphasize-lines: 53-55,62-67
+ :language: python
More information about the tor-commits
mailing list