Refactor cli main function

The logic of setting up beets and running a command was scattered
across the `_raw_main` and `_configure` functions. This is an attempt
to bring more structure to the process and to diminish the dependency
on global state.
This commit is contained in:
Thomas Scholtes 2014-09-05 19:05:06 +02:00
parent 17549b873e
commit 273874f3a2
2 changed files with 64 additions and 55 deletions

View file

@ -694,6 +694,16 @@ class SubcommandsOptionParser(optparse.OptionParser):
# Super constructor.
optparse.OptionParser.__init__(self, *args, **kwargs)
self.add_option('-l', '--library', dest='library',
help='library database file to use')
self.add_option('-d', '--directory', dest='directory',
help="destination music directory")
self.add_option('-v', '--verbose', dest='verbose', action='store_true',
help='print debugging information')
self.add_option('-c', '--config', dest='config',
help='path to configuration file')
self.add_option('-h', '--help', dest='help', action='store_true',
help='how this help message and exit')
# Our root parser needs to stop on the first unrecognized argument.
self.disable_interspersed_args()
@ -855,11 +865,18 @@ def _load_plugins():
plugins.send("pluginload")
def _configure(args):
"""Parse the command line, load configuration files (including
loading any indicated plugins), and return the invoked subcomand,
the subcommand options, and the subcommand arguments.
def _setup(options, lib=None):
"""Prepare and global state and updates it with command line options.
Returns a list of subcommands, a list of plugins, and a library instance.
"""
# Configure the MusicBrainz API.
mb.configure()
_configure(options)
_load_plugins()
# Temporary: Migrate from 1.0-style configuration.
from beets.ui import migrate
migrate.automigrate()
@ -867,21 +884,20 @@ def _configure(args):
# Get the default subcommands.
from beets.ui.commands import default_commands
# Construct the root parser.
parser = SubcommandsOptionParser()
parser.add_option('-l', '--library', dest='library',
help='library database file to use')
parser.add_option('-d', '--directory', dest='directory',
help="destination music directory")
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
help='print debugging information')
parser.add_option('-c', '--config', dest='config',
help='path to configuration file')
parser.add_option('-h', '--help', dest='help', action='store_true',
help='how this help message and exit')
subcommands = list(default_commands)
subcommands.append(migrate.migrate_cmd)
subcommands.extend(plugins.commands())
# Parse the command-line!
options, subargs = parser.parse_global_options(args)
if lib is None:
lib = _open_library(config)
plugins.send("library_opened", lib=lib)
return subcommands, plugins, lib
def _configure(options):
"""Amend the global configuration object with command line options.
"""
# Add any additional config files specified with --config. This
# special handling lets specified plugins get loaded before we
@ -906,54 +922,47 @@ def _configure(args):
log.debug('no user configuration found at {0}'.format(
util.displayable_path(config_path)))
# Add builtin subcommands
parser.add_subcommand(*default_commands)
parser.add_subcommand(migrate.migrate_cmd)
log.debug(u'data directory: {0}'
.format(util.displayable_path(config.config_dir())))
# Now add the plugin commands to the parser.
_load_plugins()
for cmd in plugins.commands():
parser.add_subcommand(cmd)
# Parse the remainder of the command line with loaded plugins.
return parser.parse_subcommand(subargs)
def _open_library(config):
"""Create a new library instance from the configuration.
"""
dbpath = config['library'].as_filename()
try:
lib = library.Library(
dbpath,
config['directory'].as_filename(),
get_path_formats(),
get_replacements(),
)
except sqlite3.OperationalError:
raise UserError(u"database file {0} could not be opened".format(
util.displayable_path(dbpath)
))
log.debug(u'library database: {0}\n'
u'library directory: {1}'
.format(
util.displayable_path(lib.path),
util.displayable_path(lib.directory),
))
return lib
def _raw_main(args, lib=None):
"""A helper function for `main` without top-level exception
handling.
"""
subcommand, suboptions, subargs = _configure(args)
if lib is None:
# Open library file.
dbpath = config['library'].as_filename()
try:
lib = library.Library(
dbpath,
config['directory'].as_filename(),
get_path_formats(),
get_replacements(),
)
except sqlite3.OperationalError:
raise UserError(u"database file {0} could not be opened".format(
util.displayable_path(dbpath)
))
plugins.send("library_opened", lib=lib)
parser = SubcommandsOptionParser()
options, subargs = parser.parse_global_options(args)
log.debug(u'data directory: {0}\n'
u'library database: {1}\n'
u'library directory: {2}'
.format(
util.displayable_path(config.config_dir()),
util.displayable_path(lib.path),
util.displayable_path(lib.directory),
))
subcommands, plugins, lib = _setup(options, lib)
# Configure the MusicBrainz API.
mb.configure()
parser.add_subcommand(*subcommands)
subcommand, suboptions, subargs = parser.parse_subcommand(subargs)
# Invoke the subcommand.
subcommand.func(lib, suboptions, subargs)
plugins.send('cli_exit', lib=lib)

View file

@ -740,7 +740,7 @@ class ConfigTest(unittest.TestCase, TestHelper):
beetsdir = os.path.join(self.temp_dir, 'beetsfile')
open(beetsdir, 'a').close()
os.environ['BEETSDIR'] = beetsdir
self.assertRaises(ConfigError, ui._raw_main, 'test')
self.assertRaises(ConfigError, ui._raw_main, ['test'])
def test_beetsdir_config_does_not_load_default_user_config(self):
os.environ['BEETSDIR'] = self.beetsdir