[tor-commits] [arm/master] Moving argument parsing to its own module

atagar at torproject.org atagar at torproject.org
Sun Dec 22 02:29:07 UTC 2013


commit d61e330a715fbf266f7c9471f1a5d35073480a65
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Dec 21 16:38:12 2013 -0800

    Moving argument parsing to its own module
    
    Our starter is way too large. Argument parsing is a good candidate for its own
    submodule.
---
 arm/arguments.py               |  117 ++++++++++++++++++++++++++++++++++++++++
 arm/starter.py                 |  104 ++++-------------------------------
 test/arguments.py              |   66 +++++++++++++++++++++++
 test/starter/arg_parsing.py    |   66 -----------------------
 test/starter/get_controller.py |   13 ++---
 5 files changed, 200 insertions(+), 166 deletions(-)

diff --git a/arm/arguments.py b/arm/arguments.py
new file mode 100644
index 0000000..4f796cc
--- /dev/null
+++ b/arm/arguments.py
@@ -0,0 +1,117 @@
+"""
+Commandline argument parsing for arm.
+"""
+
+import collections
+import getopt
+import os
+
+import arm.logPanel
+
+import stem.connection
+import stem.util.conf
+
+CONFIG = stem.util.conf.config_dict("arm", {
+  'attribute.debug_log_path': '',
+  'msg.help': '',
+})
+
+DEFAULT_ARGS = {
+  'control_address': '127.0.0.1',
+  'control_port': 9051,
+  'user_provided_port': False,
+  'control_socket': '/var/run/tor/control',
+  'user_provided_socket': False,
+  'config': os.path.expanduser("~/.arm/armrc"),
+  'debug': False,
+  'blind': False,
+  'logged_events': 'N3',
+  'print_version': False,
+  'print_help': False,
+}
+
+OPT = "i:s:c:dbe:vh"
+
+OPT_EXPANDED = [
+  "interface=",
+  "socket=",
+  "config=",
+  "debug",
+  "blind",
+  "event=",
+  "version",
+  "help",
+]
+
+
+def parse(argv):
+  """
+  Parses our arguments, providing a named tuple with their values.
+
+  :param list argv: input arguments to be parsed
+
+  :returns: a **named tuple** with our parsed arguments
+
+  :raises: **ValueError** if we got an invalid argument
+  :raises: **getopt.GetoptError** if the arguments don't conform with what we
+    accept
+  """
+
+  args = dict(DEFAULT_ARGS)
+
+  for opt, arg in getopt.getopt(argv, OPT, OPT_EXPANDED)[0]:
+    if opt in ("-i", "--interface"):
+      if ':' in arg:
+        address, port = arg.split(':', 1)
+      else:
+        address, port = None, arg
+
+      if address is not None:
+        if not stem.util.connection.is_valid_ipv4_address(address):
+          raise ValueError("'%s' isn't a valid IPv4 address" % address)
+
+        args['control_address'] = address
+
+      if not stem.util.connection.is_valid_port(port):
+        raise ValueError("'%s' isn't a valid port number" % port)
+
+      args['control_port'] = int(port)
+      args['user_provided_port'] = True
+    elif opt in ("-s", "--socket"):
+      args['control_socket'] = arg
+      args['user_provided_socket'] = True
+    elif opt in ("-c", "--config"):
+      args['config'] = arg
+    elif opt in ("-d", "--debug"):
+      args['debug'] = True
+    elif opt in ("-b", "--blind"):
+      args['blind'] = True
+    elif opt in ("-e", "--event"):
+      args['logged_events'] = arg
+    elif opt in ("-v", "--version"):
+      args['print_version'] = True
+    elif opt in ("-h", "--help"):
+      args['print_help'] = True
+
+  # translates our args dict into a named tuple
+
+  Args = collections.namedtuple('Args', args.keys())
+  return Args(**args)
+
+
+def get_help():
+  """
+  Provides our --help usage information.
+
+  :returns: **str** with our usage information
+  """
+
+  return CONFIG['msg.help'].format(
+    address = DEFAULT_ARGS['control_address'],
+    port = DEFAULT_ARGS['control_port'],
+    socket = DEFAULT_ARGS['control_socket'],
+    config = DEFAULT_ARGS['config'],
+    debug_path = CONFIG['attribute.debug_log_path'],
+    events = DEFAULT_ARGS['logged_events'],
+    event_flags = arm.logPanel.EVENT_LISTING,
+  )
diff --git a/arm/starter.py b/arm/starter.py
index e497c72..fd96236 100644
--- a/arm/starter.py
+++ b/arm/starter.py
@@ -4,7 +4,6 @@ information. This starts the applicatin, getting a tor connection and parsing
 arguments.
 """
 
-import collections
 import curses
 import getopt
 import getpass
@@ -17,6 +16,7 @@ import time
 import threading
 
 import arm
+import arm.arguments
 import arm.controller
 import arm.logPanel
 import arm.util.panel
@@ -33,13 +33,12 @@ import stem.util.connection
 import stem.util.log
 import stem.util.system
 
-LOG_DUMP_PATH = os.path.expanduser("~/.arm/log")
 SETTINGS_PATH = os.path.join(os.path.dirname(__file__), 'settings.cfg')
 
 CONFIG = stem.util.conf.config_dict("arm", {
+  'attribute.debug_log_path': '',
   'tor.password': None,
   'startup.events': 'N3',
-  'msg.help': '',
   'msg.debug_header': '',
   'msg.wrong_port_type': '',
   'msg.wrong_socket_type': '',
@@ -54,26 +53,6 @@ CONFIG = stem.util.conf.config_dict("arm", {
   'msg.unknown_term': '',
 })
 
-# Our default arguments. The _get_args() function provides a named tuple of
-# this merged with our argv.
-
-ARGS = {
-  'control_address': '127.0.0.1',
-  'control_port': 9051,
-  'user_provided_port': False,
-  'control_socket': '/var/run/tor/control',
-  'user_provided_socket': False,
-  'config': os.path.expanduser("~/.arm/armrc"),
-  'debug': False,
-  'blind': False,
-  'logged_events': 'N3',
-  'print_version': False,
-  'print_help': False,
-}
-
-OPT = "i:s:c:dbe:vh"
-OPT_EXPANDED = ["interface=", "socket=", "config=", "debug", "blind", "event=", "version", "help"]
-
 
 def _load_settings(config = 'arm'):
   """
@@ -99,61 +78,6 @@ def _load_settings(config = 'arm'):
   return config
 
 
-def _get_args(argv):
-  """
-  Parses our arguments, providing a named tuple with their values.
-
-  :param list argv: input arguments to be parsed
-
-  :returns: a **named tuple** with our parsed arguments
-
-  :raises: **ValueError** if we got an invalid argument
-  :raises: **getopt.GetoptError** if the arguments don't conform with what we
-    accept
-  """
-
-  args = dict(ARGS)
-
-  for opt, arg in getopt.getopt(argv, OPT, OPT_EXPANDED)[0]:
-    if opt in ("-i", "--interface"):
-      if ':' in arg:
-        address, port = arg.split(':', 1)
-      else:
-        address, port = None, arg
-
-      if address is not None:
-        if not stem.util.connection.is_valid_ipv4_address(address):
-          raise ValueError("'%s' isn't a valid IPv4 address" % address)
-
-        args['control_address'] = address
-
-      if not stem.util.connection.is_valid_port(port):
-        raise ValueError("'%s' isn't a valid port number" % port)
-
-      args['control_port'] = int(port)
-      args['user_provided_port'] = True
-    elif opt in ("-s", "--socket"):
-      args['control_socket'] = arg
-      args['user_provided_socket'] = True
-    elif opt in ("-c", "--config"):
-      args['config'] = arg
-    elif opt in ("-d", "--debug"):
-      args['debug'] = True
-    elif opt in ("-b", "--blind"):
-      args['blind'] = True
-    elif opt in ("-e", "--event"):
-      args['logged_events'] = arg
-    elif opt in ("-v", "--version"):
-      args['print_version'] = True
-    elif opt in ("-h", "--help"):
-      args['print_help'] = True
-
-  # translates our args dict into a named tuple
-
-  Args = collections.namedtuple('Args', args.keys())
-  return Args(**args)
-
-
 def _get_controller(args):
   """
   Provides a Controller for the endpoint specified in the given arguments.
@@ -225,17 +149,17 @@ def _authenticate(controller, password):
 
 def _setup_debug_logging():
   """
