def __init__(self):
super(_Options, self).__init__()
self.__dict__['_parse_callbacks'] = []
+ self.define("help", type=bool, help="show this help information",
+ callback=self._help_callback)
def __getattr__(self, name):
if isinstance(self.get(name), _Option):
raise AttributeError("Unrecognized option %r" % name)
def define(self, name, default=None, type=None, help=None, metavar=None,
- multiple=False, group=None):
+ multiple=False, group=None, callback=None):
if name in self:
raise Error("Option %r already defined in %s", name,
self[name].file_name)
group_name = file_name
self[name] = _Option(name, file_name=file_name, default=default,
type=type, help=help, metavar=metavar,
- multiple=multiple, group_name=group_name)
+ multiple=multiple, group_name=group_name,
+ callback=callback)
def parse_command_line(self, args=None, final=True):
if args is None:
else:
raise Error('Option %r requires a value' % name)
option.parse(value)
- if self.help:
- print_help()
- sys.exit(0)
if final:
self.run_parse_callbacks()
if final:
self.run_parse_callbacks()
- def print_help(self, file=sys.stdout):
- """Prints all the command line options to stdout."""
+ def print_help(self, file=None):
+ """Prints all the command line options to stderr (or another file)."""
+ if file is None:
+ file = sys.stderr
print >> file, "Usage: %s [OPTIONS]" % sys.argv[0]
print >> file, "\nOptions:\n"
by_group = {}
print >> file, "%-34s %s" % (' ', line)
print >> file
+ def _help_callback(self, value):
+ if value:
+ self.print_help()
+ sys.exit(0)
+
def add_parse_callback(self, callback):
self._parse_callbacks.append(stack_context.wrap(callback))
class _Option(object):
- def __init__(self, name, default=None, type=basestring, help=None, metavar=None,
- multiple=False, file_name=None, group_name=None):
+ def __init__(self, name, default=None, type=basestring, help=None,
+ metavar=None, multiple=False, file_name=None, group_name=None,
+ callback=None):
if default is None and multiple:
default = []
self.name = name
self.multiple = multiple
self.file_name = file_name
self.group_name = group_name
+ self.callback = callback
self.default = default
self._value = None
self._value.append(_parse(part))
else:
self._value = _parse(value)
+ if self.callback is not None:
+ self.callback(self._value)
return self.value()
def set(self, value):
raise Error("Option %r is required to be a %s (%s given)" %
(self.name, self.type.__name__, type(value)))
self._value = value
+ if self.callback is not None:
+ self.callback(self._value)
# Supported date/time formats in our options
_DATETIME_FORMATS = [
def define(name, default=None, type=None, help=None, metavar=None,
- multiple=False, group=None):
+ multiple=False, group=None, callback=None):
"""Defines a new command line option.
If type is given (one of str, float, int, datetime, or timedelta)
Command line option names must be unique globally. They can be parsed
from the command line with parse_command_line() or parsed from a
config file with parse_config_file.
+
+ If a callback is given, it will be run with the new value whenever
+ the option is changed. This can be used to combine command-line
+ and file-based options::
+
+ define("config", type=str, help="path to config file",
+ callback=lambda path: parse_config_file(path, final=False))
+
+ With this definition, options in the file specified by ``--config`` will
+ override options set earlier on the command line, but can be overridden
+ by later flags.
"""
return options.define(name, default=default, type=type, help=help,
- metavar=metavar, multiple=multiple, group=group)
+ metavar=metavar, multiple=multiple, group=group,
+ callback=callback)
def parse_command_line(args=None, final=True):
# Default options
-define("help", type=bool, help="show this help information")
define_logging_options(options)
from __future__ import absolute_import, division, with_statement
+import sys
+
from tornado.options import _Options
from tornado.test.util import unittest
+try:
+ from cStringIO import StringIO # python 2
+except ImportError:
+ from io import StringIO # python 3
class OptionsTest(unittest.TestCase):
- def setUp(self):
- self.options = _Options()
- define = self.options.define
- # these are currently required
- define("help", default=False)
-
- define("port", default=80)
-
def test_parse_command_line(self):
- self.options.parse_command_line(["main.py", "--port=443"])
- self.assertEqual(self.options.port, 443)
+ options = _Options()
+ options.define("port", default=80)
+ options.parse_command_line(["main.py", "--port=443"])
+ self.assertEqual(options.port, 443)
def test_parse_callbacks(self):
+ options = _Options()
self.called = False
def callback():
self.called = True
- self.options.add_parse_callback(callback)
+ options.add_parse_callback(callback)
# non-final parse doesn't run callbacks
- self.options.parse_command_line(["main.py"], final=False)
+ options.parse_command_line(["main.py"], final=False)
self.assertFalse(self.called)
# final parse does
- self.options.parse_command_line(["main.py"])
+ options.parse_command_line(["main.py"])
self.assertTrue(self.called)
# callbacks can be run more than once on the same options
# object if there are multiple final parses
self.called = False
- self.options.parse_command_line(["main.py"])
+ options.parse_command_line(["main.py"])
self.assertTrue(self.called)
+
+ def test_help(self):
+ options = _Options()
+ try:
+ orig_stderr = sys.stderr
+ sys.stderr = StringIO()
+ with self.assertRaises(SystemExit):
+ options.parse_command_line(["main.py", "--help"])
+ usage = sys.stderr.getvalue()
+ finally:
+ sys.stderr = orig_stderr
+ self.assertIn("Usage:", usage)