from tornado.options import options, parse_command_line
options.logging = None
parse_command_line()
+
+.. versionchanged:: 4.3
+ Dashes and underscores are fully interchangeable in option names;
+ options can be defined, set, and read with any mix of the two.
+ Dashes are typical for command-line usage while config files require
+ underscores.
"""
from __future__ import absolute_import, division, print_function, with_statement
self.define("help", type=bool, help="show this help information",
callback=self._help_callback)
+ def _normalize_name(self, name):
+ return name.replace('_', '-')
+
def __getattr__(self, name):
+ name = self._normalize_name(name)
if isinstance(self._options.get(name), _Option):
return self._options[name].value()
raise AttributeError("Unrecognized option %r" % name)
def __setattr__(self, name, value):
+ name = self._normalize_name(name)
if isinstance(self._options.get(name), _Option):
return self._options[name].set(value)
raise AttributeError("Unrecognized option %r" % name)
def __iter__(self):
- return iter(self._options)
+ return (opt.name for opt in self._options.values())
+
+ def __contains__(self, name):
+ name = self._normalize_name(name)
+ return name in self._options
- def __getitem__(self, item):
- return self._options[item].value()
+ def __getitem__(self, name):
+ name = self._normalize_name(name)
+ return self._options[name].value()
def items(self):
"""A sequence of (name, value) pairs.
.. versionadded:: 3.1
"""
- return [(name, opt.value()) for name, opt in self._options.items()]
+ return [(opt.name, opt.value()) for name, opt in self._options.items()]
def groups(self):
"""The set of option-groups created by ``define``.
.. versionadded:: 3.1
"""
return dict(
- (name, opt.value()) for name, opt in self._options.items()
+ (opt.name, opt.value()) for name, opt in self._options.items()
if not group or group == opt.group_name)
def as_dict(self):
.. versionadded:: 3.1
"""
return dict(
- (name, opt.value()) for name, opt in self._options.items())
+ (opt.name, opt.value()) for name, opt in self._options.items())
def define(self, name, default=None, type=None, help=None, metavar=None,
multiple=False, group=None, callback=None):
group_name = group
else:
group_name = file_name
- self._options[name] = _Option(name, file_name=file_name,
- default=default, type=type, help=help,
- metavar=metavar, multiple=multiple,
- group_name=group_name,
- callback=callback)
+ normalized = self._normalize_name(name)
+ option = _Option(name, file_name=file_name,
+ default=default, type=type, help=help,
+ metavar=metavar, multiple=multiple,
+ group_name=group_name,
+ callback=callback)
+ self._options[normalized] = option
def parse_command_line(self, args=None, final=True):
"""Parses all options given on the command line (defaults to
break
arg = args[i].lstrip("-")
name, equals, value = arg.partition("=")
- name = name.replace('-', '_')
+ name = self._normalize_name(name)
if name not in self._options:
self.print_help()
raise Error('Unrecognized command line option: %r' % name)
with open(path, 'rb') as f:
exec_in(native_str(f.read()), config, config)
for name in config:
- if name in self._options:
- self._options[name].set(config[name])
+ normalized = self._normalize_name(name)
+ if normalized in self._options:
+ self._options[normalized].set(config[name])
if final:
self.run_parse_callbacks()
print("\n%s options:\n" % os.path.normpath(filename), file=file)
o.sort(key=lambda option: option.name)
for option in o:
- prefix = option.name
+ # Always print names with dashes in a CLI context.
+ prefix = self._normalize_name(option.name)
if option.metavar:
prefix += "=" + option.metavar
description = option.help or ""
options.define('foo')
self.assertRegexpMatches(str(cm.exception),
'Option.*foo.*already defined')
+
+ def test_dash_underscore_cli(self):
+ # Dashes and underscores should be interchangeable.
+ for defined_name in ['foo-bar', 'foo_bar']:
+ for flag in ['--foo-bar=a', '--foo_bar=a']:
+ options = OptionParser()
+ options.define(defined_name)
+ options.parse_command_line(['main.py', flag])
+ # Attr-style access always uses underscores.
+ self.assertEqual(options.foo_bar, 'a')
+ # Dict-style access allows both.
+ self.assertEqual(options['foo-bar'], 'a')
+ self.assertEqual(options['foo_bar'], 'a')
+
+ def test_dash_underscore_file(self):
+ # No matter how an option was defined, it can be set with underscores
+ # in a config file.
+ for defined_name in ['foo-bar', 'foo_bar']:
+ options = OptionParser()
+ options.define(defined_name)
+ options.parse_config_file(os.path.join(os.path.dirname(__file__),
+ "options_test.cfg"))
+ self.assertEqual(options.foo_bar, 'a')
+
+ def test_dash_underscore_introspection(self):
+ # Original names are preserved in introspection APIs.
+ options = OptionParser()
+ options.define('with-dash', group='g')
+ options.define('with_underscore', group='g')
+ all_options = ['help', 'with-dash', 'with_underscore']
+ self.assertEqual(sorted(options), all_options)
+ self.assertEqual(sorted(k for (k, v) in options.items()), all_options)
+ self.assertEqual(sorted(options.as_dict().keys()), all_options)
+
+ self.assertEqual(sorted(options.group_dict('g')),
+ ['with-dash', 'with_underscore'])
+
+ # --help shows CLI-style names with dashes.
+ buf = StringIO()
+ options.print_help(buf)
+ self.assertIn('--with-dash', buf.getvalue())
+ self.assertIn('--with-underscore', buf.getvalue())