Merge pull request #441 from jodal/feature/argparse

Switch from optparse to argparse
This commit is contained in:
Stein Magnus Jodal 2013-04-30 15:01:02 -07:00
commit ddef55e0e5
8 changed files with 157 additions and 137 deletions

View File

@ -14,6 +14,14 @@ v0.15.0 (UNRELEASED)
- Mopidy no longer supports Python 2.6. Currently, the only Python version
supported by Mopidy is Python 2.7. (Fixes: :issue:`344`)
**Command line options**
- Converted from the optparse to the argparse library for handling command line
options.
- :option:`mopidy --show-config` will now take into consideration any
:option:`mopidy --option` arguments appearing later on the command line.
v0.14.1 (2013-04-28)
====================

View File

@ -1,7 +1,6 @@
from __future__ import unicode_literals
import logging
import optparse
import os
import signal
import sys
@ -24,11 +23,11 @@ sys.path.insert(
0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
from mopidy import ext
from mopidy import commands, ext
from mopidy.audio import Audio
from mopidy import config as config_lib
from mopidy.core import Core
from mopidy.utils import deps, log, path, process, versioning
from mopidy.utils import log, path, process
logger = logging.getLogger('mopidy.main')
@ -37,11 +36,13 @@ def main():
signal.signal(signal.SIGTERM, process.exit_handler)
signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)
loop = gobject.MainLoop()
options = parse_options()
config_files = options.config.split(b':')
config_overrides = options.overrides
args = commands.parser.parse_args(args=mopidy_args)
if args.show_config:
commands.show_config(args)
if args.show_deps:
commands.show_deps()
loop = gobject.MainLoop()
enabled_extensions = [] # Make sure it is defined before the finally block
logging_initialized = False
@ -50,17 +51,18 @@ def main():
try:
# Initial config without extensions to bootstrap logging.
logging_config, _ = config_lib.load(config_files, [], config_overrides)
logging_config, _ = config_lib.load(
args.config_files, [], args.config_overrides)
# TODO: setup_logging needs defaults in-case config values are None
log.setup_logging(
logging_config, options.verbosity_level, options.save_debug_log)
logging_config, args.verbosity_level, args.save_debug_log)
logging_initialized = True
installed_extensions = ext.load_extensions()
config, config_errors = config_lib.load(
config_files, installed_extensions, config_overrides)
args.config_files, installed_extensions, args.config_overrides)
# Filter out disabled extensions and remove any config errors for them.
for extension in installed_extensions:
@ -123,78 +125,6 @@ def check_config_errors(errors):
sys.exit(1)
def check_config_override(option, opt, override):
try:
return config_lib.parse_override(override)
except ValueError:
raise optparse.OptionValueError(
'option %s: must have the format section/key=value' % opt)
def parse_options():
parser = optparse.OptionParser(
version='Mopidy %s' % versioning.get_version())
# Ugly extension of optparse type checking magic :/
optparse.Option.TYPES += ('config_override',)
optparse.Option.TYPE_CHECKER['config_override'] = check_config_override
parser.add_option(
'-q', '--quiet',
action='store_const', const=0, dest='verbosity_level',
help='less output (warning level)')
parser.add_option(
'-v', '--verbose',
action='count', default=1, dest='verbosity_level',
help='more output (debug level)')
parser.add_option(
'--save-debug-log',
action='store_true', dest='save_debug_log',
help='save debug log to "./mopidy.log"')
parser.add_option(
'--show-config',
action='callback', callback=show_config_callback,
help='show current config')
parser.add_option(
'--show-deps',
action='callback', callback=deps.show_deps_optparse_callback,
help='show dependencies and their versions')
parser.add_option(
'--config',
action='store', dest='config',
default=b'$XDG_CONFIG_DIR/mopidy/mopidy.conf',
help='config files to use, colon seperated, later files override')
parser.add_option(
'-o', '--option',
action='append', dest='overrides', type='config_override',
help='`section/key=value` values to override config options')
return parser.parse_args(args=mopidy_args)[0]
def show_config_callback(option, opt, value, parser):
# TODO: don't use callback for this as --config or -o set after
# --show-config will be ignored.
files = getattr(parser.values, 'config', b'').split(b':')
overrides = getattr(parser.values, 'overrides', [])
extensions = ext.load_extensions()
config, errors = config_lib.load(files, extensions, overrides)
# Clear out any config for disabled extensions.
for extension in extensions:
if not ext.validate_extension(extension):
config[extension.ext_name] = {b'enabled': False}
errors[extension.ext_name] = {
b'enabled': b'extension disabled its self.'}
elif not config[extension.ext_name]['enabled']:
config[extension.ext_name] = {b'enabled': False}
errors[extension.ext_name] = {
b'enabled': b'extension disabled by config.'}
print config_lib.format(config, extensions, errors)
sys.exit(0)
def check_old_locations():
dot_mopidy_dir = path.expand_path(b'~/.mopidy')
if os.path.isdir(dot_mopidy_dir):

