[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Lazy fetching for help responses
commit 267ea3a7815ed39a8360c088b7ff6bdd20e79b84
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sat May 3 12:51:04 2014 -0700
Lazy fetching for help responses
Our prior commit changed /help to be eagerly feched and chached. Caching for
this is good, but cached lazy fetches are even better. No reason to make a
handful of GETINFO requests at startup if they won't be needed.
---
stem/interpretor/commands.py | 172 ++++++++++++++++++++++--------------------
1 file changed, 90 insertions(+), 82 deletions(-)
diff --git a/stem/interpretor/commands.py b/stem/interpretor/commands.py
index f43e0d1..a6d1894 100644
--- a/stem/interpretor/commands.py
+++ b/stem/interpretor/commands.py
@@ -15,100 +15,127 @@ OUTPUT_FORMAT = (Color.BLUE, )
BOLD_OUTPUT_FORMAT = (Color.BLUE, Attr.BOLD)
ERROR_FORMAT = (Attr.BOLD, Color.RED)
+try:
+ # added in python 3.2
+ from functools import lru_cache
+except ImportError:
+ from stem.util.lru_cache import lru_cache
+
@uses_settings
-def help_output(controller, config):
+def help_output(controller, arg, config):
"""
- Provides the output for our /help commands.
+ Provides our /help response.
:param stem.Controller controller: tor control connection
+ :param str arg: controller or interpretor command to provide help output for
:param stem.util.conf.Config config: interpretor configuration
- :returns: **dict** mapping arguments to their help output
+ :returns: **str** with our help response
"""
- result = {}
- usage_info = config.get('help.usage', {})
+ # Normalizing inputs first so we can better cache responses.
- general_help = ''
+ arg = arg.upper()
- for line in msg('help.general').splitlines():
- cmd_start = line.find(' - ')
+ # If there's multiple arguments then just take the first. This is
+ # particularly likely if they're trying to query a full command (for
+ # instance "/help GETINFO version")
- if cmd_start != -1:
- general_help += format(line[:cmd_start], *BOLD_OUTPUT_FORMAT)
- general_help += format(line[cmd_start:] + '\n', *OUTPUT_FORMAT)
- else:
- general_help += format(line + '\n', *BOLD_OUTPUT_FORMAT)
+ arg = arg.split(' ')[0]
+
+ # strip slash if someone enters an interpretor command (ex. "/help /help")
+
+ if arg.startswith('/'):
+ arg = arg[1:]
- result[''] = general_help
+ return _help_output(controller, arg, config)
- for arg, usage in usage_info.items():
- description = config.get('help.description.%s' % arg.lower(), '')
- output = format(usage + '\n', *BOLD_OUTPUT_FORMAT)
+@lru_cache()
+def _help_output(controller, arg, config):
+ if not arg:
+ general_help = ''
- for line in description.splitlines():
- output += format(' ' + line + '\n', *OUTPUT_FORMAT)
+ for line in msg('help.general').splitlines():
+ cmd_start = line.find(' - ')
+
+ if cmd_start != -1:
+ general_help += format(line[:cmd_start], *BOLD_OUTPUT_FORMAT)
+ general_help += format(line[cmd_start:] + '\n', *OUTPUT_FORMAT)
+ else:
+ general_help += format(line + '\n', *BOLD_OUTPUT_FORMAT)
+
+ return general_help
+
+ usage_info = config.get('help.usage', {})
- output += '\n'
+ if not arg in usage_info:
+ return format("No help information available for '%s'..." % arg, *ERROR_FORMAT)
- if arg == 'GETINFO':
- results = controller.get_info('info/names', None)
+ output = format(usage_info[arg] + '\n', *BOLD_OUTPUT_FORMAT)
- if results:
- for line in results.splitlines():
- if ' -- ' in line:
- opt, summary = line.split(' -- ', 1)
+ description = config.get('help.description.%s' % arg.lower(), '')
- output += format("%-33s" % opt, *BOLD_OUTPUT_FORMAT)
- output += format(" - %s\n" % summary, *OUTPUT_FORMAT)
- elif arg == 'GETCONF':
- results = controller.get_info('config/names', None)
+ for line in description.splitlines():
+ output += format(' ' + line + '\n', *OUTPUT_FORMAT)
- if results:
- options = [opt.split(' ', 1)[0] for opt in results.splitlines()]
+ output += '\n'
- for i in range(0, len(options), 2):
- line = ''
+ if arg == 'GETINFO':
+ results = controller.get_info('info/names', None)
- for entry in options[i:i + 1]:
- line += '%-42s' % entry
+ if results:
+ for line in results.splitlines():
+ if ' -- ' in line:
+ opt, summary = line.split(' -- ', 1)
- output += format(line + '\n', *OUTPUT_FORMAT)
- elif arg == 'SIGNAL':
- signal_options = config.get('help.signal.options', {})
+ output += format("%-33s" % opt, *BOLD_OUTPUT_FORMAT)
+ output += format(" - %s\n" % summary, *OUTPUT_FORMAT)
+ elif arg == 'GETCONF':
+ results = controller.get_info('config/names', None)
- for signal, summary in signal_options.items():
- output += format('%-15s' % signal, *BOLD_OUTPUT_FORMAT)
- output += format(' - %s\n' % summary, *OUTPUT_FORMAT)
- elif arg == 'SETEVENTS':
- results = controller.get_info('events/names', None)
+ if results:
+ options = [opt.split(' ', 1)[0] for opt in results.splitlines()]
- if results:
- entries = results.split()
+ for i in range(0, len(options), 2):
+ line = ''
- # displays four columns of 20 characters
+ for entry in options[i:i + 1]:
+ line += '%-42s' % entry
- for i in range(0, len(entries), 4):
- line = ''
+ output += format(line + '\n', *OUTPUT_FORMAT)
+ elif arg == 'SIGNAL':
+ signal_options = config.get('help.signal.options', {})
- for entry in entries[i:i + 4]:
- line += '%-20s' % entry
+ for signal, summary in signal_options.items():
+ output += format('%-15s' % signal, *BOLD_OUTPUT_FORMAT)
+ output += format(' - %s\n' % summary, *OUTPUT_FORMAT)
+ elif arg == 'SETEVENTS':
+ results = controller.get_info('events/names', None)
- output += format(line + '\n', *OUTPUT_FORMAT)
- elif arg == 'USEFEATURE':
- results = controller.get_info('features/names', None)
+ if results:
+ entries = results.split()
- if results:
- output += format(results + '\n', *OUTPUT_FORMAT)
- elif arg in ('LOADCONF', 'POSTDESCRIPTOR'):
- # gives a warning that this option isn't yet implemented
- output += format(msg('msg.multiline_unimplemented_notice') + '\n', *ERROR_FORMAT)
+ # displays four columns of 20 characters
- result[arg] = output
+ for i in range(0, len(entries), 4):
+ line = ''
- return result
+ for entry in entries[i:i + 4]:
+ line += '%-20s' % entry
+
+ output += format(line + '\n', *OUTPUT_FORMAT)
+ elif arg == 'USEFEATURE':
+ results = controller.get_info('features/names', None)
+
+ if results:
+ output += format(results + '\n', *OUTPUT_FORMAT)
+ elif arg in ('LOADCONF', 'POSTDESCRIPTOR'):
+ # gives a warning that this option isn't yet implemented
+ output += format(msg('msg.multiline_unimplemented_notice') + '\n', *ERROR_FORMAT)
+
+ return output
class ControlInterpretor(object):
@@ -120,7 +147,6 @@ class ControlInterpretor(object):
def __init__(self, controller):
self._controller = controller
self._received_events = []
- self._help_output = help_output(controller)
def register_event(self, event):
"""
@@ -129,31 +155,13 @@ class ControlInterpretor(object):
self._received_events.append(event)
- @uses_settings
- def do_help(self, arg, config):
+ def do_help(self, arg):
"""
Performs the '/help' operation, giving usage information for the given
argument or a general summary if there wasn't one.
"""
- arg = arg.upper()
- usage_info = config.get('help.usage', {})
-
- # If there's multiple arguments then just take the first. This is
- # particularly likely if they're trying to query a full command (for
- # instance "/help GETINFO version")
-
- arg = arg.split(' ')[0]
-
- # strip slash if someone enters an interpretor command (ex. "/help /help")
-
- if arg.startswith('/'):
- arg = arg[1:]
-
- if arg in self._help_output:
- return self._help_output[arg]
- else:
- return format("No help information available for '%s'..." % arg, *ERROR_FORMAT)
+ return help_output(self._controller, arg)
def do_events(self, arg):
"""
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits