commit 32b44dc3f9fcea7e5dd19a9cade9f2f947f32672
Author: Damian Johnson <atagar(a)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)