83
mopidy/commands.py Normal file
View File

@ -0,0 +1,83 @@
from __future__ import unicode_literals
import argparse
import sys
from mopidy import config as config_lib, ext
from mopidy.utils import deps, versioning
def config_files_type(value):
return value.split(b':')
def config_override_type(value):
try:
section, remainder = value.split(b'/', 1)
key, value = remainder.split(b'=', 1)
return (section.strip(), key.strip(), value.strip())
except ValueError:
raise argparse.ArgumentTypeError(
'%s must have the format section/key=value' % value)
parser = argparse.ArgumentParser()
parser.add_argument(
'--version', action='version',
version='Mopidy %s' % versioning.get_version())
parser.add_argument(
'-q', '--quiet',
action='store_const', const=0, dest='verbosity_level',
help='less output (warning level)')
parser.add_argument(
'-v', '--verbose',
action='count', default=1, dest='verbosity_level',
help='more output (debug level)')
parser.add_argument(
'--save-debug-log',
action='store_true', dest='save_debug_log',
help='save debug log to "./mopidy.log"')
parser.add_argument(
'--show-config',
action='store_true', dest='show_config',
help='show current config')
parser.add_argument(
'--show-deps',
action='store_true', dest='show_deps',
help='show dependencies and their versions')
parser.add_argument(
'--config',
action='store', dest='config_files', type=config_files_type,
default=b'$XDG_CONFIG_DIR/mopidy/mopidy.conf',
help='config files to use, colon seperated, later files override')
parser.add_argument(
'-o', '--option',
action='append', dest='config_overrides', type=config_override_type,
help='`section/key=value` values to override config options')
def show_config(args):
"""Prints the effective config and exits."""
extensions = ext.load_extensions()
config, errors = config_lib.load(
args.config_files, extensions, args.config_overrides)
# Clear out any config for disabled extensions.
for extension in extensions:
if not ext.validate_extension(extension):
config[extension.ext_name] = {b'enabled': False}
errors[extension.ext_name] = {
b'enabled': b'extension disabled its self.'}
elif not config[extension.ext_name]['enabled']:
config[extension.ext_name] = {b'enabled': False}
errors[extension.ext_name] = {
b'enabled': b'extension disabled by config.'}
print config_lib.format(config, extensions, errors)
sys.exit(0)
def show_deps():
"""Prints a list of all dependencies and exits."""
print deps.format_dependency_list()
sys.exit(0)

View File

@ -140,13 +140,6 @@ def _format(config, comments, schemas, display):
return b'\n'.join(output)
def parse_override(override):
"""Parse ``section/key=value`` command line overrides"""
section, remainder = override.split(b'/', 1)
key, value = remainder.split(b'=', 1)
return (section.strip(), key.strip(), value.strip())
class Proxy(collections.Mapping):
def __init__(self, data):
self._data = data

View File

