Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add per-option callbacks, and implement --help with them.

  • Loading branch information...
commit f61352c87fe46dac9482ca28876e6c8bbf49d3b8 1 parent fc6f1d5
@bdarnell bdarnell authored
View
48 tornado/options.py
@@ -75,6 +75,8 @@ class _Options(dict):
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):
@@ -87,7 +89,7 @@ def __setattr__(self, name, value):
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)
@@ -107,7 +109,8 @@ def define(self, name, default=None, type=None, help=None, metavar=None,
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:
@@ -134,9 +137,6 @@ def parse_command_line(self, args=None, final=True):
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()
@@ -153,8 +153,10 @@ def parse_config_file(self, path, final=True):
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 = {}
@@ -180,6 +182,11 @@ def print_help(self, file=sys.stdout):
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))
@@ -189,8 +196,9 @@ def run_parse_callbacks(self):
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
@@ -200,6 +208,7 @@ def __init__(self, name, default=None, type=basestring, help=None, metavar=None,
self.multiple = multiple
self.file_name = file_name
self.group_name = group_name
+ self.callback = callback
self.default = default
self._value = None
@@ -226,6 +235,8 @@ def parse(self, value):
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):
@@ -242,6 +253,8 @@ 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 = [
@@ -316,7 +329,7 @@ def _parse_string(self, value):
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)
@@ -338,9 +351,21 @@ def define(name, default=None, type=None, help=None, metavar=None,
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):
@@ -377,5 +402,4 @@ def add_parse_callback(callback):
# Default options
-define("help", type=bool, help="show this help information")
define_logging_options(options)
View
41 tornado/test/options_test.py
@@ -1,38 +1,51 @@
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)
View
6 website/sphinx/releases/next.rst
@@ -89,3 +89,9 @@ In progress
to supress these callbacks.
* Function `tornado.options.enable_pretty_logging` has been moved to the
`tornado.log` module.
+* `tornado.options.define` now takes a ``callback`` argument. This callback
+ will be run with the new value whenever the option is changed. This is
+ especially useful for options that set other options, such as by reading
+ from a config file.
+* `tornado.option.parse_command_line` ``--help`` output now goes to ``stderr``
+ rather than ``stdout``.
Please sign in to comment.
Something went wrong with that request. Please try again.