Merge pull request #923 from ZyX-I/merge-config

Implement configuration merging
This commit is contained in:
Nikolai Aleksandrovich Pavlov 2014-08-05 14:05:23 +04:00
commit c95b288527
10 changed files with 1036 additions and 548 deletions

View File

@ -30,6 +30,24 @@ corresponds to :file:`~/.config/powerline` on both platforms.
If you need per-instance configuration please refer to :ref:`Local configuration
overrides <local-configuration-overrides>`.
.. note:: If you have multiple configuration files with the same name in
different directories then these files will be merged. Merging happens in
the following order:
* :file:`{powerline_root}/powerline/config_files` is checked for
configuration first. Configuration from this source has least priority.
* :file:`$XDG_CONFIG_DIRS/powerline` directories are the next ones to check.
Checking happens in the reversed order: directories mentioned last are
checked before directories mentioned first. Each new found file is merged
with the result of previous merge.
* :file:`$XDG_CONFIG_HOME/powerline` directory is the last to check.
Configuration from there has top priority.
When merging configuration only dictionaries are merged and they are merged
recursively: keys from next file overrule those from the previous unless
corresponding values are both dictionaries in which case these dictionaries
are merged and key is assigned the result of the merge.
.. _quick-guide:
Quick setup guide

View File

@ -14,13 +14,25 @@ from powerline.lib import mergedicts
from threading import Lock, Event
def _find_config_file(search_paths, config_file):
def _config_loader_condition(path):
return path and os.path.isfile(path)
def _find_config_files(search_paths, config_file, config_loader=None, loader_callback=None):
config_file += '.json'
found = False
for path in search_paths:
config_file_path = os.path.join(path, config_file)
if os.path.isfile(config_file_path):
return config_file_path
raise IOError('Config file not found in search path: {0}'.format(config_file))
yield config_file_path
found = True
elif config_loader:
config_loader.register_missing(_config_loader_condition, loader_callback, config_file_path)
if not found:
raise IOError('Config file not found in search paths ({0}): {1}'.format(
', '.join(search_paths),
config_file
))
class PowerlineLogger(object):
@ -103,14 +115,14 @@ def get_config_paths():
config_paths = [config_path]
config_dirs = os.environ.get('XDG_CONFIG_DIRS', DEFAULT_SYSTEM_CONFIG_DIR)
if config_dirs is not None:
config_paths.extend([os.path.join(d, 'powerline') for d in config_dirs.split(':')])
config_paths[:0] = reversed([os.path.join(d, 'powerline') for d in config_dirs.split(':')])
plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files')
config_paths.append(plugin_path)
config_paths.insert(0, plugin_path)
return config_paths
def generate_config_finder(get_config_paths=get_config_paths):
'''Generate find_config_file function
'''Generate find_config_files function
This function will find .json file given its path.
@ -123,17 +135,17 @@ def generate_config_finder(get_config_paths=get_config_paths):
to it or raise IOError if it failed to find the file.
'''
config_paths = get_config_paths()
return lambda cfg_path: _find_config_file(config_paths, cfg_path)
return lambda *args: _find_config_files(config_paths, *args)
def load_config(cfg_path, find_config_file, config_loader, loader_callback=None):
def load_config(cfg_path, find_config_files, config_loader, loader_callback=None):
'''Load configuration file and setup watches
Watches are only set up if loader_callback is not None.
:param str cfg_path:
Path for configuration file that should be loaded.
:param function find_config_file:
:param function find_config_files:
Function that finds configuration file. Check out the description of
the return value of ``generate_config_finder`` function.
:param ConfigLoader config_loader:
@ -144,16 +156,16 @@ def load_config(cfg_path, find_config_file, config_loader, loader_callback=None)
:return: Configuration file contents.
'''
try:
path = find_config_file(cfg_path)
except IOError:
if loader_callback:
config_loader.register_missing(find_config_file, loader_callback, cfg_path)
raise
else:
found_files = find_config_files(cfg_path, config_loader, loader_callback)
ret = None
for path in found_files:
if loader_callback:
config_loader.register(loader_callback, path)
return config_loader.load(path)
if ret is None:
ret = config_loader.load(path)
else:
mergedicts(ret, config_loader.load(path))
return ret
def _get_log_handler(common_config):
@ -286,7 +298,7 @@ class Powerline(object):
elif self.renderer_module[-1] == '.':
self.renderer_module = self.renderer_module[:-1]
self.find_config_file = generate_config_finder(self.get_config_paths)
self.find_config_files = generate_config_finder(self.get_config_paths)
self.cr_kwargs_lock = Lock()
self.cr_kwargs = {}
@ -437,7 +449,7 @@ class Powerline(object):
'''Load configuration and setup watches.'''
return load_config(
cfg_path,
self.find_config_file,
self.find_config_files,
self.config_loader,
self.cr_callbacks[type]
)
@ -445,7 +457,7 @@ class Powerline(object):
def _purge_configs(self, type):
function = self.cr_callbacks[type]
self.config_loader.unregister_functions(set((function,)))
self.config_loader.unregister_missing(set(((self.find_config_file, function),)))
self.config_loader.unregister_missing(set(((self.find_config_files, function),)))
def load_theme_config(self, name):
'''Get theme configuration.
@ -601,7 +613,7 @@ class Powerline(object):
pass
functions = tuple(self.cr_callbacks.values())
self.config_loader.unregister_functions(set(functions))
self.config_loader.unregister_missing(set(((self.find_config_file, function) for function in functions)))
self.config_loader.unregister_missing(set(((self.find_config_files, function) for function in functions)))
def __enter__(self):
return self

View File

@ -119,9 +119,9 @@ def source_tmux_files(pl, args):
def create_powerline_logger(args):
find_config_file = generate_config_finder()
find_config_files = generate_config_finder()
config_loader = ConfigLoader(run_once=True)
config = load_config('config', find_config_file, config_loader)
config = load_config('config', find_config_files, config_loader)
common_config = finish_common_config(config['common'])
logger = create_logger(common_config)
return PowerlineLogger(use_daemon_threads=True, logger=logger, ext='config')

View File

@ -1,8 +1,8 @@
# vim:fileencoding=utf-8:noet
from powerline.lint.markedjson import load
from powerline import generate_config_finder, get_config_paths
from powerline.lib.config import load_json_config
from powerline import generate_config_finder, get_config_paths, load_config
from powerline.lib.config import ConfigLoader
from powerline.lint.markedjson.error import echoerr, MarkedError
from powerline.segments.vim import vim_modes
from powerline.lint.inspect import getconfigargspec
@ -159,7 +159,13 @@ class Spec(object):
for item in value:
if isinstance(item_func, int):
spec = self.specs[item_func]
proceed, fhadproblem = spec.match(item, value.mark, data, context + (('list item ' + unicode(i), item),), echoerr)
proceed, fhadproblem = spec.match(
item,
value.mark,
data,
context + (('list item ' + unicode(i), item),),
echoerr
)
else:
proceed, echo, fhadproblem = item_func(item, data, context, echoerr)
if echo and fhadproblem:
@ -192,7 +198,13 @@ class Spec(object):
def check_tuple(self, value, context_mark, data, context, echoerr, start, end):
hadproblem = False
for (i, item, spec) in zip(itertools.count(), value, self.specs[start:end]):
proceed, ihadproblem = spec.match(item, value.mark, data, context + (('tuple item ' + unicode(i), item),), echoerr)
proceed, ihadproblem = spec.match(
item,
value.mark,
data,
context + (('tuple item ' + unicode(i), item),),
echoerr
)
if ihadproblem:
hadproblem = True
if not proceed:
@ -221,10 +233,16 @@ class Spec(object):
def len(self, comparison, cint, msg_func=None):
cmp_func = self.cmp_funcs[comparison]
msg_func = msg_func or (lambda value: 'length of {0!r} is not {1} {2}'.format(value, self.cmp_msgs[comparison], cint))
self.checks.append(('check_func',
(lambda value, *args: (True, True, not cmp_func(len(value), cint))),
msg_func))
msg_func = (
msg_func
or (lambda value: 'length of {0!r} is not {1} {2}'.format(
value, self.cmp_msgs[comparison], cint))
)
self.checks.append((
'check_func',
(lambda value, *args: (True, True, not cmp_func(len(value), cint))),
msg_func
))
return self
def cmp(self, comparison, cint, msg_func=None):
@ -236,16 +254,20 @@ class Spec(object):
self.type(type(cint))
cmp_func = self.cmp_funcs[comparison]
msg_func = msg_func or (lambda value: '{0} is not {1} {2}'.format(value, self.cmp_msgs[comparison], cint))
self.checks.append(('check_func',
(lambda value, *args: (True, True, not cmp_func(value.value, cint))),
msg_func))
self.checks.append((
'check_func',
(lambda value, *args: (True, True, not cmp_func(value.value, cint))),
msg_func
))
return self
def unsigned(self, msg_func=None):
self.type(int)
self.checks.append(('check_func',
(lambda value, *args: (True, True, value < 0)),
lambda value: '{0} must be greater then zero'.format(value)))
self.checks.append((
'check_func',
(lambda value, *args: (True, True, value < 0)),
(lambda value: '{0} must be greater then zero'.format(value))
))
return self
def list(self, item_func, msg_func=None):
@ -286,25 +308,35 @@ class Spec(object):
self.type(unicode)
compiled = re.compile(regex)
msg_func = msg_func or (lambda value: 'String "{0}" does not match "{1}"'.format(value, regex))
self.checks.append(('check_func',
(lambda value, *args: (True, True, not compiled.match(value.value))),
msg_func))
self.checks.append((
'check_func',
(lambda value, *args: (True, True, not compiled.match(value.value))),
msg_func
))
return self
def ident(self, msg_func=None):
msg_func = msg_func or (lambda value: 'String "{0}" is not an alphanumeric/underscore identifier'.format(value))
msg_func = (
msg_func
or (lambda value: 'String "{0}" is not an alphanumeric/underscore identifier'.format(value))
)
return self.re('^\w+$', msg_func)
def oneof(self, collection, msg_func=None):
msg_func = msg_func or (lambda value: '"{0}" must be one of {1!r}'.format(value, list(collection)))
self.checks.append(('check_func',
lambda value, *args: (True, True, value not in collection),
msg_func))
self.checks.append((
'check_func',
(lambda value, *args: (True, True, value not in collection)),
msg_func
))
return self
def error(self, msg):
self.checks.append(('check_func', lambda *args: (True, True, True),
lambda value: msg.format(value)))
self.checks.append((
'check_func',
(lambda *args: (True, True, True)),
(lambda value: msg.format(value))
))
return self
def either(self, *specs):
@ -334,7 +366,13 @@ class Spec(object):
for key, vali in self.keys.items():
valspec = self.specs[vali]
if key in value:
proceed, mhadproblem = valspec.match(value[key], value.mark, data, context + ((key, value[key]),), echoerr)
proceed, mhadproblem = valspec.match(
value[key],
value.mark,
data,
context + ((key, value[key]),),
echoerr
)
if mhadproblem:
hadproblem = True
if not proceed:
@ -358,7 +396,13 @@ class Spec(object):
if khadproblem:
hadproblem = True
if proceed:
proceed, vhadproblem = valspec.match(value[key], value.mark, data, context + ((key, value[key]),), echoerr)
proceed, vhadproblem = valspec.match(
value[key],
value.mark,
data,
context + ((key, value[key]),),
echoerr
)
if vhadproblem:
hadproblem = True
break
@ -416,9 +460,14 @@ def check_matcher_func(ext, match_name, data, context, echoerr):
if hasattr(func, 'func_code') and hasattr(func.func_code, 'co_argcount'):
if func.func_code.co_argcount != 1:
echoerr(context='Error while loading matcher functions',
problem='function {0} accepts {1} arguments instead of 1. Are you sure it is the proper function?'.format(match_function, func.func_code.co_argcount),
problem_mark=match_name.mark)
echoerr(
context='Error while loading matcher functions',
problem=(
'function {0} accepts {1} arguments instead of 1. '
'Are you sure it is the proper function?'
).format(match_function, func.func_code.co_argcount),
problem_mark=match_name.mark
)
return True, False
@ -477,17 +526,30 @@ main_spec = (Spec(
left=divside_spec(),
right=divside_spec(),
),
spaces=Spec().unsigned().cmp('le', 2,
lambda value: 'Are you sure you need such a big ({0}) number of spaces?'.format(value)),
spaces=Spec().unsigned().cmp(
'le', 2, lambda value: 'Are you sure you need such a big ({0}) number of spaces?'.format(value)
),
term_truecolor=Spec().type(bool).optional(),
# Python is capable of loading from zip archives. Thus checking path
# only for existence of the path, not for it being a directory
paths=Spec().list((lambda value, *args: (True, True, not os.path.exists(os.path.expanduser(value.value)))),
lambda value: 'path does not exist: {0}'.format(value)).optional(),
log_file=Spec().type(unicode).func(lambda value, *args: (True, True, not os.path.isdir(os.path.dirname(os.path.expanduser(value)))),
lambda value: 'directory does not exist: {0}'.format(os.path.dirname(value))).optional(),
log_level=Spec().re('^[A-Z]+$').func(lambda value, *args: (True, True, not hasattr(logging, value)),
lambda value: 'unknown debugging level {0}'.format(value)).optional(),
paths=Spec().list(
(lambda value, *args: (True, True, not os.path.exists(os.path.expanduser(value.value)))),
(lambda value: 'path does not exist: {0}'.format(value))
).optional(),
log_file=Spec().type(unicode).func(
(
lambda value, *args: (
True,
True,
not os.path.isdir(os.path.dirname(os.path.expanduser(value)))
)
),
(lambda value: 'directory does not exist: {0}'.format(os.path.dirname(value)))
).optional(),
log_level=Spec().re('^[A-Z]+$').func(
(lambda value, *args: (True, True, not hasattr(logging, value))),
(lambda value: 'unknown debugging level {0}'.format(value))
).optional(),
log_format=Spec().type(unicode).optional(),
interval=Spec().either(Spec().cmp('gt', 0.0), Spec().type(type(None))).optional(),
reload_config=Spec().type(bool).optional(),
@ -497,8 +559,9 @@ main_spec = (Spec(
vim=Spec(
colorscheme=colorscheme_spec(),
theme=theme_spec(),
local_themes=Spec()
.unknown_spec(lambda *args: check_matcher_func('vim', *args), theme_spec())
local_themes=Spec().unknown_spec(
lambda *args: check_matcher_func('vim', *args), theme_spec()
),
).optional(),
ipython=Spec(
colorscheme=colorscheme_spec(),
@ -517,24 +580,28 @@ main_spec = (Spec(
select=theme_spec(),
),
).optional(),
).unknown_spec(check_ext,
Spec(
colorscheme=colorscheme_spec(),
theme=theme_spec(),
))
.context_message('Error while loading extensions configuration (key {key})'),
).unknown_spec(
check_ext,
Spec(
colorscheme=colorscheme_spec(),
theme=theme_spec(),
)
).context_message('Error while loading extensions configuration (key {key})'),
).context_message('Error while loading main configuration'))
term_color_spec = Spec().unsigned().cmp('le', 255).copy
true_color_spec = Spec().re('^[0-9a-fA-F]{6}$',
lambda value: '"{0}" is not a six-digit hexadecimal unsigned integer written as a string'.format(value)).copy
true_color_spec = Spec().re(
'^[0-9a-fA-F]{6}$',
(lambda value: '"{0}" is not a six-digit hexadecimal unsigned integer written as a string'.format(value))
).copy
colors_spec = (Spec(
colors=Spec().unknown_spec(
Spec().ident(),
Spec().either(
Spec().tuple(term_color_spec(), true_color_spec()),
term_color_spec()))
.context_message('Error while checking colors (key {key})'),
term_color_spec()
)
).context_message('Error while checking colors (key {key})'),
gradients=Spec().unknown_spec(
Spec().ident(),
Spec().tuple(
@ -546,10 +613,14 @@ colors_spec = (Spec(
def check_color(color, data, context, echoerr):
if color not in data['colors_config'].get('colors', {}) and color not in data['colors_config'].get('gradients', {}):
echoerr(context='Error while checking highlight group in colorscheme (key {key})'.format(key=context_key(context)),
problem='found unexistent color or gradient {0}'.format(color),
problem_mark=color.mark)
if (color not in data['colors_config'].get('colors', {})
and color not in data['colors_config'].get('gradients', {})):
echoerr(
context='Error while checking highlight group in colorscheme (key {key})'.format(
key=context_key(context)),
problem='found unexistent color or gradient {0}'.format(color),
problem_mark=color.mark
)
return True, False, True
return True, False, False
@ -618,9 +689,13 @@ def check_group(group, data, context, echoerr):
if not proceed:
break
if not_found:
new_echoerr(context='Error while checking group definition in colorscheme (key {key})'.format(key=context_key(context)),
problem='name {0} is not present in {1} {2} colorschemes: {3}'.format(group, tofind, ext, ', '.join(not_found)),
problem_mark=group.mark)
new_echoerr(
context='Error while checking group definition in colorscheme (key {key})'.format(
key=context_key(context)),
problem='name {0} is not present in {1} {2} colorschemes: {3}'.format(
group, tofind, ext, ', '.join(not_found)),
problem_mark=group.mark
)
new_echoerr.echo_all()
return True, False, hadproblem
@ -717,25 +792,34 @@ def check_key_compatibility(segment, data, context, echoerr):
keys = set(segment)
if not ((keys - generic_keys) < type_keys[segment_type]):
unknown_keys = keys - generic_keys - type_keys[segment_type]
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
context_mark=context[-1][1].mark,
problem='found keys not used with the current segment type: {0}'.format(
list_sep.join(unknown_keys)),
problem_mark=list(unknown_keys)[0].mark)
echoerr(
context='Error while checking segments (key {key})'.format(key=context_key(context)),
context_mark=context[-1][1].mark,
problem='found keys not used with the current segment type: {0}'.format(
list_sep.join(unknown_keys)),
problem_mark=list(unknown_keys)[0].mark
)
hadproblem = True
if not (keys >= required_keys[segment_type]):
missing_keys = required_keys[segment_type] - keys
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
context_mark=context[-1][1].mark,
problem='found missing required keys: {0}'.format(
list_sep.join(missing_keys)))
echoerr(
context='Error while checking segments (key {key})'.format(key=context_key(context)),
context_mark=context[-1][1].mark,
problem='found missing required keys: {0}'.format(
list_sep.join(missing_keys))
)
hadproblem = True
if not (segment_type == 'function' or (keys & highlight_keys)):
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
context_mark=context[-1][1].mark,
problem='found missing keys required to determine highlight group. Either highlight_group or name key must be present')
echoerr(
context='Error while checking segments (key {key})'.format(key=context_key(context)),
context_mark=context[-1][1].mark,
problem=(
'found missing keys required to determine highlight group. '
'Either highlight_group or name key must be present'
)
)
hadproblem = True
return True, False, hadproblem
@ -842,35 +926,52 @@ def check_segment_name(name, data, context, echoerr):
if divider_hl_group:
r = hl_exists(divider_hl_group, data, context, echoerr, allow_gradients=True)
if r:
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}\n(Group name was obtained from function documentation.)'.format(
divider_hl_group, list_sep.join(r)),
problem_mark=name.mark)
echoerr(
context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem=(
'found highlight group {0} not defined in the following colorschemes: {1}\n'
'(Group name was obtained from function documentation.)'
).format(divider_hl_group, list_sep.join(r)),
problem_mark=name.mark
)
hadproblem = True
if hl_groups:
greg = re.compile(r'``([^`]+)``( \(gradient\))?')
hl_groups = [[greg.match(subs).groups() for subs in s.split(' or ')] for s in (list_sep.join(hl_groups)).split(', ')]
hl_groups = [[greg.match(subs).groups() for subs in s.split(' or ')]
for s in (list_sep.join(hl_groups)).split(', ')]
for required_pack in hl_groups:
rs = [hl_exists(hl_group, data, context, echoerr, allow_gradients=('force' if gradient else False))
for hl_group, gradient in required_pack]
if all(rs):
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight groups list ({0}) with all groups not defined in some colorschemes\n(Group names were taken from function documentation.)'.format(
list_sep.join((h[0] for h in required_pack))),
problem_mark=name.mark)
echoerr(
context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem=(
'found highlight groups list ({0}) with all groups not defined in some colorschemes\n'
'(Group names were taken from function documentation.)'
).format(list_sep.join((h[0] for h in required_pack))),
problem_mark=name.mark
)
for r, h in zip(rs, required_pack):
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
h[0], list_sep.join(r)))
echoerr(
context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
h[0], list_sep.join(r))
)
hadproblem = True
else:
r = hl_exists(name, data, context, echoerr, allow_gradients=True)
if r:
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}\n(If not specified otherwise in documentation, highlight group for function segments\nis the same as the function name.)'.format(
name, list_sep.join(r)),
problem_mark=name.mark)
echoerr(
context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem=(
'found highlight group {0} not defined in the following colorschemes: {1}\n'
'(If not specified otherwise in documentation, '
'highlight group for function segments\n'
'is the same as the function name.)'
).format(name, list_sep.join(r)),
problem_mark=name.mark
)
hadproblem = True
return True, False, hadproblem
@ -915,17 +1016,23 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False):
if hasgradient:
hadgradient = True
if allow_gradients is False and not hascolor and hasgradient:
echoerr(context='Error while checking highlight group in theme (key {key})'.format(key=context_key(context)),
context_mark=getattr(hl_group, 'mark', None),
problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color),
problem_mark=color.mark)
echoerr(
context='Error while checking highlight group in theme (key {key})'.format(
key=context_key(context)),
context_mark=getattr(hl_group, 'mark', None),
problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color),
problem_mark=color.mark
)
r.append(colorscheme)
continue
if allow_gradients == 'force' and not hadgradient:
echoerr(context='Error while checking highlight group in theme (key {key})'.format(key=context_key(context)),
context_mark=getattr(hl_group, 'mark', None),
problem='group {0} should have at least one gradient color, but it has no'.format(hl_group),
problem_mark=group_config.mark)
echoerr(
context='Error while checking highlight group in theme (key {key})'.format(
key=context_key(context)),
context_mark=getattr(hl_group, 'mark', None),
problem='group {0} should have at least one gradient color, but it has no'.format(hl_group),
problem_mark=group_config.mark
)
r.append(colorscheme)
return r
@ -933,10 +1040,12 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False):
def check_highlight_group(hl_group, data, context, echoerr):
r = hl_exists(hl_group, data, context, echoerr)
if r:
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
hl_group, list_sep.join(r)),
problem_mark=hl_group.mark)
echoerr(
context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
hl_group, list_sep.join(r)),
problem_mark=hl_group.mark
)
return True, False, True
return True, False, False
@ -944,15 +1053,19 @@ def check_highlight_group(hl_group, data, context, echoerr):
def check_highlight_groups(hl_groups, data, context, echoerr):
rs = [hl_exists(hl_group, data, context, echoerr) for hl_group in hl_groups]
if all(rs):
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight groups list ({0}) with all groups not defined in some colorschemes'.format(
list_sep.join((unicode(h) for h in hl_groups))),
problem_mark=hl_groups.mark)
echoerr(
context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight groups list ({0}) with all groups not defined in some colorschemes'.format(
list_sep.join((unicode(h) for h in hl_groups))),
problem_mark=hl_groups.mark
)
for r, hl_group in zip(rs, hl_groups):
echoerr(context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
echoerr(
context='Error while checking theme (key {key})'.format(key=context_key(context)),
problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
hl_group, list_sep.join(r)),
problem_mark=hl_group.mark)
problem_mark=hl_group.mark
)
return True, False, True
return True, False, False
@ -1005,9 +1118,11 @@ def check_args_variant(segment, args, data, context, echoerr):
hadproblem = False
if required_args - present_args:
echoerr(context='Error while checking segment arguments (key {key})'.format(key=context_key(context)),
context_mark=args.mark,
problem='some of the required keys are missing: {0}'.format(list_sep.join(required_args - present_args)))
echoerr(
context='Error while checking segment arguments (key {key})'.format(key=context_key(context)),
context_mark=args.mark,
problem='some of the required keys are missing: {0}'.format(list_sep.join(required_args - present_args))
)
hadproblem = True
if not all_args >= present_args:
@ -1019,7 +1134,13 @@ def check_args_variant(segment, args, data, context, echoerr):
if isinstance(segment, ThreadedSegment):
for key in set(threaded_args_specs) & present_args:
proceed, khadproblem = threaded_args_specs[key].match(args[key], args.mark, data, context + ((key, args[key]),), echoerr)
proceed, khadproblem = threaded_args_specs[key].match(
args[key],
args.mark,
data,
context + ((key, args[key]),),
echoerr
)
if khadproblem:
hadproblem = True
if not proceed:
@ -1070,7 +1191,10 @@ def get_all_possible_segments(data, context, echoerr):
for segments in theme_config.get('segments', {}).values():
for segment in segments:
if segment.get('type', 'function') == 'function':
module = segment.get('module', context[0][1].get('default_module', 'powerline.segments.' + data['ext']))
module = segment.get(
'module',
context[0][1].get('default_module', 'powerline.segments.' + data['ext'])
)
func = import_segment(name, data, context, echoerr, module=module)
if func:
yield func
@ -1085,7 +1209,7 @@ segment_module_spec = Spec().type(unicode).func(check_segment_module).optional()
sub_segments_spec = Spec()
segment_spec = Spec(
type=Spec().oneof(type_keys).optional(),
name=Spec().re('^[a-zA-Z_]\w+$').func(check_segment_name).optional(),
name=Spec().re('^[a-zA-Z_]\w*$').func(check_segment_name).optional(),
exclude_modes=Spec().list(vim_mode_spec()).optional(),
include_modes=Spec().list(vim_mode_spec()).optional(),
draw_hard_divider=Spec().type(bool).optional(),
@ -1101,11 +1225,15 @@ segment_spec = Spec(
args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_variant, *args, **kwargs)),
contents=Spec().type(unicode).optional(),
highlight_group=Spec().list(
highlight_group_spec().re('^(?:(?!:divider$).)+$',
lambda value: 'it is recommended that only divider highlight group names end with ":divider"')
highlight_group_spec().re(
'^(?:(?!:divider$).)+$',
(lambda value: 'it is recommended that only divider highlight group names end with ":divider"')
)
).func(check_highlight_groups).optional(),
divider_highlight_group=highlight_group_spec().func(check_highlight_group).re(':divider$',
lambda value: 'it is recommended that divider highlight group names end with ":divider"').optional(),
divider_highlight_group=highlight_group_spec().func(check_highlight_group).re(
':divider$',
(lambda value: 'it is recommended that divider highlight group names end with ":divider"')
).optional(),
segments=sub_segments_spec,
).func(check_full_segment_data)
sub_segments_spec.optional().list(segment_spec)
@ -1114,8 +1242,8 @@ segdict_spec=Spec(
left=segments_spec().context_message('Error while loading segments from left side (key {key})'),
right=segments_spec().context_message('Error while loading segments from right side (key {key})'),
).func(
lambda value, *args: (True, True, not (('left' in value) or ('right' in value))),
lambda value: 'segments dictionary must contain either left, right or both keys'
(lambda value, *args: (True, True, not (('left' in value) or ('right' in value)))),
(lambda value: 'segments dictionary must contain either left, right or both keys')
).context_message('Error while loading segments (key {key})').copy
theme_spec = (Spec(
default_module=segment_module_spec(),
@ -1133,9 +1261,19 @@ theme_spec = (Spec(
).context_message('Error while loading theme'))
def generate_json_config_loader(lhadproblem):
def load_json_config(config_file_path, load=load, open_file=open_file):
with open_file(config_file_path) as config_file_fp:
r, hadproblem = load(config_file_fp)
if hadproblem:
lhadproblem[0] = True
return r
return load_json_config
def check(paths=None, debug=False):
search_paths = paths or get_config_paths()
find_config_file = generate_config_finder(lambda: search_paths)
find_config_files = generate_config_finder(lambda: search_paths)
logger = logging.getLogger('powerline-lint')
logger.setLevel(logging.DEBUG if debug else logging.ERROR)
@ -1143,6 +1281,11 @@ def check(paths=None, debug=False):
ee = EchoErr(echoerr, logger)
lhadproblem = [False]
load_json_config = generate_json_config_loader(lhadproblem)
config_loader = ConfigLoader(run_once=True, load=load_json_config)
paths = {
'themes': defaultdict(lambda: []),
'colorschemes': defaultdict(lambda: []),
@ -1198,17 +1341,9 @@ def check(paths=None, debug=False):
typ,
))
lhadproblem = [False]
def load_config(stream):
r, hadproblem = load(stream)
if hadproblem:
lhadproblem[0] = True
return r
hadproblem = False
try:
main_config = load_json_config(find_config_file('config'), load=load_config, open_file=open_file)
main_config = load_config('config', find_config_files, config_loader)
except IOError:
main_config = {}
sys.stderr.write('\nConfiguration file not found: config.json\n')
@ -1218,13 +1353,18 @@ def check(paths=None, debug=False):
sys.stderr.write(str(e) + '\n')
hadproblem = True
else:
if main_spec.match(main_config, data={'configs': configs, 'lists': lists}, context=(('', main_config),), echoerr=ee)[1]:
if main_spec.match(
main_config,
data={'configs': configs, 'lists': lists},
context=(('', main_config),),
echoerr=ee
)[1]:
hadproblem = True
import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])]
try:
colors_config = load_json_config(find_config_file('colors'), load=load_config, open_file=open_file)
colors_config = load_config('colors', find_config_files, config_loader)
except IOError:
colors_config = {}
sys.stderr.write('\nConfiguration file not found: colors.json\n')
@ -1330,8 +1470,14 @@ def check(paths=None, debug=False):
theme_configs[ext][theme] = config
for ext, configs in theme_configs.items():
data = {'ext': ext, 'colorscheme_configs': colorscheme_configs, 'import_paths': import_paths,
'main_config': main_config, 'ext_theme_configs': configs, 'colors_config': colors_config}
data = {
'ext': ext,
'colorscheme_configs': colorscheme_configs,
'import_paths': import_paths,
'main_config': main_config,
'ext_theme_configs': configs,
'colors_config': colors_config
}
for theme, config in configs.items():
data['theme'] = theme
if theme_spec.match(config, context=(('', config),), data=data, echoerr=ee)[1]:

View File

@ -16,8 +16,21 @@ def gen_new(cls):
return __new__
def gen_init(cls):
def __init__(self, value, mark):
return cls.__init__(self, value)
return __init__
def gen_getnewargs(cls):
def __getnewargs__(self):
return (self.value, self.mark)
return __getnewargs__
class MarkedUnicode(unicode):
__new__ = gen_new(unicode)
__getnewargs__ = gen_getnewargs(unicode)
def _proc_partition(self, part_result):
pointdiff = 1
@ -41,33 +54,43 @@ class MarkedUnicode(unicode):
class MarkedInt(int):
__new__ = gen_new(int)
__getnewargs__ = gen_getnewargs(int)
class MarkedFloat(float):
__new__ = gen_new(float)
__getnewargs__ = gen_getnewargs(float)
class MarkedDict(dict):
__new__ = gen_new(dict)
def __init__(self, value, mark):
super(MarkedDict, self).__init__(value)
__init__ = gen_init(dict)
__getnewargs__ = gen_getnewargs(dict)
def copy(self):
return MarkedDict(super(MarkedDict, self).copy(), self.mark)
class MarkedList(list):
__new__ = gen_new(list)
__init__ = gen_init(list)
__getnewargs__ = gen_getnewargs(list)
class MarkedValue:
def __init__(self, value, mark):
self.mark = mark
self.value = value
__getinitargs__ = gen_getnewargs(None)
specialclasses = {
unicode: MarkedUnicode,
int: MarkedInt,
float: MarkedFloat,
dict: MarkedDict,
list: MarkedList,
}
classcache = {}

View File

@ -3,37 +3,53 @@ from threading import Lock
from powerline.renderer import Renderer
from powerline.lib.config import ConfigLoader
from powerline import Powerline
from tests.lib import Args, replace_attr
from copy import deepcopy
from time import sleep
from functools import wraps
import os
access_log = []
access_lock = Lock()
class TestHelpers(object):
def __init__(self, config):
self.config = config
self.access_log = []
self.access_lock = Lock()
def loader_condition(self, path):
return (path in self.config) and path
def load_json_config(config_file_path, *args, **kwargs):
global access_log
with access_lock:
access_log.append(config_file_path)
try:
return deepcopy(config_container['config'][config_file_path])
except KeyError:
raise IOError(config_file_path)
def find_config_files(self, cfg_path, config_loader, loader_callback):
if cfg_path.endswith('.json'):
cfg_path = cfg_path[:-5]
if cfg_path.startswith('/'):
cfg_path = cfg_path.lstrip('/')
with self.access_lock:
self.access_log.append('check:' + cfg_path)
if cfg_path in self.config:
yield cfg_path
else:
if config_loader:
config_loader.register_missing(self.loader_condition, loader_callback, cfg_path)
raise IOError(('fcf:' if cfg_path.endswith('raise') else '') + cfg_path)
def load_json_config(self, config_file_path, *args, **kwargs):
if config_file_path.endswith('.json'):
config_file_path = config_file_path[:-5]
if config_file_path.startswith('/'):
config_file_path = config_file_path.lstrip('/')
with self.access_lock:
self.access_log.append('load:' + config_file_path)
try:
return deepcopy(self.config[config_file_path])
except KeyError:
raise IOError(config_file_path)
def _find_config_file(config, search_paths, config_file):
if config_file.endswith('raise') and config_file not in config:
raise IOError('fcf:' + config_file)
return config_file
def pop_events():
global access_log
with access_lock:
r = access_log[:]
access_log = []
return r
def pop_events(self):
with self.access_lock:
r = self.access_log[:]
self.access_log = []
return r
def log_call(func):
@ -44,7 +60,7 @@ def log_call(func):
return ret
class Watcher(object):
class TestWatcher(object):
events = set()
lock = Lock()
@ -109,20 +125,46 @@ class EvenSimplerRenderer(Renderer):
class TestPowerline(Powerline):
_created = False
def __init__(self, _helpers, **kwargs):
super(TestPowerline, self).__init__(**kwargs)
self._helpers = _helpers
self.find_config_files = _helpers.find_config_files
@staticmethod
def get_local_themes(local_themes):
return local_themes
@staticmethod
def get_config_paths():
return ['']
def _will_create_renderer(self):
return self.cr_kwargs
renderer = SimpleRenderer
def _pop_events(self):
return self._helpers.pop_events()
def get_powerline(**kwargs):
renderer = EvenSimplerRenderer
class TestConfigLoader(ConfigLoader):
def __init__(self, _helpers, **kwargs):
watcher = TestWatcher()
super(TestConfigLoader, self).__init__(
load=_helpers.load_json_config,
watcher=watcher,
watcher_type='test',
**kwargs
)
def get_powerline(config, **kwargs):
helpers = TestHelpers(config)
return get_powerline_raw(
helpers,
TestPowerline,
_helpers=helpers,
ext='test',
renderer_module='tests.lib.config_mock',
logger=Logger(),
@ -130,45 +172,42 @@ def get_powerline(**kwargs):
)
def get_powerline_raw(PowerlineClass, **kwargs):
def select_renderer(simpler_renderer=False):
global renderer
watcher = Watcher()
if kwargs.pop('simpler_renderer', False):
renderer = EvenSimplerRenderer
else:
renderer = SimpleRenderer
renderer = EvenSimplerRenderer if simpler_renderer else SimpleRenderer
def get_powerline_raw(helpers, PowerlineClass, **kwargs):
if not isinstance(helpers, TestHelpers):
helpers = TestHelpers(helpers)
select_renderer(kwargs.pop('simpler_renderer', False))
pl = PowerlineClass(
config_loader=ConfigLoader(
load=load_json_config,
watcher=watcher,
watcher_type='test',
config_loader=TestConfigLoader(
_helpers=helpers,
run_once=kwargs.get('run_once')
),
**kwargs
)
pl._watcher = watcher
pl._watcher = pl.config_loader.watcher
return pl
config_container = None
def swap_attributes(cfg_container, powerline_module, replaces):
global config_container
config_container = cfg_container
if not replaces:
replaces = {
'_find_config_file': lambda *args: _find_config_file(config_container['config'], *args),
}
for attr, val in replaces.items():
old_val = getattr(powerline_module, attr)
setattr(powerline_module, attr, val)
replaces[attr] = old_val
return replaces
def swap_attributes(config, powerline_module):
return replace_attr(powerline_module, 'os', Args(
path=Args(
isfile=lambda path: path.lstrip('/').replace('.json', '') in config,
join=os.path.join,
expanduser=lambda path: path,
realpath=lambda path: path,
dirname=os.path.dirname,
),
environ={},
))
def add_watcher_events(p, *args, **kwargs):
p._watcher._reset(args)
if isinstance(p._watcher, TestWatcher):
p._watcher._reset(args)
while not p._will_create_renderer():
sleep(kwargs.get('interval', 0.1))
if not kwargs.get('wait', True):

View File

@ -71,6 +71,7 @@ class TestParser(TestCase):
'-c', 'common.spaces=4',
'-t', 'default.segment_data.hostname.before=H:',
'-p', '.',
'-p', '..',
'-R', 'smth={"abc":"def"}',
], {
'ext': ['shell'],
@ -90,7 +91,7 @@ class TestParser(TestCase):
}
}
},
'config_path': ['.'],
'config_path': ['.', '..'],
'renderer_arg': {'smth': {'abc': 'def'}},
}),
(['shell', '-R', 'arg=true'], {'ext': ['shell'], 'renderer_arg': {'arg': True}}),

View File

@ -0,0 +1,263 @@
# vim:fileencoding=utf-8:noet
from __future__ import unicode_literals
from powerline import Powerline
from tests import TestCase
from tests.lib.config_mock import select_renderer
from shutil import rmtree
import os
import json
from powerline.lib import mergedicts_copy as mdc
from subprocess import check_call
from operator import add
CONFIG_DIR = 'tests/config'
root_config = lambda: {
'common': {
'dividers': {
'left': {
'hard': '#>',
'soft': '|>',
},
'right': {
'hard': '<#',
'soft': '<|',
},
},
'spaces': 0,
'interval': None,
'watcher': 'auto',
},
'ext': {
'test': {
'theme': 'default',
'colorscheme': 'default',
},
},
}
colors_config = lambda: {
'colors': {
'c1': 1,
'c2': 2,
},
'gradients': {
},
}
colorscheme_config = lambda: {
'groups': {
'g': {'fg': 'c1', 'bg': 'c2', 'attr': []},
}
}
theme_config = lambda: {
'segment_data': {
's': {
'before': 'b',
},
},
'segments': {
'left': [
{
'type': 'string',
'name': 's',
'contents': 't',
'highlight_group': ['g'],
},
],
'right': [],
}
}
main_tree = lambda: {
'1/config': root_config(),
'1/colors': colors_config(),
'1/colorschemes/default': colorscheme_config(),
'1/themes/test/default': theme_config(),
}
def mkdir_recursive(directory):
if os.path.isdir(directory):
return
mkdir_recursive(os.path.dirname(directory))
os.mkdir(directory)
class TestPowerline(Powerline):
def get_config_paths(self):
return tuple(sorted([
os.path.join(CONFIG_DIR, d)
for d in os.listdir(CONFIG_DIR)
]))
class WithConfigTree(object):
__slots__ = ('tree', 'p', 'p_kwargs')
def __init__(self, tree, p_kwargs={'run_once': True}):
self.tree = tree
self.p = None
self.p_kwargs = p_kwargs
def __enter__(self, *args):
os.mkdir(CONFIG_DIR)
for k, v in self.tree.items():
fname = os.path.join(CONFIG_DIR, k) + '.json'
mkdir_recursive(os.path.dirname(fname))
with open(fname, 'w') as F:
json.dump(v, F)
select_renderer(simpler_renderer=True)
self.p = TestPowerline(
ext='test',
renderer_module='tests.lib.config_mock',
**self.p_kwargs
)
if os.environ.get('POWERLINE_RUN_LINT_DURING_TESTS'):
try:
check_call(['scripts/powerline-lint'] + reduce(add, (
['-p', d] for d in self.p.get_config_paths()
)))
except:
self.__exit__()
raise
return self.p.__enter__(*args)
def __exit__(self, *args):
try:
rmtree(CONFIG_DIR)
finally:
if self.p:
self.p.__exit__(*args)
class TestMerging(TestCase):
def assertRenderEqual(self, p, output, **kwargs):
self.assertEqual(p.render(**kwargs).replace(' ', ' '), output)
def test_not_merged_config(self):
with WithConfigTree(main_tree()) as p:
self.assertRenderEqual(p, '{12} bt{2-}#>{--}')
def test_root_config_merging(self):
with WithConfigTree(mdc(main_tree(), {
'2/config': {
'common': {
'dividers': {
'left': {
'hard': '!>',
}
}
}
},
})) as p:
self.assertRenderEqual(p, '{12} bt{2-}!>{--}')
with WithConfigTree(mdc(main_tree(), {
'2/config': {
'common': {
'dividers': {
'left': {
'hard': '!>',
}
}
}
},
'3/config': {
'common': {
'dividers': {
'left': {
'hard': '>>',
}
}
}
},
})) as p:
self.assertRenderEqual(p, '{12} bt{2-}>>{--}')
with WithConfigTree(mdc(main_tree(), {
'2/config': {
'common': {
'spaces': 1,
}
},
'3/config': {
'common': {
'dividers': {
'left': {
'hard': '>>',
}
}
}
},
})) as p:
self.assertRenderEqual(p, '{12} bt {2-}>>{--}')
def test_colors_config_merging(self):
with WithConfigTree(mdc(main_tree(), {
'2/colors': {
'colors': {
'c1': 3,
}
},
})) as p:
self.assertRenderEqual(p, '{32} bt{2-}#>{--}')
with WithConfigTree(mdc(main_tree(), {
'2/colors': {
'colors': {
'c1': 3,
}
},
'3/colors': {
'colors': {
'c1': 4,
}
},
})) as p:
self.assertRenderEqual(p, '{42} bt{2-}#>{--}')
with WithConfigTree(mdc(main_tree(), {
'2/colors': {
'colors': {
'c1': 3,
}
},
'3/colors': {
'colors': {
'c2': 4,
}
},
})) as p:
self.assertRenderEqual(p, '{34} bt{4-}#>{--}')
def test_colorschemes_merging(self):
with WithConfigTree(mdc(main_tree(), {
'2/colorschemes/default': {
'groups': {
'g': {'fg': 'c2', 'bg': 'c1', 'attr': []},
}
},
})) as p:
self.assertRenderEqual(p, '{21} bt{1-}#>{--}')
def test_theme_merging(self):
with WithConfigTree(mdc(main_tree(), {
'2/themes/test/default': {
'segment_data': {
's': {
'after': 'a',
}
}
},
})) as p:
self.assertRenderEqual(p, '{12} bta{2-}#>{--}')
if __name__ == '__main__':
from tests import main
main()

