[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