-  Configures us to log at stem's trace level to LOG_DUMP_PATH.
+  Configures us to log at stem's trace level to 'attribute.debug_log_path'.
 
   :raises: **IOError** if we can't log to this location
   """
 
-  debug_dir = os.path.dirname(LOG_DUMP_PATH)
+  debug_dir = os.path.dirname(CONFIG['attribute.debug_log_path'])
 
   if not os.path.exists(debug_dir):
     os.makedirs(debug_dir)
 
-  debug_handler = logging.FileHandler(LOG_DUMP_PATH, mode = 'w')
+  debug_handler = logging.FileHandler(CONFIG['attribute.debug_log_path'], mode = 'w')
   debug_handler.setLevel(stem.util.log.logging_level(stem.util.log.TRACE))
   debug_handler.setFormatter(logging.Formatter(
     fmt = '%(asctime)s [%(levelname)s] %(message)s',
@@ -285,10 +209,11 @@ def _shutdown_daemons():
 def main():
   config = stem.util.conf.get_config("arm")
   config.set('attribute.start_time', str(int(time.time())))
+  config.set('attribute.debug_log_path', os.path.expanduser("~/.arm/log"))
 
   try:
     _load_settings()
-    args = _get_args(sys.argv[1:])
+    args = arm.arguments.parse(sys.argv[1:])
   except getopt.GetoptError as exc:
     print "%s (for usage provide --help)" % exc
     sys.exit(1)
@@ -297,16 +222,7 @@ def main():
     sys.exit(1)
 
   if args.print_help:
-    print CONFIG['msg.help'].format(
-      address = ARGS['control_address'],
-      port = ARGS['control_port'],
-      socket = ARGS['control_socket'],
-      config = ARGS['config'],
-      debug_path = LOG_DUMP_PATH,
-      events = ARGS['logged_events'],
-      event_flags = arm.logPanel.EVENT_LISTING,
-    )
-
+    print arm.arguments.get_help()
     sys.exit()
 
   if args.print_version:
@@ -317,7 +233,7 @@ def main():
     try:
       _setup_debug_logging()
     except IOError as exc:
-      print "Unable to write to our debug log file (%s): %s" % (LOG_DUMP_PATH, exc.strerror)
+      print "Unable to write to our debug log file (%s): %s" % (CONFIG['attribute.debug_log_path'], exc.strerror)
       sys.exit(1)
 
     stem.util.log.trace(CONFIG['msg.debug_header'].format(
@@ -330,7 +246,7 @@ def main():
       armrc_content = _armrc_dump(args.config),
     ))
 
-    print "Saving a debug log to %s, please check it for sensitive information before sharing" % LOG_DUMP_PATH
+    print "Saving a debug log to %s, please check it for sensitive information before sharing" % CONFIG['attribute.debug_log_path']
 
   # loads user's personal armrc if available
 
diff --git a/test/arguments.py b/test/arguments.py
new file mode 100644
index 0000000..c7e1168
--- /dev/null
+++ b/test/arguments.py
@@ -0,0 +1,66 @@
+import getopt
+import unittest
+
+from arm.arguments import parse, DEFAULT_ARGS
+
+
+class TestArgumentParsing(unittest.TestCase):
+  def test_that_we_get_default_values(self):
+    args = parse([])
+
+    for attr in DEFAULT_ARGS:
+      self.assertEqual(DEFAULT_ARGS[attr], getattr(args, attr))
+
+  def test_that_we_load_arguments(self):
+    args = parse(['--interface', '10.0.0.25:80'])
+    self.assertEqual('10.0.0.25', args.control_address)
+    self.assertEqual(80, args.control_port)
+
+    args = parse(['--interface', '80'])
+    self.assertEqual(DEFAULT_ARGS['control_address'], args.control_address)
+    self.assertEqual(80, args.control_port)
+
+    args = parse(['--socket', '/tmp/my_socket', '--config', '/tmp/my_config'])
+    self.assertEqual('/tmp/my_socket', args.control_socket)
+    self.assertEqual('/tmp/my_config', args.config)
+
+    args = parse(['--debug', '--blind'])
+    self.assertEqual(True, args.debug)
+    self.assertEqual(True, args.blind)
+
+    args = parse(['--event', 'D1'])
+    self.assertEqual('D1', args.logged_events)
+
+    args = parse(['--version'])
+    self.assertEqual(True, args.print_version)
+
+    args = parse(['--help'])
+    self.assertEqual(True, args.print_help)
+
+  def test_examples(self):
+    args = parse(['-b', '-i', '1643'])
+    self.assertEqual(True, args.blind)
+    self.assertEqual(1643, args.control_port)
+
+    args = parse(['-e', 'we', '-c', '/tmp/cfg'])
+    self.assertEqual('we', args.logged_events)
+    self.assertEqual('/tmp/cfg', args.config)
+
+  def test_that_we_reject_unrecognized_arguments(self):
+    self.assertRaises(getopt.GetoptError, parse, ['--blarg', 'stuff'])
+
+  def test_that_we_reject_invalid_interfaces(self):
+    invalid_inputs = (
+      '',
+      '    ',
+      'blarg',
+      '127.0.0.1',
+      '127.0.0.1:',
+      ':80',
+      '400.0.0.1:80',
+      '127.0.0.1:-5',
+      '127.0.0.1:500000',
+    )
+
+    for invalid_input in invalid_inputs:
+      self.assertRaises(ValueError, parse, ['--interface', invalid_input])
diff --git a/test/starter/arg_parsing.py b/test/starter/arg_parsing.py
deleted file mode 100644
index f6c60f5..0000000
--- a/test/starter/arg_parsing.py
+++ /dev/null
@@ -1,66 +0,0 @@
-import getopt
-import unittest
-
-from arm.starter import _get_args, ARGS
-
-
-class TestArgumentParsing(unittest.TestCase):
-  def test_that_we_get_default_values(self):
-    args = _get_args([])
-
-    for attr in ARGS:
-      self.assertEqual(ARGS[attr], getattr(args, attr))
-
-  def test_that_we_load_arguments(self):
-    args = _get_args(['--interface', '10.0.0.25:80'])
-    self.assertEqual('10.0.0.25', args.control_address)
-    self.assertEqual(80, args.control_port)
-
-    args = _get_args(['--interface', '80'])
-    self.assertEqual(ARGS['control_address'], args.control_address)
-    self.assertEqual(80, args.control_port)
-
-    args = _get_args(['--socket', '/tmp/my_socket', '--config', '/tmp/my_config'])
-    self.assertEqual('/tmp/my_socket', args.control_socket)
-    self.assertEqual('/tmp/my_config', args.config)
-
-    args = _get_args(['--debug', '--blind'])
-    self.assertEqual(True, args.debug)
-    self.assertEqual(True, args.blind)
-
-    args = _get_args(['--event', 'D1'])
-    self.assertEqual('D1', args.logged_events)
-
-    args = _get_args(['--version'])
-    self.assertEqual(True, args.print_version)
-
-    args = _get_args(['--help'])
-    self.assertEqual(True, args.print_help)
-
-  def test_examples(self):
-    args = _get_args(['-b', '-i', '1643'])
-    self.assertEqual(True, args.blind)
-    self.assertEqual(1643, args.control_port)
-
-    args = _get_args(['-e', 'we', '-c', '/tmp/cfg'])
-    self.assertEqual('we', args.logged_events)
-    self.assertEqual('/tmp/cfg', args.config)
-
-  def test_that_we_reject_unrecognized_arguments(self):
-    self.assertRaises(getopt.GetoptError, _get_args, ['--blarg', 'stuff'])
-
-  def test_that_we_reject_invalid_interfaces(self):
-    invalid_inputs = (
-      '',
-      '    ',
-      'blarg',
-      '127.0.0.1',
-      '127.0.0.1:',
-      ':80',
-      '400.0.0.1:80',
-      '127.0.0.1:-5',
-      '127.0.0.1:500000',
-    )
-
-    for invalid_input in invalid_inputs:
-      self.assertRaises(ValueError, _get_args, ['--interface', invalid_input])
diff --git a/test/starter/get_controller.py b/test/starter/get_controller.py
index 974b4b5..dcd5f0c 100644
--- a/test/starter/get_controller.py
+++ b/test/starter/get_controller.py
@@ -2,7 +2,8 @@ import unittest
 
 from mock import Mock, patch
 
-from arm.starter import _get_args, _get_controller
+from arm.arguments import parse
+from arm.starter import _get_controller
 
 import stem
 import stem.connection
@@ -39,11 +40,11 @@ class TestGetController(unittest.TestCase):
   def test_getting_a_control_port(self, from_port_mock):
     from_port_mock.return_value = 'success'
 
-    self.assertEqual('success', _get_controller(_get_args([])))
+    self.assertEqual('success', _get_controller(parse([])))
     from_port_mock.assert_called_once_with('127.0.0.1', 9051)
     from_port_mock.reset_mock()
 
-    self.assertEqual('success', _get_controller(_get_args(['--interface', '255.0.0.10:80'])))
+    self.assertEqual('success', _get_controller(parse(['--interface', '255.0.0.10:80'])))
     from_port_mock.assert_called_once_with('255.0.0.10', 80)
 
   @patch('os.path.exists', Mock(return_value = True))
@@ -51,16 +52,16 @@ class TestGetController(unittest.TestCase):
   def test_getting_a_control_socket(self, from_socket_file_mock):
     from_socket_file_mock.return_value = 'success'
 
-    self.assertEqual('success', _get_controller(_get_args([])))
+    self.assertEqual('success', _get_controller(parse([])))
     from_socket_file_mock.assert_called_once_with('/var/run/tor/control')
     from_socket_file_mock.reset_mock()
 
-    self.assertEqual('success', _get_controller(_get_args(['--socket', '/tmp/my_socket'])))
+    self.assertEqual('success', _get_controller(parse(['--socket', '/tmp/my_socket'])))
     from_socket_file_mock.assert_called_once_with('/tmp/my_socket')
 
   def _assert_get_controller_fails_with(self, args, msg):
     try:
-      _get_controller(_get_args(args))
+      _get_controller(parse(args))
       self.fail()
     except ValueError, exc:
       self.assertEqual(msg, str(exc))





More information about the tor-commits mailing list