View File

@ -1,11 +1,12 @@
# vim:fileencoding=utf-8:noet
from __future__ import unicode_literals
import powerline as powerline_module
from time import sleep
from tests import TestCase
from tests.lib import replace_item
from tests.lib.config_mock import swap_attributes, get_powerline, pop_events, add_watcher_events
from copy import deepcopy
from functools import wraps
from tests import TestCase
from tests.lib.config_mock import get_powerline, add_watcher_events
config = {
@ -93,201 +94,192 @@ config = {
}
def with_new_config(func):
@wraps(func)
def f(self):
return func(self, deepcopy(config))
return f
class TestConfigReload(TestCase):
def assertAccessEvents(self, *args):
self.assertEqual(set(pop_events()), set(args))
def assertAccessEvents(self, p, *args):
events = set()
for event in args:
if ':' not in event:
events.add('check:' + event)
events.add('load:' + event)
else:
events.add(event)
self.assertEqual(set(p._pop_events()), events)
def test_noreload(self):
with get_powerline(run_once=True) as p:
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.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['config']['common']['spaces'] = 1
add_watcher_events(p, 'config', wait=False, interval=0.05)
# When running once thread should not start
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents()
self.assertEqual(p.logger._pop_msgs(), [])
# Without the following assertion test_reload_colors may fail for
# unknown reason (with AssertionError telling about “config” accessed
# one more time then needed)
pop_events()
@with_new_config
def test_noreload(self, config):
with get_powerline(config, 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.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['config']['common']['spaces'] = 1
add_watcher_events(p, 'config', wait=False, interval=0.05)
# When running once thread should not start
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents(p)
self.assertEqual(p.logger._pop_msgs(), [])
def test_reload_main(self):
with get_powerline(run_once=False) as p:
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.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
@with_new_config
def test_reload_main(self, config):
with get_powerline(config, 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.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['config']['common']['spaces'] = 1
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.assertAccessEvents('config')
self.assertEqual(p.logger._pop_msgs(), [])
config['config']['common']['spaces'] = 1
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.assertAccessEvents(p, 'config')
self.assertEqual(p.logger._pop_msgs(), [])
config['config']['ext']['test']['theme'] = 'nonexistent'
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.assertAccessEvents('config', 'themes/test/nonexistent')
# It should normally handle file missing error
self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: themes/test/nonexistent'])
config['config']['ext']['test']['theme'] = 'nonexistent'
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.assertAccessEvents(p, 'config', 'check:themes/test/nonexistent')
# It should normally handle file missing error
self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: themes/test/nonexistent'])
config['config']['ext']['test']['theme'] = 'default'
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.assertAccessEvents('config', 'themes/test/default')
self.assertEqual(p.logger._pop_msgs(), [])
config['config']['ext']['test']['theme'] = 'default'
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.assertAccessEvents(p, 'config', 'themes/test/default')
self.assertEqual(p.logger._pop_msgs(), [])
config['config']['ext']['test']['colorscheme'] = 'nonexistent'
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.assertAccessEvents('config', 'colorschemes/nonexistent', 'colorschemes/test/__main__', 'colorschemes/test/nonexistent')
# It should normally handle file missing error
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'] = 'nonexistent'
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.assertAccessEvents(p, 'config', 'check:colorschemes/nonexistent', 'check:colorschemes/test/__main__', 'check:colorschemes/test/nonexistent')
# It should normally handle file missing error
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'
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.assertAccessEvents('config', 'colorschemes/2', 'colorschemes/test/__main__', 'colorschemes/test/2')
self.assertEqual(p.logger._pop_msgs(), [])
config['config']['ext']['test']['colorscheme'] = '2'
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.assertAccessEvents(p, 'config', 'check:colorschemes/2', 'check:colorschemes/test/__main__', 'colorschemes/test/2')
self.assertEqual(p.logger._pop_msgs(), [])
config['config']['ext']['test']['theme'] = '2'
add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>><None None None>')
self.assertAccessEvents('config', 'themes/test/2')
self.assertEqual(p.logger._pop_msgs(), [])
config['config']['ext']['test']['theme'] = '2'
add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>><None None None>')
self.assertAccessEvents(p, 'config', 'themes/test/2')
self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.renderer.local_themes, None)
config['config']['ext']['test']['local_themes'] = 'something'
add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>><None None None>')
self.assertAccessEvents('config')
self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.renderer.local_themes, 'something')
pop_events()
self.assertEqual(p.renderer.local_themes, None)
config['config']['ext']['test']['local_themes'] = 'something'
add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>><None None None>')
self.assertAccessEvents(p, 'config')
self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.renderer.local_themes, 'something')
def test_reload_unexistent(self):
with get_powerline(run_once=False) as p:
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.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
@with_new_config
def test_reload_unexistent(self, config):
with get_powerline(config, 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.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['config']['ext']['test']['colorscheme'] = 'nonexistentraise'
add_watcher_events(p, 'config')
# It may appear that p.logger._pop_msgs() is called after given
# exception is added to the mesagges, but before config_loader
# exception was added (this one:
# “exception:test:config_loader:Error while running condition
# function for key colorschemes/test/nonexistentraise:
# fcf:colorschemes/test/nonexistentraise”).
# sleep(0.1)
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
# For colorschemes/{test/,}*raise find_config_file raises
# IOError, but it does not do so for colorschemes/test/__main__,
# so powerline is trying to load this, but not other
# colorschemes/*
self.assertAccessEvents('config', 'colorschemes/test/__main__')
self.assertIn('exception:test:powerline:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs())
config['config']['ext']['test']['colorscheme'] = 'nonexistentraise'
add_watcher_events(p, 'config')
# It may appear that p.logger._pop_msgs() is called after given
# exception is added to the mesagges, but before config_loader
# exception was added (this one:
# “exception:test:config_loader:Error while running condition
# function for key colorschemes/test/nonexistentraise:
# fcf:colorschemes/test/nonexistentraise”).
# sleep(0.1)
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
# For colorschemes/{test/,}*raise find_config_file raises
# IOError, but it does not do so for check:colorschemes/test/__main__,
# so powerline is trying to load this, but not other
# colorschemes/*
self.assertAccessEvents(p, 'config', 'check:colorschemes/test/__main__', 'check:colorschemes/nonexistentraise', 'check:colorschemes/test/nonexistentraise')
self.assertIn('exception:test:powerline:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs())
config['colorschemes/nonexistentraise'] = {}
config['colorschemes/test/nonexistentraise'] = {
'groups': {
"str1": {"fg": "col1", "bg": "col3", "attr": ["bold"]},
"str2": {"fg": "col2", "bg": "col4", "attr": ["underline"]},
},
}
while not p._will_create_renderer():
sleep(0.1)
self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>><None None None>')
# Same as above
self.assertAccessEvents('colorschemes/nonexistentraise', 'colorschemes/test/nonexistentraise', 'colorschemes/test/__main__')
self.assertEqual(p.logger._pop_msgs(), [])
pop_events()
config['colorschemes/nonexistentraise'] = {}
config['colorschemes/test/nonexistentraise'] = {
'groups': {
"str1": {"fg": "col1", "bg": "col3", "attr": ["bold"]},
"str2": {"fg": "col2", "bg": "col4", "attr": ["underline"]},
},
}
while not p._will_create_renderer():
sleep(0.1)
self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>><None None None>')
# Same as above
self.assertAccessEvents(p, 'colorschemes/nonexistentraise', 'colorschemes/test/nonexistentraise', 'check:colorschemes/test/__main__')
self.assertEqual(p.logger._pop_msgs(), [])
def test_reload_colors(self):
with get_powerline(run_once=False) as p:
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.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
@with_new_config
def test_reload_colors(self, config):
with get_powerline(config, 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.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['colors']['colors']['col1'] = 5
add_watcher_events(p, 'colors')
self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('colors')
self.assertEqual(p.logger._pop_msgs(), [])
pop_events()
config['colors']['colors']['col1'] = 5
add_watcher_events(p, 'colors')
self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents(p, 'colors')
self.assertEqual(p.logger._pop_msgs(), [])
def test_reload_colorscheme(self):
with get_powerline(run_once=False) as p:
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.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
@with_new_config
def test_reload_colorscheme(self, config):
with get_powerline(config, 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.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3'
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.assertAccessEvents('colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default')
self.assertEqual(p.logger._pop_msgs(), [])
pop_events()
config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3'
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.assertAccessEvents(p, 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default')
self.assertEqual(p.logger._pop_msgs(), [])
def test_reload_theme(self):
with get_powerline(run_once=False) as p:
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.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
@with_new_config
def test_reload_theme(self, config):
with get_powerline(config, 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.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default')
self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('themes/test/default')
self.assertEqual(p.logger._pop_msgs(), [])
pop_events()
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default')
self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents(p, 'themes/test/default')
self.assertEqual(p.logger._pop_msgs(), [])
def test_reload_theme_main(self):
with replace_item(globals(), 'config', deepcopy(config)):
config['config']['common']['interval'] = None
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.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
@with_new_config
def test_reload_theme_main(self, config):
config['config']['common']['interval'] = None
with get_powerline(config, 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.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default', wait=False)
self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('themes/test/default')
self.assertEqual(p.logger._pop_msgs(), [])
self.assertTrue(p._watcher._calls)
pop_events()
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default', wait=False)
self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents(p, 'themes/test/default')
self.assertEqual(p.logger._pop_msgs(), [])
self.assertTrue(p._watcher._calls)
def test_run_once_no_theme_reload(self):
with replace_item(globals(), 'config', deepcopy(config)):
config['config']['common']['interval'] = None
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.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
@with_new_config
def test_run_once_no_theme_reload(self, config):
config['config']['common']['interval'] = None
with get_powerline(config, 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.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default', wait=False)
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents()
self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p._watcher._calls, [])
pop_events()
replaces = {}
def setUpModule():
global replaces
replaces = swap_attributes(globals(), powerline_module, replaces)
tearDownModule = setUpModule
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default', wait=False)
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents(p)
self.assertEqual(p.logger._pop_msgs(), [])
if __name__ == '__main__':

View File

@ -1,11 +1,8 @@
# vim:fileencoding=utf-8:noet
from __future__ import unicode_literals, absolute_import, division
import tests.vim as vim_module
import powerline as powerline_module
from tests import TestCase
from tests.lib import replace_item
from tests.lib.config_mock import swap_attributes, get_powerline
from tests.lib.config_mock import get_powerline_raw
from tests.lib.config_mock import get_powerline, get_powerline_raw, swap_attributes
from functools import wraps
from copy import deepcopy
import sys
@ -123,11 +120,19 @@ config = {
}
def add_p_arg(func):
def with_new_config(func):
@wraps(func)
def f(self):
with get_powerline(run_once=True, simpler_renderer=True) as p:
func(self, p)
return func(self, deepcopy(config))
return f
def add_args(func):
@wraps(func)
def f(self):
new_config = deepcopy(config)
with get_powerline(new_config, run_once=True, simpler_renderer=True) as p:
func(self, p, new_config)
return f
@ -140,166 +145,158 @@ class TestRender(TestCase):
class TestLines(TestRender):
@add_p_arg
def test_without_above(self, p):
@add_args
def test_without_above(self, p, config):
self.assertRenderEqual(p, '{121} s{24}>>{344}g{34}>{34}<{344}f {--}')
self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', width=10)
# self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344} f {--}', width=11)
self.assertEqual(list(p.render_above_lines()), [])
def test_with_above(self):
with replace_item(globals(), 'config', deepcopy(config)):
old_segments = deepcopy(config['themes/test/default']['segments'])
config['themes/test/default']['segments']['above'] = [old_segments]
with get_powerline(run_once=True, simpler_renderer=True) as p:
self.assertRenderLinesEqual(p, [
'{121} s{24}>>{344}g{34}>{34}<{344}f {--}',
])
self.assertRenderLinesEqual(p, [
'{121} s {24}>>{344}g{34}>{34}<{344}f {--}',
], width=10)
@with_new_config
def test_with_above(self, config):
old_segments = deepcopy(config['themes/test/default']['segments'])
config['themes/test/default']['segments']['above'] = [old_segments]
with get_powerline(config, run_once=True, simpler_renderer=True) as p:
self.assertRenderLinesEqual(p, [
'{121} s{24}>>{344}g{34}>{34}<{344}f {--}',
])
self.assertRenderLinesEqual(p, [
'{121} s {24}>>{344}g{34}>{34}<{344}f {--}',
], width=10)
config['themes/test/default']['segments']['above'] = [old_segments] * 2
with get_powerline(run_once=True, simpler_renderer=True) as p:
self.assertRenderLinesEqual(p, [
'{121} s{24}>>{344}g{34}>{34}<{344}f {--}',
'{121} s{24}>>{344}g{34}>{34}<{344}f {--}',
])
self.assertRenderLinesEqual(p, [
'{121} s {24}>>{344}g{34}>{34}<{344}f {--}',
'{121} s {24}>>{344}g{34}>{34}<{344}f {--}',
], width=10)
config['themes/test/default']['segments']['above'] = [old_segments] * 2
with get_powerline(config, run_once=True, simpler_renderer=True) as p:
self.assertRenderLinesEqual(p, [
'{121} s{24}>>{344}g{34}>{34}<{344}f {--}',
'{121} s{24}>>{344}g{34}>{34}<{344}f {--}',
])
self.assertRenderLinesEqual(p, [
'{121} s {24}>>{344}g{34}>{34}<{344}f {--}',
'{121} s {24}>>{344}g{34}>{34}<{344}f {--}',
], width=10)
class TestSegments(TestRender):
@add_p_arg
def test_display(self, p):
with replace_item(globals(), 'config', deepcopy(config)):
config['themes/test/default']['segments']['left'][0]['display'] = False
config['themes/test/default']['segments']['left'][1]['display'] = True
config['themes/test/default']['segments']['right'][0]['display'] = False
self.assertRenderEqual(p, '{344} g{4-}>>{--}')
@add_args
def test_display(self, p, config):
config['themes/test/default']['segments']['left'][0]['display'] = False
config['themes/test/default']['segments']['left'][1]['display'] = True
config['themes/test/default']['segments']['right'][0]['display'] = False
self.assertRenderEqual(p, '{344} g{4-}>>{--}')
class TestColorschemesHierarchy(TestRender):
@add_p_arg
def test_group_redirects(self, p):
with replace_item(globals(), 'config', deepcopy(config)):
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('a', 'd1', draw_hard_divider=False),
highlighted_string('b', 'd2', draw_hard_divider=False),
highlighted_string('c', 'd3', draw_hard_divider=False),
highlighted_string('A', 'm1', draw_hard_divider=False),
highlighted_string('B', 'm2', draw_hard_divider=False),
highlighted_string('C', 'm3', draw_hard_divider=False),
highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{78} a{910}b{1112}c{56}A{910}B{1112}C{56}1{78}2{910}3{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_args
def test_group_redirects(self, p, config):
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('a', 'd1', draw_hard_divider=False),
highlighted_string('b', 'd2', draw_hard_divider=False),
highlighted_string('c', 'd3', draw_hard_divider=False),
highlighted_string('A', 'm1', draw_hard_divider=False),
highlighted_string('B', 'm2', draw_hard_divider=False),
highlighted_string('C', 'm3', draw_hard_divider=False),
highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{78} a{910}b{1112}c{56}A{910}B{1112}C{56}1{78}2{910}3{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_p_arg
def test_group_redirects_no_main(self, p):
with replace_item(globals(), 'config', deepcopy(config)):
del config['colorschemes/test/__main__']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('a', 'd1', draw_hard_divider=False),
highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{78} a{56}1{78}2{910}3{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_args
def test_group_redirects_no_main(self, p, config):
del config['colorschemes/test/__main__']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('a', 'd1', draw_hard_divider=False),
highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{78} a{56}1{78}2{910}3{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_p_arg
def test_group_redirects_no_top_default(self, p):
with replace_item(globals(), 'config', deepcopy(config)):
del config['colorschemes/default']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('c', 'd3', draw_soft_divider=False),
highlighted_string('C', 'm3', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{1112} c{1112}C{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_args
def test_group_redirects_no_top_default(self, p, config):
del config['colorschemes/default']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('c', 'd3', draw_soft_divider=False),
highlighted_string('C', 'm3', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{1112} c{1112}C{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_p_arg
def test_group_redirects_no_test_default(self, p):
with replace_item(globals(), 'config', deepcopy(config)):
del config['colorschemes/test/default']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('A', 'm1', draw_hard_divider=False),
highlighted_string('B', 'm2', draw_hard_divider=False),
highlighted_string('C', 'm3', draw_hard_divider=False),
highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{56} A{910}B{1112}C{56}1{78}2{910}3{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_args
def test_group_redirects_no_test_default(self, p, config):
del config['colorschemes/test/default']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('A', 'm1', draw_hard_divider=False),
highlighted_string('B', 'm2', draw_hard_divider=False),
highlighted_string('C', 'm3', draw_hard_divider=False),
highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{56} A{910}B{1112}C{56}1{78}2{910}3{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_p_arg
def test_group_redirects_only_main(self, p):
with replace_item(globals(), 'config', deepcopy(config)):
del config['colorschemes/default']
del config['colorschemes/test/default']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('C', 'm3', draw_hard_divider=False),
],
'right': [],
}
# Powerline is not able to work without default colorscheme
# somewhere, thus it will output error string
self.assertRenderEqual(p, 'colorschemes/test/default')
self.assertEqual(p.logger._pop_msgs(), [
'exception:test:powerline:Failed to load colorscheme: colorschemes/default',
'exception:test:powerline:Failed to load colorscheme: colorschemes/test/default',
'exception:test:powerline:Failed to create renderer: colorschemes/test/default',
'exception:test:powerline:Failed to render: colorschemes/test/default',
])
@add_args
def test_group_redirects_only_main(self, p, config):
del config['colorschemes/default']
del config['colorschemes/test/default']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('C', 'm3', draw_hard_divider=False),
],
'right': [],
}
# Powerline is not able to work without default colorscheme
# somewhere, thus it will output error string
self.assertRenderEqual(p, 'colorschemes/test/default')
self.assertEqual(p.logger._pop_msgs(), [
'exception:test:powerline:Failed to load colorscheme: colorschemes/default',
'exception:test:powerline:Failed to load colorscheme: colorschemes/test/default',
'exception:test:powerline:Failed to create renderer: colorschemes/test/default',
'exception:test:powerline:Failed to render: colorschemes/test/default',
])
@add_p_arg
def test_group_redirects_only_top_default(self, p):
with replace_item(globals(), 'config', deepcopy(config)):
del config['colorschemes/test/__main__']
del config['colorschemes/test/default']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{56} 1{78}2{910}3{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_args
def test_group_redirects_only_top_default(self, p, config):
del config['colorschemes/test/__main__']
del config['colorschemes/test/default']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{56} 1{78}2{910}3{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_p_arg
def test_group_redirects_only_test_default(self, p):
with replace_item(globals(), 'config', deepcopy(config)):
del config['colorschemes/default']
del config['colorschemes/test/__main__']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('s', 'str1', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{121} s{--}')
self.assertEqual(p.logger._pop_msgs(), [])
@add_args
def test_group_redirects_only_test_default(self, p, config):
del config['colorschemes/default']
del config['colorschemes/test/__main__']
config['themes/test/default']['segments'] = {
'left': [
highlighted_string('s', 'str1', draw_hard_divider=False),
],
'right': [],
}
self.assertRenderEqual(p, '{121} s{--}')
self.assertEqual(p.logger._pop_msgs(), [])
class TestVim(TestCase):
@ -307,28 +304,25 @@ class TestVim(TestCase):
# Regression test: test that segment obtains environment from vim, not
# from os.environ.
from powerline.vim import VimPowerline
with vim_module._with('environ', TEST='abc'):
with get_powerline_raw(VimPowerline) as powerline:
window = vim_module.current.window
window_id = 1
winnr = window.number
self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0abc%#Pl_4_192_NONE_None_NONE#>>')
vim_module._environ['TEST'] = 'def'
self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0def%#Pl_4_192_NONE_None_NONE#>>')
replaces = {}
import powerline as powerline_module
import vim
vim.vars['powerline_config_path'] = '/'
with swap_attributes(config, powerline_module):
with vim_module._with('environ', TEST='abc'):
with get_powerline_raw(config, VimPowerline) as powerline:
window = vim_module.current.window
window_id = 1
winnr = window.number
self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0abc%#Pl_4_192_NONE_None_NONE#>>')
vim_module._environ['TEST'] = 'def'
self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0def%#Pl_4_192_NONE_None_NONE#>>')
def setUpModule():
global replaces
replaces = swap_attributes(globals(), powerline_module, replaces)
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path')))
def tearDownModule():
global replaces
replaces = swap_attributes(globals(), powerline_module, replaces)
sys.path.pop(0)