Initial support for #770

What was done:
- Implemented loading using configuration hierarhy as proposed in the issue
- Implemented group aliasing

What was not:
- Some tests (config_reload) are failing
- Other (test_configuration) are spamming console with unexpected messages
- No support for powerline-lint
- No tests for new functionality
- Specifically I have not checked whether group aliasing actually works
- Colorschemes were not ported

Some other things: I have named this branch `config-ng` because I have other
ideas about configuration and it would be good to include them making only one
possibly backwards-incompatible merge commit instead of many. Specifically I am
going to rebase `merge-config` branch here.
This commit is contained in:
ZyX 2014-02-04 21:37:42 +04:00 committed by ZyX
parent 2d1a964e32
commit 97266b7ffc
3 changed files with 101 additions and 29 deletions

View File

@ -9,6 +9,7 @@ from powerline.colorscheme import Colorscheme
from powerline.lib.config import ConfigLoader from powerline.lib.config import ConfigLoader
from powerline.lib.unicode import safe_unicode, FailedUnicode from powerline.lib.unicode import safe_unicode, FailedUnicode
from powerline.config import DEFAULT_SYSTEM_CONFIG_DIR from powerline.config import DEFAULT_SYSTEM_CONFIG_DIR
from powerline.lib import mergedicts
from threading import Lock, Event from threading import Lock, Event
@ -220,6 +221,22 @@ def finish_common_config(common_config):
return common_config return common_config
if sys.version_info < (3,):
# `raise exception[0], None, exception[1]` is a SyntaxError in python-3*
# Not using ('''…''') because this syntax does not work in python-2.6
exec(('def reraise(exception):\n'
' if type(exception) is tuple:\n'
' raise exception[0], None, exception[1]\n'
' else:\n'
' raise exception\n'))
else:
def reraise(exception):
if type(exception) is tuple:
raise exception[0].with_traceback(exception[1])
else:
raise exception
class Powerline(object): class Powerline(object):
'''Main powerline class, entrance point for all powerline uses. Sets '''Main powerline class, entrance point for all powerline uses. Sets
powerline up and loads the configuration. powerline up and loads the configuration.
@ -455,7 +472,38 @@ class Powerline(object):
:return: dictionary with :ref:`colorscheme configuration <config-colorschemes>`. :return: dictionary with :ref:`colorscheme configuration <config-colorschemes>`.
''' '''
return self._load_config(os.path.join('colorschemes', self.ext, name), 'colorscheme') # TODO Make sure no colorscheme name ends with __ (do it in
# powerline-lint)
levels = (
os.path.join('colorschemes', name),
os.path.join('colorschemes', self.ext, '__main__'),
os.path.join('colorschemes', self.ext, name),
)
config = {}
loaded = 0
exceptions = []
for cfg_path in levels:
try:
lvl_config = self._load_config(cfg_path, 'colorscheme')
except IOError as e:
if sys.version_info < (3,):
tb = sys.exc_info()[2]
exceptions.append((e, tb))
else:
exceptions.append(e)
else:
if not cfg_path.endswith('__'):
loaded += 1
mergedicts(config, lvl_config)
if not loaded:
for exception in exceptions:
if type(exception) is tuple:
e = exception[0]
else:
e = exception
self.exception('Failed to load colorscheme: {0}', e, exception=exception)
raise e
return config
def load_colors_config(self): def load_colors_config(self):
'''Get colorscheme. '''Get colorscheme.
@ -562,5 +610,11 @@ class Powerline(object):
def exception(self, msg, *args, **kwargs): def exception(self, msg, *args, **kwargs):
if 'prefix' not in kwargs: if 'prefix' not in kwargs:
kwargs['prefix'] = 'powerline' kwargs['prefix'] = 'powerline'
exception = kwargs.pop('exception', None)
pl = getattr(self, 'pl', None) or get_fallback_logger() pl = getattr(self, 'pl', None) or get_fallback_logger()
if exception:
try:
reraise(exception)
except Exception:
return pl.exception(msg, *args, **kwargs)
return pl.exception(msg, *args, **kwargs) return pl.exception(msg, *args, **kwargs)

View File

@ -1,6 +1,10 @@
# vim:fileencoding=utf-8:noet # vim:fileencoding=utf-8:noet
from copy import copy from copy import copy
try:
from __builtin__ import unicode
except ImportError:
unicode = str
DEFAULT_MODE_KEY = None DEFAULT_MODE_KEY = None
@ -70,32 +74,41 @@ class Colorscheme(object):
else: else:
return self.colors[gradient] return self.colors[gradient]
def get_highlighting(self, groups, mode, gradient_level=None): def get_group_props(self, mode, trans, group, translate_colors=True):
trans = self.translations.get(mode, {}) if isinstance(group, (str, unicode)):
for group in hl_iter(groups): try:
if 'groups' in trans and group in trans['groups']: group_props = trans['groups'][group]
except KeyError:
try: try:
group_props = trans['groups'][group] group_props = self.groups[group]
except KeyError: except KeyError:
continue return None
break else:
return self.get_group_props(mode, trans, group_props, True)
else: else:
try: return self.get_group_props(mode, trans, group_props, False)
group_props = copy(self.groups[group]) else:
except KeyError: if translate_colors:
continue group_props = copy(group)
try: try:
ctrans = trans['colors'] ctrans = trans['colors']
except KeyError:
pass
else:
for key in ('fg', 'bg'): for key in ('fg', 'bg'):
try: try:
group_props[key] = ctrans[group_props[key]] group_props[key] = ctrans[group_props[key]]
except KeyError: except KeyError:
pass pass
except KeyError: return group_props
pass else:
return group
def get_highlighting(self, groups, mode, gradient_level=None):
trans = self.translations.get(mode, {})
for group in hl_iter(groups):
group_props = self.get_group_props(mode, trans, group)
if group_props:
break break
else: else:
raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(hl_iter(groups))) raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(hl_iter(groups)))

View File

@ -101,7 +101,7 @@ class TestConfigReload(TestCase):
with get_powerline(run_once=True) as p: with get_powerline(run_once=True) as p:
with replace_item(globals(), 'config', deepcopy(config)): with replace_item(globals(), 'config', deepcopy(config)):
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['config']['common']['spaces'] = 1 config['config']['common']['spaces'] = 1
add_watcher_events(p, 'config', wait=False, interval=0.05) add_watcher_events(p, 'config', wait=False, interval=0.05)
# When running once thread should not start # When running once thread should not start
@ -117,7 +117,7 @@ class TestConfigReload(TestCase):
with get_powerline(run_once=False) as p: with get_powerline(run_once=False) as p:
with replace_item(globals(), 'config', deepcopy(config)): with replace_item(globals(), 'config', deepcopy(config)):
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['config']['common']['spaces'] = 1 config['config']['common']['spaces'] = 1
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
@ -141,14 +141,19 @@ class TestConfigReload(TestCase):
config['config']['ext']['test']['colorscheme'] = 'nonexistent' config['config']['ext']['test']['colorscheme'] = 'nonexistent'
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>><None None None>')
self.assertAccessEvents('config', 'colorschemes/test/nonexistent') self.assertAccessEvents('config', 'colorschemes/nonexistent', 'colorschemes/test/__main__', 'colorschemes/test/nonexistent')
# It should normally handle file missing error # It should normally handle file missing error
self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: colorschemes/test/nonexistent']) self.assertEqual(p.logger._pop_msgs(), [
'exception:test:powerline:Failed to load colorscheme: colorschemes/nonexistent',
'exception:test:powerline:Failed to load colorscheme: colorschemes/test/__main__',
'exception:test:powerline:Failed to load colorscheme: colorschemes/test/nonexistent',
'exception:test:powerline:Failed to create renderer: colorschemes/test/nonexistent'
])
config['config']['ext']['test']['colorscheme'] = '2' config['config']['ext']['test']['colorscheme'] = '2'
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<2 3 1> s <3 4 False>>><1 4 4>g <4 False False>>><None None None>') self.assertEqual(p.render(), '<2 3 1> s <3 4 False>>><1 4 4>g <4 False False>>><None None None>')
self.assertAccessEvents('config', 'colorschemes/test/2') self.assertAccessEvents('config', 'colorschemes/2', 'colorschemes/test/__main__', 'colorschemes/test/2')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
config['config']['ext']['test']['theme'] = '2' config['config']['ext']['test']['theme'] = '2'
@ -170,7 +175,7 @@ class TestConfigReload(TestCase):
with get_powerline(run_once=False) as p: with get_powerline(run_once=False) as p:
with replace_item(globals(), 'config', deepcopy(config)): with replace_item(globals(), 'config', deepcopy(config)):
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['config']['ext']['test']['colorscheme'] = 'nonexistentraise' config['config']['ext']['test']['colorscheme'] = 'nonexistentraise'
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
@ -192,7 +197,7 @@ class TestConfigReload(TestCase):
}, },
} }
while not p._will_create_renderer(): while not p._will_create_renderer():
sleep(0.000001) sleep(0.1)
self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('colorschemes/test/nonexistentraise') self.assertAccessEvents('colorschemes/test/nonexistentraise')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
@ -202,7 +207,7 @@ class TestConfigReload(TestCase):
with get_powerline(run_once=False) as p: with get_powerline(run_once=False) as p:
with replace_item(globals(), 'config', deepcopy(config)): with replace_item(globals(), 'config', deepcopy(config)):
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['colors']['colors']['col1'] = 5 config['colors']['colors']['col1'] = 5
add_watcher_events(p, 'colors') add_watcher_events(p, 'colors')
@ -215,12 +220,12 @@ class TestConfigReload(TestCase):
with get_powerline(run_once=False) as p: with get_powerline(run_once=False) as p:
with replace_item(globals(), 'config', deepcopy(config)): with replace_item(globals(), 'config', deepcopy(config)):
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3' config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3'
add_watcher_events(p, 'colorschemes/test/default') add_watcher_events(p, 'colorschemes/test/default')
self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('colorschemes/test/default') self.assertAccessEvents('colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
pop_events() pop_events()
@ -228,7 +233,7 @@ class TestConfigReload(TestCase):
with get_powerline(run_once=False) as p: with get_powerline(run_once=False) as p:
with replace_item(globals(), 'config', deepcopy(config)): with replace_item(globals(), 'config', deepcopy(config)):
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default') add_watcher_events(p, 'themes/test/default')
@ -242,7 +247,7 @@ class TestConfigReload(TestCase):
config['config']['common']['interval'] = None config['config']['common']['interval'] = None
with get_powerline(run_once=False) as p: with get_powerline(run_once=False) as p:
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default', wait=False) add_watcher_events(p, 'themes/test/default', wait=False)
@ -257,7 +262,7 @@ class TestConfigReload(TestCase):
config['config']['common']['interval'] = None config['config']['common']['interval'] = None
with get_powerline(run_once=True) as p: with get_powerline(run_once=True) as p:
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default', wait=False) add_watcher_events(p, 'themes/test/default', wait=False)