main: Convert main program flow to command helpers

- Moves all startup code from start() into mopidy.commands
- Moves deps and config to mopidy.commands
- Plugs in use of commands parsing provided by new helpers
- Allows commands to override base verbosity level
- Removes defunct bootstrap logging helper.
This commit is contained in:
Thomas Adamcik 2013-11-14 23:14:27 +01:00
parent a6c3b78a6f
commit 63899059be
2 changed files with 175 additions and 171 deletions

View File

@ -18,10 +18,8 @@ sys.argv[1:] = []
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, versioning
logger = logging.getLogger('mopidy.main')
@ -37,24 +35,32 @@ def main():
create_file_structures()
check_old_locations()
parser, subparser = commands.build_parser()
root_cmd = commands.RootCommand()
config_cmd = commands.ConfigCommand()
deps_cmd = commands.DepsCommand()
root_cmd.set(extension=None)
root_cmd.add_child('config', config_cmd)
root_cmd.add_child('deps', deps_cmd)
installed_extensions = ext.load_extensions()
extension_sub_commands = {}
for extension in installed_extensions:
for cls in extension.get_sub_commands():
cmd_parser = subparser.add_parser(bytes(cls.name),
help=cls.help)
extension_sub_commands[cls.name] = (extension, cls(cmd_parser))
ext_cmd = extension.get_command()
if ext_cmd:
ext_cmd.set(extension=extension)
root_cmd.add_child(extension.ext_name, ext_cmd)
args = parser.parse_args(args=mopidy_args)
if args.command in ('deps', 'config'):
args.verbosity_level -= 1
args = root_cmd.parse(mopidy_args)
config, config_errors = config_lib.load(
args.config_files, installed_extensions, args.config_overrides)
log.setup_logging(config, args.verbosity_level, args.save_debug_log)
verbosity_level = args.base_verbosity_level
if args.verbosity_level:
verbosity_level += args.verbosity_level
log.setup_logging(config, verbosity_level, args.save_debug_log)
enabled_extensions = []
for extension in installed_extensions:
@ -70,48 +76,38 @@ def main():
enabled_extensions.append(extension)
log_extension_info(installed_extensions, enabled_extensions)
ext.register_gstreamer_elements(enabled_extensions)
if args.command == 'config':
logger.info('Dumping sanitized user config and exiting.')
print config_lib.format(
config, installed_extensions, config_errors)
sys.exit(0)
elif args.command == 'deps':
logger.info('Dumping debug info about dependencies and exiting.')
print deps.format_dependency_list()
sys.exit(0)
# Config and deps commands are simply special cased for now.
if args.command == config_cmd:
return args.command.run(
config, config_errors, installed_extensions)
elif args.command == deps_cmd:
return args.command.run()
# Remove errors for extensions that are not enabled:
for extension in installed_extensions:
if extension not in enabled_extensions:
config_errors.pop(extension.ext_name, None)
check_config_errors(config_errors)
# Read-only config from here on, please.
proxied_config = config_lib.Proxy(config)
if args.command in extension_sub_commands:
extension, cmd = extension_sub_commands[args.command]
if args.extension and args.extension not in enabled_extensions:
logger.error(
'Unable to run command provided by disabled extension %s',
args.extension.ext_name)
return 1
if extension not in enabled_extensions:
parser.error('Can not run sub-command %s from the disabled '
'extension %s.' % (cmd.name, extension.ext_name))
# Anything that wants to exit after this point must use
# mopidy.utils.process.exit_process as actors can have been started.
try:
return args.command.run(args, proxied_config, enabled_extensions)
except NotImplementedError:
print root_cmd.format_help()
return 1
logging.info('Running %s command provided by %s.', cmd.name,
extension.ext_name)
sys.exit(cmd.run(args, proxied_config, enabled_extensions))
if args.command == 'run':
ext.register_gstreamer_elements(enabled_extensions)
# Anything that wants to exit after this point must use
# mopidy.utils.process.exit_process as actors have been started.
start(proxied_config, enabled_extensions)
sys.exit(0)
parser.error(
'Unknown command %s, this should never happen.' % args.command)
except KeyboardInterrupt:
pass
except Exception as ex:
@ -119,16 +115,6 @@ def main():
raise
def bootstrap_logging(args):
# Initial config without extensions to bootstrap logging.
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, args.verbosity_level, args.save_debug_log)
def create_file_structures():
path.get_or_create_dir(b'$XDG_DATA_DIR/mopidy')
path.get_or_create_file(b'$XDG_CONFIG_DIR/mopidy/mopidy.conf')
@ -169,89 +155,5 @@ def check_config_errors(errors):
sys.exit(1)
def start(config, extensions):
loop = gobject.MainLoop()
try:
audio = start_audio(config)
backends = start_backends(config, extensions, audio)
core = start_core(audio, backends)
start_frontends(config, extensions, core)
loop.run()
except KeyboardInterrupt:
logger.info('Interrupted. Exiting...')
return
finally:
loop.quit()
stop_frontends(extensions)
stop_core()
stop_backends(extensions)
stop_audio()
process.stop_remaining_actors()
def start_audio(config):
logger.info('Starting Mopidy audio')
return Audio.start(config=config).proxy()
def stop_audio():
logger.info('Stopping Mopidy audio')
process.stop_actors_by_class(Audio)
def start_backends(config, extensions, audio):
backend_classes = []
for extension in extensions:
backend_classes.extend(extension.get_backend_classes())
logger.info(
'Starting Mopidy backends: %s',
', '.join(b.__name__ for b in backend_classes) or 'none')
backends = []
for backend_class in backend_classes:
backend = backend_class.start(config=config, audio=audio).proxy()
backends.append(backend)
return backends
def stop_backends(extensions):
logger.info('Stopping Mopidy backends')
for extension in extensions:
for backend_class in extension.get_backend_classes():
process.stop_actors_by_class(backend_class)
def start_core(audio, backends):
logger.info('Starting Mopidy core')
return Core.start(audio=audio, backends=backends).proxy()
def stop_core():
logger.info('Stopping Mopidy core')
process.stop_actors_by_class(Core)
def start_frontends(config, extensions, core):
frontend_classes = []
for extension in extensions:
frontend_classes.extend(extension.get_frontend_classes())
logger.info(
'Starting Mopidy frontends: %s',
', '.join(f.__name__ for f in frontend_classes) or 'none')
for frontend_class in frontend_classes:
frontend_class.start(config=config, core=core)
def stop_frontends(extensions):
logger.info('Stopping Mopidy frontends')
for extension in extensions:
for frontend_class in extension.get_frontend_classes():
process.stop_actors_by_class(frontend_class)
if __name__ == '__main__':
main()
sys.exit(main())

View File

@ -1,8 +1,15 @@
from __future__ import unicode_literals
import argparse
import gobject
import logging
from mopidy.utils import versioning
from mopidy import config as config_lib
from mopidy.audio import Audio
from mopidy.core import Core
from mopidy.utils import command, deps, process, versioning
logger = logging.getLogger('mopidy.commands')
def config_files_type(value):
@ -19,39 +26,134 @@ def config_override_type(value):
'%s must have the format section/key=value' % value)
def build_parser():
parser = argparse.ArgumentParser()
parser.set_defaults(verbosity_level=0)
parser.add_argument(
'--version', action='version',
version='Mopidy %s' % versioning.get_version())
parser.add_argument(
'-q', '--quiet',
action='store_const', const=-1, dest='verbosity_level',
help='less output (warning level)')
parser.add_argument(
'-v', '--verbose',
action='count', 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(
'--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')
class RootCommand(command.Command):
def __init__(self):
super(RootCommand, self).__init__()
self.set(base_verbosity_level=0)
self.add_argument(
'-h', '--help',
action='help', help='Show this message and exit')
self.add_argument(
'--version', action='version',
version='Mopidy %s' % versioning.get_version())
self.add_argument(
'-q', '--quiet',
action='store_const', const=-1, dest='verbosity_level',
help='less output (warning level)')
self.add_argument(
'-v', '--verbose',
action='count', dest='verbosity_level', default=0,
help='more output (debug level)')
self.add_argument(
'--save-debug-log',
action='store_true', dest='save_debug_log',
help='save debug log to "./mopidy.log"')
self.add_argument(
'--config',
action='store', dest='config_files', type=config_files_type,
default=b'$XDG_CONFIG_DIR/mopidy/mopidy.conf', metavar='FILES',
help='config files to use, colon seperated, later files override')
self.add_argument(
'-o', '--option',
action='append', dest='config_overrides',
type=config_override_type, metavar='OPTIONS',
help='`section/key=value` values to override config options')
subparser = parser.add_subparsers(
title='commands', metavar='COMMAND', dest='command')
def run(self, args, config, extensions):
loop = gobject.MainLoop()
try:
audio = self.start_audio(config)
backends = self.start_backends(config, extensions, audio)
core = self.start_core(audio, backends)
self.start_frontends(config, extensions, core)
loop.run()
except KeyboardInterrupt:
logger.info('Interrupted. Exiting...')
return
finally:
loop.quit()
self.stop_frontends(extensions)
self.stop_core()
self.stop_backends(extensions)
self.stop_audio()
process.stop_remaining_actors()
subparser.add_parser('run', help='start mopidy server')
subparser.add_parser('config', help='show current config')
subparser.add_parser('deps', help='show dependencies')
def start_audio(self, config):
logger.info('Starting Mopidy audio')
return Audio.start(config=config).proxy()
return parser, subparser
def start_backends(self, config, extensions, audio):
backend_classes = []
for extension in extensions:
backend_classes.extend(extension.get_backend_classes())
logger.info(
'Starting Mopidy backends: %s',
', '.join(b.__name__ for b in backend_classes) or 'none')
backends = []
for backend_class in backend_classes:
backend = backend_class.start(config=config, audio=audio).proxy()
backends.append(backend)
return backends
def start_core(self, audio, backends):
logger.info('Starting Mopidy core')
return Core.start(audio=audio, backends=backends).proxy()
def start_frontends(self, config, extensions, core):
frontend_classes = []
for extension in extensions:
frontend_classes.extend(extension.get_frontend_classes())
logger.info(
'Starting Mopidy frontends: %s',
', '.join(f.__name__ for f in frontend_classes) or 'none')
for frontend_class in frontend_classes:
frontend_class.start(config=config, core=core)
def stop_frontends(self, extensions):
logger.info('Stopping Mopidy frontends')
for extension in extensions:
for frontend_class in extension.get_frontend_classes():
process.stop_actors_by_class(frontend_class)
def stop_core(self):
logger.info('Stopping Mopidy core')
process.stop_actors_by_class(Core)
def stop_backends(self, extensions):
logger.info('Stopping Mopidy backends')
for extension in extensions:
for backend_class in extension.get_backend_classes():
process.stop_actors_by_class(backend_class)
def stop_audio(self):
logger.info('Stopping Mopidy audio')
process.stop_actors_by_class(Audio)
class ConfigCommand(command.Command):
"""Show currently active configuration."""
def __init__(self):
super(ConfigCommand, self).__init__()
self.set(base_verbosity_level=-1)
def run(self, config, errors, extensions):
print config_lib.format(config, extensions, errors)
return 0
class DepsCommand(command.Command):
"""Show dependencies and debug information."""
def __init__(self):
super(DepsCommand, self).__init__()
self.set(base_verbosity_level=-1)
def run(self):
print deps.format_dependency_list()
return 0