[tor-commits] [nyx/master] Fetch manual information on demand

atagar at torproject.org atagar at torproject.org
Sun Aug 27 19:39:47 UTC 2017


commit 32b44dc3f9fcea7e5dd19a9cade9f2f947f32672
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Aug 26 15:32:51 2017 -0700

    Fetch manual information on demand
    
    Fetching just the manual information we need, when we need it. Rather than
    loading the whole manual this now retrieves information from the sqlite manual
    cache. This should mean slightly faster startup and reduced memory usage.
    However, this no longer parses the local man page (so information cannot be
    more up-to-date than stem's cache).
    
    We can re-introduce usage of local man pages later if this becomes something
    folks care about.
---
 nyx/panel/config.py  | 97 +++++++++++++++++++++++++++++++++-------------------
 test/panel/config.py |  7 ++--
 2 files changed, 64 insertions(+), 40 deletions(-)

diff --git a/nyx/panel/config.py b/nyx/panel/config.py
index 07a04b0..db04dbb 100644
--- a/nyx/panel/config.py
+++ b/nyx/panel/config.py
@@ -7,7 +7,6 @@ and the resulting configuration files saved.
 """
 
 import curses
-import os
 
 import nyx.curses
 import nyx.panel
@@ -17,9 +16,9 @@ import stem.control
 import stem.manual
 import stem.util.connection
 
+from nyx import tor_controller, input_prompt, show_message
 from nyx.curses import WHITE, NORMAL, BOLD, HIGHLIGHT
 from nyx.menu import MenuItem, Submenu
-from nyx import tor_controller, data_directory, input_prompt, show_message
 
 from stem.util import conf, enum, log, str_tools
 
@@ -50,14 +49,16 @@ class ConfigEntry(object):
 
   :var str name: name of the configuration option
   :var str value_type: type of value
-  :var stem.manual.ConfigOption manual: manual information about the option
   """
 
-  def __init__(self, name, value_type, manual):
+  def __init__(self, name, value_type):
     self.name = name
     self.value_type = value_type
-    self.manual = manual.config_options.get(name, stem.manual.ConfigOption(name))
-    self._index = list(manual.config_options.keys()).index(name) if name in manual.config_options else 99999
+    self._category = None
+    self._usage = None
+    self._summary = None
+    self._description = None
+    self._position = None
 
   def value(self):
     """
@@ -98,7 +99,7 @@ class ConfigEntry(object):
     """
 
     if attr == SortAttr.CATEGORY:
-      return self.manual.category
+      return self.category
     elif attr == SortAttr.NAME:
       return self.name
     elif attr == SortAttr.VALUE:
@@ -106,16 +107,59 @@ class ConfigEntry(object):
     elif attr == SortAttr.VALUE_TYPE:
       return self.value_type
     elif attr == SortAttr.USAGE:
-      return self.manual.usage
+      return self.usage
     elif attr == SortAttr.SUMMARY:
-      return self.manual.summary
+      return self.summary
     elif attr == SortAttr.DESCRIPTION:
-      return self.manual.description
+      return self.description
     elif attr == SortAttr.MAN_PAGE_ENTRY:
-      return self._index
+      return self.position
     elif attr == SortAttr.IS_SET:
       return not self.is_set()
 
+  @property
+  def category(self):
+    if not self._category:
+      self._fetch_attr()
+
+    return self._category
+
+  @property
+  def usage(self):
+    if not self._usage:
+      self._fetch_attr()
+
+    return self._usage
+
+  @property
+  def summary(self):
+    if not self._summary:
+      self._fetch_attr()
+
+    return self._summary
+
+  @property
+  def description(self):
+    if not self._description:
+      self._fetch_attr()
+
+    return self._description
+
+  @property
+  def position(self):
+    if not self._position:
+      self._fetch_attr()
+
+    return self._position
+
+  def _fetch_attr(self):
+    result = stem.manual.query('SELECT category, usage, summary, description, position FROM torrc WHERE key=?', self.name.upper()).fetchone()
+
+    if result:
+      self._category, self._usage, self._summary, self._description, self._position = result
+    else:
+      log.info("No manual information found for '%s'" % self.name)
+
 
 class ConfigPanel(nyx.panel.Panel):
   """
@@ -130,23 +174,6 @@ class ConfigPanel(nyx.panel.Panel):
     self._sort_order = CONFIG['config_order']
     self._show_all = False  # show all options, or just the important ones
 
-    cached_manual_path = data_directory('manual')
-
-    if cached_manual_path:
-      if os.path.exists(cached_manual_path):
-        manual = stem.manual.Manual.from_cache(cached_manual_path)
-      else:
-        try:
-          manual = stem.manual.Manual.from_man()
-
-          try:
-            manual.save(cached_manual_path)
-          except IOError as exc:
-            log.debug("Unable to cache manual information to '%s'. This is fine, but means starting Nyx takes a little longer than usual: " % (cached_manual_path, exc))
-        except IOError as exc:
-          log.debug("Unable to use 'man tor' to get information about config options (%s), using bundled information instead" % exc)
-          manual = stem.manual.Manual.from_cache()
-
     try:
       for line in tor_controller().get_info('config/names').splitlines():
         # Lines of the form "<option> <type>[ <documentation>]". Documentation
@@ -165,7 +192,7 @@ class ConfigPanel(nyx.panel.Panel):
         elif value_type == 'Virtual' and not CONFIG['show_virtual_options']:
           continue
 
-        self._contents.append(ConfigEntry(name, value_type, manual))
+        self._contents.append(ConfigEntry(name, value_type))
 
       self._contents = sorted(self._contents, key = lambda entry: [entry.sort_value(field) for field in self._sort_order])
     except stem.ControllerError as exc:
@@ -302,13 +329,13 @@ def _draw_line(subwindow, x, y, entry, is_selected, value_width, description_wid
   Show an individual configuration line.
   """
 
-  attr = [CONFIG['attr.config.category_color'].get(entry.manual.category, WHITE)]
+  attr = [CONFIG['attr.config.category_color'].get(entry.category, WHITE)]
   attr.append(BOLD if entry.is_set() else NORMAL)
   attr.append(HIGHLIGHT if is_selected else NORMAL)
 
   option_label = str_tools.crop(entry.name, NAME_WIDTH).ljust(NAME_WIDTH + 1)
   value_label = str_tools.crop(entry.value(), value_width).ljust(value_width + 1)
-  summary_label = str_tools.crop(entry.manual.summary, description_width).ljust(description_width)
+  summary_label = str_tools.crop(entry.summary, description_width).ljust(description_width)
 
   subwindow.addstr(x, y, option_label + value_label + summary_label, *attr)
 
@@ -318,14 +345,14 @@ def _draw_selection_details(subwindow, selected):
   Shows details of the currently selected option.
   """
 
-  attr = ', '.join(('custom' if selected.is_set() else 'default', selected.value_type, 'usage: %s' % selected.manual.usage))
-  selected_color = CONFIG['attr.config.category_color'].get(selected.manual.category, WHITE)
+  attr = ', '.join(('custom' if selected.is_set() else 'default', selected.value_type, 'usage: %s' % selected.usage))
+  selected_color = CONFIG['attr.config.category_color'].get(selected.category, WHITE)
   subwindow.box(0, 0, subwindow.width, DETAILS_HEIGHT)
 
-  subwindow.addstr(2, 1, '%s (%s Option)' % (selected.name, selected.manual.category), selected_color, BOLD)
+  subwindow.addstr(2, 1, '%s (%s Option)' % (selected.name, selected.category), selected_color, BOLD)
   subwindow.addstr(2, 2, 'Value: %s (%s)' % (selected.value(), str_tools.crop(attr, max(0, subwindow.width - len(selected.value()) - 13))), selected_color, BOLD)
 
-  description = 'Description: %s' % selected.manual.description
+  description = 'Description: %s' % selected.description
 
   for i in range(DETAILS_HEIGHT - 4):
     if not description:
diff --git a/test/panel/config.py b/test/panel/config.py
index 0289406..99bbaec 100644
--- a/test/panel/config.py
+++ b/test/panel/config.py
@@ -4,7 +4,6 @@ Unit tests for nyx.panel.config.
 
 import unittest
 
-import stem.manual
 import nyx.panel.config
 import test
 
@@ -32,8 +31,7 @@ class TestConfigPanel(unittest.TestCase):
     tor_controller_mock().get_info.return_value = True
     tor_controller_mock().get_conf.return_value = ['9051']
 
-    manual = stem.manual.Manual.from_cache()
-    entry = nyx.panel.config.ConfigEntry('ControlPort', 'LineList', manual)
+    entry = nyx.panel.config.ConfigEntry('ControlPort', 'LineList')
 
     rendered = test.render(nyx.panel.config._draw_line, 0, 0, entry, False, 10, 35)
     self.assertEqual(EXPECTED_LINE, rendered.content)
@@ -44,8 +42,7 @@ class TestConfigPanel(unittest.TestCase):
     tor_controller_mock().get_info.return_value = True
     tor_controller_mock().get_conf.return_value = ['9051']
 
-    manual = stem.manual.Manual.from_cache()
-    selected = nyx.panel.config.ConfigEntry('ControlPort', 'LineList', manual)
+    selected = nyx.panel.config.ConfigEntry('ControlPort', 'LineList')
 
     rendered = test.render(nyx.panel.config._draw_selection_details, selected)
     self.assertEqual(EXPECTED_DETAIL_DIALOG, rendered.content)





More information about the tor-commits mailing list