1# vim:fileencoding=utf-8:noet
2from __future__ import (unicode_literals, division, absolute_import, print_function)
3
4import os
5import re
6import logging
7
8from collections import defaultdict
9
10from powerline.lib.threaded import ThreadedSegment
11from powerline.lib.unicode import unicode
12from powerline.lint.markedjson.markedvalue import MarkedUnicode
13from powerline.lint.markedjson.error import DelayedEchoErr, Mark
14from powerline.lint.selfcheck import havemarks
15from powerline.lint.context import JStr, list_themes
16from powerline.lint.imp import WithPath, import_function, import_segment
17from powerline.lint.spec import Spec
18from powerline.lint.inspect import getconfigargspec
19
20
21list_sep = JStr(', ')
22
23
24generic_keys = set((
25	'exclude_modes', 'include_modes',
26	'exclude_function', 'include_function',
27	'width', 'align',
28	'name',
29	'draw_soft_divider', 'draw_hard_divider',
30	'priority',
31	'after', 'before',
32	'display'
33))
34type_keys = {
35	'function': set(('function', 'args', 'draw_inner_divider')),
36	'string': set(('contents', 'type', 'highlight_groups', 'divider_highlight_group')),
37	'segment_list': set(('function', 'segments', 'args', 'type')),
38}
39required_keys = {
40	'function': set(('function',)),
41	'string': set(()),
42	'segment_list': set(('function', 'segments',)),
43}
44highlight_keys = set(('highlight_groups', 'name'))
45
46
47def get_function_strings(function_name, context, ext):
48	if '.' in function_name:
49		module, function_name = function_name.rpartition('.')[::2]
50	else:
51		module = context[0][1].get(
52			'default_module', MarkedUnicode('powerline.segments.' + ext, None))
53	return module, function_name
54
55
56def check_matcher_func(ext, match_name, data, context, echoerr):
57	havemarks(match_name)
58	import_paths = [os.path.expanduser(path) for path in context[0][1].get('common', {}).get('paths', [])]
59
60	match_module, separator, match_function = match_name.rpartition('.')
61	if not separator:
62		match_module = 'powerline.matchers.{0}'.format(ext)
63		match_function = match_name
64	with WithPath(import_paths):
65		try:
66			func = getattr(__import__(str(match_module), fromlist=[str(match_function)]), str(match_function))
67		except ImportError:
68			echoerr(context='Error while loading matcher functions',
69			        problem='failed to load module {0}'.format(match_module),
70			        problem_mark=match_name.mark)
71			return True, False, True
72		except AttributeError:
73			echoerr(context='Error while loading matcher functions',
74			        problem='failed to load matcher function {0}'.format(match_function),
75			        problem_mark=match_name.mark)
76			return True, False, True
77
78	if not callable(func):
79		echoerr(context='Error while loading matcher functions',
80		        problem='loaded “function” {0} is not callable'.format(match_function),
81		        problem_mark=match_name.mark)
82		return True, False, True
83
84	if hasattr(func, 'func_code') and hasattr(func.func_code, 'co_argcount'):
85		if func.func_code.co_argcount != 1:
86			echoerr(
87				context='Error while loading matcher functions',
88				problem=(
89					'function {0} accepts {1} arguments instead of 1. '
90					'Are you sure it is the proper function?'
91				).format(match_function, func.func_code.co_argcount),
92				problem_mark=match_name.mark
93			)
94
95	return True, False, False
96
97
98def check_ext(ext, data, context, echoerr):
99	havemarks(ext)
100	hadsomedirs = False
101	hadproblem = False
102	if ext not in data['lists']['exts']:
103		hadproblem = True
104		echoerr(context='Error while loading {0} extension configuration'.format(ext),
105		        context_mark=ext.mark,
106		        problem='extension configuration does not exist')
107	else:
108		for typ in ('themes', 'colorschemes'):
109			if ext not in data['configs'][typ] and not data['configs']['top_' + typ]:
110				hadproblem = True
111				echoerr(context='Error while loading {0} extension configuration'.format(ext),
112				        context_mark=ext.mark,
113				        problem='{0} configuration does not exist'.format(typ))
114			else:
115				hadsomedirs = True
116	return hadsomedirs, hadproblem
117
118
119def check_config(d, theme, data, context, echoerr):
120	if len(context) == 4:
121		ext = context[-2][0]
122	else:
123		# local_themes
124		ext = context[-3][0]
125	if ext not in data['lists']['exts']:
126		echoerr(context='Error while loading {0} extension configuration'.format(ext),
127		        context_mark=ext.mark,
128		        problem='extension configuration does not exist')
129		return True, False, True
130	if (
131		(ext not in data['configs'][d] or theme not in data['configs'][d][ext])
132		and theme not in data['configs']['top_' + d]
133	):
134		echoerr(context='Error while loading {0} from {1} extension configuration'.format(d[:-1], ext),
135		        problem='failed to find configuration file {0}/{1}/{2}.json'.format(d, ext, theme),
136		        problem_mark=theme.mark)
137		return True, False, True
138	return True, False, False
139
140
141def check_top_theme(theme, data, context, echoerr):
142	havemarks(theme)
143	if theme not in data['configs']['top_themes']:
144		echoerr(context='Error while checking extension configuration (key {key})'.format(key=context.key),
145		        context_mark=context[-2][0].mark,
146		        problem='failed to find top theme {0}'.format(theme),
147		        problem_mark=theme.mark)
148		return True, False, True
149	return True, False, False
150
151
152def check_color(color, data, context, echoerr):
153	havemarks(color)
154	if (color not in data['colors_config'].get('colors', {})
155		and color not in data['colors_config'].get('gradients', {})):
156		echoerr(
157			context='Error while checking highlight group in colorscheme (key {key})'.format(
158				key=context.key),
159			problem='found unexistent color or gradient {0}'.format(color),
160			problem_mark=color.mark
161		)
162		return True, False, True
163	return True, False, False
164
165
166def check_translated_group_name(group, data, context, echoerr):
167	return check_group(group, data, context, echoerr)
168
169
170def check_group(group, data, context, echoerr):
171	havemarks(group)
172	if not isinstance(group, unicode):
173		return True, False, False
174	colorscheme = data['colorscheme']
175	ext = data['ext']
176	configs = None
177	if ext:
178		def listed_key(d, k):
179			try:
180				return [d[k]]
181			except KeyError:
182				return []
183
184		if colorscheme == '__main__':
185			colorscheme_names = set(data['ext_colorscheme_configs'][ext])
186			colorscheme_names.update(data['top_colorscheme_configs'])
187			colorscheme_names.discard('__main__')
188			configs = [
189				(
190					name,
191					listed_key(data['ext_colorscheme_configs'][ext], name)
192					+ listed_key(data['ext_colorscheme_configs'][ext], '__main__')
193					+ listed_key(data['top_colorscheme_configs'], name)
194				)
195				for name in colorscheme_names
196			]
197		else:
198			configs = [
199				(
200					colorscheme,
201					listed_key(data['ext_colorscheme_configs'][ext], colorscheme)
202					+ listed_key(data['ext_colorscheme_configs'][ext], '__main__')
203					+ listed_key(data['top_colorscheme_configs'], colorscheme)
204				)
205			]
206	else:
207		try:
208			configs = [(colorscheme, [data['top_colorscheme_configs'][colorscheme]])]
209		except KeyError:
210			pass
211	hadproblem = False
212	for new_colorscheme, config_lst in configs:
213		not_found = []
214		new_data = data.copy()
215		new_data['colorscheme'] = new_colorscheme
216		for config in config_lst:
217			havemarks(config)
218			try:
219				group_data = config['groups'][group]
220			except KeyError:
221				not_found.append(config.mark.name)
222			else:
223				proceed, echo, chadproblem = check_group(
224					group_data,
225					new_data,
226					context,
227					echoerr,
228				)
229				if chadproblem:
230					hadproblem = True
231				if not proceed:
232					break
233		if not_found and len(not_found) == len(config_lst):
234			echoerr(
235				context='Error while checking group definition in colorscheme (key {key})'.format(
236					key=context.key),
237				problem='name {0} is not present anywhere in {1} {2} {3} colorschemes: {4}'.format(
238					group, len(not_found), ext, new_colorscheme, ', '.join(not_found)),
239				problem_mark=group.mark
240			)
241			hadproblem = True
242	return True, False, hadproblem
243
244
245def check_key_compatibility(segment, data, context, echoerr):
246	havemarks(segment)
247	segment_type = segment.get('type', MarkedUnicode('function', None))
248	havemarks(segment_type)
249
250	if segment_type not in type_keys:
251		echoerr(context='Error while checking segments (key {key})'.format(key=context.key),
252		        problem='found segment with unknown type {0}'.format(segment_type),
253		        problem_mark=segment_type.mark)
254		return False, False, True
255
256	hadproblem = False
257
258	keys = set(segment)
259	if not ((keys - generic_keys) < type_keys[segment_type]):
260		unknown_keys = keys - generic_keys - type_keys[segment_type]
261		echoerr(
262			context='Error while checking segments (key {key})'.format(key=context.key),
263			context_mark=context[-1][1].mark,
264			problem='found keys not used with the current segment type: {0}'.format(
265				list_sep.join(unknown_keys)),
266			problem_mark=list(unknown_keys)[0].mark
267		)
268		hadproblem = True
269
270	if not (keys >= required_keys[segment_type]):
271		missing_keys = required_keys[segment_type] - keys
272		echoerr(
273			context='Error while checking segments (key {key})'.format(key=context.key),
274			context_mark=context[-1][1].mark,
275			problem='found missing required keys: {0}'.format(
276				list_sep.join(missing_keys))
277		)
278		hadproblem = True
279
280	if not (segment_type == 'function' or (keys & highlight_keys)):
281		echoerr(
282			context='Error while checking segments (key {key})'.format(key=context.key),
283			context_mark=context[-1][1].mark,
284			problem=(
285				'found missing keys required to determine highlight group. '
286				'Either highlight_groups or name key must be present'
287			)
288		)
289		hadproblem = True
290
291	return True, False, hadproblem
292
293
294def check_segment_module(module, data, context, echoerr):
295	havemarks(module)
296	with WithPath(data['import_paths']):
297		try:
298			__import__(str(module))
299		except ImportError as e:
300			if echoerr.logger.level >= logging.DEBUG:
301				echoerr.logger.exception(e)
302			echoerr(context='Error while checking segments (key {key})'.format(key=context.key),
303			        problem='failed to import module {0}'.format(module),
304			        problem_mark=module.mark)
305			return True, False, True
306	return True, False, False
307
308
309def check_full_segment_data(segment, data, context, echoerr):
310	if 'name' not in segment and 'function' not in segment:
311		return True, False, False
312
313	ext = data['ext']
314	theme_segment_data = context[0][1].get('segment_data', {})
315	main_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None)
316	if not main_theme_name or data['theme'] == main_theme_name:
317		top_segment_data = {}
318	else:
319		top_segment_data = data['ext_theme_configs'].get(main_theme_name, {}).get('segment_data', {})
320
321	if segment.get('type', 'function') == 'function':
322		function_name = segment.get('function')
323		if function_name:
324			module, function_name = get_function_strings(function_name, context, ext)
325			names = [module + '.' + function_name, function_name]
326		else:
327			names = []
328	elif segment.get('name'):
329		names = [segment['name']]
330	else:
331		return True, False, False
332
333	segment_copy = segment.copy()
334
335	for key in ('before', 'after', 'args', 'contents'):
336		if key not in segment_copy:
337			for segment_data in [theme_segment_data, top_segment_data]:
338				for name in names:
339					try:
340						val = segment_data[name][key]
341						k = segment_data[name].keydict[key]
342						segment_copy[k] = val
343					except KeyError:
344						pass
345
346	return check_key_compatibility(segment_copy, data, context, echoerr)
347
348
349highlight_group_spec = Spec().ident().copy
350_highlight_group_spec = highlight_group_spec().context_message(
351	'Error while checking function documentation while checking theme (key {key})')
352
353
354def check_hl_group_name(hl_group, context_mark, context, echoerr):
355	'''Check highlight group name: it should match naming conventions
356
357	:param str hl_group:
358		Checked group.
359	:param Mark context_mark:
360		Context mark. May be ``None``.
361	:param Context context:
362		Current context.
363	:param func echoerr:
364		Function used for error reporting.
365
366	:return: ``False`` if check succeeded and ``True`` if it failed.
367	'''
368	return _highlight_group_spec.match(hl_group, context_mark=context_mark, context=context, echoerr=echoerr)[1]
369
370
371def check_segment_function(function_name, data, context, echoerr):
372	havemarks(function_name)
373	ext = data['ext']
374	module, function_name = get_function_strings(function_name, context, ext)
375	if context[-2][1].get('type', 'function') == 'function':
376		func = import_segment(function_name, data, context, echoerr, module=module)
377
378		if not func:
379			return True, False, True
380
381		hl_groups = []
382		divider_hl_group = None
383
384		hadproblem = False
385
386		if func.__doc__:
387			NO_H_G_USED_STR = 'No highlight groups are used (literal segment).'
388			H_G_USED_STR = 'Highlight groups used: '
389			LHGUS = len(H_G_USED_STR)
390			D_H_G_USED_STR = 'Divider highlight group used: '
391			LDHGUS = len(D_H_G_USED_STR)
392			pointer = 0
393			mark_name = '<{0} docstring>'.format(function_name)
394			for i, line in enumerate(func.__doc__.split('\n')):
395				if H_G_USED_STR in line:
396					idx = line.index(H_G_USED_STR) + LHGUS
397					if hl_groups is None:
398						idx -= LHGUS
399						mark = Mark(mark_name, i + 1, idx + 1, func.__doc__, pointer + idx)
400						echoerr(
401							context='Error while checking theme (key {key})'.format(key=context.key),
402							context_mark=function_name.mark,
403							problem=(
404								'found highlight group definition in addition to sentence stating that '
405								'no highlight groups are used'
406							),
407							problem_mark=mark,
408						)
409						hadproblem = True
410						continue
411					hl_groups.append((
412						line[idx:],
413						(mark_name, i + 1, idx + 1, func.__doc__),
414						pointer + idx
415					))
416				elif D_H_G_USED_STR in line:
417					idx = line.index(D_H_G_USED_STR) + LDHGUS + 2
418					mark = Mark(mark_name, i + 1, idx + 1, func.__doc__, pointer + idx)
419					divider_hl_group = MarkedUnicode(line[idx:-3], mark)
420				elif NO_H_G_USED_STR in line:
421					idx = line.index(NO_H_G_USED_STR)
422					if hl_groups:
423						mark = Mark(mark_name, i + 1, idx + 1, func.__doc__, pointer + idx)
424						echoerr(
425							context='Error while checking theme (key {key})'.format(key=context.key),
426							context_mark=function_name.mark,
427							problem=(
428								'found sentence stating that no highlight groups are used '
429								'in addition to highlight group definition'
430							),
431							problem_mark=mark,
432						)
433						hadproblem = True
434						continue
435					hl_groups = None
436				pointer += len(line) + len('\n')
437
438		if divider_hl_group:
439			r = hl_exists(divider_hl_group, data, context, echoerr, allow_gradients=True)
440			if r:
441				echoerr(
442					context='Error while checking theme (key {key})'.format(key=context.key),
443					context_mark=function_name.mark,
444					problem=(
445						'found highlight group {0} not defined in the following colorschemes: {1}\n'
446						'(Group name was obtained from function documentation.)'
447					).format(divider_hl_group, list_sep.join(r)),
448					problem_mark=divider_hl_group.mark,
449				)
450				hadproblem = True
451			if check_hl_group_name(divider_hl_group, function_name.mark, context, echoerr):
452				hadproblem = True
453
454		if hl_groups:
455			greg = re.compile(r'``([^`]+)``( \(gradient\))?')
456			parsed_hl_groups = []
457			for line, mark_args, pointer in hl_groups:
458				for s in line.split(', '):
459					required_pack = []
460					sub_pointer = pointer
461					for subs in s.split(' or '):
462						match = greg.match(subs)
463						try:
464							if not match:
465								continue
466							hl_group = MarkedUnicode(
467								match.group(1),
468								Mark(*mark_args, pointer=sub_pointer + match.start(1))
469							)
470							if check_hl_group_name(hl_group, function_name.mark, context, echoerr):
471								hadproblem = True
472							gradient = bool(match.group(2))
473							required_pack.append((hl_group, gradient))
474						finally:
475							sub_pointer += len(subs) + len(' or ')
476					parsed_hl_groups.append(required_pack)
477					pointer += len(s) + len(', ')
478			del hl_group, gradient
479			for required_pack in parsed_hl_groups:
480				rs = [
481					hl_exists(hl_group, data, context, echoerr, allow_gradients=('force' if gradient else False))
482					for hl_group, gradient in required_pack
483				]
484				if all(rs):
485					echoerr(
486						context='Error while checking theme (key {key})'.format(key=context.key),
487						problem=(
488							'found highlight groups list ({0}) with all groups not defined in some colorschemes\n'
489							'(Group names were taken from function documentation.)'
490						).format(list_sep.join((h[0] for h in required_pack))),
491						problem_mark=function_name.mark
492					)
493					for r, h in zip(rs, required_pack):
494						echoerr(
495							context='Error while checking theme (key {key})'.format(key=context.key),
496							problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
497								h[0], list_sep.join(r))
498						)
499					hadproblem = True
500		elif hl_groups is not None:
501			r = hl_exists(function_name, data, context, echoerr, allow_gradients=True)
502			if r:
503				echoerr(
504					context='Error while checking theme (key {key})'.format(key=context.key),
505					problem=(
506						'found highlight group {0} not defined in the following colorschemes: {1}\n'
507						'(If not specified otherwise in documentation, '
508						'highlight group for function segments\n'
509						'is the same as the function name.)'
510					).format(function_name, list_sep.join(r)),
511					problem_mark=function_name.mark
512				)
513				hadproblem = True
514
515		return True, False, hadproblem
516	elif context[-2][1].get('type') != 'segment_list':
517		if function_name not in context[0][1].get('segment_data', {}):
518			main_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None)
519			if data['theme'] == main_theme_name:
520				main_theme = {}
521			else:
522				main_theme = data['ext_theme_configs'].get(main_theme_name, {})
523			if (
524				function_name not in main_theme.get('segment_data', {})
525				and function_name not in data['ext_theme_configs'].get('__main__', {}).get('segment_data', {})
526				and not any(((function_name in theme.get('segment_data', {})) for theme in data['top_themes'].values()))
527			):
528				echoerr(context='Error while checking segments (key {key})'.format(key=context.key),
529				        problem='found useless use of name key (such name is not present in theme/segment_data)',
530				        problem_mark=function_name.mark)
531
532	return True, False, False
533
534
535def hl_group_in_colorscheme(hl_group, cconfig, allow_gradients, data, context, echoerr):
536	havemarks(hl_group, cconfig)
537	if hl_group not in cconfig.get('groups', {}):
538		return False
539	elif not allow_gradients or allow_gradients == 'force':
540		group_config = cconfig['groups'][hl_group]
541		while isinstance(group_config, unicode):
542			try:
543				group_config = cconfig['groups'][group_config]
544			except KeyError:
545				# No such group. Error was already reported when checking
546				# colorschemes.
547				return True
548		havemarks(group_config)
549		hadgradient = False
550		for ckey in ('fg', 'bg'):
551			color = group_config.get(ckey)
552			if not color:
553				# No color. Error was already reported when checking
554				# colorschemes.
555				return True
556			havemarks(color)
557			# Gradients are only allowed for function segments. Note that
558			# whether *either* color or gradient exists should have been
559			# already checked
560			hascolor = color in data['colors_config'].get('colors', {})
561			hasgradient = color in data['colors_config'].get('gradients', {})
562			if hasgradient:
563				hadgradient = True
564			if allow_gradients is False and not hascolor and hasgradient:
565				echoerr(
566					context='Error while checking highlight group in theme (key {key})'.format(
567						key=context.key),
568					context_mark=hl_group.mark,
569					problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color),
570					problem_mark=color.mark
571				)
572				return False
573		if allow_gradients == 'force' and not hadgradient:
574			echoerr(
575				context='Error while checking highlight group in theme (key {key})'.format(
576					key=context.key),
577				context_mark=hl_group.mark,
578				problem='group {0} should have at least one gradient color, but it has no'.format(hl_group),
579				problem_mark=group_config.mark
580			)
581			return False
582	return True
583
584
585def hl_exists(hl_group, data, context, echoerr, allow_gradients=False):
586	havemarks(hl_group)
587	ext = data['ext']
588	if ext not in data['colorscheme_configs']:
589		# No colorschemes. Error was already reported, no need to report it
590		# twice
591		return []
592	r = []
593	found = False
594	for colorscheme, cconfig in data['colorscheme_configs'][ext].items():
595		if hl_group_in_colorscheme(hl_group, cconfig, allow_gradients, data, context, echoerr):
596			found = True
597		else:
598			r.append(colorscheme)
599	if not found:
600		pass
601	return r
602
603
604def check_highlight_group(hl_group, data, context, echoerr):
605	havemarks(hl_group)
606	r = hl_exists(hl_group, data, context, echoerr)
607	if r:
608		echoerr(
609			context='Error while checking theme (key {key})'.format(key=context.key),
610			problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
611				hl_group, list_sep.join(r)),
612			problem_mark=hl_group.mark
613		)
614		return True, False, True
615	return True, False, False
616
617
618def check_highlight_groups(hl_groups, data, context, echoerr):
619	havemarks(hl_groups)
620	rs = [hl_exists(hl_group, data, context, echoerr) for hl_group in hl_groups]
621	if all(rs):
622		echoerr(
623			context='Error while checking theme (key {key})'.format(key=context.key),
624			problem='found highlight groups list ({0}) with all groups not defined in some colorschemes'.format(
625				list_sep.join((unicode(h) for h in hl_groups))),
626			problem_mark=hl_groups.mark
627		)
628		for r, hl_group in zip(rs, hl_groups):
629			echoerr(
630				context='Error while checking theme (key {key})'.format(key=context.key),
631				problem='found highlight group {0} not defined in the following colorschemes: {1}'.format(
632					hl_group, list_sep.join(r)),
633				problem_mark=hl_group.mark
634			)
635		return True, False, True
636	return True, False, False
637
638
639def check_segment_data_key(key, data, context, echoerr):
640	havemarks(key)
641	has_module_name = '.' in key
642	found = False
643	for ext, theme in list_themes(data, context):
644		for segments in theme.get('segments', {}).values():
645			for segment in segments:
646				if 'name' in segment:
647					if key == segment['name']:
648						found = True
649						break
650				else:
651					function_name = segment.get('function')
652					if function_name:
653						module, function_name = get_function_strings(function_name, ((None, theme),), ext)
654						if has_module_name:
655							full_name = module + '.' + function_name
656							if key == full_name:
657								found = True
658								break
659						else:
660							if key == function_name:
661								found = True
662								break
663			if found:
664				break
665		if found:
666			break
667	else:
668		if data['theme_type'] != 'top':
669			echoerr(context='Error while checking segment data',
670			        problem='found key {0} that cannot be associated with any segment'.format(key),
671			        problem_mark=key.mark)
672			return True, False, True
673
674	return True, False, False
675
676
677threaded_args_specs = {
678	'interval': Spec().cmp('gt', 0.0),
679	'update_first': Spec().type(bool),
680	'shutdown_event': Spec().error('Shutdown event must be set by powerline'),
681}
682
683
684def check_args_variant(func, args, data, context, echoerr):
685	havemarks(args)
686	argspec = getconfigargspec(func)
687	present_args = set(args)
688	all_args = set(argspec.args)
689	required_args = set(argspec.args[:-len(argspec.defaults)])
690
691	hadproblem = False
692
693	if required_args - present_args:
694		echoerr(
695			context='Error while checking segment arguments (key {key})'.format(key=context.key),
696			context_mark=args.mark,
697			problem='some of the required keys are missing: {0}'.format(list_sep.join(required_args - present_args))
698		)
699		hadproblem = True
700
701	if not all_args >= present_args:
702		echoerr(context='Error while checking segment arguments (key {key})'.format(key=context.key),
703		        context_mark=args.mark,
704		        problem='found unknown keys: {0}'.format(list_sep.join(present_args - all_args)),
705		        problem_mark=next(iter(present_args - all_args)).mark)
706		hadproblem = True
707
708	if isinstance(func, ThreadedSegment):
709		for key in set(threaded_args_specs) & present_args:
710			proceed, khadproblem = threaded_args_specs[key].match(
711				args[key],
712				args.mark,
713				data,
714				context.enter_key(args, key),
715				echoerr
716			)
717			if khadproblem:
718				hadproblem = True
719			if not proceed:
720				return hadproblem
721
722	return hadproblem
723
724
725def check_args(get_functions, args, data, context, echoerr):
726	new_echoerr = DelayedEchoErr(echoerr)
727	count = 0
728	hadproblem = False
729	for func in get_functions(data, context, new_echoerr):
730		count += 1
731		shadproblem = check_args_variant(func, args, data, context, echoerr)
732		if shadproblem:
733			hadproblem = True
734
735	if not count:
736		hadproblem = True
737		if new_echoerr:
738			new_echoerr.echo_all()
739		else:
740			echoerr(context='Error while checking segment arguments (key {key})'.format(key=context.key),
741			        context_mark=context[-2][1].mark,
742			        problem='no suitable segments found')
743
744	return True, False, hadproblem
745
746
747def get_one_segment_function(data, context, echoerr):
748	ext = data['ext']
749	function_name = context[-2][1].get('function')
750	if function_name:
751		module, function_name = get_function_strings(function_name, context, ext)
752		func = import_segment(function_name, data, context, echoerr, module=module)
753		if func:
754			yield func
755
756
757common_names = defaultdict(set)
758
759
760def register_common_name(name, cmodule, cname):
761	s = cmodule + '.' + cname
762	cmodule_mark = Mark('<common name definition>', 1, 1, s, 1)
763	cname_mark = Mark('<common name definition>', 1, len(cmodule) + 1, s, len(cmodule) + 1)
764	common_names[name].add((MarkedUnicode(cmodule, cmodule_mark), MarkedUnicode(cname, cname_mark)))
765
766
767def get_all_possible_functions(data, context, echoerr):
768	name = context[-2][0]
769	module, name = name.rpartition('.')[::2]
770	if module:
771		func = import_segment(name, data, context, echoerr, module=module)
772		if func:
773			yield func
774	else:
775		if name in common_names:
776			for cmodule, cname in common_names[name]:
777				cfunc = import_segment(cname, data, context, echoerr, module=MarkedUnicode(cmodule, None))
778				if cfunc:
779					yield cfunc
780		for ext, theme_config in list_themes(data, context):
781			for segments in theme_config.get('segments', {}).values():
782				for segment in segments:
783					if segment.get('type', 'function') == 'function':
784						function_name = segment.get('function')
785						current_name = segment.get('name')
786						if function_name:
787							module, function_name = get_function_strings(function_name, ((None, theme_config),), ext)
788							if current_name == name or function_name == name:
789								func = import_segment(function_name, data, context, echoerr, module=module)
790								if func:
791									yield func
792
793
794def check_exinclude_function(name, data, context, echoerr):
795	ext = data['ext']
796	module, name = name.rpartition('.')[::2]
797	if not module:
798		module = MarkedUnicode('powerline.selectors.' + ext, None)
799	func = import_function('selector', name, data, context, echoerr, module=module)
800	if not func:
801		return True, False, True
802	return True, False, False
803
804
805def check_log_file_level(this_level, data, context, echoerr):
806	'''Check handler level specified in :ref:`log_file key <config-common-log>`
807
808	This level must be greater or equal to the level in :ref:`log_level key
809	<config-common-log_level>`.
810	'''
811	havemarks(this_level)
812	hadproblem = False
813	top_level = context[0][1].get('common', {}).get('log_level', 'WARNING')
814	top_level_str = top_level
815	top_level_mark = getattr(top_level, 'mark', None)
816	if (
817		not isinstance(top_level, unicode) or not hasattr(logging, top_level)
818		or not isinstance(this_level, unicode) or not hasattr(logging, this_level)
819	):
820		return True, False, hadproblem
821	top_level = getattr(logging, top_level)
822	this_level_str = this_level
823	this_level_mark = this_level.mark
824	this_level = getattr(logging, this_level)
825	if this_level < top_level:
826		echoerr(
827			context='Error while checking log level index (key {key})'.format(
828				key=context.key),
829			context_mark=this_level_mark,
830			problem='found level that is less critical then top level ({0} < {0})'.format(
831				this_level_str, top_level_str),
832			problem_mark=top_level_mark,
833		)
834		hadproblem = True
835	return True, False, hadproblem
836
837
838def check_logging_handler(handler_name, data, context, echoerr):
839	havemarks(handler_name)
840	import_paths = [os.path.expanduser(path) for path in context[0][1].get('common', {}).get('paths', [])]
841
842	handler_module, separator, handler_class = handler_name.rpartition('.')
843	if not separator:
844		handler_module = 'logging.handlers'
845		handler_class = handler_name
846	with WithPath(import_paths):
847		try:
848			handler = getattr(__import__(str(handler_module), fromlist=[str(handler_class)]), str(handler_class))
849		except ImportError:
850			echoerr(context='Error while loading logger class (key {key})'.format(key=context.key),
851			        problem='failed to load module {0}'.format(handler_module),
852			        problem_mark=handler_name.mark)
853			return True, False, True
854		except AttributeError:
855			echoerr(context='Error while loading logger class (key {key})'.format(key=context.key),
856			        problem='failed to load handler class {0}'.format(handler_class),
857			        problem_mark=handler_name.mark)
858			return True, False, True
859
860	if not issubclass(handler, logging.Handler):
861		echoerr(context='Error while loading logger class (key {key})'.format(key=context.key),
862		        problem='loaded class {0} is not a logging.Handler subclass'.format(handler_class),
863		        problem_mark=handler_name.mark)
864		return True, False, True
865
866	return True, False, False
867