diff --git a/powerline/__init__.py b/powerline/__init__.py index f2a7a166..3e192cbb 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -407,6 +407,9 @@ class Powerline(object): 'common_config': self.common_config, 'run_once': self.run_once, 'shutdown_event': self.shutdown_event, + # Note: creates implicit reference to self meaning + # reference cycle. + 'get_module_attr': self.get_module_attr, }, ) @@ -468,11 +471,12 @@ class Powerline(object): self.renderer_options['theme_config'] = self.load_theme_config(self.ext_config.get('theme', 'default')) if create_renderer: - try: - Renderer = __import__(self.renderer_module, fromlist=['renderer']).renderer - except Exception as e: - self.exception('Failed to import renderer module: {0}', str(e)) - sys.exit(1) + Renderer = self.get_module_attr(self.renderer_module, 'renderer') + if not Renderer: + if hasattr(self, 'renderer'): + return + else: + raise ImportError('Failed to obtain renderer') # Renderer updates configuration file via segments’ .startup thus it # should be locked to prevent state when configuration was updated, @@ -652,6 +656,34 @@ class Powerline(object): with self.cr_kwargs_lock: self.cr_kwargs.clear() + def get_module_attr(self, module, attr, prefix='powerline'): + '''Import module and get its attribute. + + Replaces ``from {module} import {attr}``. + + :param str module: + Module name, will be passed as first argument to ``__import__``. + :param str attr: + Module attribute, will be passed to ``__import__`` as the only value + in ``fromlist`` tuple. + + :return: + Attribute value or ``None``. Note: there is no way to distinguish + between successfull import of attribute equal to ``None`` and + unsuccessfull import. + ''' + oldpath = sys.path + sys.path = self.import_paths + sys.path + module = str(module) + attr = str(attr) + try: + return getattr(__import__(module, fromlist=(attr,)), attr) + except Exception as e: + self.pl.exception('Failed to import attr {0} from module {1}: {2}', attr, module, str(e), prefix=prefix) + return None + finally: + sys.path = oldpath + def render(self, *args, **kwargs): '''Update/create renderer if needed and pass all arguments further to ``self.renderer.render()``. diff --git a/powerline/matcher.py b/powerline/matcher.py deleted file mode 100644 index 37049e2f..00000000 --- a/powerline/matcher.py +++ /dev/null @@ -1,19 +0,0 @@ -# vim:fileencoding=utf-8:noet - -from __future__ import absolute_import -import sys - - -def gen_matcher_getter(ext, import_paths): - def get(match_name): - match_module, separator, match_function = match_name.rpartition('.') - if not separator: - match_module = 'powerline.matchers.{0}'.format(ext) - match_function = match_name - oldpath = sys.path - sys.path = import_paths + sys.path - try: - return getattr(__import__(str(match_module), fromlist=[str(match_function)]), match_function) - finally: - sys.path = oldpath - return get diff --git a/powerline/segment.py b/powerline/segment.py index 144f4623..b2a7086c 100644 --- a/powerline/segment.py +++ b/powerline/segment.py @@ -55,14 +55,11 @@ def get_segment_key(merge, *args, **kwargs): def get_function(data, segment): - oldpath = sys.path - sys.path = data['path'] + sys.path segment_module = str(segment.get('module', data['default_module'])) - name = str(segment['name']) - try: - return None, getattr(__import__(segment_module, fromlist=[name]), name), segment_module - finally: - sys.path = oldpath + function = data['get_module_attr'](segment_module, segment['name'], prefix='segment_generator') + if not function: + raise ImportError('Failed to obtain segment function') + return None, function, segment_module def get_string(data, segment): @@ -162,10 +159,10 @@ def process_segment(pl, side, segment_info, parsed_segments, segment): parsed_segments.append(segment) -def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=None): +def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, get_module_attr): data = { 'default_module': default_module or 'powerline.segments.' + ext, - 'path': common_config['paths'], + 'get_module_attr': get_module_attr, } def get_key(merge, segment, module, key, default=None): diff --git a/powerline/theme.py b/powerline/theme.py index 378f8495..d2617dc5 100644 --- a/powerline/theme.py +++ b/powerline/theme.py @@ -29,6 +29,7 @@ class Theme(object): theme_config, common_config, pl, + get_module_attr, main_theme_config=None, run_once=False, shutdown_event=None): @@ -53,7 +54,7 @@ class Theme(object): theme_configs = [theme_config] if main_theme_config: theme_configs.append(main_theme_config) - get_segment = gen_segment_getter(pl, ext, common_config, theme_configs, theme_config.get('default_module')) + get_segment = gen_segment_getter(pl, ext, common_config, theme_configs, theme_config.get('default_module'), get_module_attr) for segdict in itertools.chain((theme_config['segments'],), theme_config['segments'].get('above', ())): self.segments.append(new_empty_segment_line()) diff --git a/powerline/vim.py b/powerline/vim.py index 2676ac29..a09e24cc 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -6,7 +6,6 @@ import sys from powerline.bindings.vim import vim_get_func, vim_getvar from powerline import Powerline from powerline.lib import mergedicts -from powerline.matcher import gen_matcher_getter import vim from itertools import count @@ -80,18 +79,30 @@ class VimPowerline(Powerline): ) def get_local_themes(self, local_themes): - self.get_matcher = gen_matcher_getter(self.ext, self.import_paths) - if not local_themes: return {} return dict(( - ( - (None if key == '__tabline__' else self.get_matcher(key)), - {'config': self.load_theme_config(val)} + (matcher, {'config': self.load_theme_config(val)}) + for matcher, key, val in ( + ( + (None if k == '__tabline__' else self.get_matcher(k)), + k, + v + ) + for k, v in local_themes.items() + ) if ( + matcher or + key == '__tabline__' ) - for key, val in local_themes.items()) - ) + )) + + def get_matcher(self, match_name): + match_module, separator, match_function = match_name.rpartition('.') + if not separator: + match_module = 'powerline.matchers.{0}'.format(self.ext) + match_function = match_name + return self.get_module_attr(match_module, match_function, prefix='matcher_generator') def get_config_paths(self): try: