From 999c84a5cba0d8dbc9d3875232b378d170da6af3 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Tue, 22 Dec 2020 20:58:38 +1100 Subject: [PATCH 01/13] docs: fix simple typo, proided -> provided (#2144) There is a small typo in powerline/lint/spec.py. Should read `provided` rather than `proided`. --- powerline/lint/spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lint/spec.py b/powerline/lint/spec.py index 3111e14f..6a54441c 100644 --- a/powerline/lint/spec.py +++ b/powerline/lint/spec.py @@ -163,7 +163,7 @@ class Spec(object): '''Define message which will be used when unknown key was found “Unknown” is a key that was not provided at the initialization and via - :py:meth:`Spec.update` and did not match any ``keyfunc`` proided via + :py:meth:`Spec.update` and did not match any ``keyfunc`` provided via :py:meth:`Spec.unknown_spec`. :param msgfunc: From c31f83f831f96f4b83a22b5f0d2f34acb30ee150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Schieli?= Date: Wed, 6 Jan 2021 10:20:15 +0100 Subject: [PATCH 02/13] Emulate a right prompt in bash (#2148) * Allow passing args from render() to hl() and hlstyle() functions * New escape arg in ShellRenderer's hlstyle() to enable/disable escaping * Fix invalid escape sequence * Emulate a right prompt in bash Fixes #2103 * Document the new hl_args argument --- powerline/renderer.py | 42 +++++++---- powerline/renderers/i3bar.py | 2 +- powerline/renderers/ipython/since_5.py | 2 +- powerline/renderers/lemonbar.py | 2 +- powerline/renderers/pango_markup.py | 2 +- powerline/renderers/shell/__init__.py | 4 +- powerline/renderers/shell/bash.py | 82 +++++++++++++++++++++- powerline/renderers/tmux.py | 2 +- powerline/renderers/vim.py | 2 +- tests/test_shells/inputs/bash | 12 ++-- tests/test_shells/outputs/bash.daemon.ok | 21 +++--- tests/test_shells/outputs/bash.nodaemon.ok | 21 +++--- 12 files changed, 147 insertions(+), 47 deletions(-) diff --git a/powerline/renderer.py b/powerline/renderer.py index d1e3f1d4..31aca80e 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -251,7 +251,7 @@ class Renderer(object): for line in range(theme.get_line_number() - 1, 0, -1): yield self.render(side=None, line=line, **kwargs) - def render(self, mode=None, width=None, side=None, line=0, output_raw=False, output_width=False, segment_info=None, matcher_info=None): + def render(self, mode=None, width=None, side=None, line=0, output_raw=False, output_width=False, segment_info=None, matcher_info=None, hl_args=None): '''Render all segments. When a width is provided, low-priority segments are dropped one at @@ -286,6 +286,11 @@ class Renderer(object): :param matcher_info: Matcher information. Is processed in :py:meth:`get_segment_info` method. + :param dict hl_args: + Additional arguments to pass on the :py:meth:`hl` and + :py:meth`hlstyle` methods. They are ignored in the default + implementation, but renderer-specific overrides can make use of + them as run-time "configuration" information. ''' theme = self.get_theme(matcher_info) return self.do_render( @@ -297,6 +302,7 @@ class Renderer(object): output_width=output_width, segment_info=self.get_segment_info(segment_info, mode), theme=theme, + hl_args=hl_args ) def compute_divider_widths(self, theme): @@ -324,7 +330,7 @@ class Renderer(object): :return: Results of joining these segments. ''' - def do_render(self, mode, width, side, line, output_raw, output_width, segment_info, theme): + def do_render(self, mode, width, side, line, output_raw, output_width, segment_info, theme, hl_args): '''Like Renderer.render(), but accept theme in place of matcher_info ''' segments = list(theme.get_segments(side, line, segment_info, mode)) @@ -333,14 +339,16 @@ class Renderer(object): self._prepare_segments(segments, output_width or width) + hl_args = hl_args or dict() + if not width: # No width specified, so we don’t need to crop or pad anything if output_width: current_width = self._render_length(theme, segments, self.compute_divider_widths(theme)) return construct_returned_value(self.hl_join([ segment['_rendered_hl'] - for segment in self._render_segments(theme, segments) - ]) + self.hlstyle(), segments, current_width, output_raw, output_width) + for segment in self._render_segments(theme, segments, hl_args) + ]) + self.hlstyle(**hl_args), segments, current_width, output_raw, output_width) divider_widths = self.compute_divider_widths(theme) @@ -394,10 +402,10 @@ class Renderer(object): rendered_highlighted = self.hl_join([ segment['_rendered_hl'] - for segment in self._render_segments(theme, segments) + for segment in self._render_segments(theme, segments, hl_args) ]) if rendered_highlighted: - rendered_highlighted += self.hlstyle() + rendered_highlighted += self.hlstyle(**hl_args) return construct_returned_value(rendered_highlighted, segments, current_width, output_raw, output_width) @@ -470,7 +478,7 @@ class Renderer(object): ret += segment_len return ret - def _render_segments(self, theme, segments, render_highlighted=True): + def _render_segments(self, theme, segments, hl_args, render_highlighted=True): '''Internal segment rendering method. This method loops through the segment array and compares the @@ -527,6 +535,10 @@ class Renderer(object): contents_highlighted = '' draw_divider = segment['draw_' + divider_type + '_divider'] + segment_hl_args = {} + segment_hl_args.update(segment['highlight']) + segment_hl_args.update(hl_args) + # XXX Make sure self.hl() calls are called in the same order # segments are displayed. This is needed for Vim renderer to work. if draw_divider: @@ -546,14 +558,14 @@ class Renderer(object): if side == 'left': if render_highlighted: - contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) - divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) + contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args) + divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False, **hl_args) segment['_rendered_raw'] = contents_raw + divider_raw segment['_rendered_hl'] = contents_highlighted + divider_highlighted else: if render_highlighted: - divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) - contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) + divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False, **hl_args) + contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args) segment['_rendered_raw'] = divider_raw + contents_raw segment['_rendered_hl'] = divider_highlighted + contents_highlighted else: @@ -562,7 +574,7 @@ class Renderer(object): else: contents_raw = contents_raw + outer_padding - contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) + contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args) segment['_rendered_raw'] = contents_raw segment['_rendered_hl'] = contents_highlighted prev_segment = segment @@ -576,7 +588,7 @@ class Renderer(object): ''' return string.translate(self.character_translations) - def hlstyle(fg=None, bg=None, attrs=None): + def hlstyle(fg=None, bg=None, attrs=None, **kwargs): '''Output highlight style string. Assuming highlighted string looks like ``{style}{contents}`` this method @@ -585,10 +597,10 @@ class Renderer(object): ''' raise NotImplementedError - def hl(self, contents, fg=None, bg=None, attrs=None): + def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs): '''Output highlighted chunk. This implementation just outputs :py:meth:`hlstyle` joined with ``contents``. ''' - return self.hlstyle(fg, bg, attrs) + (contents or '') + return self.hlstyle(fg, bg, attrs, **kwargs) + (contents or '') diff --git a/powerline/renderers/i3bar.py b/powerline/renderers/i3bar.py index 020a93d8..3eab61fa 100644 --- a/powerline/renderers/i3bar.py +++ b/powerline/renderers/i3bar.py @@ -17,7 +17,7 @@ class I3barRenderer(Renderer): # We don’t need to explicitly reset attributes, so skip those calls return '' - def hl(self, contents, fg=None, bg=None, attrs=None): + def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs): segment = { 'full_text': contents, 'separator': False, diff --git a/powerline/renderers/ipython/since_5.py b/powerline/renderers/ipython/since_5.py index 8a26da72..88c7625e 100644 --- a/powerline/renderers/ipython/since_5.py +++ b/powerline/renderers/ipython/since_5.py @@ -90,7 +90,7 @@ class IPythonPygmentsRenderer(IPythonRenderer): def hl_join(segments): return reduce(operator.iadd, segments, []) - def hl(self, contents, fg=None, bg=None, attrs=None): + def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs): '''Output highlighted chunk. This implementation outputs a list containing a single pair diff --git a/powerline/renderers/lemonbar.py b/powerline/renderers/lemonbar.py index f378f235..8156807b 100644 --- a/powerline/renderers/lemonbar.py +++ b/powerline/renderers/lemonbar.py @@ -21,7 +21,7 @@ class LemonbarRenderer(Renderer): # We don’t need to explicitly reset attributes, so skip those calls return '' - def hl(self, contents, fg=None, bg=None, attrs=None): + def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs): text = '' if fg is not None: diff --git a/powerline/renderers/pango_markup.py b/powerline/renderers/pango_markup.py index 1b7d624b..3c1a6755 100644 --- a/powerline/renderers/pango_markup.py +++ b/powerline/renderers/pango_markup.py @@ -15,7 +15,7 @@ class PangoMarkupRenderer(Renderer): # We don’t need to explicitly reset attributes, so skip those calls return '' - def hl(self, contents, fg=None, bg=None, attrs=None): + def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs): '''Highlight a segment.''' awesome_attr = [] if fg is not None: diff --git a/powerline/renderers/shell/__init__.py b/powerline/renderers/shell/__init__.py index ebb05019..d7dbf96b 100644 --- a/powerline/renderers/shell/__init__.py +++ b/powerline/renderers/shell/__init__.py @@ -105,7 +105,7 @@ class ShellRenderer(PromptRenderer): self.used_term_escape_style = self.term_escape_style return super(ShellRenderer, self).do_render(segment_info=segment_info, **kwargs) - def hlstyle(self, fg=None, bg=None, attrs=None): + def hlstyle(self, fg=None, bg=None, attrs=None, escape=True, **kwargs): '''Highlight a segment. If an argument is None, the argument is ignored. If an argument is @@ -162,7 +162,7 @@ class ShellRenderer(PromptRenderer): r = '\033Ptmux;' + r.replace('\033', '\033\033') + '\033\\' elif self.screen_escape: r = '\033P' + r.replace('\033', '\033\033') + '\033\\' - return self.escape_hl_start + r + self.escape_hl_end + return self.escape_hl_start + r + self.escape_hl_end if escape else r def get_theme(self, matcher_info): if not matcher_info: diff --git a/powerline/renderers/shell/bash.py b/powerline/renderers/shell/bash.py index 783bd501..5ccf206b 100644 --- a/powerline/renderers/shell/bash.py +++ b/powerline/renderers/shell/bash.py @@ -6,13 +6,91 @@ from powerline.renderers.shell import ShellRenderer class BashPromptRenderer(ShellRenderer): '''Powerline bash prompt segment renderer.''' - escape_hl_start = '\[' - escape_hl_end = '\]' + escape_hl_start = '\\[' + escape_hl_end = '\\]' character_translations = ShellRenderer.character_translations.copy() character_translations[ord('$')] = '\\$' character_translations[ord('`')] = '\\`' character_translations[ord('\\')] = '\\\\' + def do_render(self, side, line, width, output_width, output_raw, hl_args, **kwargs): + + # we are rendering the normal left prompt + if side == 'left' and line == 0 and width is not None: + + # we need left prompt's width to render the raw spacer + output_width = output_width or output_raw + + left = super(BashPromptRenderer, self).do_render( + side=side, + line=line, + output_width=output_width, + width=width, + output_raw=output_raw, + hl_args=hl_args, + **kwargs + ) + left_rendered = left[0] if output_width else left + + # we don't escape color sequences in the right prompt so we can do escaping as a whole + if hl_args: + hl_args = hl_args.copy() + hl_args.update({'escape': False}) + else: + hl_args = {'escape': False} + + right = super(BashPromptRenderer, self).do_render( + side='right', + line=line, + output_width=True, + width=width, + output_raw=output_raw, + hl_args=hl_args, + **kwargs + ) + + ret = [] + if right[-1] > 0: + # if the right prompt is not empty we embed it in the left prompt + # it must be escaped as a whole so readline doesn't see it + ret.append(''.join(( + left_rendered, + self.escape_hl_start, + '\033[s', # save the cursor position + '\033[{0}C'.format(width), # move to the right edge of the terminal + '\033[{0}D'.format(right[-1] - 1), # move back to the right prompt position + right[0], + '\033[u', # restore the cursor position + self.escape_hl_end + ))) + if output_raw: + ret.append(''.join(( + left[1], + ' ' * (width - left[-1] - right[-1]), + right[1] + ))) + else: + ret.append(left_rendered) + if output_raw: + ret.append(left[1]) + if output_width: + ret.append(left[-1]) + if len(ret) == 1: + return ret[0] + else: + return ret + + else: + return super(BashPromptRenderer, self).do_render( + side=side, + line=line, + width=width, + output_width=output_width, + output_raw=output_raw, + hl_args=hl_args, + **kwargs + ) + renderer = BashPromptRenderer diff --git a/powerline/renderers/tmux.py b/powerline/renderers/tmux.py index 74066fab..fc3282a1 100644 --- a/powerline/renderers/tmux.py +++ b/powerline/renderers/tmux.py @@ -38,7 +38,7 @@ class TmuxRenderer(Renderer): width = 10 return super(TmuxRenderer, self).render(width=width, segment_info=segment_info, **kwargs) - def hlstyle(self, fg=None, bg=None, attrs=None): + def hlstyle(self, fg=None, bg=None, attrs=None, **kwargs): '''Highlight a segment.''' # We don’t need to explicitly reset attributes, so skip those calls if not attrs and not bg and not fg: diff --git a/powerline/renderers/vim.py b/powerline/renderers/vim.py index 281177ce..a92d51c2 100644 --- a/powerline/renderers/vim.py +++ b/powerline/renderers/vim.py @@ -123,7 +123,7 @@ class VimRenderer(Renderer): def reset_highlight(self): self.hl_groups.clear() - def hlstyle(self, fg=None, bg=None, attrs=None): + def hlstyle(self, fg=None, bg=None, attrs=None, **kwargs): '''Highlight a segment. If an argument is None, the argument is ignored. If an argument is diff --git a/tests/test_shells/inputs/bash b/tests/test_shells/inputs/bash index 1b68b6f6..b0d14785 100644 --- a/tests/test_shells/inputs/bash +++ b/tests/test_shells/inputs/bash @@ -4,6 +4,7 @@ set_theme_option() { set_theme() { export POWERLINE_CONFIG_OVERRIDES="ext.shell.theme=$1" } +set_theme_option default.segment_data.hostname.args.only_if_ssh false set_theme_option default_leftonly.segment_data.hostname.args.only_if_ssh false ABOVE_LEFT='[{ "left": [ @@ -40,7 +41,9 @@ VIRTUAL_ENV= bgscript.sh & waitpid.sh false kill `cat pid` ; sleep 1s +set_theme_option default.segment_data.hostname.display false set_theme_option default_leftonly.segment_data.hostname.display false +set_theme_option default.segment_data.user.display false set_theme_option default_leftonly.segment_data.user.display false echo ' abc @@ -56,14 +59,15 @@ cd ../'$(echo)' cd ../'`echo`' cd ../'«Unicode!»' (exit 42)|(exit 43) -set_theme_option default_leftonly.segments.above "$ABOVE_LEFT" +set_theme default +set_theme_option default.segments.above "$ABOVE_LEFT" export DISPLAYED_ENV_VAR=foo unset DISPLAYED_ENV_VAR -set_theme_option default_leftonly.segments.above "$ABOVE_FULL" +set_theme_option default.segments.above "$ABOVE_FULL" export DISPLAYED_ENV_VAR=foo unset DISPLAYED_ENV_VAR -set_theme_option default_leftonly.segments.above -set_theme_option default_leftonly.dividers.left.hard \$ABC +set_theme_option default.segments.above +set_theme_option default.dividers.left.hard \$ABC false true is the last line exit diff --git a/tests/test_shells/outputs/bash.daemon.ok b/tests/test_shells/outputs/bash.daemon.ok index 89907c83..7b8cd6cb 100644 --- a/tests/test_shells/outputs/bash.daemon.ok +++ b/tests/test_shells/outputs/bash.daemon.ok @@ -7,7 +7,9 @@   HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  false   HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1]+ Terminated bgscript.sh +  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default.segment_data.hostname.display false   HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default_leftonly.segment_data.hostname.display false + USER   BRANCH  …  tmp  shell  3rd  set_theme_option default.segment_data.user.display false  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default_leftonly.segment_data.user.display false   BRANCH  …  tmp  shell  3rd  echo '                                    abc @@ -27,16 +29,17 @@ def   BRANCH  …  shell  3rd  $(echo)  cd ../'`echo`'   BRANCH  …  shell  3rd  `echo`  cd ../'«Unicode!»'   BRANCH  …  shell  3rd  «Unicode!»  (exit 42)|(exit 43) -  BRANCH  …  shell  3rd  «Unicode!»  42  43  set_theme_option default_leftonly.segments.above "$ABOVE_LEFT" -  BRANCH  …  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo +  BRANCH  …  shell  3rd  «Unicode!»  42  43  set_theme default + …  shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above "$ABOVE_LEFT" + …  shell  3rd  «Unicode!»     BRANCH export DISPLAYED_ENV_VAR=foo  foo   -  BRANCH  …  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR -  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above "$ABOVE_FULL" + …  shell  3rd  «Unicode!»     BRANCH unset DISPLAYED_ENV_VAR + …  shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above "$ABOVE_FULL"                                                                                                                                                                                                                                                                                                              -  BRANCH  …  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo + …  shell  3rd  «Unicode!»     BRANCH export DISPLAYED_ENV_VAR=foo                                                                                                                                                                                                                                                                                                        foo  -  BRANCH  …  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR + …  shell  3rd  «Unicode!»     BRANCH unset DISPLAYED_ENV_VAR                                                                                                                                                                                                                                                                                                              -  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above -  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.dividers.left.hard \$ABC -  BRANCH $ABC…  shell  3rd  «Unicode!» $ABCfalse + …  shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above + …  shell  3rd  «Unicode!»     BRANCH set_theme_option default.dividers.left.hard \$ABC + …  shell  3rd  «Unicode!» $ABC   BRANCH false diff --git a/tests/test_shells/outputs/bash.nodaemon.ok b/tests/test_shells/outputs/bash.nodaemon.ok index c65dcc14..458c1a18 100644 --- a/tests/test_shells/outputs/bash.nodaemon.ok +++ b/tests/test_shells/outputs/bash.nodaemon.ok @@ -7,7 +7,9 @@   HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  false   HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  1  kill `cat pid` ; sleep 1s [1]+ Terminated bgscript.sh +  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default.segment_data.hostname.display false   HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default_leftonly.segment_data.hostname.display false + USER   BRANCH  …  tmp  shell  3rd  set_theme_option default.segment_data.user.display false  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default_leftonly.segment_data.user.display false   BRANCH  …  tmp  shell  3rd  echo '    abc @@ -27,16 +29,17 @@ def   BRANCH  …  shell  3rd  $(echo)  cd ../'`echo`'   BRANCH  …  shell  3rd  `echo`  cd ../'«Unicode!»'   BRANCH  …  shell  3rd  «Unicode!»  (exit 42)|(exit 43) -  BRANCH  …  shell  3rd  «Unicode!»  42  43  set_theme_option default_leftonly.segments.above "$ABOVE_LEFT" -  BRANCH  …  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo +  BRANCH  …  shell  3rd  «Unicode!»  42  43  set_theme default + …  shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above "$ABOVE_LEFT" + …  shell  3rd  «Unicode!»     BRANCH export DISPLAYED_ENV_VAR=foo  foo   -  BRANCH  …  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR -  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above "$ABOVE_FULL" + …  shell  3rd  «Unicode!»     BRANCH unset DISPLAYED_ENV_VAR + …  shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above "$ABOVE_FULL"                                                                                                                                                                                                                                                                                                              -  BRANCH  …  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo + …  shell  3rd  «Unicode!»     BRANCH export DISPLAYED_ENV_VAR=foo                                                                                                                                                                                                                                                                                                        foo  -  BRANCH  …  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR + …  shell  3rd  «Unicode!»     BRANCH unset DISPLAYED_ENV_VAR                                                                                                                                                                                                                                                                                                              -  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above -  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.dividers.left.hard \$ABC -  BRANCH $ABC…  shell  3rd  «Unicode!» $ABCfalse + …  shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above + …  shell  3rd  «Unicode!»     BRANCH set_theme_option default.dividers.left.hard \$ABC + …  shell  3rd  «Unicode!» $ABC   BRANCH false From 1869cc8bedbd6085959e8566cad6729f3e7e2620 Mon Sep 17 00:00:00 2001 From: sol <1731279+s-ol@users.noreply.github.com> Date: Fri, 8 Jan 2021 11:25:53 +0100 Subject: [PATCH 03/13] Use updated i3ipc syntax in i3 segments/listers (#2062) Close #2046 Co-authored-by: s-ol --- docs/source/develop/segments.rst | 8 +-- docs/source/usage/wm-widgets.rst | 3 +- powerline/bindings/wm/__init__.py | 17 +---- powerline/listers/i3wm.py | 13 ++-- powerline/segments/i3wm.py | 20 +++--- tests/test_python/test_listers.py | 112 ++++++----------------------- tests/test_python/test_segments.py | 10 +-- 7 files changed, 48 insertions(+), 135 deletions(-) diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index 543ddd57..59549f62 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -528,10 +528,10 @@ i3wm in lemonbar bindings. ``workspace`` - dictionary containing the workspace name under the key ``"name"`` and - boolean values for the ``"visible"``, ``"urgent"`` and ``"focused"`` - keys, indicating the state of the workspace. Currently only provided by - the :py:func:`powerline.listers.i3wm.workspace_lister` lister. + the `i3-ipc` workspace object corresponding to this workspace. + Contains string attributes ``name`` and ``output``, as well as boolean + attributes for ``visible``, ``urgent`` and ``focused``. Currently only + provided by the :py:func:`powerline.listers.i3wm.workspace_lister` lister. Segment class ============= diff --git a/docs/source/usage/wm-widgets.rst b/docs/source/usage/wm-widgets.rst index 204c008c..bc63da63 100644 --- a/docs/source/usage/wm-widgets.rst +++ b/docs/source/usage/wm-widgets.rst @@ -77,8 +77,7 @@ to run with i3, simply ``exec`` this in the i3 config file and set the ``--i3`` 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 `_). +Running the binding in i3-mode will require `i3ipc `_. See the `lemonbar documentation `_ for more information and options. diff --git a/powerline/bindings/wm/__init__.py b/powerline/bindings/wm/__init__.py index 646b701a..d2c6f30a 100644 --- a/powerline/bindings/wm/__init__.py +++ b/powerline/bindings/wm/__init__.py @@ -24,15 +24,6 @@ def i3_subscribe(conn, event, callback): :param func callback: Function to run on event. ''' - try: - import i3ipc - except ImportError: - import i3 - conn.Subscription(callback, event) - return - else: - pass - conn.on(event, callback) from threading import Thread @@ -57,12 +48,8 @@ def get_i3_connection(): ''' global conn if not conn: - try: - import i3ipc - except ImportError: - import i3 as conn - else: - conn = i3ipc.Connection() + import i3ipc + conn = i3ipc.Connection() return conn diff --git a/powerline/listers/i3wm.py b/powerline/listers/i3wm.py index 0bbcfdc3..b1984e94 100644 --- a/powerline/listers/i3wm.py +++ b/powerline/listers/i3wm.py @@ -50,19 +50,14 @@ def workspace_lister(pl, segment_info, only_show=None, output=None): ( updated( segment_info, - output=w['output'], - workspace={ - 'name': w['name'], - 'visible': w['visible'], - 'urgent': w['urgent'], - 'focused': w['focused'], - }, + output=w.output, + workspace=w, ), { 'draw_inner_divider': None } ) for w in get_i3_connection().get_workspaces() - if (((not only_show or any(w[typ] for typ in only_show)) - and (not output or w['output'] == output))) + if (((not only_show or any(getattr(w, typ) for typ in only_show)) + and (not output or w.output == output))) ) diff --git a/powerline/segments/i3wm.py b/powerline/segments/i3wm.py index 3f508ee2..a6c0ee85 100644 --- a/powerline/segments/i3wm.py +++ b/powerline/segments/i3wm.py @@ -12,11 +12,11 @@ WORKSPACE_REGEX = re.compile(r'^[0-9]+: ?') def workspace_groups(w): group = [] - if w['focused']: + if w.focused: group.append('w_focused') - if w['urgent']: + if w.urgent: group.append('w_urgent') - if w['visible']: + if w.visible: group.append('w_visible') group.append('workspace') return group @@ -52,12 +52,12 @@ def workspaces(pl, segment_info, only_show=None, output=None, strip=0): return [ { - 'contents': w['name'][strip:], + 'contents': w.name[strip:], 'highlight_groups': workspace_groups(w) } for w in get_i3_connection().get_workspaces() - if ((not only_show or any(w[typ] for typ in only_show)) - and (not output or w['output'] == output)) + if ((not only_show or any(getattr(w, typ) for typ in only_show)) + and (not output or w.output == output)) ] @@ -80,7 +80,7 @@ def workspace(pl, segment_info, workspace=None, strip=False): try: w = next(( w for w in get_i3_connection().get_workspaces() - if w['name'] == workspace + if w.name == workspace )) except StopIteration: return None @@ -90,13 +90,13 @@ def workspace(pl, segment_info, workspace=None, strip=False): try: w = next(( w for w in get_i3_connection().get_workspaces() - if w['focused'] + if w.focused )) except StopIteration: return None return [{ - 'contents': format_name(w['name'], strip=strip), + 'contents': format_name(w.name, strip=strip), 'highlight_groups': workspace_groups(w) }] @@ -150,6 +150,6 @@ def scratchpad(pl, icons=SCRATCHPAD_ICONS): 'contents': icons.get(w.scratchpad_state, icons['changed']), 'highlight_groups': scratchpad_groups(w) } - for w in get_i3_connection().get_tree().descendents() + for w in get_i3_connection().get_tree().descendants() if w.scratchpad_state != 'none' ] diff --git a/tests/test_python/test_listers.py b/tests/test_python/test_listers.py index a33f0333..f2368dfa 100644 --- a/tests/test_python/test_listers.py +++ b/tests/test_python/test_listers.py @@ -8,14 +8,16 @@ from tests.modules import TestCase class TestI3WM(TestCase): + workspaces = [ + Args(name='1: w1', output='LVDS1', focused=False, urgent=False, visible=False), + Args(name='2: w2', output='LVDS1', focused=False, urgent=False, visible=True), + Args(name='3: w3', output='HDMI1', focused=False, urgent=True, visible=True), + Args(name='4: w4', output='DVI01', focused=True, urgent=True, visible=True), + ] + @staticmethod def get_workspaces(): - return iter([ - {'name': '1: w1', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': False}, - {'name': '2: w2', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': True}, - {'name': '3: w3', 'output': 'HDMI1', 'focused': False, 'urgent': True, 'visible': True}, - {'name': '4: w4', 'output': 'DVI01', 'focused': True, 'urgent': True, 'visible': True}, - ]) + return iter(TestI3WM.workspaces) @staticmethod def get_outputs(pl): @@ -46,42 +48,22 @@ class TestI3WM(TestCase): ({ 'a': 1, 'output': 'LVDS1', - 'workspace': { - 'name': '1: w1', - 'focused': False, - 'urgent': False, - 'visible': False - } + 'workspace': self.workspaces[0], }, {'draw_inner_divider': None}), ({ 'a': 1, 'output': 'LVDS1', - 'workspace': { - 'name': '2: w2', - 'focused': False, - 'urgent': False, - 'visible': True - } + 'workspace': self.workspaces[1], }, {'draw_inner_divider': None}), ({ 'a': 1, 'output': 'HDMI1', - 'workspace': { - 'name': '3: w3', - 'focused': False, - 'urgent': True, - 'visible': True - } + 'workspace': self.workspaces[2], }, {'draw_inner_divider': None}), ({ 'a': 1, 'output': 'DVI01', - 'workspace': { - 'name': '4: w4', - 'focused': True, - 'urgent': True, - 'visible': True - } + 'workspace': self.workspaces[3], }, {'draw_inner_divider': None}), ] ) @@ -92,22 +74,12 @@ class TestI3WM(TestCase): ({ 'a': 1, 'output': 'LVDS1', - 'workspace': { - 'name': '1: w1', - 'focused': False, - 'urgent': False, - 'visible': False - } + 'workspace': self.workspaces[0], }, {'draw_inner_divider': None}), ({ 'a': 1, 'output': 'LVDS1', - 'workspace': { - 'name': '2: w2', - 'focused': False, - 'urgent': False, - 'visible': True - } + 'workspace': self.workspaces[1], }, {'draw_inner_divider': None}), ] ) @@ -121,22 +93,12 @@ class TestI3WM(TestCase): ({ 'a': 1, 'output': 'LVDS1', - 'workspace': { - 'name': '1: w1', - 'focused': False, - 'urgent': False, - 'visible': False - } + 'workspace': self.workspaces[0], }, {'draw_inner_divider': None}), ({ 'a': 1, 'output': 'LVDS1', - 'workspace': { - 'name': '2: w2', - 'focused': False, - 'urgent': False, - 'visible': True - } + 'workspace': self.workspaces[1], }, {'draw_inner_divider': None}), ] ) @@ -151,42 +113,22 @@ class TestI3WM(TestCase): ({ 'a': 1, 'output': 'LVDS1', - 'workspace': { - 'name': '1: w1', - 'focused': False, - 'urgent': False, - 'visible': False - } + 'workspace': self.workspaces[0], }, {'draw_inner_divider': None}), ({ 'a': 1, 'output': 'LVDS1', - 'workspace': { - 'name': '2: w2', - 'focused': False, - 'urgent': False, - 'visible': True - } + 'workspace': self.workspaces[1], }, {'draw_inner_divider': None}), ({ 'a': 1, 'output': 'HDMI1', - 'workspace': { - 'name': '3: w3', - 'focused': False, - 'urgent': True, - 'visible': True - } + 'workspace': self.workspaces[2], }, {'draw_inner_divider': None}), ({ 'a': 1, 'output': 'DVI01', - 'workspace': { - 'name': '4: w4', - 'focused': True, - 'urgent': True, - 'visible': True - } + 'workspace': self.workspaces[3], }, {'draw_inner_divider': None}), ] ) @@ -201,22 +143,12 @@ class TestI3WM(TestCase): ({ 'a': 1, 'output': 'HDMI1', - 'workspace': { - 'name': '3: w3', - 'focused': False, - 'urgent': True, - 'visible': True - } + 'workspace': self.workspaces[2], }, {'draw_inner_divider': None}), ({ 'a': 1, 'output': 'DVI01', - 'workspace': { - 'name': '4: w4', - 'focused': True, - 'urgent': True, - 'visible': True - } + 'workspace': self.workspaces[3], }, {'draw_inner_divider': None}), ] ) diff --git a/tests/test_python/test_segments.py b/tests/test_python/test_segments.py index 6c441143..1ee8cd0a 100644 --- a/tests/test_python/test_segments.py +++ b/tests/test_python/test_segments.py @@ -1004,10 +1004,10 @@ class TestI3WM(TestCase): @staticmethod def get_workspaces(): return iter([ - {'name': '1: w1', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': False}, - {'name': '2: w2', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': True}, - {'name': '3: w3', 'output': 'HDMI1', 'focused': False, 'urgent': True, 'visible': True}, - {'name': '4: w4', 'output': 'DVI01', 'focused': True, 'urgent': True, 'visible': True}, + Args(name='1: w1', output='LVDS1', focused = False, urgent = False, visible = False), + Args(name='2: w2', output='LVDS1', focused = False, urgent = False, visible = True), + Args(name='3: w3', output='HDMI1', focused = False, urgent = True, visible = True), + Args(name='4: w4', output='DVI01', focused = True, urgent = True, visible = True), ]) def test_workspaces(self): @@ -1093,7 +1093,7 @@ class TestI3WM(TestCase): def get_tree(self): return self - def descendents(self): + def descendants(self): nodes_unfocused = [Args(focused = False)] nodes_focused = [Args(focused = True)] From a1309cc11620d4b2341f79dbaff7147111f9b95e Mon Sep 17 00:00:00 2001 From: Philip Wellnitz Date: Mon, 1 Mar 2021 16:45:49 +0900 Subject: [PATCH 04/13] bump version to 2.8.2 --- powerline/version.py | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/powerline/version.py b/powerline/version.py index 01e89fa4..8b5195f1 100644 --- a/powerline/version.py +++ b/powerline/version.py @@ -4,7 +4,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct import subprocess from traceback import print_exc -__version__ = "2.8.1" +__version__ = "2.8.2" def get_version(): try: diff --git a/setup.py b/setup.py index c706e52d..662051fb 100644 --- a/setup.py +++ b/setup.py @@ -70,10 +70,10 @@ setup( 'Natural Language :: English', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], From 7310d53adef5ba5829712b1ac06839be1b4852b9 Mon Sep 17 00:00:00 2001 From: Philip Wellnitz Date: Mon, 15 Mar 2021 03:45:17 +0900 Subject: [PATCH 05/13] i3 segments rework (#2159) rework i3wm segments --- .../config_files/colorschemes/default.json | 3 +- powerline/config_files/themes/wm/default.json | 12 - powerline/listers/i3wm.py | 12 +- powerline/segments/i3wm.py | 216 +++++++++++++++--- tests/modules/__init__.py | 1 + tests/test_python/test_segments.py | 145 +++++++++--- 6 files changed, 305 insertions(+), 84 deletions(-) diff --git a/powerline/config_files/colorschemes/default.json b/powerline/config_files/colorschemes/default.json index 7e271ef3..767e72e4 100644 --- a/powerline/config_files/colorschemes/default.json +++ b/powerline/config_files/colorschemes/default.json @@ -51,6 +51,7 @@ "cwd:current_folder": "information:regular", "cwd:divider": { "fg": "gray7", "bg": "gray4", "attrs": [] }, "virtualenv": { "fg": "white", "bg": "darkcyan", "attrs": [] }, - "attached_clients": { "fg": "gray8", "bg": "gray0", "attrs": [] } + "attached_clients": { "fg": "gray8", "bg": "gray0", "attrs": [] }, + "workspace": "information:regular" } } diff --git a/powerline/config_files/themes/wm/default.json b/powerline/config_files/themes/wm/default.json index 579080d1..3d468b49 100644 --- a/powerline/config_files/themes/wm/default.json +++ b/powerline/config_files/themes/wm/default.json @@ -1,10 +1,6 @@ { "segments": { "right": [ - { - "function": "powerline.segments.common.wthr.weather", - "priority": 50 - }, { "function": "powerline.segments.common.time.date" }, @@ -15,14 +11,6 @@ "format": "%H:%M", "istime": true } - }, - { - "function": "powerline.segments.common.mail.email_imap_alert", - "priority": 10, - "args": { - "username": "", - "password": "" - } } ] } diff --git a/powerline/listers/i3wm.py b/powerline/listers/i3wm.py index b1984e94..9224d160 100644 --- a/powerline/listers/i3wm.py +++ b/powerline/listers/i3wm.py @@ -26,20 +26,20 @@ def output_lister(pl, segment_info): def workspace_lister(pl, segment_info, only_show=None, output=None): '''List all workspaces in segment_info format - Sets the segment info values of ``workspace`` and ``output`` to the name of + Sets the segment info values of ``workspace`` and ``output`` to the name of the i3 workspace and the ``xrandr`` output respectively and the keys ``"visible"``, ``"urgent"`` and ``"focused"`` to a boolean indicating these states. :param list only_show: - Specifies which workspaces to list. Valid entries are ``"visible"``, - ``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces + Specifies which workspaces to list. Valid entries are ``"visible"``, + ``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces are listed. :param str output: - May be set to the name of an X output. If specified, only workspaces - on that output are listed. Overrides automatic output detection by - the lemonbar renderer and bindings. Set to ``false`` to force + May be set to the name of an X output. If specified, only workspaces + on that output are listed. Overrides automatic output detection by + the lemonbar renderer and bindings. Set to ``false`` to force all workspaces to be shown. ''' diff --git a/powerline/segments/i3wm.py b/powerline/segments/i3wm.py index a6c0ee85..57ab3770 100644 --- a/powerline/segments/i3wm.py +++ b/powerline/segments/i3wm.py @@ -6,17 +6,18 @@ import re from powerline.theme import requires_segment_info from powerline.bindings.wm import get_i3_connection - WORKSPACE_REGEX = re.compile(r'^[0-9]+: ?') - def workspace_groups(w): group = [] if w.focused: + group.append('workspace:focused') group.append('w_focused') if w.urgent: + group.append('workspace:urgent') group.append('w_urgent') if w.visible: + group.append('workspace:visible') group.append('w_visible') group.append('workspace') return group @@ -28,58 +29,191 @@ def format_name(name, strip=False): return name +def is_empty_workspace(workspace, containers): + if workspace.focused or workspace.visible: + return False + wins = [win for win in containers[workspace.name].leaves()] + return False if len(wins) > 0 else True + +WS_ICONS = {"multiple": "M"} + +def get_icon(workspace, separator, icons, show_multiple_icons, ws_containers): + icons_tmp = WS_ICONS + icons_tmp.update(icons) + icons = icons_tmp + + wins = [win for win in ws_containers[workspace.name].leaves() \ + if win.parent.scratchpad_state == 'none'] + if len(wins) == 0: + return '' + + result = '' + cnt = 0 + for key in icons: + if not icons[key] or len(icons[key]) < 1: + continue + if any(key in win.window_class for win in wins if win.window_class): + result += (separator if cnt > 0 else '') + icons[key] + cnt += 1 + if not show_multiple_icons and cnt > 1: + if 'multiple' in icons: + return icons['multiple'] + else: + return '' + return result + @requires_segment_info -def workspaces(pl, segment_info, only_show=None, output=None, strip=0): +def workspaces(pl, segment_info, only_show=None, output=None, strip=0, format='{name}', + icons=WS_ICONS, sort_workspaces=False, show_output=False, priority_workspaces=[], + hide_empty_workspaces=False): '''Return list of used workspaces :param list only_show: - Specifies which workspaces to show. Valid entries are ``"visible"``, - ``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces + Specifies which workspaces to show. Valid entries are ``"visible"``, + ``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces are shown. - :param str output: - May be set to the name of an X output. If specified, only workspaces - on that output are shown. Overrides automatic output detection by + 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. - + Use "__all__" to show workspaces on all outputs. :param int strip: - Specifies how many characters from the front of each workspace name + Specifies how many characters from the front of each workspace name should be stripped (e.g. to remove workspace numbers). Defaults to zero. + :param str format: + Specifies the format used to display workspaces; defaults to ``{name}``. + Valid fields are: ``name`` (workspace name), ``number`` (workspace number + if present), `stipped_name`` (workspace name stripped of leading number), + ``icon`` (if available, icon for application running in the workspace, + uses the ``multiple`` icon instead of multiple different icons), ``multi_icon`` + (similar to ``icon``, but does not use ``multiple``, instead joins all icons + with a single space) + :param dict icons: + A dictionary mapping a substring of window classes to strings to be used as an + icon for that window class. + Further, there is a ``multiple`` icon for workspaces containing more than one + window. + :param bool sort_workspaces: + Sort the workspaces displayed by their name according to the natural ordering. + :param bool show_output: + Shows the name of the output if more than one output is connected. + :param list priority_workspaces: + A list of workspace names to be sorted before any other workspaces in the given + order. + :param bool hide_empty_workspaces: + Hides all workspaces without any open window. + Also hides non-focussed workspaces containing only an open scratchpad. - Highlight groups used: ``workspace`` or ``w_visible``, ``workspace`` or ``w_focused``, ``workspace`` or ``w_urgent``. + + Highlight groups used: ``workspace`` or ``w_visible``, ``workspace:visible``, ``workspace`` or ``w_focused``, ``workspace:focused``, ``workspace`` or ``w_urgent``, ``workspace:urgent``, ``workspace`` or ``output``. ''' - output = output or segment_info.get('output') + conn = get_i3_connection() - return [ - { - 'contents': w.name[strip:], + if not output == "__all__": + output = output or segment_info.get('output') + else: + output = None + + if output: + output = [output] + else: + output = [o.name for o in conn.get_outputs() if o.active] + + + def sort_ws(ws): + if sort_workspaces: + def natural_key(ws): + str = ws.name + return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', str)] + ws = sorted(ws, key=natural_key) + result = [] + for n in priority_workspaces: + result += [w for w in ws if w.name == n] + return result + [w for w in ws if not w.name in priority_workspaces] + + ws_containers = {w_con.name : w_con for w_con in conn.get_tree().workspaces()} + + if len(output) <= 1: + res = [] + if show_output: + res += [{ + 'contents': output[0], + 'highlight_groups': ['output'] + }] + res += [{ + 'contents': format.format(name = w.name[min(len(w.name), strip):], + stripped_name = format_name(w.name, strip=True), + number = w.num, + icon = get_icon(w, '', icons, False, ws_containers), + multi_icon = get_icon(w, ' ', icons, True, ws_containers)), 'highlight_groups': workspace_groups(w) - } - for w in get_i3_connection().get_workspaces() - if ((not only_show or any(getattr(w, typ) for typ in only_show)) - and (not output or w.output == output)) - ] - + } for w in sort_ws(conn.get_workspaces()) \ + if (not only_show or any(getattr(w, tp) for tp in only_show)) \ + if w.output == output[0] \ + if not (hide_empty_workspaces and is_empty_workspace(w, ws_containers))] + return res + else: + res = [] + for n in output: + if show_output: + res += [{ + 'contents': n, + 'highlight_groups': ['output'] + }] + res += [{ + 'contents': format.format(name = w.name[min(len(w.name), strip):], + stripped_name = format_name(w.name, strip=True), + number = w.num, + icon = get_icon(w, '', icons, False, ws_containers), + multi_icon = get_icon(w, ' ', icons, True, ws_containers)), + 'highlight_groups': workspace_groups(w) + } for w in sort_ws(conn.get_workspaces()) \ + if (not only_show or any(getattr(w, tp) for tp in only_show)) \ + if w.output == n \ + if not (hide_empty_workspaces and is_empty_workspace(w, ws_containers))] + return res @requires_segment_info -def workspace(pl, segment_info, workspace=None, strip=False): +def workspace(pl, segment_info, workspace=None, strip=False, format=None, icons=WS_ICONS): '''Return the specified workspace name :param str workspace: - Specifies which workspace to show. If unspecified, may be set by the - ``list_workspaces`` lister if used, otherwise falls back to + Specifies which workspace to show. If unspecified, may be set by the + ``list_workspaces`` lister if used, otherwise falls back to currently focused workspace. :param bool strip: - Specifies whether workspace numbers (in the ``1: name`` format) should + Specifies whether workspace numbers (in the ``1: name`` format) should be stripped from workspace names before being displayed. Defaults to false. + Deprecated: Use {name} or {stripped_name} of format instead. - Highlight groups used: ``workspace`` or ``w_visible``, ``workspace`` or ``w_focused``, ``workspace`` or ``w_urgent``. + :param str format: + Specifies the format used to display workspaces; defaults to ``{name}``. + Valid fields are: ``name`` (workspace name), ``number`` (workspace number + if present), `stipped_name`` (workspace name stripped of leading number), + ``icon`` (if available, icon for application running in the workspace, + uses the ``multiple`` icon instead of multiple different icons), ``multi_icon`` + (similar to ``icon``, but does not use ``multiple``, instead joins all icons + with a single space) + + :param dict icons: + A dictionary mapping a substring of window classes to strings to be used as an + icon for that window class. + Further, there is a ``multiple`` icon for workspaces containing more than one + window. + + Highlight groups used: ``workspace`` or ``w_visible``, ``workspace:visible``, ``workspace`` or ``w_focused``, ``workspace:focused``, ``workspace`` or ``w_urgent``, ``workspace:urgent``, ``workspace``. ''' + if format == None: + format = '{stripped_name}' if strip else '{name}' + + conn = get_i3_connection() + ws_containers = {w_con.name : w_con for w_con in conn.get_tree().workspaces()} + if workspace: try: w = next(( - w for w in get_i3_connection().get_workspaces() + w for w in conn.get_workspaces() if w.name == workspace )) except StopIteration: @@ -89,16 +223,20 @@ def workspace(pl, segment_info, workspace=None, strip=False): else: try: w = next(( - w for w in get_i3_connection().get_workspaces() + w for w in conn.get_workspaces() if w.focused )) except StopIteration: return None return [{ - 'contents': format_name(w.name, strip=strip), + 'contents': format.format(name = w.name, + stripped_name = format_name(w.name, strip=True), + number = w.num, + icon = get_icon(w, '', icons, False, ws_containers), + multi_icon = get_icon(w, ' ', icons, True, ws_containers)), 'highlight_groups': workspace_groups(w) - }] + }] @requires_segment_info @@ -139,7 +277,7 @@ def scratchpad(pl, icons=SCRATCHPAD_ICONS): '''Returns the windows currently on the scratchpad :param dict icons: - Specifies the strings to show for the different scratchpad window states. Must + Specifies the strings to show for the different scratchpad window states. Must contain the keys ``fresh`` and ``changed``. Highlight groups used: ``scratchpad`` or ``scratchpad:visible``, ``scratchpad`` or ``scratchpad:focused``, ``scratchpad`` or ``scratchpad:urgent``. @@ -153,3 +291,19 @@ def scratchpad(pl, icons=SCRATCHPAD_ICONS): for w in get_i3_connection().get_tree().descendants() if w.scratchpad_state != 'none' ] + +def active_window(pl, cutoff=100): + '''Returns the title of the currently active window. + + :param int cutoff: + Maximum title length. If the title is longer, the window_class is used instead. + + Highlight groups used: ``active_window_title``. + ''' + + focused = get_i3_connection().get_tree().find_focused() + cont = focused.name + if len(cont) > cutoff: + cont = focused.window_class + + return [{'contents': cont, 'highlight_groups': ['active_window_title']}] if focused.name != focused.workspace().name else [] diff --git a/tests/modules/__init__.py b/tests/modules/__init__.py index 12aae20d..5d671b6c 100644 --- a/tests/modules/__init__.py +++ b/tests/modules/__init__.py @@ -33,6 +33,7 @@ class PowerlineDummyTest(object): class PowerlineTestSuite(object): def __init__(self, name): self.name = name + self.suite = '' def __enter__(self): self.saved_current_suite = os.environ['POWERLINE_CURRENT_SUITE'] diff --git a/tests/test_python/test_segments.py b/tests/test_python/test_segments.py index 1ee8cd0a..c7a9870a 100644 --- a/tests/test_python/test_segments.py +++ b/tests/test_python/test_segments.py @@ -1001,81 +1001,158 @@ class TestWthr(TestCommon): class TestI3WM(TestCase): - @staticmethod - def get_workspaces(): - return iter([ - Args(name='1: w1', output='LVDS1', focused = False, urgent = False, visible = False), - Args(name='2: w2', output='LVDS1', focused = False, urgent = False, visible = True), - Args(name='3: w3', output='HDMI1', focused = False, urgent = True, visible = True), - Args(name='4: w4', output='DVI01', focused = True, urgent = True, visible = True), - ]) - def test_workspaces(self): + class Conn(object): + def get_tree(self): + return self + + def descendents(self): + nodes_unfocused = [Args(focused = False)] + nodes_focused = [Args(focused = True)] + + workspace_scratch = lambda: Args(name='__i3_scratch') + workspace_noscratch = lambda: Args(name='2: w2') + return [ + Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_unfocused), + Args(scratchpad_state='changed', urgent=True, workspace=workspace_noscratch, nodes=nodes_focused), + Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_unfocused), + Args(scratchpad_state=None, urgent=False, workspace=workspace_noscratch, nodes=nodes_unfocused), + Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_focused), + Args(scratchpad_state=None, urgent=True, workspace=workspace_noscratch, nodes=nodes_unfocused), + ] + + def workspaces(self): + return iter([ + Args(name='1: w1', output='LVDS1', focused=False, urgent=False, visible=False, num=1, leaves=lambda: []), + Args(name='2: w2', output='LVDS1', focused=False, urgent=False, visible=True, num=2, leaves=lambda: []), + Args(name='3: w3', output='HDMI1', focused=False, urgent=True, visible=True, num=3, leaves=lambda: []), + Args(name='4: w4', output='DVI01', focused=True, urgent=True, visible=True, num=None, leaves=lambda: []) + ]) + + def get_workspaces(self): + return iter([ + Args(name='1: w1', output='LVDS1', focused=False, urgent=False, visible=False, num=1, leaves=lambda: []), + Args(name='2: w2', output='LVDS1', focused=False, urgent=False, visible=True, num=2, leaves=lambda: []), + Args(name='3: w3', output='HDMI1', focused=False, urgent=True, visible=True, num=3, leaves=lambda: []), + Args(name='4: w4', output='DVI01', focused=True, urgent=True, visible=True, num=None, leaves=lambda: []) + ]) + + def get_outputs(self): + return iter([ + Args(name='LVDS1', active=True), + Args(name='HDMI1', active=True), + Args(name='DVI01', active=True), + Args(name='HDMI2', active=False), + ]) + pl = Pl() - with replace_attr(i3wm, 'get_i3_connection', lambda: Args(get_workspaces=self.get_workspaces)): + with replace_attr(i3wm, 'get_i3_connection', lambda: Conn()): 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']}, + {'contents': '2: w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']}, + {'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, + {'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, ]) 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']}, + {'contents': '2: w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']}, + {'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, + {'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, ]) 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']}, + {'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, + {'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, ]) 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']}, + {'contents': '2: w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']}, + {'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, + {'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', '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']}, - {'contents': 'w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']}, - {'contents': 'w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']}, + {'contents': 'w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']}, + {'contents': 'w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, + {'contents': 'w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, ]) 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']}, + {'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, ]) 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']}, + {'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, ]) self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], strip=3, output='LVDS1'), [ - {'contents': 'w2', 'highlight_groups': ['w_visible', 'workspace']}, + {'contents': 'w2', 'highlight_groups': ['workspace:visible', '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']}, + {'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', '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']}, + {'contents': 'w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']}, ]) def test_workspace(self): + class Conn(object): + def get_tree(self): + return self + + def descendents(self): + nodes_unfocused = [Args(focused = False)] + nodes_focused = [Args(focused = True)] + + workspace_scratch = lambda: Args(name='__i3_scratch') + workspace_noscratch = lambda: Args(name='2: w2') + return [ + Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_unfocused), + Args(scratchpad_state='changed', urgent=True, workspace=workspace_noscratch, nodes=nodes_focused), + Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_unfocused), + Args(scratchpad_state=None, urgent=False, workspace=workspace_noscratch, nodes=nodes_unfocused), + Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_focused), + Args(scratchpad_state=None, urgent=True, workspace=workspace_noscratch, nodes=nodes_unfocused), + ] + + def workspaces(self): + return iter([ + Args(name='1: w1', output='LVDS1', focused=False, urgent=False, visible=False, num=1, leaves=lambda: []), + Args(name='2: w2', output='LVDS1', focused=False, urgent=False, visible=True, num=2, leaves=lambda: []), + Args(name='3: w3', output='HDMI1', focused=False, urgent=True, visible=True, num=3, leaves=lambda: []), + Args(name='4: w4', output='DVI01', focused=True, urgent=True, visible=True, num=None, leaves=lambda: []) + ]) + + def get_workspaces(self): + return iter([ + Args(name='1: w1', output='LVDS1', focused=False, urgent=False, visible=False, num=1, leaves=lambda: []), + Args(name='2: w2', output='LVDS1', focused=False, urgent=False, visible=True, num=2, leaves=lambda: []), + Args(name='3: w3', output='HDMI1', focused=False, urgent=True, visible=True, num=3, leaves=lambda: []), + Args(name='4: w4', output='DVI01', focused=True, urgent=True, visible=True, num=None, leaves=lambda: []) + ]) + + def get_outputs(self): + return iter([ + Args(name='LVDS1', active=True), + Args(name='HDMI1', active=True), + Args(name='DVI01', active=True), + Args(name='HDMI2', active=False), + ]) + pl = Pl() - with replace_attr(i3wm, 'get_i3_connection', lambda: Args(get_workspaces=self.get_workspaces)): + with replace_attr(i3wm, 'get_i3_connection', lambda: Conn()): segment_info = {} self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info, workspace='1: w1'), [ {'contents': '1: w1', 'highlight_groups': ['workspace']}, ]) self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info, workspace='3: w3', strip=True), [ - {'contents': 'w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']}, + {'contents': 'w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, ]) self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info, workspace='9: w9'), None) self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info), [ - {'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']}, + {'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, ]) - segment_info['workspace'] = next(self.get_workspaces()) + segment_info['workspace'] = next(Conn().get_workspaces()) self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info, workspace='4: w4'), [ - {'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']}, + {'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']}, ]) self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info, strip=True), [ {'contents': 'w1', 'highlight_groups': ['workspace']}, From 8f0d3f8bd227ba0329aed9a3ca51dc0228de6747 Mon Sep 17 00:00:00 2001 From: MrFishFinger <4768596+MrFishFinger@users.noreply.github.com> Date: Mon, 26 Jul 2021 09:01:39 +0200 Subject: [PATCH 06/13] doco - add a comma, to make sentence more readable (#2173) --- docs/source/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/installation.rst b/docs/source/installation.rst index eb40ed02..6be736d3 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -54,7 +54,7 @@ Generic requirements Pip installation ================ -Due to a naming conflict with an unrelated project powerline is available on +Due to a naming conflict with an unrelated project, powerline is available on PyPI under the ``powerline-status`` name: .. code-block:: sh From 57aea1e400c08d76a7da84444ba530ef640426e1 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Fri, 20 Aug 2021 19:17:03 -0700 Subject: [PATCH 07/13] powerline.zsh: squelch which output. (#2176) If powerline-config isn't on $PATH, GNU which will output "no powerline-config in ($PATH)" to stderr, which we previously weren't silencing. --- powerline/bindings/zsh/powerline.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/bindings/zsh/powerline.zsh b/powerline/bindings/zsh/powerline.zsh index 5d7cb28a..ff46cb06 100644 --- a/powerline/bindings/zsh/powerline.zsh +++ b/powerline/bindings/zsh/powerline.zsh @@ -197,7 +197,7 @@ _powerline_add_widget() { } if test -z "${POWERLINE_CONFIG_COMMAND}" ; then - if which powerline-config >/dev/null ; then + if which powerline-config >/dev/null 2>/dev/null ; then typeset -g POWERLINE_CONFIG_COMMAND=powerline-config else typeset -g POWERLINE_CONFIG_COMMAND="${_POWERLINE_SOURCED:h:h:h:h}/scripts/powerline-config" From 82c1373ba0b424c57e8c12cb5f6f1a7ee3829c27 Mon Sep 17 00:00:00 2001 From: Darren <3759672+svdarren@users.noreply.github.com> Date: Fri, 20 Aug 2021 21:21:14 -0500 Subject: [PATCH 08/13] Added tips to installation docs (#2175) * Added virtual environment tips Documented the use of `POWERLINE_COMMAND` env variable for improved performance with virtual environments. * Clarify client versions Adding details from the GitHub issues to clarify the installation docs. * Added troubleshooting tip for Python environments --- docs/source/installation/osx.rst | 10 +++++++--- docs/source/troubleshooting.rst | 4 ++++ docs/source/usage/shell-prompts.rst | 21 +++++++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index e520348d..d625407b 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -16,9 +16,13 @@ Python package brew install python .. note:: - In case :file:`powerline.sh` as a client ``socat`` and ``coreutils`` need - to be installed. ``coreutils`` may be installed using ``brew install - coreutils``. + There are three variants of the powerline client. The fastest is + written in C and will be compiled if the compiler and libraries are + detected during installation. The second fastest option is + :file:`powerline.sh` which requires ``socat`` and ``coreutils``. + ``coreutils`` may be installed using ``brew install + coreutils``. If neither of these are viable, then Powerline will + utilize a fallback client written in Python. 2. Install Powerline using one of the following commands: diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 30f5d4cf..2b917474 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -193,8 +193,12 @@ I am suffering bad lags before displaying shell prompt To get rid of these lags there currently are two options: * Run ``powerline-daemon``. Powerline does not automatically start it for you. + See installation instructions for more details. * Compile and install ``libzpython`` module that lives in https://bitbucket.org/ZyX_I/zpython. This variant is zsh-specific. +* If you are a python package manager, be sure to set ``POWERLINE_COMMAND`` + to your Powerline command. See installation instructions for details. + Prompt is spoiled after completing files in ksh ----------------------------------------------- diff --git a/docs/source/usage/shell-prompts.rst b/docs/source/usage/shell-prompts.rst index 1ddb0a14..17774da6 100644 --- a/docs/source/usage/shell-prompts.rst +++ b/docs/source/usage/shell-prompts.rst @@ -5,8 +5,9 @@ Shell prompts ************* .. note:: - Powerline daemon is not run automatically by any of my bindings. It is - advised to add + Powerline can operate without a background daemon, but the shell performance + can be very slow. The Powerline daemon improves this performance + significantly, but must be started separately. It is advised to add .. code-block:: bash @@ -142,3 +143,19 @@ following in ``~/.profile``: .. warning:: Busybox has two shells: ``ash`` and ``hush``. Second is known to segfault in busybox 1.22.1 when using :file:`powerline.sh` script. + +Python Virtual Environments (conda, pyenv) +========================================== + +If your system uses virtual environments to manage different Python versions, +such as pyenv or anaconda, these tools will add a performance delay to every +shell prompt. This delay can be bypassed by explicitly specifying your command +path. + + .. code-block:: bash + + export POWERLINE_COMMAND={path_to_powerline} + +where ``{path_to_powerline}`` is the full filepath for powerline. If you +installed Powerline within an environment, you can find this path for pyenv +with ``pyenv which powerline`` or for conda with ``which powerline``. From 9ce39e6333865f3460a047983d763ace797bcff8 Mon Sep 17 00:00:00 2001 From: Albert Paul Date: Mon, 21 Mar 2022 10:59:31 +0100 Subject: [PATCH 09/13] Remove git protocol (#2199) * Fix outdated Github url git:// is deprecated from Github starting from Jan. 11th, 2022. * Update git protocols --- docs/source/installation.rst | 9 ++++----- docs/source/installation/linux.rst | 2 +- docs/source/installation/osx.rst | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 6be736d3..8dc42360 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -66,7 +66,7 @@ development version .. code-block:: sh - pip install --user git+git://github.com/powerline/powerline + pip install --user git+https://github.com/powerline/powerline may be used. If powerline was already checked out into some directory @@ -85,10 +85,9 @@ will have to be done (:file:`~/.local/bin` should be replaced with some path present in ``$PATH``). .. note:: - If ISP blocks git protocol for some reason github also provides ``ssh`` - (``git+ssh://git@github.com/powerline/powerline``) and ``https`` - (``git+https://github.com/powerline/powerline``) protocols. ``git`` protocol - should be the fastest, but least secure one though. + We can use either ``https``(``git+ssh://git@github.com/powerline/powerline``) + or ``https``(``git+https://github.com/powerline/powerline``) protocols. + ``git`` protocol is deprecated by Github. Fonts installation ================== diff --git a/docs/source/installation/linux.rst b/docs/source/installation/linux.rst index 95b6636e..e134707c 100644 --- a/docs/source/installation/linux.rst +++ b/docs/source/installation/linux.rst @@ -29,7 +29,7 @@ should be followed: .. code-block:: sh - pip install --user git+git://github.com/powerline/powerline + pip install --user git+https://github.com/powerline/powerline will get the latest development version. diff --git a/docs/source/installation/osx.rst b/docs/source/installation/osx.rst index d625407b..1d6fafc3 100644 --- a/docs/source/installation/osx.rst +++ b/docs/source/installation/osx.rst @@ -34,7 +34,7 @@ Python package .. code-block:: sh - pip install --user git+git://github.com/powerline/powerline + pip install --user git+https://github.com/powerline/powerline will get latest development version. From 90af71c122da8db311dad1d60ab91ca73b76268e Mon Sep 17 00:00:00 2001 From: PHP Wellnitz Date: Sat, 9 Apr 2022 04:27:02 +0900 Subject: [PATCH 10/13] Fix #2200 --- powerline/lint/markedjson/constructor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lint/markedjson/constructor.py b/powerline/lint/markedjson/constructor.py index 2a95d840..372d84b3 100644 --- a/powerline/lint/markedjson/constructor.py +++ b/powerline/lint/markedjson/constructor.py @@ -122,7 +122,7 @@ class BaseConstructor: mapping = {} for key_node, value_node in node.value: key = self.construct_object(key_node, deep=deep) - if not isinstance(key, collections.Hashable): + if not isinstance(key, collections.abc.Hashable): self.echoerr( 'While constructing a mapping', node.start_mark, 'found unhashable key', key_node.start_mark From 567e2c930b38b1c850d3e920ebc7c9eae9778a1d Mon Sep 17 00:00:00 2001 From: PHP Wellnitz Date: Thu, 19 May 2022 16:52:00 +0900 Subject: [PATCH 11/13] fix #2194 --- powerline/bindings/ipython/since_7.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/powerline/bindings/ipython/since_7.py b/powerline/bindings/ipython/since_7.py index a555d64a..4d35b68b 100644 --- a/powerline/bindings/ipython/since_7.py +++ b/powerline/bindings/ipython/since_7.py @@ -1,14 +1,13 @@ # vim:fileencoding=utf-8:noet -from __future__ import (unicode_literals, division, absolute_import, print_function) - from weakref import ref +from atexit import register as atexit from IPython.terminal.prompts import Prompts from pygments.token import Token # NOQA from powerline.ipython import IPythonPowerline from powerline.renderers.ipython.since_7 import PowerlinePromptStyle -from powerline.bindings.ipython.post_0_11 import PowerlineMagics, ShutdownHook +from powerline.bindings.ipython.post_0_11 import PowerlineMagics class ConfigurableIPythonPowerline(IPythonPowerline): @@ -20,7 +19,7 @@ class ConfigurableIPythonPowerline(IPythonPowerline): super(ConfigurableIPythonPowerline, self).init( renderer_module='.since_7') - def do_setup(self, ip, prompts, shutdown_hook): + def do_setup(self, ip, prompts): prompts.powerline = self msfn_missing = () @@ -50,18 +49,16 @@ class ConfigurableIPythonPowerline(IPythonPowerline): magics = PowerlineMagics(ip, self) ip.register_magics(magics) - if shutdown_hook: - shutdown_hook.powerline = ref(self) + atexit(self.shutdown) class PowerlinePrompts(Prompts): '''Class that returns powerline prompts ''' def __init__(self, shell): - shutdown_hook = ShutdownHook(shell) powerline = ConfigurableIPythonPowerline(shell) self.shell = shell - powerline.do_setup(shell, self, shutdown_hook) + powerline.do_setup(shell, self) self.last_output_count = None self.last_output = {} From 3555dc97c94ab6978e23b7a48df964f64f8352c8 Mon Sep 17 00:00:00 2001 From: PHP Wellnitz Date: Thu, 19 May 2022 16:57:18 +0900 Subject: [PATCH 12/13] change get_version to adhere to PEP 440 --- powerline/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/version.py b/powerline/version.py index 8b5195f1..0cceed84 100644 --- a/powerline/version.py +++ b/powerline/version.py @@ -8,7 +8,7 @@ __version__ = "2.8.2" def get_version(): try: - return __version__ + '.dev9999+git.' + str(subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()) + return __version__ + 'b' + subprocess.check_output(['git', 'rev-list', '--count', __version__ + '..HEAD']).strip().decode() except Exception: print_exc() return __version__ From 8af6302c81b495328f7455fc2e4c21a80551a093 Mon Sep 17 00:00:00 2001 From: PHP Wellnitz Date: Thu, 19 May 2022 17:06:54 +0900 Subject: [PATCH 13/13] bump version to 2.8.3 --- powerline/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/version.py b/powerline/version.py index 0cceed84..8e131abd 100644 --- a/powerline/version.py +++ b/powerline/version.py @@ -4,7 +4,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct import subprocess from traceback import print_exc -__version__ = "2.8.2" +__version__ = "2.8.3" def get_version(): try: