From 2c445a935621124e33ddd1e2140b9c5c982ed7db Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 22:09:34 +0400 Subject: [PATCH 1/6] Add more tests to test_configuration Dynamic configuration tests. Purpose: make sure that default configuration does not throw. Tests catch only very trivial problems (like missing imports after refactoring) --- tests/test_configuration.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 5851ec3f..1ae26355 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -57,6 +57,35 @@ class TestConfig(TestCase): from powerline.shell import ShellPowerline with replace_module_attr(common, 'urllib_read', urllib_read): ShellPowerline(Args(ext=['tmux'])).renderer.render() + reload(common) + + def test_zsh(self): + from powerline.shell import ShellPowerline + ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt')).renderer.render() + + def test_bash(self): + from powerline.shell import ShellPowerline + ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})])).renderer.render() + + def test_ipython(self): + from powerline.ipython import IpythonPowerline + + class IpyPowerline(IpythonPowerline): + path = None + config_overrides = None + theme_overrides = {} + + IpyPowerline().renderer.render() + + def test_wm(self): + from powerline.segments import common + from imp import reload + reload(common) + from powerline.shell import ShellPowerline + with replace_module_attr(common, 'urllib_read', urllib_read): + from powerline import Powerline + Powerline(ext='wm', renderer_module='pango_markup').renderer.render() + reload(common) old_cwd = None From 696478593d3c9f0ee8352ce5760796866ad5f43d Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 22:13:43 +0400 Subject: [PATCH 2/6] Move some functions out of classes No need to have static methods that are not supposed to be overridden and that do not benefit from `self' argument they do not receive --- powerline/renderer.py | 25 +++++++++---------------- powerline/renderers/shell.py | 11 +++++++++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index e91be682..4d3c497b 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -9,6 +9,13 @@ except NameError: NBSP = ' ' +def construct_returned_value(rendered_highlighted, segments, output_raw): + if output_raw: + return rendered_highlighted, ''.join((segment['_rendered_raw'] for segment in segments)) + else: + return rendered_highlighted + + class Renderer(object): def __init__(self, theme_config, local_themes, theme_kwargs, colorscheme, **options): self.__dict__.update(options) @@ -52,7 +59,7 @@ class Renderer(object): if not width: # No width specified, so we don't need to crop or pad anything - return self._returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) + return construct_returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) # Create an ordered list of segments that can be dropped segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0] @@ -77,7 +84,7 @@ class Renderer(object): rendered_highlighted = ''.join([segment['_rendered_hl'] for segment in self._render_segments(theme, segments)]) + self.hlstyle() - return self._returned_value(rendered_highlighted, segments, output_raw) + return construct_returned_value(rendered_highlighted, segments, output_raw) def _render_segments(self, theme, segments, render_highlighted=True): '''Internal segment rendering method. @@ -154,24 +161,10 @@ class Renderer(object): segment['_len'] = len(segment['_rendered_raw']) yield segment - @staticmethod - def _returned_value(rendered_highlighted, segments, output_raw): - if output_raw: - return rendered_highlighted, ''.join((segment['_rendered_raw'] for segment in segments)) - else: - return rendered_highlighted - @staticmethod def escape(string): return string - @staticmethod - def _int_to_rgb(int): - r = (int >> 16) & 0xff - g = (int >> 8) & 0xff - b = int & 0xff - return r, g, b - def hlstyle(fg=None, bg=None, attr=None): raise NotImplementedError diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index b2ad0197..e6e82019 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -4,6 +4,13 @@ from powerline.renderer import Renderer from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE +def int_to_rgb(num): + r = (num >> 16) & 0xff + g = (num >> 8) & 0xff + b = num & 0xff + return r, g, b + + class ShellRenderer(Renderer): '''Powerline shell segment renderer.''' escape_hl_start = '' @@ -25,7 +32,7 @@ class ShellRenderer(Renderer): ansi += [39] else: if self.term_truecolor: - ansi += [38, 2] + list(self._int_to_rgb(fg[1])) + ansi += [38, 2] + list(int_to_rgb(fg[1])) else: ansi += [38, 5, fg[0]] if bg is not None: @@ -33,7 +40,7 @@ class ShellRenderer(Renderer): ansi += [49] else: if self.term_truecolor: - ansi += [48, 2] + list(self._int_to_rgb(bg[1])) + ansi += [48, 2] + list(int_to_rgb(bg[1])) else: ansi += [48, 5, bg[0]] if attr is not None: From b2b05cb81cff8da74b4a2ac47e30fe23b904db14 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 22:22:18 +0400 Subject: [PATCH 3/6] Removed underscore_to_camelcase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to bother both developers (that need to create class names compatible with file name, which is not obvious if not looking into main Powerline class) and computer (that needs to recompute class name each time) if it is possible to bother only developers (or only computer, but this contributes to higher startup times). About not obvious: when you look into zsh_prompt.py and see only ZshPromptRenderer name you only think that powerline upstream is following strict code style. You don’t think there is a technical reason for such naming, like you don’t think there is technical reason for having blank lines. When you look into zsh_prompt.py and see `renderer = ZshPromptRenderer` it is obvious that there is technical reason for writing code this way because new variable is never used in the module itself. --- powerline/__init__.py | 8 +++----- powerline/lib/__init__.py | 5 ----- powerline/renderers/bash_prompt.py | 3 +++ powerline/renderers/ipython.py | 3 +++ powerline/renderers/pango_markup.py | 3 +++ powerline/renderers/shell.py | 3 +++ powerline/renderers/tmux.py | 3 +++ powerline/renderers/vim.py | 4 ++++ powerline/renderers/zsh_prompt.py | 3 +++ tests/test_configuration.py | 3 +-- tests/test_lib.py | 6 +----- 11 files changed, 27 insertions(+), 17 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 752d845a..3ef4c554 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -6,19 +6,18 @@ import os import sys from powerline.colorscheme import Colorscheme -from powerline.lib import underscore_to_camelcase def open_file(path): return open(path, 'r') -def load_json_config(search_paths, config_file, load=json.load, open=open_file): +def load_json_config(search_paths, config_file, load=json.load, open_file=open_file): config_file += '.json' for path in search_paths: config_file_path = os.path.join(path, config_file) if os.path.isfile(config_file_path): - with open(config_file_path) as config_file_fp: + with open_file(config_file_path) as config_file_fp: return load(config_file_fp) raise IOError('Config file not found in search path: {0}'.format(config_file)) @@ -65,9 +64,8 @@ class Powerline(object): # Load and initialize extension renderer renderer_module_name = renderer_module or ext renderer_module_import = 'powerline.renderers.{0}'.format(renderer_module_name) - renderer_class_name = '{0}Renderer'.format(underscore_to_camelcase(renderer_module_name)) try: - Renderer = getattr(__import__(renderer_module_import, fromlist=[renderer_class_name]), renderer_class_name) + Renderer = __import__(renderer_module_import, fromlist=['renderer']).renderer except ImportError as e: sys.stderr.write('Error while importing renderer module: {0}\n'.format(e)) sys.exit(1) diff --git a/powerline/lib/__init__.py b/powerline/lib/__init__.py index 549d7455..fc97407d 100644 --- a/powerline/lib/__init__.py +++ b/powerline/lib/__init__.py @@ -7,11 +7,6 @@ from powerline.lib.humanize_bytes import humanize_bytes # NOQA from powerline.lib.url import urllib_read, urllib_urlencode # NOQA -def underscore_to_camelcase(string): - '''Return a underscore_separated_string as CamelCase.''' - return ''.join(word.capitalize() or '_' for word in string.split('_')) - - def mergedicts(d1, d2): '''Recursively merge two dictionaries. First dictionary is modified in-place. ''' diff --git a/powerline/renderers/bash_prompt.py b/powerline/renderers/bash_prompt.py index b9f911ad..01f42f74 100644 --- a/powerline/renderers/bash_prompt.py +++ b/powerline/renderers/bash_prompt.py @@ -11,3 +11,6 @@ class BashPromptRenderer(ShellRenderer): @staticmethod def escape(string): return string.replace('\\', '\\\\').replace('$', '\\$').replace('`', '\\`') + + +renderer = BashPromptRenderer diff --git a/powerline/renderers/ipython.py b/powerline/renderers/ipython.py index d1e4c7bd..2fa63acd 100644 --- a/powerline/renderers/ipython.py +++ b/powerline/renderers/ipython.py @@ -7,3 +7,6 @@ class IpythonRenderer(ShellRenderer): '''Powerline ipython segment renderer.''' escape_hl_start = '\x01' escape_hl_end = '\x02' + + +renderer = IpythonRenderer diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py index 54e78868..f5e85b6a 100644 --- a/powerline/renderers/pango_markup.py +++ b/powerline/renderers/pango_markup.py @@ -29,3 +29,6 @@ class PangoMarkupRenderer(Renderer): if attr & ATTR_UNDERLINE: awesome_attr += ['underline="single"'] return '' + contents + '' + + +renderer = PangoMarkupRenderer diff --git a/powerline/renderers/shell.py b/powerline/renderers/shell.py index e6e82019..40777c46 100644 --- a/powerline/renderers/shell.py +++ b/powerline/renderers/shell.py @@ -65,3 +65,6 @@ class ShellRenderer(Renderer): @staticmethod def escape(string): return string.replace('\\', '\\\\') + + +renderer = ShellRenderer diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index 57dd3cd8..34e4329e 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -39,3 +39,6 @@ class TmuxRenderer(Renderer): else: tmux_attr += ['nounderscore'] return '#[' + ','.join(tmux_attr) + ']' + + +renderer = TmuxRenderer diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 5099e4dc..13f80d21 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -9,6 +9,7 @@ from powerline.theme import Theme import vim + vim_mode = vim_get_func('mode') vim_getwinvar = vim_get_func('getwinvar') vim_setwinvar = vim_get_func('setwinvar') @@ -120,3 +121,6 @@ class VimRenderer(Renderer): attr=','.join(hl_group['attr']), )) return '%#' + self.hl_groups[(fg, bg, attr)]['name'] + '#' + + +renderer = VimRenderer diff --git a/powerline/renderers/zsh_prompt.py b/powerline/renderers/zsh_prompt.py index f66f7359..2e6e5e77 100644 --- a/powerline/renderers/zsh_prompt.py +++ b/powerline/renderers/zsh_prompt.py @@ -11,3 +11,6 @@ class ZshPromptRenderer(ShellRenderer): @staticmethod def escape(string): return string.replace('%', '%%').replace('\\', '\\\\') + + +renderer = ZshPromptRenderer diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 1ae26355..bd5f9e89 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -81,9 +81,8 @@ class TestConfig(TestCase): from powerline.segments import common from imp import reload reload(common) - from powerline.shell import ShellPowerline + from powerline import Powerline with replace_module_attr(common, 'urllib_read', urllib_read): - from powerline import Powerline Powerline(ext='wm', renderer_module='pango_markup').renderer.render() reload(common) diff --git a/tests/test_lib.py b/tests/test_lib.py index e48730d1..5b3e2192 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,5 +1,5 @@ # vim:fileencoding=utf-8:noet -from powerline.lib import mergedicts, underscore_to_camelcase, add_divider_highlight_group, humanize_bytes +from powerline.lib import mergedicts, add_divider_highlight_group, humanize_bytes from powerline.lib.vcs import guess from subprocess import call, PIPE import os @@ -8,10 +8,6 @@ from tests import TestCase class TestLib(TestCase): - def test_underscore_to_camelcase(self): - self.assertEqual(underscore_to_camelcase('abc_def_ghi'), 'AbcDefGhi') - self.assertEqual(underscore_to_camelcase('abc_def__ghi'), 'AbcDef_Ghi') - def test_mergedicts(self): d = {} mergedicts(d, {'abc': {'def': 'ghi'}}) From 417e823e9695ab7e669314bc7a7d93b4669b9fc3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 11 Mar 2013 22:24:00 +0400 Subject: [PATCH 4/6] Fix two flake8 errors --- powerline/lint/__init__.py | 2 +- powerline/lint/markedjson/reader.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 05108d57..0242e1da 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -604,7 +604,7 @@ def check_segment_name(name, data, context, echoerr): except AttributeError: echoerr(context='Error while loading segment function (key {key})'.format(key=context_key(context)), problem='failed to load function {0} from module {1}'.format(name, module), - problem_mark=match_name.mark) + problem_mark=name.mark) return True, False, True if not callable(func): diff --git a/powerline/lint/markedjson/reader.py b/powerline/lint/markedjson/reader.py index 47b99362..f59605ee 100644 --- a/powerline/lint/markedjson/reader.py +++ b/powerline/lint/markedjson/reader.py @@ -88,7 +88,6 @@ class Reader(object): def check_printable(self, data): match = NON_PRINTABLE.search(data) if match: - character = match.group() self.update_pointer(match.start()) raise ReaderError('while reading from stream', None, 'found special characters which are not allowed', From 271cfe06b1164fc98fa15581b46645adf994f987 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Mar 2013 00:58:27 +0400 Subject: [PATCH 5/6] Add parameter to disable current and user directories shortening Fixes #322 --- powerline/segments/vim.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/powerline/segments/vim.py b/powerline/segments/vim.py index c70eb3a1..ed794e10 100644 --- a/powerline/segments/vim.py +++ b/powerline/segments/vim.py @@ -151,16 +151,23 @@ def readonly_indicator(segment_info, text=''): @requires_segment_info -def file_directory(segment_info, shorten_home=False): +def file_directory(segment_info, shorten_user=True, shorten_cwd=True, shorten_home=False): '''Return file directory (head component of the file path). + :param bool shorten_user: + shorten ``$HOME`` directory to :file:`~/` + + :param bool shorten_cwd: + shorten current directory to :file:`./` + :param bool shorten_home: shorten all directories in :file:`/home/` to :file:`~user/` instead of :file:`/home/user/`. ''' name = segment_info['buffer'].name if not name: return None - file_directory = vim_funcs['fnamemodify'](name, ':~:.:h') + file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '') + + (':.' if shorten_home else '') + ':h') if shorten_home and file_directory.startswith('/home/'): file_directory = '~' + file_directory[6:] return file_directory + os.sep if file_directory else None From d8c64c5e3a8f6c5acd7f8fda8c042e6ea1e5610c Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 13 Mar 2013 07:33:47 +0400 Subject: [PATCH 6/6] Fix keyword argument name --- powerline/lint/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index 0242e1da..6267e2f9 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -875,7 +875,7 @@ def check(path=None): hadproblem = False try: - main_config = load_json_config(search_paths, 'config', load=load_config, open=open_file) + main_config = load_json_config(search_paths, 'config', load=load_config, open_file=open_file) except IOError: main_config = {} sys.stderr.write('\nConfiguration file not found: config.json\n') @@ -891,7 +891,7 @@ def check(path=None): import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] try: - colors_config = load_json_config(search_paths, 'colors', load=load_config, open=open_file) + colors_config = load_json_config(search_paths, 'colors', load=load_config, open_file=open_file) except IOError: colors_config = {} sys.stderr.write('\nConfiguration file not found: colors.json\n')