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:
parent
a6c3b78a6f
commit
63899059be
@ -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())
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user