mirror of
https://github.com/beetbox/beets.git
synced 2025-12-14 20:43:41 +01:00
Merge branch 'config_command'
Conflicts: beets/ui/commands.py docs/reference/config.rst test/_common.py
This commit is contained in:
commit
c345df9155
6 changed files with 225 additions and 8 deletions
|
|
@ -22,6 +22,8 @@ import os
|
|||
import time
|
||||
import itertools
|
||||
import codecs
|
||||
import yaml
|
||||
import platform
|
||||
|
||||
import beets
|
||||
from beets import ui
|
||||
|
|
@ -34,6 +36,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
|
||||
|
||||
|
|
@ -1245,3 +1248,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)
|
||||
|
|
|
|||
|
|
@ -629,12 +629,19 @@ 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 the configuration options from the YAML file in the
|
||||
user's configuration directory (given by `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))
|
||||
|
||||
|
|
@ -734,3 +741,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 = []
|
||||
|
|
|
|||
|
|
@ -304,6 +304,30 @@ fields
|
|||
Show the item and album metadata fields available for use in :doc:`query` and
|
||||
:doc:`pathformat`. Includes any template fields provided by plugins.
|
||||
|
||||
.. _config-cmd:
|
||||
|
||||
config
|
||||
``````
|
||||
::
|
||||
|
||||
beet config [-pd]
|
||||
beet config -e
|
||||
|
||||
Show or edit the user configuration. Without any options this command
|
||||
prints a YAML representation of the current user configuration. If the
|
||||
``--path`` option is given it instead prints the aboslute path to the
|
||||
user configuration file. Note that this path may not exist. The
|
||||
``--default`` option can be set with or without the ``--path`` option.
|
||||
If it is set it also load the default configuration from the beets
|
||||
package. Showing the configuration or the paths also works if an
|
||||
additional ``--config`` option is given on the command line.
|
||||
|
||||
If the ``--edit`` option is given, beets will open the user configuration
|
||||
in an editor. If the ``EDITOR`` environment variable is set it uses that
|
||||
command to start the editor. Otherwise, beets tries the ``open`` command on
|
||||
OSX, the ``xdg-open`` command on Unixes and will try to execute the
|
||||
configuration file directly on Windows.
|
||||
|
||||
.. _global-flags:
|
||||
|
||||
Global Flags
|
||||
|
|
|
|||
|
|
@ -3,13 +3,10 @@ Configuration
|
|||
|
||||
Beets has an extensive configuration system that lets you customize nearly
|
||||
every aspect of its operation. To configure beets, you'll edit a file called
|
||||
``config.yaml``. The location of this file depends on your OS:
|
||||
|
||||
* On Unix-like OSes, you want ``~/.config/beets/config.yaml``.
|
||||
* On Windows, use ``%APPDATA%\beets\config.yaml``. This is usually in a
|
||||
directory like ``C:\Users\You\AppData\Roaming``.
|
||||
* On OS X, you can use either the Unix location or ``~/Library/Application
|
||||
Support/beets/config.yaml``.
|
||||
``config.yaml``. The ``beets config -p`` command shows you where beets
|
||||
expects its configuration file to be placed. You can start editing it
|
||||
right away by running ``beets config -e``. This will create the file if
|
||||
it not already exists and open it in your default text editor.
|
||||
|
||||
It is also possible to customize the location of the configuration file and
|
||||
even use multiple layers of configuration. See `Configuration Location`_,
|
||||
|
|
|
|||
|
|
@ -272,3 +272,13 @@ def platform_posix():
|
|||
yield
|
||||
finally:
|
||||
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
115
test/test_config_command.py
Normal 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
|
||||
Loading…
Reference in a new issue