Merge pull request #1253 from ZyX-I/fix-lint

Fix powerline-lint
This commit is contained in:
Nikolai Aleksandrovich Pavlov 2015-01-08 00:01:03 +03:00
commit 594c6df611
4 changed files with 198 additions and 109 deletions

View File

@ -35,6 +35,7 @@ def mergedicts(d1, d2, remove=True):
First dictionary is modified in-place. First dictionary is modified in-place.
''' '''
_setmerged(d1, d2)
for k in d2: for k in d2:
if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict): if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict):
mergedicts(d1[k], d2[k], remove) mergedicts(d1[k], d2[k], remove)
@ -58,6 +59,11 @@ def mergedefaults(d1, d2):
d1.setdefault(k, d2[k]) d1.setdefault(k, d2[k])
def _setmerged(d1, d2):
if hasattr(d1, 'setmerged'):
d1.setmerged(d2)
def mergedicts_copy(d1, d2): def mergedicts_copy(d1, d2):
'''Recursively merge two dictionaries. '''Recursively merge two dictionaries.
@ -65,6 +71,7 @@ def mergedicts_copy(d1, d2):
that first dictionary supports .copy() method. that first dictionary supports .copy() method.
''' '''
ret = d1.copy() ret = d1.copy()
_setmerged(ret, d2)
for k in d2: for k in d2:
if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict): if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict):
ret[k] = mergedicts_copy(d1[k], d2[k]) ret[k] = mergedicts_copy(d1[k], d2[k])

View File

@ -5,6 +5,7 @@ import os
import logging import logging
from collections import defaultdict from collections import defaultdict
from itertools import chain
from functools import partial from functools import partial
from powerline import generate_config_finder, get_config_paths, load_config from powerline import generate_config_finder, get_config_paths, load_config
@ -12,6 +13,7 @@ from powerline.segments.vim import vim_modes
from powerline.lib.dict import mergedicts_copy from powerline.lib.dict import mergedicts_copy
from powerline.lib.config import ConfigLoader from powerline.lib.config import ConfigLoader
from powerline.lib.unicode import unicode from powerline.lib.unicode import unicode
from powerline.lib.path import join
from powerline.lint.markedjson import load from powerline.lint.markedjson import load
from powerline.lint.markedjson.error import echoerr, EchoErr, MarkedError from powerline.lint.markedjson.error import echoerr, EchoErr, MarkedError
from powerline.lint.checks import (check_matcher_func, check_ext, check_config, check_top_theme, from powerline.lint.checks import (check_matcher_func, check_ext, check_config, check_top_theme,
@ -294,6 +296,74 @@ def register_common_names():
register_common_name('player', 'powerline.segments.common.players', '_player') register_common_name('player', 'powerline.segments.common.players', '_player')
def load_json_file(path):
with open_file(path) as F:
try:
config, hadproblem = load(F)
except MarkedError as e:
return True, None, str(e)
else:
return hadproblem, config, None
def updated_with_config(d):
hadproblem, config, error = load_json_file(d['path'])
d.update(
hadproblem=hadproblem,
config=config,
error=error,
)
return d
def find_all_ext_config_files(search_paths, subdir):
for config_root in search_paths:
top_config_subpath = join(config_root, subdir)
if not os.path.isdir(top_config_subpath):
if os.path.exists(top_config_subpath):
yield {
'error': 'Path {0} is not a directory'.format(top_config_subpath),
'path': top_config_subpath,
}
continue
for ext_name in os.listdir(top_config_subpath):
ext_path = os.path.join(top_config_subpath, ext_name)
if not os.path.isdir(ext_path):
if ext_name.endswith('.json') and os.path.isfile(ext_path):
yield updated_with_config({
'error': False,
'path': ext_path,
'name': ext_name[:-5],
'ext': None,
'type': 'top_' + subdir,
})
else:
yield {
'error': 'Path {0} is not a directory or configuration file'.format(ext_path),
'path': ext_path,
}
continue
for config_file_name in os.listdir(ext_path):
config_file_path = os.path.join(ext_path, config_file_name)
if config_file_name.endswith('.json') and os.path.isfile(config_file_path):
yield updated_with_config({
'error': False,
'path': config_file_path,
'name': config_file_name[:-5],
'ext': ext_name,
'type': subdir,
})
else:
yield {
'error': 'Path {0} is not a configuration file'.format(config_file_path),
'path': config_file_path,
}
def dict2(d):
return defaultdict(dict, ((k, dict(v)) for k, v in d.items()))
def check(paths=None, debug=False, echoerr=echoerr, require_ext=None): def check(paths=None, debug=False, echoerr=echoerr, require_ext=None):
'''Check configuration sanity '''Check configuration sanity
@ -313,6 +383,8 @@ def check(paths=None, debug=False, echoerr=echoerr, require_ext=None):
``False`` if user configuration seems to be completely sane and ``True`` ``False`` if user configuration seems to be completely sane and ``True``
if some problems were found. if some problems were found.
''' '''
hadproblem = False
register_common_names() register_common_names()
search_paths = paths or get_config_paths() search_paths = paths or get_config_paths()
find_config_files = generate_config_finder(lambda: search_paths) find_config_files = generate_config_finder(lambda: search_paths)
@ -337,65 +409,60 @@ def check(paths=None, debug=False, echoerr=echoerr, require_ext=None):
config_loader = ConfigLoader(run_once=True, load=load_json_config) config_loader = ConfigLoader(run_once=True, load=load_json_config)
paths = {
'themes': defaultdict(lambda: []),
'colorschemes': defaultdict(lambda: []),
'top_colorschemes': [],
'top_themes': [],
}
lists = { lists = {
'colorschemes': set(), 'colorschemes': set(),
'themes': set(), 'themes': set(),
'exts': set(), 'exts': set(),
} }
for path in reversed(search_paths): found_dir = {
for typ in ('themes', 'colorschemes'): 'themes': False,
d = os.path.join(path, typ) 'colorschemes': False,
if os.path.isdir(d): }
for subp in os.listdir(d): config_paths = defaultdict(lambda: defaultdict(dict))
extpath = os.path.join(d, subp) loaded_configs = defaultdict(lambda: defaultdict(dict))
if os.path.isdir(extpath): for d in chain(
lists['exts'].add(subp) find_all_ext_config_files(search_paths, 'colorschemes'),
paths[typ][subp].append(extpath) find_all_ext_config_files(search_paths, 'themes'),
elif extpath.endswith('.json'): ):
name = subp[:-5] if d['error']:
if name != '__main__': hadproblem = True
lists[typ].add(name) ee(problem=d['error'])
paths['top_' + typ].append(extpath) continue
else: if d['hadproblem']:
hadproblem = True
if d['ext']:
found_dir[d['type']] = True
lists['exts'].add(d['ext'])
if d['name'] == '__main__':
pass
elif d['name'].startswith('__') or d['name'].endswith('__'):
hadproblem = True hadproblem = True
ee(problem='Path {0} is supposed to be a directory, but it is not'.format(d)) ee(problem='File name is not supposed to start or end with “__”: {0}'.format(
d['path']))
else:
lists[d['type']].add(d['name'])
config_paths[d['type']][d['ext']][d['name']] = d['path']
loaded_configs[d['type']][d['ext']][d['name']] = d['config']
else:
config_paths[d['type']][d['name']] = d['path']
loaded_configs[d['type']][d['name']] = d['config']
hadproblem = False
configs = defaultdict(lambda: defaultdict(lambda: {}))
for typ in ('themes', 'colorschemes'): for typ in ('themes', 'colorschemes'):
for ext in paths[typ]: if not found_dir[typ]:
for d in paths[typ][ext]: hadproblem = True
for subp in os.listdir(d): ee(problem='Subdirectory {0} was not found in paths {1}'.format(typ, ', '.join(search_paths)))
if subp.endswith('.json'):
name = subp[:-5]
if name != '__main__':
lists[typ].add(name)
if name.startswith('__') or name.endswith('__'):
hadproblem = True
ee(problem='File name is not supposed to start or end with “__”: {0}'.format(
os.path.join(d, subp)
))
configs[typ][ext][name] = os.path.join(d, subp)
for path in paths['top_' + typ]:
name = os.path.basename(path)[:-5]
configs['top_' + typ][name] = path
diff = set(configs['colorschemes']) - set(configs['themes']) diff = set(config_paths['colorschemes']) - set(config_paths['themes'])
if diff: if diff:
hadproblem = True hadproblem = True
for ext in diff: for ext in diff:
typ = 'colorschemes' if ext in configs['themes'] else 'themes' typ = 'colorschemes' if ext in config_paths['themes'] else 'themes'
if not configs['top_' + typ] or typ == 'themes': if not config_paths['top_' + typ] or typ == 'themes':
ee(problem='{0} extension {1} not present in {2}'.format( ee(problem='{0} extension {1} not present in {2}'.format(
ext, ext,
'configuration' if (ext in paths['themes'] and ext in paths['colorschemes']) else 'directory', 'configuration' if (
ext in loaded_configs['themes'] and ext in loaded_configs['colorschemes']
) else 'directory',
typ, typ,
)) ))
@ -412,7 +479,7 @@ def check(paths=None, debug=False, echoerr=echoerr, require_ext=None):
else: else:
if used_main_spec.match( if used_main_spec.match(
main_config, main_config,
data={'configs': configs, 'lists': lists}, data={'configs': config_paths, 'lists': lists},
context=Context(main_config), context=Context(main_config),
echoerr=ee echoerr=ee
)[1]: )[1]:
@ -437,42 +504,19 @@ def check(paths=None, debug=False, echoerr=echoerr, require_ext=None):
if lhadproblem[0]: if lhadproblem[0]:
hadproblem = True hadproblem = True
top_colorscheme_configs = {} top_colorscheme_configs = dict(loaded_configs['top_colorschemes'])
data = { data = {
'ext': None, 'ext': None,
'top_colorscheme_configs': top_colorscheme_configs, 'top_colorscheme_configs': top_colorscheme_configs,
'ext_colorscheme_configs': {}, 'ext_colorscheme_configs': {},
'colors_config': colors_config 'colors_config': colors_config
} }
for colorscheme, cfile in configs['top_colorschemes'].items(): for colorscheme, config in loaded_configs['top_colorschemes'].items():
with open_file(cfile) as config_file_fp:
try:
config, lhadproblem = load(config_file_fp)
except MarkedError as e:
ee(problem=str(e))
hadproblem = True
continue
if lhadproblem:
hadproblem = True
top_colorscheme_configs[colorscheme] = config
data['colorscheme'] = colorscheme data['colorscheme'] = colorscheme
if top_colorscheme_spec.match(config, context=Context(config), data=data, echoerr=ee)[1]: if top_colorscheme_spec.match(config, context=Context(config), data=data, echoerr=ee)[1]:
hadproblem = True hadproblem = True
ext_colorscheme_configs = defaultdict(lambda: {}) ext_colorscheme_configs = dict2(loaded_configs['colorschemes'])
for ext in configs['colorschemes']:
for colorscheme, cfile in configs['colorschemes'][ext].items():
with open_file(cfile) as config_file_fp:
try:
config, lhadproblem = load(config_file_fp)
except MarkedError as e:
ee(problem=str(e))
hadproblem = True
continue
if lhadproblem:
hadproblem = True
ext_colorscheme_configs[ext][colorscheme] = config
for ext, econfigs in ext_colorscheme_configs.items(): for ext, econfigs in ext_colorscheme_configs.items():
data = { data = {
'ext': ext, 'ext': ext,
@ -512,33 +556,8 @@ def check(paths=None, debug=False, echoerr=echoerr, require_ext=None):
config = mconfig config = mconfig
colorscheme_configs[colorscheme] = config colorscheme_configs[colorscheme] = config
theme_configs = defaultdict(lambda: {}) theme_configs = dict2(loaded_configs['themes'])
for ext in configs['themes']: top_theme_configs = dict(loaded_configs['top_themes'])
for theme, sfile in configs['themes'][ext].items():
with open_file(sfile) as config_file_fp:
try:
config, lhadproblem = load(config_file_fp)
except MarkedError as e:
ee(problem=str(e))
hadproblem = True
continue
if lhadproblem:
hadproblem = True
theme_configs[ext][theme] = config
top_theme_configs = {}
for top_theme, top_theme_file in configs['top_themes'].items():
with open_file(top_theme_file) as config_file_fp:
try:
config, lhadproblem = load(config_file_fp)
except MarkedError as e:
ee(problem=str(e))
hadproblem = True
continue
if lhadproblem:
hadproblem = True
top_theme_configs[top_theme] = config
for ext, configs in theme_configs.items(): for ext, configs in theme_configs.items():
data = { data = {
'ext': ext, 'ext': ext,
@ -562,12 +581,12 @@ def check(paths=None, debug=False, echoerr=echoerr, require_ext=None):
for top_theme, config in top_theme_configs.items(): for top_theme, config in top_theme_configs.items():
data = { data = {
'ext': ext, 'ext': None,
'colorscheme_configs': colorscheme_configs, 'colorscheme_configs': colorscheme_configs,
'import_paths': import_paths, 'import_paths': import_paths,
'main_config': main_config, 'main_config': main_config,
'theme_configs': theme_configs, 'theme_configs': theme_configs,
'ext_theme_configs': configs, 'ext_theme_configs': None,
'colors_config': colors_config 'colors_config': colors_config
} }
data['theme_type'] = 'top' data['theme_type'] = 'top'

View File

@ -42,15 +42,17 @@ def strtrans(s):
class Mark: class Mark:
def __init__(self, name, line, column, buffer, pointer): def __init__(self, name, line, column, buffer, pointer, old_mark=None, merged_marks=None):
self.name = name self.name = name
self.line = line self.line = line
self.column = column self.column = column
self.buffer = buffer self.buffer = buffer
self.pointer = pointer self.pointer = pointer
self.old_mark = old_mark
self.merged_marks = merged_marks or []
def copy(self): def copy(self):
return Mark(self.name, self.line, self.column, self.buffer, self.pointer) return Mark(self.name, self.line, self.column, self.buffer, self.pointer, self.old_mark, self.merged_marks[:])
def get_snippet(self, indent=4, max_length=75): def get_snippet(self, indent=4, max_length=75):
if self.buffer is None: if self.buffer is None:
@ -85,17 +87,57 @@ class Mark:
ret.pointer += diff ret.pointer += diff
return ret return ret
def __str__(self): def set_old_mark(self, old_mark):
snippet = self.get_snippet() if self is old_mark:
where = (' in "%s", line %d, column %d' % ( return
self.name, self.line + 1, self.column + 1)) checked_marks = set([id(self)])
if snippet is not None: older_mark = old_mark
where += ':\n' + snippet while True:
if id(older_mark) in checked_marks:
raise ValueError('Trying to set recursive marks')
checked_marks.add(id(older_mark))
older_mark = older_mark.old_mark
if not older_mark:
break
self.old_mark = old_mark
def set_merged_mark(self, merged_mark):
self.merged_marks.append(merged_mark)
def to_string(self, indent=0, head_text='in ', add_snippet=True):
mark = self
where = ''
processed_marks = set()
while mark:
indentstr = ' ' * indent
where += ('%s %s"%s", line %d, column %d' % (
indentstr, head_text, mark.name, mark.line + 1, mark.column + 1))
if add_snippet:
snippet = mark.get_snippet(indent=(indent + 4))
if snippet:
where += ':\n' + snippet
if mark.merged_marks:
where += '\n' + indentstr + ' with additionally merged\n'
where += mark.merged_marks[0].to_string(indent + 4, head_text='', add_snippet=False)
for mmark in mark.merged_marks[1:]:
where += '\n' + indentstr + ' and\n'
where += mmark.to_string(indent + 4, head_text='', add_snippet=False)
if add_snippet:
processed_marks.add(id(mark))
if mark.old_mark:
where += '\n' + indentstr + ' which replaced value\n'
indent += 4
mark = mark.old_mark
if id(mark) in processed_marks:
raise ValueError('Trying to dump recursive mark')
if type(where) is str: if type(where) is str:
return where return where
else: else:
return where.encode('utf-8') return where.encode('utf-8')
def __str__(self):
return self.to_string()
def echoerr(*args, **kwargs): def echoerr(*args, **kwargs):
stream = kwargs.pop('stream', sys.stderr) stream = kwargs.pop('stream', sys.stderr)

View File

@ -65,7 +65,28 @@ class MarkedDict(dict):
r.keydict = dict(((key, key) for key in r)) r.keydict = dict(((key, key) for key in r))
return r return r
def setmerged(self, d):
try:
self.mark.set_merged_mark(d.mark)
except AttributeError:
pass
def __setitem__(self, key, value): def __setitem__(self, key, value):
try:
old_value = self[key]
except KeyError:
pass
else:
try:
key.mark.set_old_mark(self.keydict[key].mark)
except AttributeError:
pass
except KeyError:
pass
try:
value.mark.set_old_mark(old_value.mark)
except AttributeError:
pass
dict.__setitem__(self, key, value) dict.__setitem__(self, key, value)
self.keydict[key] = key self.keydict[key] = key