From 7b60b941fd65e7565ba603ba1415d28d92f78b7a Mon Sep 17 00:00:00 2001 From: S0lll0s Date: Tue, 5 Jan 2016 00:46:43 +0100 Subject: [PATCH 1/6] Let segment_info overwrite i3wm workspace output --- powerline/segments/i3wm.py | 8 ++++++-- tests/test_segments.py | 25 +++++++++++++++++-------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/powerline/segments/i3wm.py b/powerline/segments/i3wm.py index a5a82774..ea0217d6 100644 --- a/powerline/segments/i3wm.py +++ b/powerline/segments/i3wm.py @@ -19,7 +19,8 @@ def calcgrp(w): return group -def workspaces(pl, only_show=None, output=None, strip=0): +@requires_segment_info +def workspaces(pl, segment_info, only_show=None, output=None, strip=0): '''Return list of used workspaces :param list only_show: @@ -28,7 +29,8 @@ def workspaces(pl, only_show=None, output=None, strip=0): are shown. :param str output: - If specified, only workspaces on this output are shown. + If specified, only workspaces on this output are shown. If unspecified, + may be set by the lemonbar renderer and bindings. :param int strip: Specifies how many characters from the front of each workspace name @@ -45,6 +47,8 @@ def workspaces(pl, only_show=None, output=None, strip=0): else: conn = i3ipc.Connection() + output = output or ('output' in segment_info and segment_info['output']) + return [{ 'contents': w['name'][min(len(w['name']), strip):], 'highlight_groups': calcgrp(w) diff --git a/tests/test_segments.py b/tests/test_segments.py index a1c70c98..edb69a59 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -891,39 +891,48 @@ class TestI3WM(TestCase): {'name': '3: w3', 'output': 'HDMI1', 'focused': False, 'urgent': True, 'visible': True}, {'name': '4: w4', 'output': 'DVI01', 'focused': True, 'urgent': True, 'visible': True}, ]))): - self.assertEqual(i3wm.workspaces(pl=pl), [ + segment_info = {} + + self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info), [ {'contents': '1: w1', 'highlight_groups': ['workspace']}, {'contents': '2: w2', 'highlight_groups': ['w_visible', 'workspace']}, {'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']}, {'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']}, ]) - self.assertEqual(i3wm.workspaces(pl=pl, only_show=None), [ + self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=None), [ {'contents': '1: w1', 'highlight_groups': ['workspace']}, {'contents': '2: w2', 'highlight_groups': ['w_visible', 'workspace']}, {'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']}, {'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']}, ]) - self.assertEqual(i3wm.workspaces(pl=pl, only_show=['focused', 'urgent']), [ + self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['focused', 'urgent']), [ {'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']}, {'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']}, ]) - self.assertEqual(i3wm.workspaces(pl=pl, only_show=['visible']), [ + self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible']), [ {'contents': '2: w2', 'highlight_groups': ['w_visible', 'workspace']}, {'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']}, {'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']}, ]) - self.assertEqual(i3wm.workspaces(pl=pl, only_show=['visible'], strip=3), [ + self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], strip=3), [ {'contents': 'w2', 'highlight_groups': ['w_visible', 'workspace']}, {'contents': 'w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']}, {'contents': 'w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']}, ]) - self.assertEqual(i3wm.workspaces(pl=pl, only_show=['focused', 'urgent'], output='DVI01'), [ + self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['focused', 'urgent'], output='DVI01'), [ {'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']}, ]) - self.assertEqual(i3wm.workspaces(pl=pl, only_show=['visible'], output='HDMI1'), [ + self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], output='HDMI1'), [ {'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']}, ]) - self.assertEqual(i3wm.workspaces(pl=pl, only_show=['visible'], strip=3, output='LVDS1'), [ + self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], strip=3, output='LVDS1'), [ + {'contents': 'w2', 'highlight_groups': ['w_visible', 'workspace']}, + ]) + segment_info['output'] = 'LVDS1' + self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], output='HDMI1'), [ + {'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']}, + ]) + self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], strip=3), [ {'contents': 'w2', 'highlight_groups': ['w_visible', 'workspace']}, ]) From ee5f471b491cec3f0b6f08095f4aa91d5320435a Mon Sep 17 00:00:00 2001 From: S0lll0s Date: Tue, 5 Jan 2016 01:15:09 +0100 Subject: [PATCH 2/6] Deprecate "bar" bindings in favor of lemonbar --- docs/source/configuration/reference.rst | 4 + docs/source/usage/wm-widgets.rst | 21 +++-- powerline/bindings/bar/powerline-bar.py | 18 ++-- .../bindings/lemonbar/powerline-lemonbar.py | 91 +++++++++++++++++++ powerline/lemonbar.py | 21 +++++ powerline/lint/__init__.py | 6 ++ powerline/renderers/bar.py | 45 --------- powerline/renderers/lemonbar.py | 61 +++++++++++++ powerline/segments/i3wm.py | 7 +- tests/test_configuration.py | 12 +-- 10 files changed, 212 insertions(+), 74 deletions(-) create mode 100755 powerline/bindings/lemonbar/powerline-lemonbar.py create mode 100644 powerline/lemonbar.py delete mode 100644 powerline/renderers/bar.py create mode 100644 powerline/renderers/lemonbar.py diff --git a/docs/source/configuration/reference.rst b/docs/source/configuration/reference.rst index e32ee06b..99b32be1 100644 --- a/docs/source/configuration/reference.rst +++ b/docs/source/configuration/reference.rst @@ -187,6 +187,10 @@ Common configuration is a subdictionary that is a value of ``ext`` key in ``out`` and ``rewrite`` prompts (refer to IPython documentation for more details) while ``in`` prompt is the default. + For wm (:ref:`lemonbar ` only) it is a dictionary + ``{output : theme_name}`` that maps the ``xrandr`` output names to the + local themes to use on that output. + ``components`` Determines which extension components should be enabled. This key is highly extension-specific, here is the table of extensions and corresponding diff --git a/docs/source/usage/wm-widgets.rst b/docs/source/usage/wm-widgets.rst index 6c0806e2..cd32c763 100644 --- a/docs/source/usage/wm-widgets.rst +++ b/docs/source/usage/wm-widgets.rst @@ -50,24 +50,27 @@ Add the following to :file:`~/.config/qtile/config.py`: ), ] -.. _bar-usage: +.. _lemonbar-usage: -bar-aint-recursive -================== +lemonbar (formerly bar-aint-recursive) +====================================== -To run the bar simply pipe the output of the binding script into ``bar`` and -specify appropriate options, for example like this:: +To run the bar simply start the binding script: - python /path/to/powerline/bindings/bar/powerline-bar.py | bar + python /path/to/powerline/bindings/lemonbar/powerline-lemonbar.py -to run with i3, simply ``exec`` this in i3 config file:: +You can specify options to be passed to ``lemonbar`` after ``--``, like so: - exec python /path/to/powerline/bindings/bar/powerline-bar.py --i3 | bar + python /path/to/powerline/bindings/lemonbar/powerline-lemonbar.py --height 16 -- -f "Source Code Pro for Powerline-9" + +to run with i3, simply ``exec`` this in the i3 config file and set the ``--i3`` switch: + + exec python /path/to/powerline/bindings/lemonbar/powerline-lemonbar.py --i3 Running the binding in i3-mode will require `i3ipc `_ (or the outdated `i3-py `_). -See the `bar documentation `_ for more +See the `lemonbar documentation `_ for more information and options. I3 bar diff --git a/powerline/bindings/bar/powerline-bar.py b/powerline/bindings/bar/powerline-bar.py index 35496f6f..05ef7685 100755 --- a/powerline/bindings/bar/powerline-bar.py +++ b/powerline/bindings/bar/powerline-bar.py @@ -2,31 +2,27 @@ # vim:fileencoding=utf-8:noet from __future__ import (unicode_literals, division, absolute_import, print_function) +import os import sys import time from threading import Lock, Timer from argparse import ArgumentParser -from powerline import Powerline +from powerline.lemonbar import LemonbarPowerline from powerline.lib.encoding import get_unicode_writer -class BarPowerline(Powerline): - get_encoding = staticmethod(lambda: 'utf-8') - - def init(self): - super(BarPowerline, self).init(ext='wm', renderer_module='bar') - - if __name__ == '__main__': - parser = ArgumentParser(description='Powerline BAR bindings.') + parser = ArgumentParser(description='Powerline lemonbar bindings.') parser.add_argument( '--i3', action='store_true', help='Subscribe for i3 events.' ) args = parser.parse_args() - powerline = BarPowerline() + powerline = LemonbarPowerline() + powerline.update_renderer() + powerline.pl.warn("The 'bar' bindings are deprecated, please switch to 'lemonbar'") lock = Lock() modes = ['default'] write = get_unicode_writer(encoding='utf-8') @@ -60,4 +56,4 @@ if __name__ == '__main__': conn.main() while True: - time.sleep(1e10) + time.sleep(1e8) diff --git a/powerline/bindings/lemonbar/powerline-lemonbar.py b/powerline/bindings/lemonbar/powerline-lemonbar.py new file mode 100755 index 00000000..ca658813 --- /dev/null +++ b/powerline/bindings/lemonbar/powerline-lemonbar.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import time +import re +import subprocess + +from threading import Lock, Timer +from argparse import ArgumentParser, REMAINDER + +from powerline.lemonbar import LemonbarPowerline +from powerline.lib.shell import run_cmd + + +if __name__ == '__main__': + parser = ArgumentParser( + description='Powerline BAR bindings.', + usage='%(prog)s [-h] [--i3] [--height HEIGHT] [--interval INTERVAL] [--bar-command CMD] -- args' + ) + parser.add_argument( + '--i3', action='store_true', + help='Subscribe for i3 events.' + ) + parser.add_argument( + '--height', default='', + help='Bar height.' + ) + parser.add_argument( + '--interval', '-i', + type=float, default=0.5, + help='Refresh interval.' + ) + parser.add_argument( + '--bar-command', '-c', + default='lemonbar', + help='Name of the lemonbar executable to use.' + ) + parser.add_argument( + 'args', nargs=REMAINDER, + help='Extra arguments for lemonbar.' + ) + args = parser.parse_args() + + powerline = LemonbarPowerline() + powerline.update_renderer() + bars = [] + active_screens = [match.groupdict() for match in re.finditer( + '^(?P[0-9A-Za-z-]+) connected (?P\d+)x(?P\d+)\+(?P\d+)\+(?P\d+)', + run_cmd(powerline.pl, ['xrandr', '-q']), + re.MULTILINE + )] + + for screen in active_screens: + command = [args.bar_command, '-g', '{0}x{1}+{2}'.format(screen['width'], args.height, screen['x'])] + args.args[1:] + process = subprocess.Popen(command, stdin=subprocess.PIPE) + bars.append((screen['name'], process, int(screen['width']) / 5)) + + lock = Lock() + modes = ['default'] + + def render(reschedule=False): + if reschedule: + Timer(args.interval, render, kwargs={'reschedule': True}).start() + + global lock + with lock: + for output, process, width in bars: + process.stdin.write(powerline.render(mode=modes[0], width=width, matcher_info=output).encode('utf-8') + b'\n') + process.stdin.flush() + + def update(evt): + modes[0] = evt.change + render() + + render(reschedule=True) + + if args.i3: + try: + import i3ipc + except ImportError: + import i3 + i3.Subscription(lambda evt, data, sub: render(), 'workspace') + else: + conn = i3ipc.Connection() + conn.on('workspace::focus', lambda conn, evt: render()) + conn.on('mode', lambda conn, evt: update(evt)) + conn.main() + + while True: + time.sleep(1e8) diff --git a/powerline/lemonbar.py b/powerline/lemonbar.py new file mode 100644 index 00000000..b49f86b5 --- /dev/null +++ b/powerline/lemonbar.py @@ -0,0 +1,21 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline import Powerline +from powerline.lib.dict import mergedicts + + +class LemonbarPowerline(Powerline): + def init(self): + super(LemonbarPowerline, self).init(ext='wm', renderer_module='lemonbar') + + get_encoding = staticmethod(lambda: 'utf-8') + + def get_local_themes(self, local_themes): + if not local_themes: + return {} + + return dict(( + (key, {'config': self.load_theme_config(val)}) + for key, val in local_themes.items() + )) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index c464a52a..6f7fa4b2 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -126,6 +126,12 @@ main_spec = (Spec( select=ext_theme_spec(), ), ).optional(), + wm=ext_spec().update( + local_themes=Spec().unknown_spec( + Spec().re('^[0-9A-Za-z-]+$'), + ext_theme_spec() + ).optional() + ).optional(), ).unknown_spec( check_ext, ext_spec(), diff --git a/powerline/renderers/bar.py b/powerline/renderers/bar.py deleted file mode 100644 index 1a0684aa..00000000 --- a/powerline/renderers/bar.py +++ /dev/null @@ -1,45 +0,0 @@ -# vim:fileencoding=utf-8:noet -from __future__ import (unicode_literals, division, absolute_import, print_function) - -from powerline.renderer import Renderer -from powerline.colorscheme import ATTR_UNDERLINE - - -class BarRenderer(Renderer): - '''bar (bar ain't recursive) renderer - - - See documentation of `bar `_ and :ref:`the usage instructions ` - ''' - - character_translations = Renderer.character_translations.copy() - character_translations[ord('%')] = '%%' - - @staticmethod - def hlstyle(*args, **kwargs): - # We don’t need to explicitly reset attributes, so skip those calls - return '' - - def hl(self, contents, fg=None, bg=None, attrs=None): - text = '' - - if fg is not None: - if fg is not False and fg[1] is not False: - text += '%{{F#ff{0:06x}}}'.format(fg[1]) - if bg is not None: - if bg is not False and bg[1] is not False: - text += '%{{B#ff{0:06x}}}'.format(bg[1]) - - if attrs & ATTR_UNDERLINE: - text += '%{+u}' - - return text + contents + '%{F-B--u}' - - def render(self, *args, **kwargs): - return '%{{l}}{0}%{{r}}{1}'.format( - super(BarRenderer, self).render(side='left', *args, **kwargs), - super(BarRenderer, self).render(side='right', *args, **kwargs), - ) - - -renderer = BarRenderer diff --git a/powerline/renderers/lemonbar.py b/powerline/renderers/lemonbar.py new file mode 100644 index 00000000..f378f235 --- /dev/null +++ b/powerline/renderers/lemonbar.py @@ -0,0 +1,61 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderer import Renderer +from powerline.theme import Theme +from powerline.colorscheme import ATTR_UNDERLINE + + +class LemonbarRenderer(Renderer): + '''lemonbar (formerly bar/bar ain't recursive) renderer + + + See documentation of `lemonbar `_ and :ref:`the usage instructions ` + ''' + + character_translations = Renderer.character_translations.copy() + character_translations[ord('%')] = '%%{}' + + @staticmethod + def hlstyle(*args, **kwargs): + # We don’t need to explicitly reset attributes, so skip those calls + return '' + + def hl(self, contents, fg=None, bg=None, attrs=None): + text = '' + + if fg is not None: + if fg is not False and fg[1] is not False: + text += '%{{F#ff{0:06x}}}'.format(fg[1]) + if bg is not None: + if bg is not False and bg[1] is not False: + text += '%{{B#ff{0:06x}}}'.format(bg[1]) + + if attrs & ATTR_UNDERLINE: + text += '%{+u}' + + return text + contents + '%{F-B--u}' + + def render(self, *args, **kwargs): + return '%{{l}}{0}%{{r}}{1}'.format( + super(LemonbarRenderer, self).render(side='left', segment_info={'output': kwargs.get('matcher_info')}, *args, **kwargs), + super(LemonbarRenderer, self).render(side='right', segment_info={'output': kwargs.get('matcher_info')}, *args, **kwargs), + ) + + def get_theme(self, matcher_info): + if not matcher_info or matcher_info not in self.local_themes: + return self.theme + match = self.local_themes[matcher_info] + + try: + return match['theme'] + except KeyError: + match['theme'] = Theme( + theme_config=match['config'], + main_theme_config=self.theme_config, + **self.theme_kwargs + ) + return match['theme'] + + +renderer = LemonbarRenderer diff --git a/powerline/segments/i3wm.py b/powerline/segments/i3wm.py index ea0217d6..9d214cfe 100644 --- a/powerline/segments/i3wm.py +++ b/powerline/segments/i3wm.py @@ -29,8 +29,9 @@ def workspaces(pl, segment_info, only_show=None, output=None, strip=0): are shown. :param str output: - If specified, only workspaces on this output are shown. If unspecified, - may be set by the lemonbar renderer and bindings. + May be set to the name of an X output. If specified, only workspaces + on that output are shown. Overrides automatic output detection by + the lemonbar renderer and bindings. :param int strip: Specifies how many characters from the front of each workspace name @@ -47,7 +48,7 @@ def workspaces(pl, segment_info, only_show=None, output=None, strip=0): else: conn = i3ipc.Connection() - output = output or ('output' in segment_info and segment_info['output']) + output = output or segment_info.get('output') return [{ 'contents': w['name'][min(len(w['name']), strip):], diff --git a/tests/test_configuration.py b/tests/test_configuration.py index c0708c3d..3cd7d387 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -825,28 +825,28 @@ class TestVim(TestCase): sys.path.pop(0) -class TestBar(TestRender): - def test_bar(self): +class TestLemonbar(TestRender): + def test_lemonbar(self): import powerline as powerline_module with swap_attributes(config, powerline_module): - with get_powerline_raw(config, powerline_module.Powerline, replace_gcp=True, ext='wm', renderer_module='bar') as powerline: + with get_powerline_raw(config, powerline_module.Powerline, replace_gcp=True, ext='wm', renderer_module='lemonbar') as powerline: self.assertRenderEqual( powerline, '%{l}%{F#ffc00000}%{B#ff008000}%{+u} A%{F-B--u}%{F#ff008000}%{B#ffc00000}>>%{F-B--u}%{F#ff008000}%{B#ffc00000}B%{F-B--u}%{F#ffc00000}>>%{F-B--u}%{r}%{F#ffc00000}<<%{F-B--u}%{F#ff804000}%{B#ffc00000}%{+u}C%{F-B--u}%{F#ff0000c0}%{B#ffc00000}<<%{F-B--u}%{F#ff008000}%{B#ff0000c0}D %{F-B--u}' ) @with_new_config - def test_bar_escape(self, config): + def test_lemonbar_escape(self, config): import powerline as powerline_module config['themes/wm/default']['segments']['left'] = ( highlighted_string('%{asd}', 'hl1'), highlighted_string('10% %', 'hl2'), ) with swap_attributes(config, powerline_module): - with get_powerline_raw(config, powerline_module.Powerline, replace_gcp=True, ext='wm', renderer_module='bar') as powerline: + with get_powerline_raw(config, powerline_module.Powerline, replace_gcp=True, ext='wm', renderer_module='lemonbar') as powerline: self.assertRenderEqual( powerline, - '%{l}%{F#ffc00000}%{B#ff008000}%{+u} %%{asd}%{F-B--u}%{F#ff008000}%{B#ffc00000}>>%{F-B--u}%{F#ff008000}%{B#ffc00000}10%% %%%{F-B--u}%{F#ffc00000}>>%{F-B--u}%{r}%{F#ffc00000}<<%{F-B--u}%{F#ff804000}%{B#ffc00000}%{+u}C%{F-B--u}%{F#ff0000c0}%{B#ffc00000}<<%{F-B--u}%{F#ff008000}%{B#ff0000c0}D %{F-B--u}' + '%{l}%{F#ffc00000}%{B#ff008000}%{+u} %%{}{asd}%{F-B--u}%{F#ff008000}%{B#ffc00000}>>%{F-B--u}%{F#ff008000}%{B#ffc00000}10%%{} %%{}%{F-B--u}%{F#ffc00000}>>%{F-B--u}%{r}%{F#ffc00000}<<%{F-B--u}%{F#ff804000}%{B#ffc00000}%{+u}C%{F-B--u}%{F#ff0000c0}%{B#ffc00000}<<%{F-B--u}%{F#ff008000}%{B#ff0000c0}D %{F-B--u}' ) From b29988b7ec45cc895f4e9b2047a61c17acdc4d5b Mon Sep 17 00:00:00 2001 From: Foo Date: Thu, 7 Jan 2016 04:18:05 +0300 Subject: [PATCH 3/6] Add functional tests for lemonbar bindings --- tests/common.sh | 4 +- tests/run_bar_tests.sh | 194 ++++++++++++++++++ tests/test_bar/path/lemonbar | 11 + tests/test_bar/path/xrandr | 31 +++ tests/test_bar/powerline/config.json | 9 + .../powerline/themes/wm/__main__.json | 10 + .../test_bar/powerline/themes/wm/default.json | 18 ++ tests/test_bar/powerline/themes/wm/dvi.json | 18 ++ 8 files changed, 294 insertions(+), 1 deletion(-) create mode 100755 tests/run_bar_tests.sh create mode 100755 tests/test_bar/path/lemonbar create mode 100755 tests/test_bar/path/xrandr create mode 100644 tests/test_bar/powerline/config.json create mode 100644 tests/test_bar/powerline/themes/wm/__main__.json create mode 100644 tests/test_bar/powerline/themes/wm/default.json create mode 100644 tests/test_bar/powerline/themes/wm/dvi.json diff --git a/tests/common.sh b/tests/common.sh index 0b555162..14da0e19 100644 --- a/tests/common.sh +++ b/tests/common.sh @@ -18,7 +18,9 @@ exit_suite() { echo "${FAIL_SUMMARY}" fi export POWERLINE_CURRENT_SUITE="${POWERLINE_CURRENT_SUITE%/*}" - exit $FAILED + if test "x$1" != "x--continue" ; then + exit $FAILED + fi } fail() { diff --git a/tests/run_bar_tests.sh b/tests/run_bar_tests.sh new file mode 100755 index 00000000..2204b2bc --- /dev/null +++ b/tests/run_bar_tests.sh @@ -0,0 +1,194 @@ +#!/bin/sh +. tests/common.sh + +enter_suite bar + +TEST_ROOT="$ROOT/tests/bar" +TEST_PATH="$TEST_ROOT/path" +TEST_STATIC_ROOT="$ROOT/tests/test_bar" + +test -d "$TEST_ROOT" && rm -r "$TEST_ROOT" +mkdir "$TEST_ROOT" +cp -r "$TEST_STATIC_ROOT/path" "$TEST_ROOT" +cp -r "$TEST_STATIC_ROOT/powerline" "$TEST_ROOT" + +export PYTHONPATH="$ROOT${PYTHONPATH:+:}$PYTHONPATH" + +ln -s "$(which "${PYTHON}")" "$TEST_PATH"/python +ln -s "$(which sed)" "$TEST_PATH" +ln -s "$(which cat)" "$TEST_PATH" +ln -s "$(which mkdir)" "$TEST_PATH" +ln -s "$(which basename)" "$TEST_PATH" +ln -s "$TEST_PATH/lemonbar" "$TEST_PATH/bar-aint-recursive" + +DEPRECATED_SCRIPT="$ROOT/powerline/bindings/bar/powerline-bar.py" + +run() { + env -i \ + LANG=C \ + PATH="$TEST_PATH" \ + XDG_CONFIG_HOME="$TEST_ROOT" \ + XDG_CONFIG_DIRS="$TEST_ROOT/dummy" \ + PYTHONPATH="$PYTHONPATH" \ + TEST_ROOT="$TEST_ROOT" \ + LD_LIBRARY_PATH="$LD_LIBRARY_PATH" \ + "$@" || true +} + +display_log() { + local log_file="$1" + echo "$log_file:" + echo '============================================================' + cat -v "$log_file" + echo + echo '____________________________________________________________' +} + +check_log() { + local log_file="$1" + local text="$2" + local warns="$3" + if test "$warns" = "warns" ; then + local warning="$(head -n1 "$log_file" | sed 's/.*://')" + local expwarning="The 'bar' bindings are deprecated, please switch to 'lemonbar'" + if test "x$warning" != "x$expwarning" ; then + echo "Got: $warning" + echo "Exp: $expwarning" + fail "warn" F "Expected warning" + fi + sed -r -i -e '1d' "$log_file" + fi + local line="$(head -n1 "$log_file")" + local linenum="$(cat "$log_file" | wc -l)" + if test $linenum -lt 5 ; then + fail "log:lt" F "Script was run not enough times" + return 1 + elif test $linenum -gt 15 ; then + fail "log:gt" E "Script was run too many times" + return 1 + fi + local expline="%{l}%{F#ffd0d0d0}%{B#ff303030} $text-left %{F-B--u}%{F#ff303030} %{F-B--u}%{r}%{F#ff303030} %{F-B--u}%{F#ffd0d0d0}%{B#ff303030} $text-right %{F-B--u}" + if test "x$expline" != "x$line" ; then + echo "Line: '$line'" + echo "Expected: '$expline'" + fail "log:line" F "Unexpected line" + return 1 + fi + local ret=0 + while test $linenum -gt 0 ; do + echo "$line" >> "$TEST_ROOT/ok" + linenum=$(( linenum - 1 )) + done + if ! diff "$TEST_ROOT/ok" "$log_file" ; then + fail "log:diff" F "Unexpected output" + ret=1 + fi + rm "$TEST_ROOT/ok" + return $ret +} + +killscript() { + kill -KILL $1 || true +} + +if ! test -e "$DEPRECATED_SCRIPT" ; then + # TODO: uncomment when skip is available + # skip "deprecated" "Missing deprecated bar bindings script" + : +else + enter_suite "deprecated" + run python "$DEPRECATED_SCRIPT" $args > "$TEST_ROOT/deprecated.log" 2>&1 & + SPID=$! + sleep 5 + killscript $SPID + if ! check_log "$TEST_ROOT/deprecated.log" "default" warns ; then + display_log "$TEST_ROOT/deprecated.log" + fail "log" F "Checking log failed" + fi + rm "$TEST_ROOT/deprecated.log" + exit_suite --continue +fi + +LEMONBAR_SCRIPT="$ROOT/powerline/bindings/lemonbar/powerline-lemonbar.py" + +if ! test -e "$LEMONBAR_SCRIPT" ; then + # TODO: uncomment when skip is available + # skip "lemonbar" "Missing lemonbar bindings script" + : +else + enter_suite "lemonbar" + for args in "" "-i0.5" "--interval=0.5" "-- test args" "-c bar-aint-recursive" "--height=10"; do + rm -rf "$TEST_ROOT/results" + run python "$LEMONBAR_SCRIPT" $args > "$TEST_ROOT/lemonbar.log" 2>&1 & + SPID=$! + sleep 5 + killscript $SPID + sleep 0.5 + enter_suite "args($args)" + fnum=0 + for file in "$TEST_ROOT/results"/*.log ; do + if ! test -e "$file" ; then + fail "log" E "Log file is missing" + break + fi + fnum=$(( fnum + 1 )) + args_file="${file%.log}.args" + if ! test -e "$args_file" ; then + fail "args" E "$args_file is missing" + else + cat "$args_file" >> "$TEST_ROOT/args.log" + fi + text="dvi" + if cat "$args_file" | grep -q +1 ; then + text="default" + fi + if ! check_log "$file" "$text" ; then + display_log "$file" + fail "log" F "Checking log failed" + fi + rm "$file" + done + if test "$fnum" -ne 2 ; then + fail "fnum" F "Expected two output files" + fi + if test "x${args#--height}" != "x$args" ; then + height="${args#--height}" + height="${height# }" + height="${height#=}" + height="${height%% *}" + fi + command="lemonbar" + if test "x${args#-c}" != "x$args" ; then + command="${args#-c}" + command="${command# }" + command="${command#=}" + command="${command%% *}" + fi + received_args="$(cat "$TEST_ROOT/args.log" | sort)" + rm "$TEST_ROOT/args.log" + script_args="${args#*-- }" + script_args="${script_args# }" + if test "x${script_args}" '=' "x$args" ; then + script_args= + fi + expected_args="$command -g 1920x$height+0${script_args:+ }$script_args${NL}$command -g 1920x$height+1${script_args:+ }$script_args" + if test "x$expected_args" != "x$received_args" ; then + echo "args:${NL}<$received_args>" + echo "expected:${NL}<$expected_args>" + fail "args" F "Expected different args" + fi + if ! test -z "$(cat "$TEST_ROOT/lemonbar.log")" ; then + display_log "$TEST_ROOT/lemonbar.log" + fail "stderr" E "Unexpected script output" + fi + rm "$TEST_ROOT/lemonbar.log" + exit_suite --continue + done + exit_suite --continue +fi + +if test $FAILED -eq 0 ; then + rm -r "$TEST_ROOT" +fi + +exit_suite diff --git a/tests/test_bar/path/lemonbar b/tests/test_bar/path/lemonbar new file mode 100755 index 00000000..13c6030e --- /dev/null +++ b/tests/test_bar/path/lemonbar @@ -0,0 +1,11 @@ +#!/bin/sh + +RES_DIR="$TEST_ROOT/results" +mkdir -p "$RES_DIR" +RES_FILE="$RES_DIR/$$" +while test -e "$RES_FILE.log" ; do + RES_FILE="$RES_FILE.${RANDOM:-`date +%N | sed s/^0*//`}" +done + +echo $(basename $0) "$@" > "$RES_FILE.args" +cat > "$RES_FILE.log" diff --git a/tests/test_bar/path/xrandr b/tests/test_bar/path/xrandr new file mode 100755 index 00000000..d02e3005 --- /dev/null +++ b/tests/test_bar/path/xrandr @@ -0,0 +1,31 @@ +#!/bin/sh + +cat << EOF +Screen 0: minimum 8 x 8, current 1920 x 1200, maximum 16384 x 16384 +DVI-I-0 disconnected (normal left inverted right x axis y axis) +VGA-0 connected 1920x1200+1+0 (normal left inverted right x axis y axis) 520mm x 330mm + 1920x1200 59.95*+ + 1920x1080 60.00 + 1680x1050 59.95 + 1600x1200 60.00 + 1440x900 59.89 + 1280x1024 75.02 60.02 + 1280x800 59.81 + 1152x864 75.00 + 1024x768 75.03 60.00 + 800x600 75.00 60.32 + 640x480 75.00 59.94 +DVI-I-1 connected 1920x1200+0+0 (normal left inverted right x axis y axis) 520mm x 330mm + 1920x1200 59.95*+ + 1920x1080 60.00 + 1680x1050 59.95 + 1600x1200 60.00 + 1440x900 59.89 + 1280x1024 75.02 60.02 + 1280x800 59.81 + 1152x864 75.00 + 1024x768 75.03 60.00 + 800x600 75.00 60.32 + 640x480 75.00 59.94 +HDMI-0 disconnected (normal left inverted right x axis y axis) +EOF diff --git a/tests/test_bar/powerline/config.json b/tests/test_bar/powerline/config.json new file mode 100644 index 00000000..0b35d567 --- /dev/null +++ b/tests/test_bar/powerline/config.json @@ -0,0 +1,9 @@ +{ + "ext": { + "wm": { + "local_themes": { + "DVI-I-1": "dvi" + } + } + } +} diff --git a/tests/test_bar/powerline/themes/wm/__main__.json b/tests/test_bar/powerline/themes/wm/__main__.json new file mode 100644 index 00000000..5654ce8c --- /dev/null +++ b/tests/test_bar/powerline/themes/wm/__main__.json @@ -0,0 +1,10 @@ +{ + "segment_data": { + "time": { + "display": false + }, + "powerline.segments.common.time.date": { + "display": false + } + } +} diff --git a/tests/test_bar/powerline/themes/wm/default.json b/tests/test_bar/powerline/themes/wm/default.json new file mode 100644 index 00000000..d9eaf5f8 --- /dev/null +++ b/tests/test_bar/powerline/themes/wm/default.json @@ -0,0 +1,18 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "highlight_groups": ["time"], + "contents": "default-left" + } + ], + "right": [ + { + "type": "string", + "highlight_groups": ["time"], + "contents": "default-right" + } + ] + } +} diff --git a/tests/test_bar/powerline/themes/wm/dvi.json b/tests/test_bar/powerline/themes/wm/dvi.json new file mode 100644 index 00000000..4cf3731b --- /dev/null +++ b/tests/test_bar/powerline/themes/wm/dvi.json @@ -0,0 +1,18 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "highlight_groups": ["time"], + "contents": "dvi-left" + } + ], + "right": [ + { + "type": "string", + "highlight_groups": ["time"], + "contents": "dvi-right" + } + ] + } +} From 619ae9050eab5eead3ddbfa22baf4f5a5b471120 Mon Sep 17 00:00:00 2001 From: Foo Date: Thu, 7 Jan 2016 05:15:39 +0300 Subject: [PATCH 4/6] Lint test configuration --- tests/run_bar_tests.sh | 7 +++++++ tests/test_bar/powerline/themes/wm/__main__.json | 10 ---------- 2 files changed, 7 insertions(+), 10 deletions(-) delete mode 100644 tests/test_bar/powerline/themes/wm/__main__.json diff --git a/tests/run_bar_tests.sh b/tests/run_bar_tests.sh index 2204b2bc..473ee33c 100755 --- a/tests/run_bar_tests.sh +++ b/tests/run_bar_tests.sh @@ -187,6 +187,13 @@ else exit_suite --continue fi +if ! powerline-lint \ + -p "$ROOT/powerline/config_files" \ + -p "$TEST_STATIC_ROOT/powerline" +then + fail "lint" F "Checking test config failed" +fi + if test $FAILED -eq 0 ; then rm -r "$TEST_ROOT" fi diff --git a/tests/test_bar/powerline/themes/wm/__main__.json b/tests/test_bar/powerline/themes/wm/__main__.json deleted file mode 100644 index 5654ce8c..00000000 --- a/tests/test_bar/powerline/themes/wm/__main__.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "segment_data": { - "time": { - "display": false - }, - "powerline.segments.common.time.date": { - "display": false - } - } -} From 8168ad142865628ef71e4e7679625168551ca5ef Mon Sep 17 00:00:00 2001 From: Foo Date: Fri, 8 Jan 2016 04:22:07 +0300 Subject: [PATCH 5/6] Improve powerline_automan: fix some bugs and add minimal argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugs: - Default action is store_true while it should be just store. - nargs default depends on metavar while it should only depend on action. - REMAINDER nargs is not supported. Minimal argument means minimal mode which removes all section and creates a container with “synopsis” and “description” sections’ contents. --- docs/source/powerline_automan.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/docs/source/powerline_automan.py b/docs/source/powerline_automan.py index 1f79001a..85d241cb 100644 --- a/docs/source/powerline_automan.py +++ b/docs/source/powerline_automan.py @@ -6,6 +6,7 @@ import re import codecs from collections import namedtuple +from argparse import REMAINDER from functools import reduce @@ -61,9 +62,9 @@ def parse_argument(*args, **kwargs): is_option = args[0].startswith('-') is_long_option = args[0].startswith('--') is_short_option = is_option and not is_long_option - action = kwargs.get('action', 'store_true') - multi = kwargs.get('action') in ('append',) - nargs = kwargs.get('nargs') or (1 if kwargs.get('metavar') or action in ('append',) else 0) + action = kwargs.get('action', 'store') + multi = kwargs.get('action') in ('append',) or kwargs.get('nargs') is REMAINDER + nargs = kwargs.get('nargs', (1 if action in ('append', 'store') else 0)) return Argument( names=args, help=u(kwargs.get('help', '')), @@ -165,9 +166,20 @@ def format_usage_arguments(arguments, base_length=None): # `--` is automatically transformed into – (EN DASH) # when parsing into HTML. We do not need this. line[-1] += [nodes.Text(char) for char in name] + elif argument.nargs is REMAINDER: + line.append(nodes.Text('[')) + line.append(nodes.strong()) + line[-1] += [nodes.Text(char) for char in '--'] + line.append(nodes.Text('] ')) if argument.nargs: - assert(argument.nargs in (1, '?')) - with SurroundWith(line, argument.nargs == '?' and argument.is_option): + assert(argument.nargs in (1, '?', REMAINDER)) + with SurroundWith( + line, ( + True + if argument.nargs is REMAINDER + else (argument.nargs == '?' and argument.is_option) + ) + ): if argument.is_long_option: line.append(nodes.Text('=')) line.append(nodes.emphasis(text=argument.metavar)) @@ -337,15 +349,21 @@ class AutoManParser(object): class AutoMan(Directive): required_arguments = 1 optional_arguments = 0 - option_spec = dict(prog=unchanged_required) + option_spec = dict(prog=unchanged_required, minimal=bool) has_content = False def run(self): + minimal = self.options.get('minimal') module = self.arguments[0] template_args = {} template_args.update(get_authors()) get_argparser = __import__(str(module), fromlist=[str('get_argparser')]).get_argparser parser = get_argparser(AutoManParser) + if minimal: + container = nodes.container() + container += parser.automan_usage(self.options['prog']) + container += parser.automan_description() + return [container] synopsis_section = nodes.section( '', nodes.title(text='Synopsis'), From 646b0ea5ea7f3ab75cc3934931d6561404e6497b Mon Sep 17 00:00:00 2001 From: Foo Date: Fri, 8 Jan 2016 04:24:51 +0300 Subject: [PATCH 6/6] Improve powerline-lemonbar documentation Changes: - Usage determination transferred back to argparse (it wraps). - Added complete description of lemonbar script to wm-widgets.rst. - --interval and --height got their arguments clarified by metavar: it was not clear what units they use. - --bar-command got its metavar because it is better then default BAR_COMMAND. - --bar-command short variant is now -C. --- docs/source/usage/wm-widgets.rst | 6 ++++ .../bindings/lemonbar/powerline-lemonbar.py | 29 ++------------- powerline/commands/lemonbar.py | 35 +++++++++++++++++++ tests/run_bar_tests.sh | 6 ++-- 4 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 powerline/commands/lemonbar.py diff --git a/docs/source/usage/wm-widgets.rst b/docs/source/usage/wm-widgets.rst index cd32c763..1aa1a2de 100644 --- a/docs/source/usage/wm-widgets.rst +++ b/docs/source/usage/wm-widgets.rst @@ -73,6 +73,12 @@ Running the binding in i3-mode will require `i3ipc `_ for more information and options. +All ``powerline-lemonbar.py`` arguments: + +.. automan:: powerline.commands.lemonbar + :prog: powerline-lemonbar.py + :minimal: true + I3 bar ====== diff --git a/powerline/bindings/lemonbar/powerline-lemonbar.py b/powerline/bindings/lemonbar/powerline-lemonbar.py index ca658813..ae4ffb34 100755 --- a/powerline/bindings/lemonbar/powerline-lemonbar.py +++ b/powerline/bindings/lemonbar/powerline-lemonbar.py @@ -7,39 +7,14 @@ import re import subprocess from threading import Lock, Timer -from argparse import ArgumentParser, REMAINDER from powerline.lemonbar import LemonbarPowerline from powerline.lib.shell import run_cmd +from powerline.commands.lemonbar import get_argparser if __name__ == '__main__': - parser = ArgumentParser( - description='Powerline BAR bindings.', - usage='%(prog)s [-h] [--i3] [--height HEIGHT] [--interval INTERVAL] [--bar-command CMD] -- args' - ) - parser.add_argument( - '--i3', action='store_true', - help='Subscribe for i3 events.' - ) - parser.add_argument( - '--height', default='', - help='Bar height.' - ) - parser.add_argument( - '--interval', '-i', - type=float, default=0.5, - help='Refresh interval.' - ) - parser.add_argument( - '--bar-command', '-c', - default='lemonbar', - help='Name of the lemonbar executable to use.' - ) - parser.add_argument( - 'args', nargs=REMAINDER, - help='Extra arguments for lemonbar.' - ) + parser = get_argparser() args = parser.parse_args() powerline = LemonbarPowerline() diff --git a/powerline/commands/lemonbar.py b/powerline/commands/lemonbar.py new file mode 100644 index 00000000..547c52cc --- /dev/null +++ b/powerline/commands/lemonbar.py @@ -0,0 +1,35 @@ +# vim:fileencoding=utf-8:noet +# WARNING: using unicode_literals causes errors in argparse +from __future__ import (division, absolute_import, print_function) + +import argparse + + +def get_argparser(ArgumentParser=argparse.ArgumentParser): + parser = ArgumentParser( + description='Powerline BAR bindings.' + ) + parser.add_argument( + '--i3', action='store_true', + help='Subscribe for i3 events.' + ) + parser.add_argument( + '--height', default='', + metavar='PIXELS', help='Bar height.' + ) + parser.add_argument( + '--interval', '-i', + type=float, default=0.5, + metavar='SECONDS', help='Refresh interval.' + ) + parser.add_argument( + '--bar-command', '-C', + default='lemonbar', + metavar='CMD', help='Name of the lemonbar executable to use.' + ) + parser.add_argument( + 'args', nargs=argparse.REMAINDER, + help='Extra arguments for lemonbar. Should be preceded with ``--`` ' + 'argument in order not to be confused with script own arguments.' + ) + return parser diff --git a/tests/run_bar_tests.sh b/tests/run_bar_tests.sh index 473ee33c..7d658ff1 100755 --- a/tests/run_bar_tests.sh +++ b/tests/run_bar_tests.sh @@ -117,7 +117,7 @@ if ! test -e "$LEMONBAR_SCRIPT" ; then : else enter_suite "lemonbar" - for args in "" "-i0.5" "--interval=0.5" "-- test args" "-c bar-aint-recursive" "--height=10"; do + for args in "" "-i0.5" "--interval=0.5" "-- test args" "--bar-command bar-aint-recursive" "--height=10"; do rm -rf "$TEST_ROOT/results" run python "$LEMONBAR_SCRIPT" $args > "$TEST_ROOT/lemonbar.log" 2>&1 & SPID=$! @@ -158,8 +158,8 @@ else height="${height%% *}" fi command="lemonbar" - if test "x${args#-c}" != "x$args" ; then - command="${args#-c}" + if test "x${args#--bar-command}" != "x$args" ; then + command="${args#--bar-command}" command="${command# }" command="${command#=}" command="${command%% *}"