Release 2.8.2

This commit is contained in:
Philip Wellnitz 2021-03-01 16:48:13 +09:00
commit 1c33c4eb5a
38 changed files with 573 additions and 418 deletions

46
.github/workflows/main.yaml vendored Normal file
View File

@ -0,0 +1,46 @@
name: Build and Publish to PyPI
on:
push:
branches:
- master
- develop
- feature/actions
pull_request:
branches:
- develop
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Build
run: |
python setup.py sdist bdist_wheel
- name: Publish
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.PYPI_TOKEN }}
packages_dir: dist/

View File

@ -26,8 +26,7 @@ jobs:
- stage: Latest Python - stage: Latest Python
python: "3.6" python: "3.6"
- stage: Intermediate versions - stage: Intermediate versions
python: "3.3" python: "3.5"
- python: "3.4" - python: "3.4"
- python: "3.5"
# vim: et # vim: et

View File

@ -1,24 +1,24 @@
Powerline Powerline
========= =========
:Author: Kim Silkebækken (kim.silkebaekken+vim@gmail.com) .. image:: https://api.travis-ci.org/powerline/powerline.svg?branch=develop
:Source: https://github.com/powerline/powerline :target: `travis-build-status`_
:Version: beta :alt: Build status
.. _travis-build-status: https://travis-ci.org/powerline/powerline
**Powerline is a statusline plugin for vim, and provides statuslines and **Powerline is a statusline plugin for vim, and provides statuslines and
prompts for several other applications, including zsh, bash, fish, tmux, prompts for several other applications, including zsh, bash, fish, tmux,
IPython, Awesome, i3 and Qtile.** IPython, Awesome, i3 and Qtile.**
* `Support forum`_ (powerline-support@googlegroups.com) +---------+---------------------------------------------------+
* `Development discussion`_ (powerline-dev@googlegroups.com) | Author | Kim Silkebækken (kim.silkebaekken+vim@gmail.com) |
+---------+---------------------------------------------------+
| Source | https://github.com/powerline/powerline |
+---------+---------------------------------------------------+
| Version | beta |
+---------+---------------------------------------------------+
.. image:: https://api.travis-ci.org/powerline/powerline.svg?branch=develop **Powerline does not support python2 anymore and powerline will stop working with python2 in the near future.**
:target: `travis-build-status`_
:alt: Build status
.. _travis-build-status: https://travis-ci.org/powerline/powerline
.. _`Support forum`: https://groups.google.com/forum/#!forum/powerline-support
.. _`Development discussion`: https://groups.google.com/forum/#!forum/powerline-dev
Features Features
-------- --------
@ -29,8 +29,7 @@ Features
config files, and a structured, object-oriented codebase with no mandatory config files, and a structured, object-oriented codebase with no mandatory
third-party dependencies other than a Python interpreter. third-party dependencies other than a Python interpreter.
* **Stable and testable code base.** Using Python has allowed unit testing * **Stable and testable code base.** Using Python has allowed unit testing
of all the project code. The code is tested to work in Python 2.6+ and of all the project code. The code is tested to work in Python 3.6+.
Python 3.
* **Support for prompts and statuslines in many applications.** Originally * **Support for prompts and statuslines in many applications.** Originally
created exclusively for vim statuslines, the project has evolved to created exclusively for vim statuslines, the project has evolved to
provide statuslines in tmux and several WMs, and prompts for shells like provide statuslines in tmux and several WMs, and prompts for shells like
@ -60,6 +59,8 @@ Configuration
Basic powerline configuration is done via `JSON` files located at `.config/powerline/`. It is a good idea to start by copying the default configuration located at `powerline_root/powerline/config_files/` to `.config/powerline/`. Basic powerline configuration is done via `JSON` files located at `.config/powerline/`. It is a good idea to start by copying the default configuration located at `powerline_root/powerline/config_files/` to `.config/powerline/`.
If you installed the powerline from the AUR or via pip, `powerline_root` should be `/usr/lib/python3.6/site-packages/` or something similar, depending on your python version. If you installed the powerline from the AUR or via pip, `powerline_root` should be `/usr/lib/python3.6/site-packages/` or something similar, depending on your python version.
If you installed powerline via apt-get 'powerline_root' should be '/usr/share/powerline/'.
This should yield you the following directory structure: This should yield you the following directory structure:
:: ::

View File

@ -117,10 +117,14 @@ segments that you may want to customize right away:
so unless youre on a VPN you probably wont have to change the location so unless youre on a VPN you probably wont have to change the location
query. query.
It is using OpenWeatherMap as a provider, which can be configured with a
personal API key. These can be generated `here
<https://home.openweathermap.org/api_keys>`_
If you want to change the location query or the temperature unit youll If you want to change the location query or the temperature unit youll
have to update the segment arguments. Open a theme file, scroll down to have to update the segment arguments. Open a theme file, scroll down to
the weather segment and update it to include unit/location query the weather segment and update it to include unit, location query or
arguments: api key arguments:
.. code-block:: javascript .. code-block:: javascript
@ -129,7 +133,8 @@ segments that you may want to customize right away:
"priority": 50, "priority": 50,
"args": { "args": {
"unit": "F", "unit": "F",
"location_query": "oslo, norway" "location_query": "oslo, norway",
"weather_api_key": "your_api_key"
} }
}, },

View File

@ -528,10 +528,10 @@ i3wm
in lemonbar bindings. in lemonbar bindings.
``workspace`` ``workspace``
dictionary containing the workspace name under the key ``"name"`` and the `i3-ipc` workspace object corresponding to this workspace.
boolean values for the ``"visible"``, ``"urgent"`` and ``"focused"`` Contains string attributes ``name`` and ``output``, as well as boolean
keys, indicating the state of the workspace. Currently only provided by attributes for ``visible``, ``urgent`` and ``focused``. Currently only
the :py:func:`powerline.listers.i3wm.workspace_lister` lister. provided by the :py:func:`powerline.listers.i3wm.workspace_lister` lister.
Segment class Segment class
============= =============

View File

@ -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 exec python /path/to/powerline/bindings/lemonbar/powerline-lemonbar.py --i3
Running the binding in i3-mode will require `i3ipc <https://github.com/acrisci/i3ipc-python>`_ Running the binding in i3-mode will require `i3ipc <https://github.com/acrisci/i3ipc-python>`_.
(or the outdated `i3-py <https://github.com/ziberna/i3-py>`_).
See the `lemonbar documentation <https://github.com/LemonBoy/bar>`_ for more See the `lemonbar documentation <https://github.com/LemonBoy/bar>`_ for more
information and options. information and options.

View File

@ -5,4 +5,5 @@ set-option -qg window-status-last-style "$_POWERLINE_ACTIVE_WINDOW_STATUS_COLOR"
set-window-option -qg window-status-style "$_POWERLINE_WINDOW_STATUS_COLOR" set-window-option -qg window-status-style "$_POWERLINE_WINDOW_STATUS_COLOR"
set-window-option -qg window-status-activity-style "$_POWERLINE_ACTIVITY_STATUS_COLOR" set-window-option -qg window-status-activity-style "$_POWERLINE_ACTIVITY_STATUS_COLOR"
set-window-option -qg window-status-bell-style "$_POWERLINE_BELL_STATUS_COLOR" set-window-option -qg window-status-bell-style "$_POWERLINE_BELL_STATUS_COLOR"
set -g status-right '#(env "$POWERLINE_COMMAND" $POWERLINE_COMMAND_ARGS tmux right --width=#{client_width} -R width_adjust=#{status-left-length} -R pane_id=#{pane_id} -R pane_current_path=#{q:pane_current_path})'
# vim: ft=tmux # vim: ft=tmux

View File

@ -1,3 +1,3 @@
# Starting from tmux-2.1 escaping of dollar signs inside #() is harmful # Starting from tmux-2.1 escaping of dollar signs inside #() is harmful
set -qg status-left "#{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_FG]#[bg=$_POWERLINE_SESSION_PREFIX_BG]#[$_POWERLINE_SESSION_PREFIX_ATTR],#[fg=$_POWERLINE_SESSION_FG]#[bg=$_POWERLINE_SESSION_BG]#[$_POWERLINE_SESSION_ATTR]} #S #{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_BG],#[fg=$_POWERLINE_SESSION_BG]}#[bg=$_POWERLINE_BACKGROUND_BG]#[nobold]$_POWERLINE_LEFT_HARD_DIVIDER#(env $POWERLINE_COMMAND $POWERLINE_COMMAND_ARGS tmux left --width=#{client_width} -R width_adjust=#{status-right-length} -R pane_id=#{pane_id})" set -qg status-left "#{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_FG]#[bg=$_POWERLINE_SESSION_PREFIX_BG]#[$_POWERLINE_SESSION_PREFIX_ATTR],#[fg=$_POWERLINE_SESSION_FG]#[bg=$_POWERLINE_SESSION_BG]#[$_POWERLINE_SESSION_ATTR]} #S #{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_BG],#[fg=$_POWERLINE_SESSION_BG]}#[bg=$_POWERLINE_BACKGROUND_BG]#[nobold]$_POWERLINE_LEFT_HARD_DIVIDER#(env $POWERLINE_COMMAND $POWERLINE_COMMAND_ARGS tmux left --width=#{client_width} -R width_adjust=#{status-right-length} -R pane_id=#{pane_id} -R pane_current_path=#{q:pane_current_path})"
set -g window-status-format "#[$_POWERLINE_WINDOW_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER_SPACES#I#{?window_flags,#F, } #[$_POWERLINE_WINDOW_DIVIDER_COLOR]$_POWERLINE_LEFT_SOFT_DIVIDER#[default]#W $_POWERLINE_LEFT_HARD_DIVIDER_SPACES" set -g window-status-format "#[$_POWERLINE_WINDOW_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER_SPACES#I#{?window_flags,#F, } #[$_POWERLINE_WINDOW_DIVIDER_COLOR]$_POWERLINE_LEFT_SOFT_DIVIDER#[default]#W $_POWERLINE_LEFT_HARD_DIVIDER_SPACES"

View File

@ -26,14 +26,14 @@ if exists('g:powerline_pycmd')
let s:pyeval = g:powerline_pyeval let s:pyeval = g:powerline_pyeval
let s:has_python = 1 let s:has_python = 1
endif endif
elseif has('python')
let s:has_python = 1
let s:pycmd = 'py'
let s:pyeval = get(g:, 'powerline_pyeval', 'pyeval')
elseif has('python3') elseif has('python3')
let s:has_python = 1 let s:has_python = 1
let s:pycmd = 'py3' let s:pycmd = 'py3'
let s:pyeval = get(g:, 'powerline_pyeval', 'py3eval') let s:pyeval = get(g:, 'powerline_pyeval', 'py3eval')
elseif has('python')
let s:has_python = 1
let s:pycmd = 'py'
let s:pyeval = get(g:, 'powerline_pyeval', 'pyeval')
else else
let s:has_python = 0 let s:has_python = 0
endif endif

View File

@ -24,15 +24,6 @@ def i3_subscribe(conn, event, callback):
:param func callback: :param func callback:
Function to run on event. Function to run on event.
''' '''
try:
import i3ipc
except ImportError:
import i3
conn.Subscription(callback, event)
return
else:
pass
conn.on(event, callback) conn.on(event, callback)
from threading import Thread from threading import Thread
@ -57,12 +48,8 @@ def get_i3_connection():
''' '''
global conn global conn
if not conn: if not conn:
try: import i3ipc
import i3ipc conn = i3ipc.Connection()
except ImportError:
import i3 as conn
else:
conn = i3ipc.Connection()
return conn return conn

View File

@ -101,10 +101,7 @@ class Environment(object):
return False return False
if hasattr(getattr(zsh, 'environ', None), '__contains__'): environ = Environment()
environ = zsh.environ
else:
environ = Environment()
if hasattr(zsh, 'expand') and zsh.expand('${:-}') == '': if hasattr(zsh, 'expand') and zsh.expand('${:-}') == '':

View File

@ -179,19 +179,19 @@ except ImportError:
return readlines(('git',) + args, directory) return readlines(('git',) + args, directory)
def stash(self): def stash(self):
return sum(1 for _ in self._gitcmd(self.directory, 'stash', 'list')) return sum(1 for _ in self._gitcmd(self.directory, '--no-optional-locks', 'stash', 'list'))
def do_status(self, directory, path): def do_status(self, directory, path):
if path: if path:
try: try:
return next(self._gitcmd(directory, 'status', '--porcelain', '--ignored', '--', path))[:2] return next(self._gitcmd(directory, '--no-optional-locks', 'status', '--porcelain', '--ignored', '--', path))[:2]
except StopIteration: except StopIteration:
return None return None
else: else:
wt_column = ' ' wt_column = ' '
index_column = ' ' index_column = ' '
untracked_column = ' ' untracked_column = ' '
for line in self._gitcmd(directory, 'status', '--porcelain'): for line in self._gitcmd(directory, '--no-optional-locks', 'status', '--porcelain'):
if line[0] == '?': if line[0] == '?':
untracked_column = 'U' untracked_column = 'U'
continue continue

View File

@ -163,7 +163,7 @@ class Spec(object):
'''Define message which will be used when unknown key was found '''Define message which will be used when unknown key was found
Unknown is a key that was not provided at the initialization and via 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`. :py:meth:`Spec.unknown_spec`.
:param msgfunc: :param msgfunc:

View File

@ -50,19 +50,14 @@ def workspace_lister(pl, segment_info, only_show=None, output=None):
( (
updated( updated(
segment_info, segment_info,
output=w['output'], output=w.output,
workspace={ workspace=w,
'name': w['name'],
'visible': w['visible'],
'urgent': w['urgent'],
'focused': w['focused'],
},
), ),
{ {
'draw_inner_divider': None 'draw_inner_divider': None
} }
) )
for w in get_i3_connection().get_workspaces() for w in get_i3_connection().get_workspaces()
if (((not only_show or any(w[typ] for typ in only_show)) if (((not only_show or any(getattr(w, typ) for typ in only_show))
and (not output or w['output'] == output))) and (not output or w.output == output)))
) )

View File

@ -251,7 +251,7 @@ class Renderer(object):
for line in range(theme.get_line_number() - 1, 0, -1): for line in range(theme.get_line_number() - 1, 0, -1):
yield self.render(side=None, line=line, **kwargs) 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. '''Render all segments.
When a width is provided, low-priority segments are dropped one at When a width is provided, low-priority segments are dropped one at
@ -286,6 +286,11 @@ class Renderer(object):
:param matcher_info: :param matcher_info:
Matcher information. Is processed in :py:meth:`get_segment_info` Matcher information. Is processed in :py:meth:`get_segment_info`
method. 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) theme = self.get_theme(matcher_info)
return self.do_render( return self.do_render(
@ -297,6 +302,7 @@ class Renderer(object):
output_width=output_width, output_width=output_width,
segment_info=self.get_segment_info(segment_info, mode), segment_info=self.get_segment_info(segment_info, mode),
theme=theme, theme=theme,
hl_args=hl_args
) )
def compute_divider_widths(self, theme): def compute_divider_widths(self, theme):
@ -324,7 +330,7 @@ class Renderer(object):
:return: Results of joining these segments. :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 '''Like Renderer.render(), but accept theme in place of matcher_info
''' '''
segments = list(theme.get_segments(side, line, segment_info, mode)) 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) self._prepare_segments(segments, output_width or width)
hl_args = hl_args or dict()
if not width: if not width:
# No width specified, so we dont need to crop or pad anything # No width specified, so we dont need to crop or pad anything
if output_width: if output_width:
current_width = self._render_length(theme, segments, self.compute_divider_widths(theme)) current_width = self._render_length(theme, segments, self.compute_divider_widths(theme))
return construct_returned_value(self.hl_join([ return construct_returned_value(self.hl_join([
segment['_rendered_hl'] segment['_rendered_hl']
for segment in self._render_segments(theme, segments) for segment in self._render_segments(theme, segments, hl_args)
]) + self.hlstyle(), segments, current_width, output_raw, output_width) ]) + self.hlstyle(**hl_args), segments, current_width, output_raw, output_width)
divider_widths = self.compute_divider_widths(theme) divider_widths = self.compute_divider_widths(theme)
@ -394,10 +402,10 @@ class Renderer(object):
rendered_highlighted = self.hl_join([ rendered_highlighted = self.hl_join([
segment['_rendered_hl'] segment['_rendered_hl']
for segment in self._render_segments(theme, segments) for segment in self._render_segments(theme, segments, hl_args)
]) ])
if rendered_highlighted: 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) return construct_returned_value(rendered_highlighted, segments, current_width, output_raw, output_width)
@ -470,7 +478,7 @@ class Renderer(object):
ret += segment_len ret += segment_len
return ret 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. '''Internal segment rendering method.
This method loops through the segment array and compares the This method loops through the segment array and compares the
@ -527,6 +535,10 @@ class Renderer(object):
contents_highlighted = '' contents_highlighted = ''
draw_divider = segment['draw_' + divider_type + '_divider'] 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 # XXX Make sure self.hl() calls are called in the same order
# segments are displayed. This is needed for Vim renderer to work. # segments are displayed. This is needed for Vim renderer to work.
if draw_divider: if draw_divider:
@ -546,14 +558,14 @@ class Renderer(object):
if side == 'left': if side == 'left':
if render_highlighted: if render_highlighted:
contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args)
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False, **hl_args)
segment['_rendered_raw'] = contents_raw + divider_raw segment['_rendered_raw'] = contents_raw + divider_raw
segment['_rendered_hl'] = contents_highlighted + divider_highlighted segment['_rendered_hl'] = contents_highlighted + divider_highlighted
else: else:
if render_highlighted: if render_highlighted:
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False, **hl_args)
contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args)
segment['_rendered_raw'] = divider_raw + contents_raw segment['_rendered_raw'] = divider_raw + contents_raw
segment['_rendered_hl'] = divider_highlighted + contents_highlighted segment['_rendered_hl'] = divider_highlighted + contents_highlighted
else: else:
@ -562,7 +574,7 @@ class Renderer(object):
else: else:
contents_raw = contents_raw + outer_padding 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_raw'] = contents_raw
segment['_rendered_hl'] = contents_highlighted segment['_rendered_hl'] = contents_highlighted
prev_segment = segment prev_segment = segment
@ -576,7 +588,7 @@ class Renderer(object):
''' '''
return string.translate(self.character_translations) 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. '''Output highlight style string.
Assuming highlighted string looks like ``{style}{contents}`` this method Assuming highlighted string looks like ``{style}{contents}`` this method
@ -585,10 +597,10 @@ class Renderer(object):
''' '''
raise NotImplementedError 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. '''Output highlighted chunk.
This implementation just outputs :py:meth:`hlstyle` joined with This implementation just outputs :py:meth:`hlstyle` joined with
``contents``. ``contents``.
''' '''
return self.hlstyle(fg, bg, attrs) + (contents or '') return self.hlstyle(fg, bg, attrs, **kwargs) + (contents or '')

View File

@ -17,7 +17,7 @@ class I3barRenderer(Renderer):
# We dont need to explicitly reset attributes, so skip those calls # We dont need to explicitly reset attributes, so skip those calls
return '' return ''
def hl(self, contents, fg=None, bg=None, attrs=None): def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs):
segment = { segment = {
'full_text': contents, 'full_text': contents,
'separator': False, 'separator': False,

View File

@ -90,7 +90,7 @@ class IPythonPygmentsRenderer(IPythonRenderer):
def hl_join(segments): def hl_join(segments):
return reduce(operator.iadd, 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. '''Output highlighted chunk.
This implementation outputs a list containing a single pair This implementation outputs a list containing a single pair

View File

@ -21,7 +21,7 @@ class LemonbarRenderer(Renderer):
# We dont need to explicitly reset attributes, so skip those calls # We dont need to explicitly reset attributes, so skip those calls
return '' return ''
def hl(self, contents, fg=None, bg=None, attrs=None): def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs):
text = '' text = ''
if fg is not None: if fg is not None:

View File

@ -15,7 +15,7 @@ class PangoMarkupRenderer(Renderer):
# We dont need to explicitly reset attributes, so skip those calls # We dont need to explicitly reset attributes, so skip those calls
return '' 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.''' '''Highlight a segment.'''
awesome_attr = [] awesome_attr = []
if fg is not None: if fg is not None:

View File

@ -105,7 +105,7 @@ class ShellRenderer(PromptRenderer):
self.used_term_escape_style = self.term_escape_style self.used_term_escape_style = self.term_escape_style
return super(ShellRenderer, self).do_render(segment_info=segment_info, **kwargs) 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. '''Highlight a segment.
If an argument is None, the argument is ignored. If an argument is 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\\' r = '\033Ptmux;' + r.replace('\033', '\033\033') + '\033\\'
elif self.screen_escape: elif self.screen_escape:
r = '\033P' + r.replace('\033', '\033\033') + '\033\\' 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): def get_theme(self, matcher_info):
if not matcher_info: if not matcher_info:

View File

@ -6,13 +6,91 @@ from powerline.renderers.shell import ShellRenderer
class BashPromptRenderer(ShellRenderer): class BashPromptRenderer(ShellRenderer):
'''Powerline bash prompt segment renderer.''' '''Powerline bash prompt segment renderer.'''
escape_hl_start = '\[' escape_hl_start = '\\['
escape_hl_end = '\]' escape_hl_end = '\\]'
character_translations = ShellRenderer.character_translations.copy() character_translations = ShellRenderer.character_translations.copy()
character_translations[ord('$')] = '\\$' character_translations[ord('$')] = '\\$'
character_translations[ord('`')] = '\\`' 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 renderer = BashPromptRenderer

View File

@ -38,7 +38,7 @@ class TmuxRenderer(Renderer):
width = 10 width = 10
return super(TmuxRenderer, self).render(width=width, segment_info=segment_info, **kwargs) 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.''' '''Highlight a segment.'''
# We dont need to explicitly reset attributes, so skip those calls # We dont need to explicitly reset attributes, so skip those calls
if not attrs and not bg and not fg: if not attrs and not bg and not fg:
@ -68,7 +68,9 @@ class TmuxRenderer(Renderer):
r = self.segment_info.copy() r = self.segment_info.copy()
if segment_info: if segment_info:
r.update(segment_info) r.update(segment_info)
if 'pane_id' in r: if 'pane_current_path' in r:
r['getcwd'] = lambda: r['pane_current_path']
elif 'pane_id' in r:
varname = 'TMUX_PWD_' + str(r['pane_id']) varname = 'TMUX_PWD_' + str(r['pane_id'])
if varname in r['environ']: if varname in r['environ']:
r['getcwd'] = lambda: r['environ'][varname] r['getcwd'] = lambda: r['environ'][varname]

View File

@ -123,7 +123,7 @@ class VimRenderer(Renderer):
def reset_highlight(self): def reset_highlight(self):
self.hl_groups.clear() 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. '''Highlight a segment.
If an argument is None, the argument is ignored. If an argument is If an argument is None, the argument is ignored. If an argument is

View File

@ -19,20 +19,24 @@ def environment(pl, segment_info, variable=None):
@requires_segment_info @requires_segment_info
def virtualenv(pl, segment_info, ignore_venv=False, ignore_conda=False): def virtualenv(pl, segment_info, ignore_venv=False, ignore_conda=False, ignored_names=("venv", ".venv")):
'''Return the name of the current Python or conda virtualenv. '''Return the name of the current Python or conda virtualenv.
:param list ignored_names:
Names of venvs to ignore. Will then get the name of the venv by ascending to the parent directory
:param bool ignore_venv: :param bool ignore_venv:
Whether to ignore virtual environments. Default is False. Whether to ignore virtual environments. Default is False.
:param bool ignore_conda: :param bool ignore_conda:
Whether to ignore conda environments. Default is False. Whether to ignore conda environments. Default is False.
''' '''
return ( if not ignore_venv:
(not ignore_venv and for candidate in reversed(segment_info['environ'].get('VIRTUAL_ENV', '').split("/")):
os.path.basename(segment_info['environ'].get('VIRTUAL_ENV', ''))) or if candidate and candidate not in ignored_names:
(not ignore_conda and return candidate
segment_info['environ'].get('CONDA_DEFAULT_ENV', '')) or if not ignore_conda:
None) for candidate in reversed(segment_info['environ'].get('CONDA_DEFAULT_ENV', '').split("/")):
if candidate and candidate not in ignored_names:
return candidate
return None
@requires_segment_info @requires_segment_info

View File

@ -31,6 +31,8 @@ def _convert_state(state):
def _convert_seconds(seconds): def _convert_seconds(seconds):
'''Convert seconds to minutes:seconds format''' '''Convert seconds to minutes:seconds format'''
if isinstance(seconds, str):
seconds = seconds.replace(",",".")
return '{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60)) return '{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60))

View File

@ -90,8 +90,6 @@ try:
self.exception('Exception while calculating cpu_percent: {0}', str(e)) self.exception('Exception while calculating cpu_percent: {0}', str(e))
def render(self, cpu_percent, format='{0:.0f}%', **kwargs): def render(self, cpu_percent, format='{0:.0f}%', **kwargs):
if not cpu_percent:
return None
return [{ return [{
'contents': format.format(cpu_percent), 'contents': format.format(cpu_percent),
'gradient_level': cpu_percent, 'gradient_level': cpu_percent,
@ -150,7 +148,8 @@ else:
@add_divider_highlight_group('background:divider') @add_divider_highlight_group('background:divider')
def uptime(pl, days_format='{days:d}d', hours_format=' {hours:d}h', minutes_format=' {minutes:d}m', seconds_format=' {seconds:d}s', shorten_len=3): def uptime(pl, days_format='{days:d}d', hours_format=' {hours:d}h', minutes_format=' {minutes:02d}m',
seconds_format=' {seconds:02d}s', shorten_len=3):
'''Return system uptime. '''Return system uptime.
:param str days_format: :param str days_format:
@ -175,9 +174,11 @@ def uptime(pl, days_format='{days:d}d', hours_format=' {hours:d}h', minutes_form
hours, minutes = divmod(minutes, 60) hours, minutes = divmod(minutes, 60)
days, hours = divmod(hours, 24) days, hours = divmod(hours, 24)
time_formatted = list(filter(None, [ time_formatted = list(filter(None, [
days_format.format(days=days) if days and days_format else None, days_format.format(days=days) if days_format else None,
hours_format.format(hours=hours) if hours and hours_format else None, hours_format.format(hours=hours) if hours_format else None,
minutes_format.format(minutes=minutes) if minutes and minutes_format else None, minutes_format.format(minutes=minutes) if minutes_format else None,
seconds_format.format(seconds=seconds) if seconds and seconds_format else None, seconds_format.format(seconds=seconds) if seconds_format else None,
]))[0:shorten_len] ]))
first_non_zero = next((i for i, x in enumerate([days, hours, minutes, seconds]) if x != 0))
time_formatted = time_formatted[first_non_zero:first_non_zero + shorten_len]
return ''.join(time_formatted).strip() return ''.join(time_formatted).strip()

View File

@ -4,22 +4,33 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
from datetime import datetime from datetime import datetime
def date(pl, format='%Y-%m-%d', istime=False): def date(pl, format='%Y-%m-%d', istime=False, timezone=None):
'''Return the current date. '''Return the current date.
:param str format: :param str format:
strftime-style date format string strftime-style date format string
:param bool istime: :param bool istime:
If true then segment uses ``time`` highlight group. If true then segment uses ``time`` highlight group.
:param string timezone:
Specify a timezone to use as ``+HHMM`` or ``-HHMM``.
(Defaults to system defaults.)
Divider highlight group used: ``time:divider``. Divider highlight group used: ``time:divider``.
Highlight groups used: ``time`` or ``date``. Highlight groups used: ``time`` or ``date``.
''' '''
try: try:
contents = datetime.now().strftime(format) tz = datetime.strptime(timezone, '%z').tzinfo if timezone else None
except ValueError:
tz = None
nw = datetime.now(tz)
try:
contents = nw.strftime(format)
except UnicodeEncodeError: except UnicodeEncodeError:
contents = datetime.now().strftime(format.encode('utf-8')).decode('utf-8') contents = nw.strftime(format.encode('utf-8')).decode('utf-8')
return [{ return [{
'contents': contents, 'contents': contents,
@ -34,59 +45,77 @@ UNICODE_TEXT_TRANSLATION = {
} }
def fuzzy_time(pl, unicode_text=False): def fuzzy_time(pl, format='{minute_str} {hour_str}', unicode_text=False, timezone=None, hour_str=['twelve', 'one', 'two', 'three', 'four',
'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven'], minute_str = {
'0': 'o\'clock', '5': 'five past', '10': 'ten past','15': 'quarter past',
'20': 'twenty past', '25': 'twenty-five past', '30': 'half past', '35': 'twenty-five to',
'40': 'twenty to', '45': 'quarter to', '50': 'ten to', '55': 'five to'
}, special_case_str = {
'(23, 58)': 'round about midnight',
'(23, 59)': 'round about midnight',
'(0, 0)': 'midnight',
'(0, 1)': 'round about midnight',
'(0, 2)': 'round about midnight',
'(12, 0)': 'noon',
}):
'''Display the current time as fuzzy time, e.g. "quarter past six". '''Display the current time as fuzzy time, e.g. "quarter past six".
:param string format:
Format used to display the fuzzy time. (Ignored when a special time
is displayed.)
:param bool unicode_text: :param bool unicode_text:
If true then hyphenminuses (regular ASCII ``-``) and single quotes are If true then hyphenminuses (regular ASCII ``-``) and single quotes are
replaced with unicode dashes and apostrophes. replaced with unicode dashes and apostrophes.
''' :param string timezone:
hour_str = ['twelve', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven'] Specify a timezone to use as ``+HHMM`` or ``-HHMM``.
minute_str = { (Defaults to system defaults.)
5: 'five past', :param string list hour_str:
10: 'ten past', Strings to be used to display the hour, starting with midnight.
15: 'quarter past', (This list may contain 12 or 24 entries.)
20: 'twenty past', :param dict minute_str:
25: 'twenty-five past', Dictionary mapping minutes to strings to be used to display them.
30: 'half past', :param dict special_case_str:
35: 'twenty-five to', Special strings for special times.
40: 'twenty to',
45: 'quarter to',
50: 'ten to',
55: 'five to',
}
special_case_str = {
(23, 58): 'round about midnight',
(23, 59): 'round about midnight',
(0, 0): 'midnight',
(0, 1): 'round about midnight',
(0, 2): 'round about midnight',
(12, 0): 'noon',
}
now = datetime.now() Highlight groups used: ``fuzzy_time``.
'''
try: try:
return special_case_str[(now.hour, now.minute)] tz = datetime.strptime(timezone, '%z').tzinfo if timezone else None
except ValueError:
tz = None
now = datetime.now(tz)
try:
# We don't want to enforce a special type of spaces/ alignment in the input
from ast import literal_eval
special_case_str = {literal_eval(x):special_case_str[x] for x in special_case_str}
result = special_case_str[(now.hour, now.minute)]
if unicode_text:
result = result.translate(UNICODE_TEXT_TRANSLATION)
return result
except KeyError: except KeyError:
pass pass
hour = now.hour hour = now.hour
if now.minute > 32: if now.minute >= 30:
if hour == 23: hour = hour + 1
hour = 0 hour = hour % len(hour_str)
else:
hour += 1
if hour > 11:
hour = hour - 12
hour = hour_str[hour]
minute = int(round(now.minute / 5.0) * 5) min_dis = 100
if minute == 60 or minute == 0: min_pos = 0
result = ' '.join([hour, 'o\'clock'])
else: for mn in minute_str:
minute = minute_str[minute] mn = int(mn)
result = ' '.join([minute, hour]) if now.minute >= mn and now.minute - mn < min_dis:
min_dis = now.minute - mn
min_pos = mn
elif now.minute < mn and mn - now.minute < min_dis:
min_dis = mn - now.minute
min_pos = mn
result = format.format(minute_str=minute_str[str(min_pos)], hour_str=hour_str[hour])
if unicode_text: if unicode_text:
result = result.translate(UNICODE_TEXT_TRANSLATION) result = result.translate(UNICODE_TEXT_TRANSLATION)

View File

@ -2,76 +2,80 @@
from __future__ import (unicode_literals, division, absolute_import, print_function) from __future__ import (unicode_literals, division, absolute_import, print_function)
import json import json
from collections import namedtuple
from powerline.lib.url import urllib_read, urllib_urlencode from powerline.lib.url import urllib_read, urllib_urlencode
from powerline.lib.threaded import KwThreadedSegment from powerline.lib.threaded import KwThreadedSegment
from powerline.segments import with_docstring from powerline.segments import with_docstring
_WeatherKey = namedtuple('Key', 'location_query weather_api_key')
# XXX Warning: module name must not be equal to the segment name as long as this # XXX Warning: module name must not be equal to the segment name as long as this
# segment is imported into powerline.segments.common module. # segment is imported into powerline.segments.common module.
# Weather condition code descriptions available at # Weather condition code descriptions available at
# http://developer.yahoo.com/weather/#codes # https://openweathermap.org/weather-conditions
weather_conditions_codes = ( weather_conditions_codes = {
('tornado', 'stormy'), # 0 200: ('stormy',),
('tropical_storm', 'stormy'), # 1 201: ('stormy',),
('hurricane', 'stormy'), # 2 202: ('stormy',),
('severe_thunderstorms', 'stormy'), # 3 210: ('stormy',),
('thunderstorms', 'stormy'), # 4 211: ('stormy',),
('mixed_rain_and_snow', 'rainy' ), # 5 212: ('stormy',),
('mixed_rain_and_sleet', 'rainy' ), # 6 221: ('stormy',),
('mixed_snow_and_sleet', 'snowy' ), # 7 230: ('stormy',),
('freezing_drizzle', 'rainy' ), # 8 231: ('stormy',),
('drizzle', 'rainy' ), # 9 232: ('stormy',),
('freezing_rain', 'rainy' ), # 10 300: ('rainy',),
('showers', 'rainy' ), # 11 301: ('rainy',),
('showers', 'rainy' ), # 12 302: ('rainy',),
('snow_flurries', 'snowy' ), # 13 310: ('rainy',),
('light_snow_showers', 'snowy' ), # 14 311: ('rainy',),
('blowing_snow', 'snowy' ), # 15 312: ('rainy',),
('snow', 'snowy' ), # 16 313: ('rainy',),
('hail', 'snowy' ), # 17 314: ('rainy',),
('sleet', 'snowy' ), # 18 321: ('rainy',),
('dust', 'foggy' ), # 19 500: ('rainy',),
('fog', 'foggy' ), # 20 501: ('rainy',),
('haze', 'foggy' ), # 21 502: ('rainy',),
('smoky', 'foggy' ), # 22 503: ('rainy',),
('blustery', 'windy' ), # 23 504: ('rainy',),
('windy', ), # 24 511: ('snowy',),
('cold', 'day' ), # 25 520: ('rainy',),
('clouds', 'cloudy'), # 26 521: ('rainy',),
('mostly_cloudy_night', 'cloudy'), # 27 522: ('rainy',),
('mostly_cloudy_day', 'cloudy'), # 28 531: ('rainy',),
('partly_cloudy_night', 'cloudy'), # 29 600: ('snowy',),
('partly_cloudy_day', 'cloudy'), # 30 601: ('snowy',),
('clear_night', 'night' ), # 31 602: ('snowy',),
('sun', 'sunny' ), # 32 611: ('snowy',),
('fair_night', 'night' ), # 33 612: ('snowy',),
('fair_day', 'day' ), # 34 613: ('snowy',),
('mixed_rain_and_hail', 'rainy' ), # 35 615: ('snowy',),
('hot', 'sunny' ), # 36 616: ('snowy',),
('isolated_thunderstorms', 'stormy'), # 37 620: ('snowy',),
('scattered_thunderstorms', 'stormy'), # 38 621: ('snowy',),
('scattered_thunderstorms', 'stormy'), # 39 622: ('snowy',),
('scattered_showers', 'rainy' ), # 40 701: ('foggy',),
('heavy_snow', 'snowy' ), # 41 711: ('foggy',),
('scattered_snow_showers', 'snowy' ), # 42 721: ('foggy',),
('heavy_snow', 'snowy' ), # 43 731: ('foggy',),
('partly_cloudy', 'cloudy'), # 44 741: ('foggy',),
('thundershowers', 'rainy' ), # 45 751: ('foggy',),
('snow_showers', 'snowy' ), # 46 761: ('foggy',),
('isolated_thundershowers', 'rainy' ), # 47 762: ('foggy',),
) 771: ('foggy',),
# ('day', (25, 34)), 781: ('foggy',),
# ('rainy', (5, 6, 8, 9, 10, 11, 12, 35, 40, 45, 47)), 800: ('sunny',),
# ('cloudy', (26, 27, 28, 29, 30, 44)), 801: ('cloudy',),
# ('snowy', (7, 13, 14, 15, 16, 17, 18, 41, 42, 43, 46)), 802: ('cloudy',),
# ('stormy', (0, 1, 2, 3, 4, 37, 38, 39)), 803: ('cloudy',),
# ('foggy', (19, 20, 21, 22, 23)), 804: ('cloudy',),
# ('sunny', (32, 36)), }
# ('night', (31, 33))):
weather_conditions_icons = { weather_conditions_icons = {
'day': 'DAY', 'day': 'DAY',
'blustery': 'WIND', 'blustery': 'WIND',
@ -88,9 +92,9 @@ weather_conditions_icons = {
} }
temp_conversions = { temp_conversions = {
'C': lambda temp: temp, 'C': lambda temp: temp - 273.15,
'F': lambda temp: (temp * 9 / 5) + 32, 'F': lambda temp: (temp * 9 / 5) - 459.67,
'K': lambda temp: temp + 273.15, 'K': lambda temp: temp,
} }
# Note: there are also unicode characters for units: ℃, ℉ and # Note: there are also unicode characters for units: ℃, ℉ and
@ -105,38 +109,37 @@ class WeatherSegment(KwThreadedSegment):
interval = 600 interval = 600
default_location = None default_location = None
location_urls = {} location_urls = {}
weather_api_key = "fbc9549d91a5e4b26c15be0dbdac3460"
@staticmethod @staticmethod
def key(location_query=None, **kwargs): def key(location_query=None, **kwargs):
return location_query
def get_request_url(self, location_query):
try: try:
return self.location_urls[location_query] weather_api_key = kwargs["weather_api_key"]
except KeyError:
weather_api_key = WeatherSegment.weather_api_key
return _WeatherKey(location_query, weather_api_key)
def get_request_url(self, weather_key):
try:
return self.location_urls[weather_key]
except KeyError: except KeyError:
if location_query is None:
location_data = json.loads(urllib_read('http://geoip.nekudo.com/api/'))
location = ','.join((
location_data['city'],
location_data['country']['name'],
location_data['country']['code']
))
self.info('Location returned by nekudo is {0}', location)
else:
location = location_query
query_data = { query_data = {
'q': "appid": weather_key.weather_api_key
'use "https://raw.githubusercontent.com/yql/yql-tables/master/weather/weather.bylocation.xml" as we;'
'select * from weather.forecast where woeid in'
' (select woeid from geo.places(1) where text="{0}") and u="c"'.format(location).encode('utf-8'),
'format': 'json',
} }
location_query = weather_key.location_query
if location_query is None:
location_data = json.loads(urllib_read('https://freegeoip.app/json/'))
query_data["lat"] = location_data["latitude"]
query_data["lon"] = location_data["longitude"]
else:
query_data["q"] = location_query
self.location_urls[location_query] = url = ( self.location_urls[location_query] = url = (
'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data)) "https://api.openweathermap.org/data/2.5/weather?" +
urllib_urlencode(query_data))
return url return url
def compute_state(self, location_query): def compute_state(self, weather_key):
url = self.get_request_url(location_query) url = self.get_request_url(weather_key)
raw_response = urllib_read(url) raw_response = urllib_read(url)
if not raw_response: if not raw_response:
self.error('Failed to get response') self.error('Failed to get response')
@ -144,22 +147,18 @@ class WeatherSegment(KwThreadedSegment):
response = json.loads(raw_response) response = json.loads(raw_response)
try: try:
condition = response['query']['results']['channel']['item']['condition'] condition = response['weather'][0]
condition_code = int(condition['code']) condition_code = int(condition['id'])
temp = float(condition['temp']) temp = float(response['main']['temp'])
except (KeyError, ValueError): except (KeyError, ValueError):
self.exception('Yahoo returned malformed or unexpected response: {0}', repr(raw_response)) self.exception('OpenWeatherMap returned malformed or unexpected response: {0}', repr(raw_response))
return None return None
try: try:
icon_names = weather_conditions_codes[condition_code] icon_names = weather_conditions_codes[condition_code]
except IndexError: except IndexError:
if condition_code == 3200: icon_names = ('unknown',)
icon_names = ('not_available',) self.error('Unknown condition code: {0}', condition_code)
self.warn('Weather is not available for location {0}', self.location)
else:
icon_names = ('unknown',)
self.error('Unknown condition code: {0}', condition_code)
return (temp, icon_names) return (temp, icon_names)
@ -179,12 +178,12 @@ class WeatherSegment(KwThreadedSegment):
temp_format = temp_format or ('{temp:.0f}' + temp_units[unit]) temp_format = temp_format or ('{temp:.0f}' + temp_units[unit])
converted_temp = temp_conversions[unit](temp) converted_temp = temp_conversions[unit](temp)
if temp <= temp_coldest: if converted_temp <= temp_coldest:
gradient_level = 0 gradient_level = 0
elif temp >= temp_hottest: elif converted_temp >= temp_hottest:
gradient_level = 100 gradient_level = 100
else: else:
gradient_level = (temp - temp_coldest) * 100.0 / (temp_hottest - temp_coldest) gradient_level = (converted_temp - temp_coldest) * 100.0 / (temp_hottest - temp_coldest)
groups = ['weather_condition_' + icon_name for icon_name in icon_names] + ['weather_conditions', 'weather'] groups = ['weather_condition_' + icon_name for icon_name in icon_names] + ['weather_conditions', 'weather']
return [ return [
{ {
@ -202,9 +201,9 @@ class WeatherSegment(KwThreadedSegment):
weather = with_docstring(WeatherSegment(), weather = with_docstring(WeatherSegment(),
'''Return weather from Yahoo! Weather. '''Return weather from OpenWeatherMaps.
Uses GeoIP lookup from http://geoip.nekudo.com to automatically determine Uses GeoIP lookup from https://freegeoip.app to automatically determine
your current location. This should be changed if youre in a VPN or if your your current location. This should be changed if youre in a VPN or if your
IP address is registered at another location. IP address is registered at another location.
@ -231,5 +230,5 @@ weather conditions.
Divider highlight group used: ``background:divider``. Divider highlight group used: ``background:divider``.
Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_gradient`` (gradient) or ``weather``. Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_gradient`` (gradient) or ``weather``.
Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo. Also uses ``weather_conditions_{condition}`` for all weather conditions supported by OpenWeatherMap.
''') ''')

View File

@ -12,11 +12,11 @@ WORKSPACE_REGEX = re.compile(r'^[0-9]+: ?')
def workspace_groups(w): def workspace_groups(w):
group = [] group = []
if w['focused']: if w.focused:
group.append('w_focused') group.append('w_focused')
if w['urgent']: if w.urgent:
group.append('w_urgent') group.append('w_urgent')
if w['visible']: if w.visible:
group.append('w_visible') group.append('w_visible')
group.append('workspace') group.append('workspace')
return group return group
@ -52,12 +52,12 @@ def workspaces(pl, segment_info, only_show=None, output=None, strip=0):
return [ return [
{ {
'contents': w['name'][strip:], 'contents': w.name[strip:],
'highlight_groups': workspace_groups(w) 'highlight_groups': workspace_groups(w)
} }
for w in get_i3_connection().get_workspaces() for w in get_i3_connection().get_workspaces()
if ((not only_show or any(w[typ] for typ in only_show)) if ((not only_show or any(getattr(w, typ) for typ in only_show))
and (not output or w['output'] == output)) and (not output or w.output == output))
] ]
@ -80,7 +80,7 @@ def workspace(pl, segment_info, workspace=None, strip=False):
try: try:
w = next(( w = next((
w for w in get_i3_connection().get_workspaces() w for w in get_i3_connection().get_workspaces()
if w['name'] == workspace if w.name == workspace
)) ))
except StopIteration: except StopIteration:
return None return None
@ -90,13 +90,13 @@ def workspace(pl, segment_info, workspace=None, strip=False):
try: try:
w = next(( w = next((
w for w in get_i3_connection().get_workspaces() w for w in get_i3_connection().get_workspaces()
if w['focused'] if w.focused
)) ))
except StopIteration: except StopIteration:
return None return None
return [{ return [{
'contents': format_name(w['name'], strip=strip), 'contents': format_name(w.name, strip=strip),
'highlight_groups': workspace_groups(w) 'highlight_groups': workspace_groups(w)
}] }]
@ -150,6 +150,6 @@ def scratchpad(pl, icons=SCRATCHPAD_ICONS):
'contents': icons.get(w.scratchpad_state, icons['changed']), 'contents': icons.get(w.scratchpad_state, icons['changed']),
'highlight_groups': scratchpad_groups(w) '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' if w.scratchpad_state != 'none'
] ]

View File

@ -4,7 +4,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
import subprocess import subprocess
from traceback import print_exc from traceback import print_exc
__version__ = "2.8.1" __version__ = "2.8.2"
def get_version(): def get_version():
try: try:

View File

@ -262,6 +262,7 @@ class VimPowerline(Powerline):
def new_win_idx(self, window_id): def new_win_idx(self, window_id):
r = None r = None
for window in vim.windows: for window in vim.windows:
try: try:
curwindow_id = window.vars['powerline_window_id'] curwindow_id = window.vars['powerline_window_id']
@ -302,7 +303,18 @@ class VimPowerline(Powerline):
return self.render(window, window_id, winnr) return self.render(window, window_id, winnr)
def tabline(self): def tabline(self):
return self.render(*self.win_idx(None), is_tabline=True) r = self.win_idx(None)
if r:
return self.render(*r, is_tabline=True)
else:
win = vim.current.window
r = (
win,
win.vars.get('powerline_window_id', self.last_window_id),
win.number,
)
return self.render(*r, is_tabline=True)
def new_window(self): def new_window(self):
return self.render(*self.win_idx(None)) return self.render(*self.win_idx(None))

View File

@ -46,14 +46,13 @@ except Exception as e:
else: else:
sys.path.append(CURRENT_DIR) sys.path.append(CURRENT_DIR)
from powerline.lib.shell import which from powerline.lib.shell import which
can_use_scripts = True
if which('socat') and which('sed') and which('sh'): if which('socat') and which('sed') and which('sh'):
print('Using powerline.sh script instead of C version (requires socat, sed and sh)') print('Using powerline.sh script instead of C version (requires socat, sed and sh)')
shutil.copyfile('client/powerline.sh', 'scripts/powerline') shutil.copyfile('client/powerline.sh', 'scripts/powerline')
can_use_scripts = True
else: else:
print('Using powerline.py script instead of C version') print('Using powerline.py script instead of C version')
shutil.copyfile('client/powerline.py', 'scripts/powerline') shutil.copyfile('client/powerline.py', 'scripts/powerline')
can_use_scripts = True
else: else:
can_use_scripts = False can_use_scripts = False
@ -71,10 +70,10 @@ setup(
'Natural Language :: English', 'Natural Language :: English',
'Operating System :: Microsoft :: Windows', 'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX', 'Operating System :: POSIX',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy', 'Programming Language :: Python :: Implementation :: PyPy',
], ],

View File

@ -44,15 +44,23 @@ def urllib_read(query_url):
return '127.0.0.1' return '127.0.0.1'
elif query_url.startswith('http://ipv6.icanhazip.com'): elif query_url.startswith('http://ipv6.icanhazip.com'):
return '2001:4801:7818:6:abc5:ba2c:ff10:275f' return '2001:4801:7818:6:abc5:ba2c:ff10:275f'
elif query_url.startswith('https://freegeoip.app/json/'):
return '{"ip":"82.145.55.16","country_code":"DE","country_name":"Germany","region_code":"NI","region_name":"Lower Saxony","city":"Meppen","zip_code":"49716","time_zone":"Europe/Berlin","latitude":52.6833,"longitude":7.3167,"metro_code":0}'
elif query_url.startswith('http://geoip.nekudo.com/api/'): elif query_url.startswith('http://geoip.nekudo.com/api/'):
return '{"city":"Meppen","country":{"name":"Germany", "code":"DE"},"location":{"accuracy_radius":100,"latitude":52.6833,"longitude":7.3167,"time_zone":"Europe\/Berlin"},"ip":"82.145.55.16"}' return '{"city":"Meppen","country":{"name":"Germany", "code":"DE"},"location":{"accuracy_radius":100,"latitude":52.6833,"longitude":7.3167,"time_zone":"Europe\/Berlin"},"ip":"82.145.55.16"}'
elif query_url.startswith('http://query.yahooapis.com/v1/public/'): elif query_url.startswith('http://query.yahooapis.com/v1/public/'):
if 'Meppen' in query_url: if 'Meppen' in query_url or '52.6833' in query_url:
return r'{"query":{"count":1,"created":"2016-05-13T19:43:18Z","lang":"en-US","results":{"channel":{"units":{"distance":"mi","pressure":"in","speed":"mph","temperature":"C"},"title":"Yahoo! Weather - Meppen, NI, DE","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-674836/","description":"Yahoo! Weather for Meppen, NI, DE","language":"en-us","lastBuildDate":"Fri, 13 May 2016 09:43 PM CEST","ttl":"60","location":{"city":"Meppen","country":"Germany","region":" NI"},"wind":{"chill":"55","direction":"350","speed":"25"},"atmosphere":{"humidity":"57","pressure":"1004.0","rising":"0","visibility":"16.1"},"astronomy":{"sunrise":"5:35 am","sunset":"9:21 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Meppen, NI, DE at 08:00 PM CEST","lat":"52.68993","long":"7.29115","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-674836/","pubDate":"Fri, 13 May 2016 08:00 PM CEST","condition":{"code":"23","date":"Fri, 13 May 2016 08:00 PM CEST","temp":"14","text":"Breezy"},"forecast":[{"code":"30","date":"13 May 2016","day":"Fri","high":"71","low":"48","text":"Partly Cloudy"},{"code":"28","date":"14 May 2016","day":"Sat","high":"54","low":"44","text":"Mostly Cloudy"},{"code":"11","date":"15 May 2016","day":"Sun","high":"55","low":"43","text":"Showers"},{"code":"28","date":"16 May 2016","day":"Mon","high":"54","low":"42","text":"Mostly Cloudy"},{"code":"28","date":"17 May 2016","day":"Tue","high":"57","low":"43","text":"Mostly Cloudy"},{"code":"12","date":"18 May 2016","day":"Wed","high":"62","low":"45","text":"Rain"},{"code":"28","date":"19 May 2016","day":"Thu","high":"63","low":"48","text":"Mostly Cloudy"},{"code":"28","date":"20 May 2016","day":"Fri","high":"67","low":"50","text":"Mostly Cloudy"},{"code":"30","date":"21 May 2016","day":"Sat","high":"71","low":"50","text":"Partly Cloudy"},{"code":"30","date":"22 May 2016","day":"Sun","high":"74","low":"54","text":"Partly Cloudy"}],"description":"<![CDATA[<img src=\"http://l.yimg.com/a/i/us/we/52/23.gif\"/>\n<BR />\n<b>Current Conditions:</b>\n<BR />Breezy\n<BR />\n<BR />\n<b>Forecast:</b>\n<BR /> Fri - Partly Cloudy. High: 71Low: 48\n<BR /> Sat - Mostly Cloudy. High: 54Low: 44\n<BR /> Sun - Showers. High: 55Low: 43\n<BR /> Mon - Mostly Cloudy. High: 54Low: 42\n<BR /> Tue - Mostly Cloudy. High: 57Low: 43\n<BR />\n<BR />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-674836/\">Full Forecast at Yahoo! Weather</a>\n<BR />\n<BR />\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)\n<BR />\n]]>","guid":{"isPermaLink":"false"}}}}}}' return r'{"query":{"count":1,"created":"2016-05-13T19:43:18Z","lang":"en-US","results":{"channel":{"units":{"distance":"mi","pressure":"in","speed":"mph","temperature":"C"},"title":"Yahoo! Weather - Meppen, NI, DE","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-674836/","description":"Yahoo! Weather for Meppen, NI, DE","language":"en-us","lastBuildDate":"Fri, 13 May 2016 09:43 PM CEST","ttl":"60","location":{"city":"Meppen","country":"Germany","region":" NI"},"wind":{"chill":"55","direction":"350","speed":"25"},"atmosphere":{"humidity":"57","pressure":"1004.0","rising":"0","visibility":"16.1"},"astronomy":{"sunrise":"5:35 am","sunset":"9:21 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Meppen, NI, DE at 08:00 PM CEST","lat":"52.68993","long":"7.29115","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-674836/","pubDate":"Fri, 13 May 2016 08:00 PM CEST","condition":{"code":"23","date":"Fri, 13 May 2016 08:00 PM CEST","temp":"14","text":"Breezy"},"forecast":[{"code":"30","date":"13 May 2016","day":"Fri","high":"71","low":"48","text":"Partly Cloudy"},{"code":"28","date":"14 May 2016","day":"Sat","high":"54","low":"44","text":"Mostly Cloudy"},{"code":"11","date":"15 May 2016","day":"Sun","high":"55","low":"43","text":"Showers"},{"code":"28","date":"16 May 2016","day":"Mon","high":"54","low":"42","text":"Mostly Cloudy"},{"code":"28","date":"17 May 2016","day":"Tue","high":"57","low":"43","text":"Mostly Cloudy"},{"code":"12","date":"18 May 2016","day":"Wed","high":"62","low":"45","text":"Rain"},{"code":"28","date":"19 May 2016","day":"Thu","high":"63","low":"48","text":"Mostly Cloudy"},{"code":"28","date":"20 May 2016","day":"Fri","high":"67","low":"50","text":"Mostly Cloudy"},{"code":"30","date":"21 May 2016","day":"Sat","high":"71","low":"50","text":"Partly Cloudy"},{"code":"30","date":"22 May 2016","day":"Sun","high":"74","low":"54","text":"Partly Cloudy"}],"description":"<![CDATA[<img src=\"http://l.yimg.com/a/i/us/we/52/23.gif\"/>\n<BR />\n<b>Current Conditions:</b>\n<BR />Breezy\n<BR />\n<BR />\n<b>Forecast:</b>\n<BR /> Fri - Partly Cloudy. High: 71Low: 48\n<BR /> Sat - Mostly Cloudy. High: 54Low: 44\n<BR /> Sun - Showers. High: 55Low: 43\n<BR /> Mon - Mostly Cloudy. High: 54Low: 42\n<BR /> Tue - Mostly Cloudy. High: 57Low: 43\n<BR />\n<BR />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-674836/\">Full Forecast at Yahoo! Weather</a>\n<BR />\n<BR />\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)\n<BR />\n]]>","guid":{"isPermaLink":"false"}}}}}}'
elif 'Moscow' in query_url: elif 'Moscow' in query_url:
return r'{"query":{"count":1,"created":"2016-05-13T19:47:01Z","lang":"en-US","results":{"channel":{"units":{"distance":"mi","pressure":"in","speed":"mph","temperature":"C"},"title":"Yahoo! Weather - Moscow, Moscow Federal City, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2122265/","description":"Yahoo! Weather for Moscow, Moscow Federal City, RU","language":"en-us","lastBuildDate":"Fri, 13 May 2016 10:47 PM MSK","ttl":"60","location":{"city":"Moscow","country":"Russia","region":" Moscow Federal City"},"wind":{"chill":"45","direction":"80","speed":"11"},"atmosphere":{"humidity":"52","pressure":"993.0","rising":"0","visibility":"16.1"},"astronomy":{"sunrise":"4:19 am","sunset":"8:34 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Moscow, Moscow Federal City, RU at 09:00 PM MSK","lat":"55.741638","long":"37.605061","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2122265/","pubDate":"Fri, 13 May 2016 09:00 PM MSK","condition":{"code":"33","date":"Fri, 13 May 2016 09:00 PM MSK","temp":"9","text":"Mostly Clear"},"forecast":[{"code":"30","date":"13 May 2016","day":"Fri","high":"62","low":"41","text":"Partly Cloudy"},{"code":"30","date":"14 May 2016","day":"Sat","high":"64","low":"43","text":"Partly Cloudy"},{"code":"30","date":"15 May 2016","day":"Sun","high":"63","low":"44","text":"Partly Cloudy"},{"code":"12","date":"16 May 2016","day":"Mon","high":"60","low":"47","text":"Rain"},{"code":"12","date":"17 May 2016","day":"Tue","high":"64","low":"48","text":"Rain"},{"code":"28","date":"18 May 2016","day":"Wed","high":"67","low":"48","text":"Mostly Cloudy"},{"code":"12","date":"19 May 2016","day":"Thu","high":"68","low":"49","text":"Rain"},{"code":"39","date":"20 May 2016","day":"Fri","high":"66","low":"50","text":"Scattered Showers"},{"code":"39","date":"21 May 2016","day":"Sat","high":"69","low":"49","text":"Scattered Showers"},{"code":"30","date":"22 May 2016","day":"Sun","high":"73","low":"50","text":"Partly Cloudy"}],"description":"<![CDATA[<img src=\"http://l.yimg.com/a/i/us/we/52/33.gif\"/>\n<BR />\n<b>Current Conditions:</b>\n<BR />Mostly Clear\n<BR />\n<BR />\n<b>Forecast:</b>\n<BR /> Fri - Partly Cloudy. High: 62Low: 41\n<BR /> Sat - Partly Cloudy. High: 64Low: 43\n<BR /> Sun - Partly Cloudy. High: 63Low: 44\n<BR /> Mon - Rain. High: 60Low: 47\n<BR /> Tue - Rain. High: 64Low: 48\n<BR />\n<BR />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2122265/\">Full Forecast at Yahoo! Weather</a>\n<BR />\n<BR />\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)\n<BR />\n]]>","guid":{"isPermaLink":"false"}}}}}}' return r'{"query":{"count":1,"created":"2016-05-13T19:47:01Z","lang":"en-US","results":{"channel":{"units":{"distance":"mi","pressure":"in","speed":"mph","temperature":"C"},"title":"Yahoo! Weather - Moscow, Moscow Federal City, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2122265/","description":"Yahoo! Weather for Moscow, Moscow Federal City, RU","language":"en-us","lastBuildDate":"Fri, 13 May 2016 10:47 PM MSK","ttl":"60","location":{"city":"Moscow","country":"Russia","region":" Moscow Federal City"},"wind":{"chill":"45","direction":"80","speed":"11"},"atmosphere":{"humidity":"52","pressure":"993.0","rising":"0","visibility":"16.1"},"astronomy":{"sunrise":"4:19 am","sunset":"8:34 pm"},"image":{"title":"Yahoo! Weather","width":"142","height":"18","link":"http://weather.yahoo.com","url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"},"item":{"title":"Conditions for Moscow, Moscow Federal City, RU at 09:00 PM MSK","lat":"55.741638","long":"37.605061","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2122265/","pubDate":"Fri, 13 May 2016 09:00 PM MSK","condition":{"code":"33","date":"Fri, 13 May 2016 09:00 PM MSK","temp":"9","text":"Mostly Clear"},"forecast":[{"code":"30","date":"13 May 2016","day":"Fri","high":"62","low":"41","text":"Partly Cloudy"},{"code":"30","date":"14 May 2016","day":"Sat","high":"64","low":"43","text":"Partly Cloudy"},{"code":"30","date":"15 May 2016","day":"Sun","high":"63","low":"44","text":"Partly Cloudy"},{"code":"12","date":"16 May 2016","day":"Mon","high":"60","low":"47","text":"Rain"},{"code":"12","date":"17 May 2016","day":"Tue","high":"64","low":"48","text":"Rain"},{"code":"28","date":"18 May 2016","day":"Wed","high":"67","low":"48","text":"Mostly Cloudy"},{"code":"12","date":"19 May 2016","day":"Thu","high":"68","low":"49","text":"Rain"},{"code":"39","date":"20 May 2016","day":"Fri","high":"66","low":"50","text":"Scattered Showers"},{"code":"39","date":"21 May 2016","day":"Sat","high":"69","low":"49","text":"Scattered Showers"},{"code":"30","date":"22 May 2016","day":"Sun","high":"73","low":"50","text":"Partly Cloudy"}],"description":"<![CDATA[<img src=\"http://l.yimg.com/a/i/us/we/52/33.gif\"/>\n<BR />\n<b>Current Conditions:</b>\n<BR />Mostly Clear\n<BR />\n<BR />\n<b>Forecast:</b>\n<BR /> Fri - Partly Cloudy. High: 62Low: 41\n<BR /> Sat - Partly Cloudy. High: 64Low: 43\n<BR /> Sun - Partly Cloudy. High: 63Low: 44\n<BR /> Mon - Rain. High: 60Low: 47\n<BR /> Tue - Rain. High: 64Low: 48\n<BR />\n<BR />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2122265/\">Full Forecast at Yahoo! Weather</a>\n<BR />\n<BR />\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)\n<BR />\n]]>","guid":{"isPermaLink":"false"}}}}}}'
else: elif query_url.startswith('https://api.openweathermap.org/data/2.5/'):
raise NotImplementedError if 'Meppen' in query_url or '52.6833' in query_url:
return r'{"coord":{"lon":7.29,"lat":52.69},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"base":"stations","main":{"temp":293.15,"feels_like":295.16,"temp_min":293.15,"temp_max":295.37,"pressure":1018,"humidity":77},"visibility":10000,"wind":{"speed":1.12,"deg":126},"clouds":{"all":0},"dt":1600196220,"sys":{"type":1,"id":1871,"country":"DE","sunrise":1600146332,"sunset":1600191996},"timezone":7200,"id":2871845,"name":"Meppen","cod":200}'
elif 'Moscow' in query_url:
return r'{"coord":{"lon":37.62,"lat":55.75},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"base":"stations","main":{"temp":283.15,"feels_like":280.78,"temp_min":283.15,"temp_max":284.26,"pressure":1019,"humidity":71},"visibility":10000,"wind":{"speed":3,"deg":330},"clouds":{"all":0},"dt":1600196224,"sys":{"type":1,"id":9029,"country":"RU","sunrise":1600138909,"sunset":1600184863},"timezone":10800,"id":524901,"name":"Moscow","cod":200}'
raise NotImplementedError
class Process(object): class Process(object):

View File

@ -8,14 +8,16 @@ from tests.modules import TestCase
class TestI3WM(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 @staticmethod
def get_workspaces(): def get_workspaces():
return iter([ return iter(TestI3WM.workspaces)
{'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},
])
@staticmethod @staticmethod
def get_outputs(pl): def get_outputs(pl):
@ -46,42 +48,22 @@ class TestI3WM(TestCase):
({ ({
'a': 1, 'a': 1,
'output': 'LVDS1', 'output': 'LVDS1',
'workspace': { 'workspace': self.workspaces[0],
'name': '1: w1',
'focused': False,
'urgent': False,
'visible': False
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
({ ({
'a': 1, 'a': 1,
'output': 'LVDS1', 'output': 'LVDS1',
'workspace': { 'workspace': self.workspaces[1],
'name': '2: w2',
'focused': False,
'urgent': False,
'visible': True
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
({ ({
'a': 1, 'a': 1,
'output': 'HDMI1', 'output': 'HDMI1',
'workspace': { 'workspace': self.workspaces[2],
'name': '3: w3',
'focused': False,
'urgent': True,
'visible': True
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
({ ({
'a': 1, 'a': 1,
'output': 'DVI01', 'output': 'DVI01',
'workspace': { 'workspace': self.workspaces[3],
'name': '4: w4',
'focused': True,
'urgent': True,
'visible': True
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
] ]
) )
@ -92,22 +74,12 @@ class TestI3WM(TestCase):
({ ({
'a': 1, 'a': 1,
'output': 'LVDS1', 'output': 'LVDS1',
'workspace': { 'workspace': self.workspaces[0],
'name': '1: w1',
'focused': False,
'urgent': False,
'visible': False
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
({ ({
'a': 1, 'a': 1,
'output': 'LVDS1', 'output': 'LVDS1',
'workspace': { 'workspace': self.workspaces[1],
'name': '2: w2',
'focused': False,
'urgent': False,
'visible': True
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
] ]
) )
@ -121,22 +93,12 @@ class TestI3WM(TestCase):
({ ({
'a': 1, 'a': 1,
'output': 'LVDS1', 'output': 'LVDS1',
'workspace': { 'workspace': self.workspaces[0],
'name': '1: w1',
'focused': False,
'urgent': False,
'visible': False
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
({ ({
'a': 1, 'a': 1,
'output': 'LVDS1', 'output': 'LVDS1',
'workspace': { 'workspace': self.workspaces[1],
'name': '2: w2',
'focused': False,
'urgent': False,
'visible': True
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
] ]
) )
@ -151,42 +113,22 @@ class TestI3WM(TestCase):
({ ({
'a': 1, 'a': 1,
'output': 'LVDS1', 'output': 'LVDS1',
'workspace': { 'workspace': self.workspaces[0],
'name': '1: w1',
'focused': False,
'urgent': False,
'visible': False
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
({ ({
'a': 1, 'a': 1,
'output': 'LVDS1', 'output': 'LVDS1',
'workspace': { 'workspace': self.workspaces[1],
'name': '2: w2',
'focused': False,
'urgent': False,
'visible': True
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
({ ({
'a': 1, 'a': 1,
'output': 'HDMI1', 'output': 'HDMI1',
'workspace': { 'workspace': self.workspaces[2],
'name': '3: w3',
'focused': False,
'urgent': True,
'visible': True
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
({ ({
'a': 1, 'a': 1,
'output': 'DVI01', 'output': 'DVI01',
'workspace': { 'workspace': self.workspaces[3],
'name': '4: w4',
'focused': True,
'urgent': True,
'visible': True
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
] ]
) )
@ -201,22 +143,12 @@ class TestI3WM(TestCase):
({ ({
'a': 1, 'a': 1,
'output': 'HDMI1', 'output': 'HDMI1',
'workspace': { 'workspace': self.workspaces[2],
'name': '3: w3',
'focused': False,
'urgent': True,
'visible': True
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
({ ({
'a': 1, 'a': 1,
'output': 'DVI01', 'output': 'DVI01',
'workspace': { 'workspace': self.workspaces[3],
'name': '4: w4',
'focused': True,
'urgent': True,
'visible': True
}
}, {'draw_inner_divider': None}), }, {'draw_inner_divider': None}),
] ]
) )

View File

@ -687,6 +687,10 @@ class TestEnv(TestCommon):
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_conda=True), 'ghi') self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_conda=True), 'ghi')
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True), None) self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True), None)
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True, ignore_conda=True), None) self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True, ignore_conda=True), None)
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignored_names=["aaa"]), "ghi")
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignored_names=["ghi"]), "def")
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignored_names=["def", "ghi"]), "abc")
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignored_names=["abc", "def", "ghi"]), None)
segment_info['environ'].pop('VIRTUAL_ENV') segment_info['environ'].pop('VIRTUAL_ENV')
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info), None) self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info), None)
@ -696,6 +700,7 @@ class TestEnv(TestCommon):
with replace_env('CONDA_DEFAULT_ENV', 'foo') as segment_info: with replace_env('CONDA_DEFAULT_ENV', 'foo') as segment_info:
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info), 'foo') self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info), 'foo')
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignored_names=["foo"]), None)
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_conda=True), None) self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_conda=True), None)
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True), 'foo') self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True), 'foo')
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True, ignore_conda=True), None) self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True, ignore_conda=True), None)
@ -718,6 +723,9 @@ class TestEnv(TestCommon):
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True), None) self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True), None)
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True, ignore_conda=True), None) self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info, ignore_venv=True, ignore_conda=True), None)
with replace_env('VIRTUAL_ENV', '/abc/def/venv') as segment_info:
self.assertEqual(self.module.virtualenv(pl=pl, segment_info=segment_info), 'def')
def test_environment(self): def test_environment(self):
pl = Pl() pl = Pl()
variable = 'FOO' variable = 'FOO'
@ -819,9 +827,11 @@ class TestTime(TestCommon):
def test_date(self): def test_date(self):
pl = Pl() pl = Pl()
with replace_attr(self.module, 'datetime', Args(now=lambda: Args(strftime=lambda fmt: fmt))): with replace_attr(self.module, 'datetime', Args(strptime=lambda timezone, fmt: Args(tzinfo=timezone), now=lambda tz:Args(strftime=lambda fmt: fmt + (tz if tz else '')))):
self.assertEqual(self.module.date(pl=pl), [{'contents': '%Y-%m-%d', 'highlight_groups': ['date'], 'divider_highlight_group': None}]) self.assertEqual(self.module.date(pl=pl), [{'contents': '%Y-%m-%d', 'highlight_groups': ['date'], 'divider_highlight_group': None}])
self.assertEqual(self.module.date(pl=pl, timezone='+0900'), [{'contents': '%Y-%m-%d+0900', 'highlight_groups': ['date'], 'divider_highlight_group': None}])
self.assertEqual(self.module.date(pl=pl, format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_groups': ['time', 'date'], 'divider_highlight_group': 'time:divider'}]) self.assertEqual(self.module.date(pl=pl, format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_groups': ['time', 'date'], 'divider_highlight_group': 'time:divider'}])
self.assertEqual(self.module.date(pl=pl, format='%H:%M', istime=True, timezone='-0900'), [{'contents': '%H:%M-0900', 'highlight_groups': ['time', 'date'], 'divider_highlight_group': 'time:divider'}])
unicode_date = self.module.date(pl=pl, format='\u231a', istime=True) unicode_date = self.module.date(pl=pl, format='\u231a', istime=True)
expected_unicode_date = [{'contents': '\u231a', 'highlight_groups': ['time', 'date'], 'divider_highlight_group': 'time:divider'}] expected_unicode_date = [{'contents': '\u231a', 'highlight_groups': ['time', 'date'], 'divider_highlight_group': 'time:divider'}]
if python_implementation() == 'PyPy' and sys.version_info >= (3,): if python_implementation() == 'PyPy' and sys.version_info >= (3,):
@ -832,23 +842,49 @@ class TestTime(TestCommon):
def test_fuzzy_time(self): def test_fuzzy_time(self):
time = Args(hour=0, minute=45) time = Args(hour=0, minute=45)
pl = Pl() pl = Pl()
with replace_attr(self.module, 'datetime', Args(now=lambda: time)): hour_str = ['12', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven']
minute_str = {'0': 'o\'clock', '5': 'five past', '10': 'ten past','15':
'quarter past','20': 'twenty past', '25': 'twenty-five past',
'30': 'half past', '35': 'twenty-five to','40': 'twenty to', '45':
'quarter to', '50': 'ten to', '55': 'five to'}
special_case_str = {
'(23, 58)': '~ midnight',
'(23, 59)': '~ midnight',
'(0, 0)': 'midnight',
'(0, 1)': '~ midnight',
'(0, 2)': '~ midnight',
'(12, 0)': 'twelve o\'clock'}
with replace_attr(self.module, 'datetime', Args(strptime=lambda timezone, fmt: Args(tzinfo=timezone), now=lambda tz: time)):
self.assertEqual(self.module.fuzzy_time(pl=pl, hour_str=hour_str, minute_str=minute_str, special_case_str=special_case_str), 'quarter to one')
self.assertEqual(self.module.fuzzy_time(pl=pl), 'quarter to one') self.assertEqual(self.module.fuzzy_time(pl=pl), 'quarter to one')
time.hour = 23 time.hour = 23
time.minute = 59 time.minute = 59
self.assertEqual(self.module.fuzzy_time(pl=pl, hour_str=hour_str, minute_str=minute_str, special_case_str=special_case_str), '~ midnight')
self.assertEqual(self.module.fuzzy_time(pl=pl), 'round about midnight') self.assertEqual(self.module.fuzzy_time(pl=pl), 'round about midnight')
time.hour = 11
time.minute = 33 time.minute = 33
self.assertEqual(self.module.fuzzy_time(pl=pl, hour_str=hour_str, minute_str=minute_str, special_case_str=special_case_str),'twenty-five to 12')
self.assertEqual(self.module.fuzzy_time(pl=pl), 'twenty-five to twelve') self.assertEqual(self.module.fuzzy_time(pl=pl), 'twenty-five to twelve')
time.minute = 60 time.hour = 12
self.assertEqual(self.module.fuzzy_time(pl=pl), 'twelve o\'clock') time.minute = 0
self.assertEqual(self.module.fuzzy_time(pl=pl, hour_str=hour_str, minute_str=minute_str, special_case_str=special_case_str), 'twelve o\'clock')
self.assertEqual(self.module.fuzzy_time(pl=pl), 'noon')
time.hour = 11
time.minute = 33 time.minute = 33
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=False, hour_str=hour_str, minute_str=minute_str, special_case_str=special_case_str), 'twenty-five to 12')
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=False), 'twenty-five to twelve') self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=False), 'twenty-five to twelve')
time.minute = 60 time.hour = 12
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=False), 'twelve o\'clock') time.minute = 0
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=False, hour_str=hour_str, minute_str=minute_str, special_case_str=special_case_str), 'twelve o\'clock')
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=False), 'noon')
time.hour = 11
time.minute = 33 time.minute = 33
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=True, hour_str=hour_str, minute_str=minute_str, special_case_str=special_case_str), 'twentyfive to 12')
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=True), 'twentyfive to twelve') self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=True), 'twentyfive to twelve')
time.minute = 60 time.hour = 12
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=True), 'twelve oclock') time.minute = 0
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=True, hour_str=hour_str, minute_str=minute_str, special_case_str=special_case_str), 'twelve oclock')
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=True),'noon')
class TestSys(TestCommon): class TestSys(TestCommon):
@ -857,10 +893,10 @@ class TestSys(TestCommon):
def test_uptime(self): def test_uptime(self):
pl = Pl() pl = Pl()
with replace_attr(self.module, '_get_uptime', lambda: 259200): with replace_attr(self.module, '_get_uptime', lambda: 259200):
self.assertEqual(self.module.uptime(pl=pl), [{'contents': '3d', 'divider_highlight_group': 'background:divider'}]) self.assertEqual(self.module.uptime(pl=pl), [{'contents': '3d 0h 00m', 'divider_highlight_group': 'background:divider'}])
with replace_attr(self.module, '_get_uptime', lambda: 93784): with replace_attr(self.module, '_get_uptime', lambda: 93784):
self.assertEqual(self.module.uptime(pl=pl), [{'contents': '1d 2h 3m', 'divider_highlight_group': 'background:divider'}]) self.assertEqual(self.module.uptime(pl=pl), [{'contents': '1d 2h 03m', 'divider_highlight_group': 'background:divider'}])
self.assertEqual(self.module.uptime(pl=pl, shorten_len=4), [{'contents': '1d 2h 3m 4s', 'divider_highlight_group': 'background:divider'}]) self.assertEqual(self.module.uptime(pl=pl, shorten_len=4), [{'contents': '1d 2h 03m 04s', 'divider_highlight_group': 'background:divider'}])
with replace_attr(self.module, '_get_uptime', lambda: 65536): with replace_attr(self.module, '_get_uptime', lambda: 65536):
self.assertEqual(self.module.uptime(pl=pl), [{'contents': '18h 12m 16s', 'divider_highlight_group': 'background:divider'}]) self.assertEqual(self.module.uptime(pl=pl), [{'contents': '18h 12m 16s', 'divider_highlight_group': 'background:divider'}])
self.assertEqual(self.module.uptime(pl=pl, shorten_len=2), [{'contents': '18h 12m', 'divider_highlight_group': 'background:divider'}]) self.assertEqual(self.module.uptime(pl=pl, shorten_len=2), [{'contents': '18h 12m', 'divider_highlight_group': 'background:divider'}])
@ -919,46 +955,47 @@ class TestWthr(TestCommon):
pl = Pl() pl = Pl()
with replace_attr(self.module, 'urllib_read', urllib_read): with replace_attr(self.module, 'urllib_read', urllib_read):
self.assertEqual(self.module.weather(pl=pl), [ self.assertEqual(self.module.weather(pl=pl), [
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_blustery', 'weather_condition_windy', 'weather_conditions', 'weather'], 'contents': 'WINDY '}, {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_sunny', 'weather_conditions', 'weather'], 'contents': 'SUN '},
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 62.857142857142854} {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '20°C', 'gradient_level': 71.42857142857143}
]) ])
self.assertEqual(self.module.weather(pl=pl, temp_coldest=0, temp_hottest=100), [ self.assertEqual(self.module.weather(pl=pl, temp_coldest=0, temp_hottest=100), [
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_blustery', 'weather_condition_windy', 'weather_conditions', 'weather'], 'contents': 'WINDY '}, {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_sunny', 'weather_conditions', 'weather'], 'contents': 'SUN '},
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 14.0} {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '20°C', 'gradient_level': 20}
]) ])
self.assertEqual(self.module.weather(pl=pl, temp_coldest=-100, temp_hottest=-50), [ self.assertEqual(self.module.weather(pl=pl, temp_coldest=-100, temp_hottest=-50), [
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_blustery', 'weather_condition_windy', 'weather_conditions', 'weather'], 'contents': 'WINDY '}, {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_sunny', 'weather_conditions', 'weather'], 'contents': 'SUN '},
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 100} {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '20°C', 'gradient_level': 100}
]) ])
self.assertEqual(self.module.weather(pl=pl, icons={'blustery': 'o'}), [ self.assertEqual(self.module.weather(pl=pl, icons={'sunny': 'o'}), [
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_blustery', 'weather_condition_windy', 'weather_conditions', 'weather'], 'contents': 'o '}, {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_sunny', 'weather_conditions', 'weather'], 'contents': 'o '},
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 62.857142857142854} {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '20°C', 'gradient_level': 71.42857142857143}
])
self.assertEqual(self.module.weather(pl=pl, icons={'windy': 'x'}), [
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_blustery', 'weather_condition_windy', 'weather_conditions', 'weather'], 'contents': 'x '},
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 62.857142857142854}
]) ])
# Test is disabled as no request has more than 1 weather condition associated currently
# self.assertEqual(self.module.weather(pl=pl, icons={'windy': 'x'}), [
# {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_blustery', 'weather_condition_windy', 'weather_conditions', 'weather'], 'contents': 'x '},
# {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 62.857142857142854}
# ])
self.assertEqual(self.module.weather(pl=pl, unit='F'), [ self.assertEqual(self.module.weather(pl=pl, unit='F'), [
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_blustery', 'weather_condition_windy', 'weather_conditions', 'weather'], 'contents': 'WINDY '}, {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_sunny', 'weather_conditions', 'weather'], 'contents': 'SUN '},
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '57°F', 'gradient_level': 62.857142857142854} {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '68°F', 'gradient_level': 100}
]) ])
self.assertEqual(self.module.weather(pl=pl, unit='K'), [ self.assertEqual(self.module.weather(pl=pl, unit='K'), [
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_blustery', 'weather_condition_windy', 'weather_conditions', 'weather'], 'contents': 'WINDY '}, {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_sunny', 'weather_conditions', 'weather'], 'contents': 'SUN '},
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '287K', 'gradient_level': 62.857142857142854} {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '293K', 'gradient_level': 100}
]) ])
self.assertEqual(self.module.weather(pl=pl, temp_format='{temp:.1e}C'), [ self.assertEqual(self.module.weather(pl=pl, temp_format='{temp:.1e}C'), [
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_blustery', 'weather_condition_windy', 'weather_conditions', 'weather'], 'contents': 'WINDY '}, {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_sunny', 'weather_conditions', 'weather'], 'contents': 'SUN '},
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '1.4e+01C', 'gradient_level': 62.857142857142854} {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '2.0e+01C', 'gradient_level': 71.42857142857143}
]) ])
with replace_attr(self.module, 'urllib_read', urllib_read): with replace_attr(self.module, 'urllib_read', urllib_read):
self.module.weather.startup(pl=pl, location_query='Meppen,06,DE') self.module.weather.startup(pl=pl, location_query='Meppen,06,DE')
self.assertEqual(self.module.weather(pl=pl), [ self.assertEqual(self.module.weather(pl=pl), [
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_blustery', 'weather_condition_windy', 'weather_conditions', 'weather'], 'contents': 'WINDY '}, {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_sunny', 'weather_conditions', 'weather'], 'contents': 'SUN '},
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 62.857142857142854} {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '20°C', 'gradient_level': 71.42857142857143}
]) ])
self.assertEqual(self.module.weather(pl=pl, location_query='Moscow,RU'), [ self.assertEqual(self.module.weather(pl=pl, location_query='Moscow,RU'), [
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_fair_night', 'weather_condition_night', 'weather_conditions', 'weather'], 'contents': 'NIGHT '}, {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_condition_sunny', 'weather_conditions', 'weather'], 'contents': 'SUN '},
{'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '9°C', 'gradient_level': 55.714285714285715} {'divider_highlight_group': 'background:divider', 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '10°C', 'gradient_level': 57.142857142857146}
]) ])
self.module.weather.shutdown() self.module.weather.shutdown()
@ -967,10 +1004,10 @@ class TestI3WM(TestCase):
@staticmethod @staticmethod
def get_workspaces(): def get_workspaces():
return iter([ return iter([
{'name': '1: w1', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': False}, Args(name='1: w1', output='LVDS1', focused = False, urgent = False, visible = False),
{'name': '2: w2', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': True}, Args(name='2: w2', output='LVDS1', focused = False, urgent = False, visible = True),
{'name': '3: w3', 'output': 'HDMI1', 'focused': False, 'urgent': True, 'visible': True}, Args(name='3: w3', output='HDMI1', focused = False, urgent = True, visible = True),
{'name': '4: w4', 'output': 'DVI01', 'focused': True, 'urgent': True, 'visible': True}, Args(name='4: w4', output='DVI01', focused = True, urgent = True, visible = True),
]) ])
def test_workspaces(self): def test_workspaces(self):
@ -1056,7 +1093,7 @@ class TestI3WM(TestCase):
def get_tree(self): def get_tree(self):
return self return self
def descendents(self): def descendants(self):
nodes_unfocused = [Args(focused = False)] nodes_unfocused = [Args(focused = False)]
nodes_focused = [Args(focused = True)] nodes_focused = [Args(focused = True)]

View File

@ -4,6 +4,7 @@ set_theme_option() {
set_theme() { set_theme() {
export POWERLINE_CONFIG_OVERRIDES="ext.shell.theme=$1" 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 set_theme_option default_leftonly.segment_data.hostname.args.only_if_ssh false
ABOVE_LEFT='[{ ABOVE_LEFT='[{
"left": [ "left": [
@ -40,7 +41,9 @@ VIRTUAL_ENV=
bgscript.sh & waitpid.sh bgscript.sh & waitpid.sh
false false
kill `cat pid` ; sleep 1s 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_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 set_theme_option default_leftonly.segment_data.user.display false
echo ' echo '
abc abc
@ -56,14 +59,15 @@ cd ../'$(echo)'
cd ../'`echo`' cd ../'`echo`'
cd ../'«Unicode!»' cd ../'«Unicode!»'
(exit 42)|(exit 43) (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 export DISPLAYED_ENV_VAR=foo
unset DISPLAYED_ENV_VAR 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 export DISPLAYED_ENV_VAR=foo
unset DISPLAYED_ENV_VAR unset DISPLAYED_ENV_VAR
set_theme_option default_leftonly.segments.above set_theme_option default.segments.above
set_theme_option default_leftonly.dividers.left.hard \$ABC set_theme_option default.dividers.left.hard \$ABC
false false
true is the last line true is the last line
exit exit

View File

@ -7,7 +7,9 @@
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  false   HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  false
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  1  kill `cat pid` ; sleep 1s   HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  1  kill `cat pid` ; sleep 1s
[1]+ Terminated bgscript.sh [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   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  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default_leftonly.segment_data.user.display false
  BRANCH  …  tmp  shell  3rd  echo '   BRANCH  …  tmp  shell  3rd  echo '
                                   abc                                    abc
@ -27,16 +29,17 @@ def
  BRANCH  …  shell  3rd  $(echo)  cd ../'`echo`'   BRANCH  …  shell  3rd  $(echo)  cd ../'`echo`'
  BRANCH  …  shell  3rd  `echo`  cd ../'«Unicode!»'   BRANCH  …  shell  3rd  `echo`  cd ../'«Unicode!»'
  BRANCH  …  shell  3rd  «Unicode!»  (exit 42)|(exit 43)   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!»  42  43  set_theme default
  BRANCH  …  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo    shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above "$ABOVE_LEFT"
   shell  3rd  «Unicode!»     BRANCH export DISPLAYED_ENV_VAR=foo
 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 "$ABOVE_FULL"    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                                                                                                                                                                                                                                                                                                         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    shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above
  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.dividers.left.hard \$ABC    shell  3rd  «Unicode!»     BRANCH set_theme_option default.dividers.left.hard \$ABC
  BRANCH $ABC…  shell  3rd  «Unicode!» $ABCfalse  …  shell  3rd  «Unicode!» $ABC   BRANCH false

View File

@ -7,7 +7,9 @@
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  false   HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  false
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  1  kill `cat pid` ; sleep 1s   HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  1  kill `cat pid` ; sleep 1s
[1]+ Terminated bgscript.sh [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   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  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default_leftonly.segment_data.user.display false
  BRANCH  …  tmp  shell  3rd  echo '   BRANCH  …  tmp  shell  3rd  echo '
   abc    abc
@ -27,16 +29,17 @@ def
  BRANCH  …  shell  3rd  $(echo)  cd ../'`echo`'   BRANCH  …  shell  3rd  $(echo)  cd ../'`echo`'
  BRANCH  …  shell  3rd  `echo`  cd ../'«Unicode!»'   BRANCH  …  shell  3rd  `echo`  cd ../'«Unicode!»'
  BRANCH  …  shell  3rd  «Unicode!»  (exit 42)|(exit 43)   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!»  42  43  set_theme default
  BRANCH  …  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo    shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above "$ABOVE_LEFT"
   shell  3rd  «Unicode!»     BRANCH export DISPLAYED_ENV_VAR=foo
 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 "$ABOVE_FULL"    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                                                                                                                                                                                                                                                                                                         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    shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above
  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.dividers.left.hard \$ABC    shell  3rd  «Unicode!»     BRANCH set_theme_option default.dividers.left.hard \$ABC
  BRANCH $ABC…  shell  3rd  «Unicode!» $ABCfalse  …  shell  3rd  «Unicode!» $ABC   BRANCH false