Merge pull request #923 from ZyX-I/merge-config
Implement configuration merging
This commit is contained in:
commit
c95b288527
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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',
|
||||
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
|
||||
))
|
||||
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',
|
||||
self.checks.append((
|
||||
'check_func',
|
||||
(lambda value, *args: (True, True, not cmp_func(value.value, cint))),
|
||||
msg_func))
|
||||
msg_func
|
||||
))
|
||||
return self
|
||||
|
||||
def unsigned(self, msg_func=None):
|
||||
self.type(int)
|
||||
self.checks.append(('check_func',
|
||||
self.checks.append((
|
||||
'check_func',
|
||||
(lambda value, *args: (True, True, value < 0)),
|
||||
lambda value: '{0} must be greater then zero'.format(value)))
|
||||
(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',
|
||||
self.checks.append((
|
||||
'check_func',
|
||||
(lambda value, *args: (True, True, not compiled.match(value.value))),
|
||||
msg_func))
|
||||
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,
|
||||
).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 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)),
|
||||
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)
|
||||
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)),
|
||||
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)
|
||||
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)),
|
||||
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)))
|
||||
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)),
|
||||
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')
|
||||
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)),
|
||||
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)))
|
||||
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)),
|
||||
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)
|
||||
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)),
|
||||
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)
|
||||
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)),
|
||||
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
|
||||
|
||||
|
@ -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)),
|
||||
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)
|
||||
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)),
|
||||
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)),
|
||||
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)))
|
||||
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]:
|
||||
|
|
|
@ -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 = {}
|
||||
|
|
|
@ -3,36 +3,52 @@ 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)
|
||||
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(config_container['config'][config_file_path])
|
||||
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 = []
|
||||
def pop_events(self):
|
||||
with self.access_lock:
|
||||
r = self.access_log[:]
|
||||
self.access_log = []
|
||||
return r
|
||||
|
||||
|
||||
|
@ -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,44 +172,41 @@ 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):
|
||||
if isinstance(p._watcher, TestWatcher):
|
||||
p._watcher._reset(args)
|
||||
while not p._will_create_renderer():
|
||||
sleep(kwargs.get('interval', 0.1))
|
||||
|
|
|
@ -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}}),
|
||||
|
|
|
@ -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()
|
|
@ -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,55 +94,65 @@ config = {
|
|||
}
|
||||
|
||||
|
||||
class TestConfigReload(TestCase):
|
||||
def assertAccessEvents(self, *args):
|
||||
self.assertEqual(set(pop_events()), set(args))
|
||||
def with_new_config(func):
|
||||
@wraps(func)
|
||||
def f(self):
|
||||
return func(self, deepcopy(config))
|
||||
return f
|
||||
|
||||
def test_noreload(self):
|
||||
with get_powerline(run_once=True) as p:
|
||||
with replace_item(globals(), 'config', deepcopy(config)):
|
||||
|
||||
class TestConfigReload(TestCase):
|
||||
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)
|
||||
|
||||
@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('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
|
||||
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()
|
||||
self.assertAccessEvents(p)
|
||||
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()
|
||||
|
||||
def test_reload_main(self):
|
||||
with get_powerline(run_once=False) as p:
|
||||
with replace_item(globals(), 'config', deepcopy(config)):
|
||||
@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('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
|
||||
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.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')
|
||||
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.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')
|
||||
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',
|
||||
|
@ -153,29 +164,28 @@ class TestConfigReload(TestCase):
|
|||
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.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.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.assertAccessEvents(p, 'config')
|
||||
self.assertEqual(p.logger._pop_msgs(), [])
|
||||
self.assertEqual(p.renderer.local_themes, 'something')
|
||||
pop_events()
|
||||
|
||||
def test_reload_unexistent(self):
|
||||
with get_powerline(run_once=False) as p:
|
||||
with replace_item(globals(), 'config', deepcopy(config)):
|
||||
@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('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
|
||||
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')
|
||||
|
@ -188,10 +198,10 @@ class TestConfigReload(TestCase):
|
|||
# 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__,
|
||||
# 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('config', 'colorschemes/test/__main__')
|
||||
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'] = {}
|
||||
|
@ -205,89 +215,71 @@ class TestConfigReload(TestCase):
|
|||
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.assertAccessEvents(p, 'colorschemes/nonexistentraise', 'colorschemes/test/nonexistentraise', 'check:colorschemes/test/__main__')
|
||||
self.assertEqual(p.logger._pop_msgs(), [])
|
||||
pop_events()
|
||||
|
||||
def test_reload_colors(self):
|
||||
with get_powerline(run_once=False) as p:
|
||||
with replace_item(globals(), 'config', deepcopy(config)):
|
||||
@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('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
|
||||
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.assertAccessEvents(p, 'colors')
|
||||
self.assertEqual(p.logger._pop_msgs(), [])
|
||||
pop_events()
|
||||
|
||||
def test_reload_colorscheme(self):
|
||||
with get_powerline(run_once=False) as p:
|
||||
with replace_item(globals(), 'config', deepcopy(config)):
|
||||
@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('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
|
||||
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.assertAccessEvents(p, 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default')
|
||||
self.assertEqual(p.logger._pop_msgs(), [])
|
||||
pop_events()
|
||||
|
||||
def test_reload_theme(self):
|
||||
with get_powerline(run_once=False) as p:
|
||||
with replace_item(globals(), 'config', deepcopy(config)):
|
||||
@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('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
|
||||
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.assertAccessEvents(p, 'themes/test/default')
|
||||
self.assertEqual(p.logger._pop_msgs(), [])
|
||||
pop_events()
|
||||
|
||||
def test_reload_theme_main(self):
|
||||
with replace_item(globals(), 'config', deepcopy(config)):
|
||||
@with_new_config
|
||||
def test_reload_theme_main(self, config):
|
||||
config['config']['common']['interval'] = None
|
||||
with get_powerline(run_once=False) as p:
|
||||
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('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
|
||||
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.assertAccessEvents(p, 'themes/test/default')
|
||||
self.assertEqual(p.logger._pop_msgs(), [])
|
||||
self.assertTrue(p._watcher._calls)
|
||||
pop_events()
|
||||
|
||||
def test_run_once_no_theme_reload(self):
|
||||
with replace_item(globals(), 'config', deepcopy(config)):
|
||||
@with_new_config
|
||||
def test_run_once_no_theme_reload(self, config):
|
||||
config['config']['common']['interval'] = None
|
||||
with get_powerline(run_once=True) as p:
|
||||
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('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
|
||||
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.assertAccessEvents(p)
|
||||
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
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -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,18 +145,18 @@ 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)):
|
||||
@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(run_once=True, simpler_renderer=True) as p:
|
||||
with get_powerline(config, run_once=True, simpler_renderer=True) as p:
|
||||
self.assertRenderLinesEqual(p, [
|
||||
'{121} s{24}>>{344}g{34}>{34}<{344}f {--}',
|
||||
])
|
||||
|
@ -160,7 +165,7 @@ class TestLines(TestRender):
|
|||
], width=10)
|
||||
|
||||
config['themes/test/default']['segments']['above'] = [old_segments] * 2
|
||||
with get_powerline(run_once=True, simpler_renderer=True) as p:
|
||||
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 {--}',
|
||||
|
@ -172,9 +177,8 @@ class TestLines(TestRender):
|
|||
|
||||
|
||||
class TestSegments(TestRender):
|
||||
@add_p_arg
|
||||
def test_display(self, p):
|
||||
with replace_item(globals(), 'config', deepcopy(config)):
|
||||
@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
|
||||
|
@ -182,9 +186,8 @@ class TestSegments(TestRender):
|
|||
|
||||
|
||||
class TestColorschemesHierarchy(TestRender):
|
||||
@add_p_arg
|
||||
def test_group_redirects(self, p):
|
||||
with replace_item(globals(), 'config', deepcopy(config)):
|
||||
@add_args
|
||||
def test_group_redirects(self, p, config):
|
||||
config['themes/test/default']['segments'] = {
|
||||
'left': [
|
||||
highlighted_string('a', 'd1', draw_hard_divider=False),
|
||||
|
@ -202,9 +205,8 @@ class TestColorschemesHierarchy(TestRender):
|
|||
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)):
|
||||
@add_args
|
||||
def test_group_redirects_no_main(self, p, config):
|
||||
del config['colorschemes/test/__main__']
|
||||
config['themes/test/default']['segments'] = {
|
||||
'left': [
|
||||
|
@ -218,9 +220,8 @@ class TestColorschemesHierarchy(TestRender):
|
|||
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)):
|
||||
@add_args
|
||||
def test_group_redirects_no_top_default(self, p, config):
|
||||
del config['colorschemes/default']
|
||||
config['themes/test/default']['segments'] = {
|
||||
'left': [
|
||||
|
@ -232,9 +233,8 @@ class TestColorschemesHierarchy(TestRender):
|
|||
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)):
|
||||
@add_args
|
||||
def test_group_redirects_no_test_default(self, p, config):
|
||||
del config['colorschemes/test/default']
|
||||
config['themes/test/default']['segments'] = {
|
||||
'left': [
|
||||
|
@ -250,9 +250,8 @@ class TestColorschemesHierarchy(TestRender):
|
|||
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)):
|
||||
@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'] = {
|
||||
|
@ -271,9 +270,8 @@ class TestColorschemesHierarchy(TestRender):
|
|||
'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)):
|
||||
@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'] = {
|
||||
|
@ -287,9 +285,8 @@ class TestColorschemesHierarchy(TestRender):
|
|||
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)):
|
||||
@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'] = {
|
||||
|
@ -307,8 +304,12 @@ class TestVim(TestCase):
|
|||
# Regression test: test that segment obtains environment from vim, not
|
||||
# from os.environ.
|
||||
from powerline.vim import VimPowerline
|
||||
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(VimPowerline) as powerline:
|
||||
with get_powerline_raw(config, VimPowerline) as powerline:
|
||||
window = vim_module.current.window
|
||||
window_id = 1
|
||||
winnr = window.number
|
||||
|
@ -317,18 +318,11 @@ class TestVim(TestCase):
|
|||
self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0def%#Pl_4_192_NONE_None_NONE#>>')
|
||||
|
||||
|
||||
replaces = {}
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue