From 63899059be1b2af1b3f00bb7edf0eac843303e91 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Thu, 14 Nov 2013 23:14:27 +0100 Subject: [PATCH] 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. --- mopidy/__main__.py | 176 ++++++++++----------------------------------- mopidy/commands.py | 170 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 175 insertions(+), 171 deletions(-) diff --git a/mopidy/__main__.py b/mopidy/__main__.py index 099c20c7..8a96a98b 100644 --- a/mopidy/__main__.py +++ b/mopidy/__main__.py @@ -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()) diff --git a/mopidy/commands.py b/mopidy/commands.py index 9d65549e..e807cb65 100644 --- a/mopidy/commands.py +++ b/mopidy/commands.py @@ -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