@ -1,8 +1,8 @@
from __future__ import unicode_literals
import argparse
import datetime
import logging
import optparse
import os
import sys
@ -33,7 +33,7 @@ from mopidy.utils import log, path, versioning
def main():
options = parse_options()
args = parse_args()
# TODO: support config files and overrides (shared from main?)
config_files = [b'/etc/mopidy/mopidy.conf',
b'$XDG_CONFIG_DIR/mopidy/mopidy.conf']
@ -43,7 +43,7 @@ def main():
# Initial config without extensions to bootstrap logging.
logging_config, _ = config_lib.load(config_files, [], config_overrides)
log.setup_root_logger()
log.setup_console_logging(logging_config, options.verbosity_level)
log.setup_console_logging(logging_config, args.verbosity_level)
extensions = ext.load_extensions()
config, errors = config_lib.load(
@ -87,18 +87,20 @@ def main():
logging.info('Done writing tag cache')
def parse_options():
parser = optparse.OptionParser(
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
'--version', action='version',
version='Mopidy %s' % versioning.get_version())
parser.add_option(
parser.add_argument(
'-q', '--quiet',
action='store_const', const=0, dest='verbosity_level',
help='less output (warning level)')
parser.add_option(
parser.add_argument(
'-v', '--verbose',
action='count', default=1, dest='verbosity_level',
help='more output (debug level)')
return parser.parse_args(args=mopidy_args)[0]
return parser.parse_args(args=mopidy_args)
def translator(data):

View File

@ -3,7 +3,6 @@ from __future__ import unicode_literals
import functools
import os
import platform
import sys
import pygst
pygst.require('0.10')
@ -14,17 +13,6 @@ import pkg_resources
from . import formatting
def show_deps_optparse_callback(*args):
"""
Prints a list of all dependencies.
Called by optparse when Mopidy is run with the :option:`--show-deps`
option.
"""
print format_dependency_list()
sys.exit(0)
def format_dependency_list(adapters=None):
if adapters is None:
dist_names = set([

44
tests/commands_test.py Normal file
View File

@ -0,0 +1,44 @@
from __future__ import unicode_literals
import argparse
import unittest
from mopidy import commands
class ConfigOverrideTypeTest(unittest.TestCase):
def test_valid_override(self):
expected = (b'section', b'key', b'value')
self.assertEqual(
expected, commands.config_override_type(b'section/key=value'))
self.assertEqual(
expected, commands.config_override_type(b'section/key=value '))
self.assertEqual(
expected, commands.config_override_type(b'section/key =value'))
self.assertEqual(
expected, commands.config_override_type(b'section /key=value'))
def test_valid_override_is_bytes(self):
section, key, value = commands.config_override_type(
b'section/key=value')
self.assertIsInstance(section, bytes)
self.assertIsInstance(key, bytes)
self.assertIsInstance(value, bytes)
def test_empty_override(self):
expected = ('section', 'key', '')
self.assertEqual(
expected, commands.config_override_type(b'section/key='))
self.assertEqual(
expected, commands.config_override_type(b'section/key= '))
def test_invalid_override(self):
self.assertRaises(
argparse.ArgumentTypeError,
commands.config_override_type, b'section/key')
self.assertRaises(
argparse.ArgumentTypeError,
commands.config_override_type, b'section=')
self.assertRaises(
argparse.ArgumentTypeError,
commands.config_override_type, b'section')

View File

@ -106,31 +106,3 @@ class ValidateTest(unittest.TestCase):
self.assertEqual({'foo': {'bar': 'bad'}}, errors)
# TODO: add more tests
class ParseOverrideTest(unittest.TestCase):
def test_valid_override(self):
expected = (b'section', b'key', b'value')
self.assertEqual(expected, config.parse_override(b'section/key=value'))
self.assertEqual(
expected, config.parse_override(b'section/key=value '))
self.assertEqual(
expected, config.parse_override(b'section/key =value'))
self.assertEqual(
expected, config.parse_override(b'section /key=value'))
def test_valid_override_is_bytes(self):
section, key, value = config.parse_override(b'section/key=value')
self.assertIsInstance(section, bytes)
self.assertIsInstance(key, bytes)
self.assertIsInstance(value, bytes)
def test_empty_override(self):
expected = ('section', 'key', '')
self.assertEqual(expected, config.parse_override(b'section/key='))
self.assertEqual(expected, config.parse_override(b'section/key= '))
def test_invalid_override(self):
self.assertRaises(ValueError, config.parse_override, b'section/key')
self.assertRaises(ValueError, config.parse_override, b'section=')
self.assertRaises(ValueError, config.parse_override, b'section')