Add config command

This commit is contained in:
Thomas Scholtes 2014-02-18 20:56:08 +01:00
parent d0e5b411cf
commit c8e32f6bef
4 changed files with 197 additions and 1 deletions

View file

@ -23,6 +23,8 @@ import time
import itertools
import codecs
from datetime import datetime
import yaml
import platform
import beets
from beets import ui
@ -35,6 +37,7 @@ from beets import importer
from beets import util
from beets.util import syspath, normpath, ancestry, displayable_path
from beets.util.functemplate import Template
from beets.util.confit import ConfigTypeError
from beets import library
from beets import config
@ -1286,3 +1289,58 @@ def write_func(lib, opts, args):
write_items(lib, decargs(args), opts.pretend)
write_cmd.func = write_func
default_commands.append(write_cmd)
config_cmd = ui.Subcommand('config', help='show or edit the user configuration')
config_cmd.parser.add_option('-p', '--paths', action='store_true',
help='show files that configuration was loaded from')
config_cmd.parser.add_option('-e', '--edit', action='store_true',
help='edit user configuration with $EDITOR')
config_cmd.parser.add_option('-d', '--defaults', action='store_true',
help='include the default configuration')
def _config_get(view):
try:
keys = view.keys()
except ConfigTypeError:
return view.get()
else:
return dict((key, _config_get(view[key])) for key in view.keys())
def config_func(lib, opts, args):
# Make sure lazy configuration is loaded
config.resolve()
if not opts.defaults:
# Remove default source
config.sources = [source for source in config.sources if not source.default]
if opts.paths:
for source in config.sources:
if source.filename:
print(source.filename)
elif opts.edit:
path = config.user_config_path()
if 'EDITOR' in os.environ:
editor = os.environ['EDITOR']
args = [editor, editor, path]
elif platform.system() == 'Darwin':
args = ['open', 'open', '-n', path]
elif platform.system() == 'Windows':
# On windows we can execute arbitrary files. The os will
# take care of starting an appropriate application
args = [path, path]
else:
# Assume Unix
args = ['xdg-open', 'xdg-open', path]
try:
os.execlp(*args)
except OSError:
raise ui.UserError("Could not edit configuration. Please"
"set the EDITOR environment variable.")
else:
config_dict = _config_get(config)
print(yaml.safe_dump(config_dict, default_flow_style=False))
config_cmd.func = config_func
default_commands.append(config_cmd)

View file

@ -621,11 +621,18 @@ class Configuration(RootView):
if read:
self.read()
def user_config_path(self):
"""Points to the location of the user configuration.
The file may not exist.
"""
return os.path.join(self.config_dir(), CONFIG_FILENAME)
def _add_user_source(self):
"""Add ``ConfigSource`` for the configuration file in
``config_dir()`` if it exists.
"""
filename = os.path.join(self.config_dir(), CONFIG_FILENAME)
filename = self.user_config_path()
if os.path.isfile(filename):
self.add(ConfigSource(load_yaml(filename) or {}, filename))
@ -720,3 +727,9 @@ class LazyConfig(Configuration):
# Buffer additions to beginning.
self._lazy_prefix[:0] = self.sources
del self.sources[:]
def clear(self):
"""Remove all sources from this configuration."""
del self.sources[:]
self._lazy_suffix = []
self._lazy_prefix = []

View file

@ -268,3 +268,13 @@ def platform_posix():
os.path = posixpath
yield
os.path = old_path
@contextmanager
def system_mock(name):
import platform
old_system = platform.system
platform.system = lambda: name
try:
yield
finally:
platform.system = old_system

115
test/test_config_command.py Normal file
View file

@ -0,0 +1,115 @@
import os
import yaml
from beets import ui
from beets import config
import _common
class ConfigCommandTest(_common.TestCase):
def setUp(self):
super(ConfigCommandTest, self).setUp()
self.io.install()
if 'EDITOR' in os.environ:
del os.environ['EDITOR']
os.environ['BEETSDIR'] = self.temp_dir
self.config_path = os.path.join(self.temp_dir, 'config.yaml')
with open(self.config_path, 'w') as file:
file.write('library: lib\n')
file.write('option: value')
self.cli_config_path = os.path.join(self.temp_dir, 'cli_config.yaml')
with open(self.cli_config_path, 'w') as file:
file.write('option: cli overwrite')
config.clear()
config._materialized = False
def tearDown(self):
super(ConfigCommandTest, self).tearDown()
self.execlp_restore()
def test_show_user_config(self):
ui._raw_main(['config'])
output = yaml.load(self.io.getoutput())
self.assertEqual(output['option'], 'value')
def test_show_user_config_with_defaults(self):
ui._raw_main(['config', '-d'])
output = yaml.load(self.io.getoutput())
self.assertEqual(output['option'], 'value')
self.assertEqual(output['library'], 'lib')
self.assertEqual(output['import']['timid'], False)
def test_show_user_config_with_cli(self):
ui._raw_main(['--config', self.cli_config_path, 'config'])
output = yaml.load(self.io.getoutput())
self.assertEqual(output['library'], 'lib')
self.assertEqual(output['option'], 'cli overwrite')
def test_config_paths(self):
ui._raw_main(['config', '-p'])
paths = self.io.getoutput().split('\n')
self.assertEqual(len(paths), 2)
self.assertEqual(paths[0], self.config_path)
def test_config_paths_with_cli(self):
ui._raw_main(['--config', self.cli_config_path, 'config', '-p'])
paths = self.io.getoutput().split('\n')
self.assertEqual(len(paths), 3)
self.assertEqual(paths[0], self.cli_config_path)
def test_edit_config_with_editor_env(self):
self.execlp_stub()
os.environ['EDITOR'] = 'myeditor'
ui._raw_main(['config', '-e'])
self.assertEqual(self._execlp_call, ['myeditor', self.config_path])
def test_edit_config_with_open(self):
self.execlp_stub()
with _common.system_mock('Darwin'):
ui._raw_main(['config', '-e'])
self.assertEqual(self._execlp_call, ['open', '-n', self.config_path])
def test_edit_config_with_xdg_open(self):
self.execlp_stub()
with _common.system_mock('Linux'):
ui._raw_main(['config', '-e'])
self.assertEqual(self._execlp_call, ['xdg-open', self.config_path])
def test_edit_config_with_windows_exec(self):
self.execlp_stub()
with _common.system_mock('Windows'):
ui._raw_main(['config', '-e'])
self.assertEqual(self._execlp_call, [self.config_path])
def test_config_editor_not_found(self):
def raise_os_error(*args):
raise OSError
os.execlp = raise_os_error
with self.assertRaises(ui.UserError) as user_error:
ui._raw_main(['config', '-e'])
self.assertIn('Could not edit configuration',
str(user_error.exception.args[0]))
def execlp_stub(self):
self._execlp_call = None
def _execlp_stub(file, *args):
self._execlp_call = [file] + list(args[1:])
self._orig_execlp = os.execlp
os.execlp = _execlp_stub
def execlp_restore(self):
if hasattr(self, '_orig_execlp'):
os.execlp = self._orig_execlp