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
python: "3.6"
- stage: Intermediate versions
python: "3.3"
python: "3.5"
- python: "3.4"
- python: "3.5"
# vim: et

View File

@ -1,24 +1,24 @@
Powerline
=========
: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
:target: `travis-build-status`_
:alt: Build status
.. _travis-build-status: https://travis-ci.org/powerline/powerline
**Powerline is a statusline plugin for vim, and provides statuslines and
prompts for several other applications, including zsh, bash, fish, tmux,
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
: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
**Powerline does not support python2 anymore and powerline will stop working with python2 in the near future.**
Features
--------
@ -29,8 +29,7 @@ Features
config files, and a structured, object-oriented codebase with no mandatory
third-party dependencies other than a Python interpreter.
* **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
Python 3.
of all the project code. The code is tested to work in Python 3.6+.
* **Support for prompts and statuslines in many applications.** Originally
created exclusively for vim statuslines, the project has evolved to
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/`.
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:
::

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
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
have to update the segment arguments. Open a theme file, scroll down to
the weather segment and update it to include unit/location query
arguments:
the weather segment and update it to include unit, location query or
api key arguments:
.. code-block:: javascript
@ -129,7 +133,8 @@ segments that you may want to customize right away:
"priority": 50,
"args": {
"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.
``workspace``
dictionary containing the workspace name under the key ``"name"`` and
boolean values for the ``"visible"``, ``"urgent"`` and ``"focused"``
keys, indicating the state of the workspace. Currently only provided by
the :py:func:`powerline.listers.i3wm.workspace_lister` lister.
the `i3-ipc` workspace object corresponding to this workspace.
Contains string attributes ``name`` and ``output``, as well as boolean
attributes for ``visible``, ``urgent`` and ``focused``. Currently only
provided by the :py:func:`powerline.listers.i3wm.workspace_lister` lister.
Segment class
=============

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
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>`_).
Running the binding in i3-mode will require `i3ipc <https://github.com/acrisci/i3ipc-python>`_.
See the `lemonbar documentation <https://github.com/LemonBoy/bar>`_ for more
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-activity-style "$_POWERLINE_ACTIVITY_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

View File

@ -1,3 +1,3 @@
# 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"

View File

@ -26,14 +26,14 @@ if exists('g:powerline_pycmd')
let s:pyeval = g:powerline_pyeval
let s:has_python = 1
endif
elseif has('python')
let s:has_python = 1
let s:pycmd = 'py'
let s:pyeval = get(g:, 'powerline_pyeval', 'pyeval')
elseif has('python3')
let s:has_python = 1
let s:pycmd = 'py3'
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
let s:has_python = 0
endif

View File

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

View File

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

View File

@ -179,19 +179,19 @@ except ImportError:
return readlines(('git',) + args, directory)
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):
if path:
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:
return None
else:
wt_column = ' '
index_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] == '?':
untracked_column = 'U'
continue

View File

@ -163,7 +163,7 @@ class Spec(object):
'''Define message which will be used when unknown key was found
Unknown is a key that was not provided at the initialization and via
:py:meth:`Spec.update` and did not match any ``keyfunc`` proided via
:py:meth:`Spec.update` and did not match any ``keyfunc`` provided via
:py:meth:`Spec.unknown_spec`.
:param msgfunc:

View File

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

View File

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

View File

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

View File

@ -90,7 +90,7 @@ class IPythonPygmentsRenderer(IPythonRenderer):
def hl_join(segments):
return reduce(operator.iadd, segments, [])
def hl(self, contents, fg=None, bg=None, attrs=None):
def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs):
'''Output highlighted chunk.
This implementation outputs a list containing a single pair

View File

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

View File

@ -15,7 +15,7 @@ class PangoMarkupRenderer(Renderer):
# We dont need to explicitly reset attributes, so skip those calls
return ''
def hl(self, contents, fg=None, bg=None, attrs=None):
def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs):
'''Highlight a segment.'''
awesome_attr = []
if fg is not None:

View File

@ -105,7 +105,7 @@ class ShellRenderer(PromptRenderer):
self.used_term_escape_style = self.term_escape_style
return super(ShellRenderer, self).do_render(segment_info=segment_info, **kwargs)
def hlstyle(self, fg=None, bg=None, attrs=None):
def hlstyle(self, fg=None, bg=None, attrs=None, escape=True, **kwargs):
'''Highlight a segment.
If an argument is None, the argument is ignored. If an argument is
@ -162,7 +162,7 @@ class ShellRenderer(PromptRenderer):
r = '\033Ptmux;' + r.replace('\033', '\033\033') + '\033\\'
elif self.screen_escape:
r = '\033P' + r.replace('\033', '\033\033') + '\033\\'
return self.escape_hl_start + r + self.escape_hl_end
return self.escape_hl_start + r + self.escape_hl_end if escape else r
def get_theme(self, matcher_info):
if not matcher_info:

View File

@ -6,13 +6,91 @@ from powerline.renderers.shell import ShellRenderer
class BashPromptRenderer(ShellRenderer):
'''Powerline bash prompt segment renderer.'''
escape_hl_start = '\['
escape_hl_end = '\]'
escape_hl_start = '\\['
escape_hl_end = '\\]'
character_translations = ShellRenderer.character_translations.copy()
character_translations[ord('$')] = '\\$'
character_translations[ord('`')] = '\\`'
character_translations[ord('\\')] = '\\\\'
def do_render(self, side, line, width, output_width, output_raw, hl_args, **kwargs):
# we are rendering the normal left prompt
if side == 'left' and line == 0 and width is not None:
# we need left prompt's width to render the raw spacer
output_width = output_width or output_raw
left = super(BashPromptRenderer, self).do_render(
side=side,
line=line,
output_width=output_width,
width=width,
output_raw=output_raw,
hl_args=hl_args,
**kwargs
)
left_rendered = left[0] if output_width else left
# we don't escape color sequences in the right prompt so we can do escaping as a whole
if hl_args:
hl_args = hl_args.copy()
hl_args.update({'escape': False})
else:
hl_args = {'escape': False}
right = super(BashPromptRenderer, self).do_render(
side='right',
line=line,
output_width=True,
width=width,
output_raw=output_raw,
hl_args=hl_args,
**kwargs
)
ret = []
if right[-1] > 0:
# if the right prompt is not empty we embed it in the left prompt
# it must be escaped as a whole so readline doesn't see it
ret.append(''.join((
left_rendered,
self.escape_hl_start,
'\033[s', # save the cursor position
'\033[{0}C'.format(width), # move to the right edge of the terminal
'\033[{0}D'.format(right[-1] - 1), # move back to the right prompt position
right[0],
'\033[u', # restore the cursor position
self.escape_hl_end
)))
if output_raw:
ret.append(''.join((
left[1],
' ' * (width - left[-1] - right[-1]),
right[1]
)))
else:
ret.append(left_rendered)
if output_raw:
ret.append(left[1])
if output_width:
ret.append(left[-1])
if len(ret) == 1:
return ret[0]
else:
return ret
else:
return super(BashPromptRenderer, self).do_render(
side=side,
line=line,
width=width,
output_width=output_width,
output_raw=output_raw,
hl_args=hl_args,
**kwargs
)
renderer = BashPromptRenderer

View File

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

View File

@ -123,7 +123,7 @@ class VimRenderer(Renderer):
def reset_highlight(self):
self.hl_groups.clear()
def hlstyle(self, fg=None, bg=None, attrs=None):
def hlstyle(self, fg=None, bg=None, attrs=None, **kwargs):
'''Highlight a segment.
If an argument is None, the argument is ignored. If an argument is

View File

@ -19,20 +19,24 @@ def environment(pl, segment_info, variable=None):
@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.
: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:
Whether to ignore virtual environments. Default is False.
:param bool ignore_conda:
Whether to ignore conda environments. Default is False.
'''
return (
(not ignore_venv and
os.path.basename(segment_info['environ'].get('VIRTUAL_ENV', ''))) or
(not ignore_conda and
segment_info['environ'].get('CONDA_DEFAULT_ENV', '')) or
None)
if not ignore_venv:
for candidate in reversed(segment_info['environ'].get('VIRTUAL_ENV', '').split("/")):
if candidate and candidate not in ignored_names:
return candidate
if not ignore_conda:
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

View File

@ -31,6 +31,8 @@ def _convert_state(state):
def _convert_seconds(seconds):
'''Convert seconds to minutes:seconds format'''
if isinstance(seconds, str):
seconds = seconds.replace(",",".")
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))
def render(self, cpu_percent, format='{0:.0f}%', **kwargs):
if not cpu_percent:
return None
return [{
'contents': format.format(cpu_percent),
'gradient_level': cpu_percent,
@ -150,7 +148,8 @@ else:
@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.
: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)
days, hours = divmod(hours, 24)
time_formatted = list(filter(None, [
days_format.format(days=days) if days and days_format else None,
hours_format.format(hours=hours) if hours and hours_format else None,
minutes_format.format(minutes=minutes) if minutes and minutes_format else None,
seconds_format.format(seconds=seconds) if seconds and seconds_format else None,
]))[0:shorten_len]
days_format.format(days=days) if days_format else None,
hours_format.format(hours=hours) if hours_format else None,
minutes_format.format(minutes=minutes) if minutes_format else None,
seconds_format.format(seconds=seconds) if seconds_format else None,
]))
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()

View File

@ -4,22 +4,33 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
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.
:param str format:
strftime-style date format string
:param bool istime:
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``.
Highlight groups used: ``time`` or ``date``.
'''
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:
contents = datetime.now().strftime(format.encode('utf-8')).decode('utf-8')
contents = nw.strftime(format.encode('utf-8')).decode('utf-8')
return [{
'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".
:param string format:
Format used to display the fuzzy time. (Ignored when a special time
is displayed.)
: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.
'''
hour_str = ['twelve', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven']
minute_str = {
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',
}
:param string timezone:
Specify a timezone to use as ``+HHMM`` or ``-HHMM``.
(Defaults to system defaults.)
:param string list hour_str:
Strings to be used to display the hour, starting with midnight.
(This list may contain 12 or 24 entries.)
:param dict minute_str:
Dictionary mapping minutes to strings to be used to display them.
:param dict special_case_str:
Special strings for special times.
now = datetime.now()
Highlight groups used: ``fuzzy_time``.
'''
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:
pass
hour = now.hour
if now.minute > 32:
if hour == 23:
hour = 0
else:
hour += 1
if hour > 11:
hour = hour - 12
hour = hour_str[hour]
if now.minute >= 30:
hour = hour + 1
hour = hour % len(hour_str)
minute = int(round(now.minute / 5.0) * 5)
if minute == 60 or minute == 0:
result = ' '.join([hour, 'o\'clock'])
else:
minute = minute_str[minute]
result = ' '.join([minute, hour])
min_dis = 100
min_pos = 0
for mn in minute_str:
mn = int(mn)
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:
result = result.translate(UNICODE_TEXT_TRANSLATION)

View File

@ -2,76 +2,80 @@
from __future__ import (unicode_literals, division, absolute_import, print_function)
import json
from collections import namedtuple
from powerline.lib.url import urllib_read, urllib_urlencode
from powerline.lib.threaded import KwThreadedSegment
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
# segment is imported into powerline.segments.common module.
# Weather condition code descriptions available at
# http://developer.yahoo.com/weather/#codes
weather_conditions_codes = (
('tornado', 'stormy'), # 0
('tropical_storm', 'stormy'), # 1
('hurricane', 'stormy'), # 2
('severe_thunderstorms', 'stormy'), # 3
('thunderstorms', 'stormy'), # 4
('mixed_rain_and_snow', 'rainy' ), # 5
('mixed_rain_and_sleet', 'rainy' ), # 6
('mixed_snow_and_sleet', 'snowy' ), # 7
('freezing_drizzle', 'rainy' ), # 8
('drizzle', 'rainy' ), # 9
('freezing_rain', 'rainy' ), # 10
('showers', 'rainy' ), # 11
('showers', 'rainy' ), # 12
('snow_flurries', 'snowy' ), # 13
('light_snow_showers', 'snowy' ), # 14
('blowing_snow', 'snowy' ), # 15
('snow', 'snowy' ), # 16
('hail', 'snowy' ), # 17
('sleet', 'snowy' ), # 18
('dust', 'foggy' ), # 19
('fog', 'foggy' ), # 20
('haze', 'foggy' ), # 21
('smoky', 'foggy' ), # 22
('blustery', 'windy' ), # 23
('windy', ), # 24
('cold', 'day' ), # 25
('clouds', 'cloudy'), # 26
('mostly_cloudy_night', 'cloudy'), # 27
('mostly_cloudy_day', 'cloudy'), # 28
('partly_cloudy_night', 'cloudy'), # 29
('partly_cloudy_day', 'cloudy'), # 30
('clear_night', 'night' ), # 31
('sun', 'sunny' ), # 32
('fair_night', 'night' ), # 33
('fair_day', 'day' ), # 34
('mixed_rain_and_hail', 'rainy' ), # 35
('hot', 'sunny' ), # 36
('isolated_thunderstorms', 'stormy'), # 37
('scattered_thunderstorms', 'stormy'), # 38
('scattered_thunderstorms', 'stormy'), # 39
('scattered_showers', 'rainy' ), # 40
('heavy_snow', 'snowy' ), # 41
('scattered_snow_showers', 'snowy' ), # 42
('heavy_snow', 'snowy' ), # 43
('partly_cloudy', 'cloudy'), # 44
('thundershowers', 'rainy' ), # 45
('snow_showers', 'snowy' ), # 46
('isolated_thundershowers', 'rainy' ), # 47
)
# ('day', (25, 34)),
# ('rainy', (5, 6, 8, 9, 10, 11, 12, 35, 40, 45, 47)),
# ('cloudy', (26, 27, 28, 29, 30, 44)),
# ('snowy', (7, 13, 14, 15, 16, 17, 18, 41, 42, 43, 46)),
# ('stormy', (0, 1, 2, 3, 4, 37, 38, 39)),
# ('foggy', (19, 20, 21, 22, 23)),
# ('sunny', (32, 36)),
# ('night', (31, 33))):
# https://openweathermap.org/weather-conditions
weather_conditions_codes = {
200: ('stormy',),
201: ('stormy',),
202: ('stormy',),
210: ('stormy',),
211: ('stormy',),
212: ('stormy',),
221: ('stormy',),
230: ('stormy',),
231: ('stormy',),
232: ('stormy',),
300: ('rainy',),
301: ('rainy',),
302: ('rainy',),
310: ('rainy',),
311: ('rainy',),
312: ('rainy',),
313: ('rainy',),
314: ('rainy',),
321: ('rainy',),
500: ('rainy',),
501: ('rainy',),
502: ('rainy',),
503: ('rainy',),
504: ('rainy',),
511: ('snowy',),
520: ('rainy',),
521: ('rainy',),
522: ('rainy',),
531: ('rainy',),
600: ('snowy',),
601: ('snowy',),
602: ('snowy',),
611: ('snowy',),
612: ('snowy',),
613: ('snowy',),
615: ('snowy',),
616: ('snowy',),
620: ('snowy',),
621: ('snowy',),
622: ('snowy',),
701: ('foggy',),
711: ('foggy',),
721: ('foggy',),
731: ('foggy',),
741: ('foggy',),
751: ('foggy',),
761: ('foggy',),
762: ('foggy',),
771: ('foggy',),
781: ('foggy',),
800: ('sunny',),
801: ('cloudy',),
802: ('cloudy',),
803: ('cloudy',),
804: ('cloudy',),
}
weather_conditions_icons = {
'day': 'DAY',
'blustery': 'WIND',
@ -88,9 +92,9 @@ weather_conditions_icons = {
}
temp_conversions = {
'C': lambda temp: temp,
'F': lambda temp: (temp * 9 / 5) + 32,
'K': lambda temp: temp + 273.15,
'C': lambda temp: temp - 273.15,
'F': lambda temp: (temp * 9 / 5) - 459.67,
'K': lambda temp: temp,
}
# Note: there are also unicode characters for units: ℃, ℉ and
@ -105,38 +109,37 @@ class WeatherSegment(KwThreadedSegment):
interval = 600
default_location = None
location_urls = {}
weather_api_key = "fbc9549d91a5e4b26c15be0dbdac3460"
@staticmethod
def key(location_query=None, **kwargs):
return location_query
def get_request_url(self, location_query):
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:
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 = {
'q':
'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',
"appid": weather_key.weather_api_key
}
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 = (
'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
def compute_state(self, location_query):
url = self.get_request_url(location_query)
def compute_state(self, weather_key):
url = self.get_request_url(weather_key)
raw_response = urllib_read(url)
if not raw_response:
self.error('Failed to get response')
@ -144,22 +147,18 @@ class WeatherSegment(KwThreadedSegment):
response = json.loads(raw_response)
try:
condition = response['query']['results']['channel']['item']['condition']
condition_code = int(condition['code'])
temp = float(condition['temp'])
condition = response['weather'][0]
condition_code = int(condition['id'])
temp = float(response['main']['temp'])
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
try:
icon_names = weather_conditions_codes[condition_code]
except IndexError:
if condition_code == 3200:
icon_names = ('not_available',)
self.warn('Weather is not available for location {0}', self.location)
else:
icon_names = ('unknown',)
self.error('Unknown condition code: {0}', condition_code)
icon_names = ('unknown',)
self.error('Unknown condition code: {0}', condition_code)
return (temp, icon_names)
@ -179,12 +178,12 @@ class WeatherSegment(KwThreadedSegment):
temp_format = temp_format or ('{temp:.0f}' + temp_units[unit])
converted_temp = temp_conversions[unit](temp)
if temp <= temp_coldest:
if converted_temp <= temp_coldest:
gradient_level = 0
elif temp >= temp_hottest:
elif converted_temp >= temp_hottest:
gradient_level = 100
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']
return [
{
@ -202,9 +201,9 @@ class WeatherSegment(KwThreadedSegment):
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
IP address is registered at another location.
@ -231,5 +230,5 @@ weather conditions.
Divider highlight group used: ``background:divider``.
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):
group = []
if w['focused']:
if w.focused:
group.append('w_focused')
if w['urgent']:
if w.urgent:
group.append('w_urgent')
if w['visible']:
if w.visible:
group.append('w_visible')
group.append('workspace')
return group
@ -52,12 +52,12 @@ def workspaces(pl, segment_info, only_show=None, output=None, strip=0):
return [
{
'contents': w['name'][strip:],
'contents': w.name[strip:],
'highlight_groups': workspace_groups(w)
}
for w in get_i3_connection().get_workspaces()
if ((not only_show or any(w[typ] for typ in only_show))
and (not output or w['output'] == output))
if ((not only_show or any(getattr(w, typ) for typ in only_show))
and (not output or w.output == output))
]
@ -80,7 +80,7 @@ def workspace(pl, segment_info, workspace=None, strip=False):
try:
w = next((
w for w in get_i3_connection().get_workspaces()
if w['name'] == workspace
if w.name == workspace
))
except StopIteration:
return None
@ -90,13 +90,13 @@ def workspace(pl, segment_info, workspace=None, strip=False):
try:
w = next((
w for w in get_i3_connection().get_workspaces()
if w['focused']
if w.focused
))
except StopIteration:
return None
return [{
'contents': format_name(w['name'], strip=strip),
'contents': format_name(w.name, strip=strip),
'highlight_groups': workspace_groups(w)
}]
@ -150,6 +150,6 @@ def scratchpad(pl, icons=SCRATCHPAD_ICONS):
'contents': icons.get(w.scratchpad_state, icons['changed']),
'highlight_groups': scratchpad_groups(w)
}
for w in get_i3_connection().get_tree().descendents()
for w in get_i3_connection().get_tree().descendants()
if w.scratchpad_state != 'none'
]

View File

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

View File

@ -262,6 +262,7 @@ class VimPowerline(Powerline):
def new_win_idx(self, window_id):
r = None
for window in vim.windows:
try:
curwindow_id = window.vars['powerline_window_id']
@ -302,7 +303,18 @@ class VimPowerline(Powerline):
return self.render(window, window_id, winnr)
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):
return self.render(*self.win_idx(None))

View File

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

View File

@ -44,15 +44,23 @@ def urllib_read(query_url):
return '127.0.0.1'
elif query_url.startswith('http://ipv6.icanhazip.com'):
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/'):
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/'):
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"}}}}}}'
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"}}}}}}'
else:
raise NotImplementedError
elif query_url.startswith('https://api.openweathermap.org/data/2.5/'):
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):

View File

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

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_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, 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')
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:
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_venv=True), 'foo')
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, 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):
pl = Pl()
variable = 'FOO'
@ -819,9 +827,11 @@ class TestTime(TestCommon):
def test_date(self):
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, 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, 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)
expected_unicode_date = [{'contents': '\u231a', 'highlight_groups': ['time', 'date'], 'divider_highlight_group': 'time:divider'}]
if python_implementation() == 'PyPy' and sys.version_info >= (3,):
@ -832,23 +842,49 @@ class TestTime(TestCommon):
def test_fuzzy_time(self):
time = Args(hour=0, minute=45)
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')
time.hour = 23
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')
time.hour = 11
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')
time.minute = 60
self.assertEqual(self.module.fuzzy_time(pl=pl), 'twelve o\'clock')
time.hour = 12
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
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')
time.minute = 60
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=False), 'twelve o\'clock')
time.hour = 12
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
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')
time.minute = 60
self.assertEqual(self.module.fuzzy_time(pl=pl, unicode_text=True), 'twelve oclock')
time.hour = 12
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):
@ -857,10 +893,10 @@ class TestSys(TestCommon):
def test_uptime(self):
pl = Pl()
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):
self.assertEqual(self.module.uptime(pl=pl), [{'contents': '1d 2h 3m', '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), [{'contents': '1d 2h 03m', '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):
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'}])
@ -919,46 +955,47 @@ class TestWthr(TestCommon):
pl = Pl()
with replace_attr(self.module, 'urllib_read', urllib_read):
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_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 62.857142857142854}
{'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': '20°C', 'gradient_level': 71.42857142857143}
])
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_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 14.0}
{'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': '20°C', 'gradient_level': 20}
])
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_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 100}
{'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': '20°C', 'gradient_level': 100}
])
self.assertEqual(self.module.weather(pl=pl, icons={'blustery': '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_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 62.857142857142854}
])
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, icons={'sunny': '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': '20°C', 'gradient_level': 71.42857142857143}
])
# 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'), [
{'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_temp_gradient', 'weather_temp', 'weather'], 'contents': '57°F', 'gradient_level': 62.857142857142854}
{'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': '68°F', 'gradient_level': 100}
])
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_temp_gradient', 'weather_temp', 'weather'], 'contents': '287K', 'gradient_level': 62.857142857142854}
{'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': '293K', 'gradient_level': 100}
])
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_temp_gradient', 'weather_temp', 'weather'], 'contents': '1.4e+01C', 'gradient_level': 62.857142857142854}
{'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': '2.0e+01C', 'gradient_level': 71.42857142857143}
])
with replace_attr(self.module, 'urllib_read', urllib_read):
self.module.weather.startup(pl=pl, location_query='Meppen,06,DE')
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_temp_gradient', 'weather_temp', 'weather'], 'contents': '14°C', 'gradient_level': 62.857142857142854}
{'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': '20°C', 'gradient_level': 71.42857142857143}
])
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_temp_gradient', 'weather_temp', 'weather'], 'contents': '9°C', 'gradient_level': 55.714285714285715}
{'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': '10°C', 'gradient_level': 57.142857142857146}
])
self.module.weather.shutdown()
@ -967,10 +1004,10 @@ class TestI3WM(TestCase):
@staticmethod
def get_workspaces():
return iter([
{'name': '1: w1', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': False},
{'name': '2: w2', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': True},
{'name': '3: w3', 'output': 'HDMI1', 'focused': False, 'urgent': True, 'visible': True},
{'name': '4: w4', 'output': 'DVI01', 'focused': True, 'urgent': True, 'visible': True},
Args(name='1: w1', output='LVDS1', focused = False, urgent = False, visible = False),
Args(name='2: w2', output='LVDS1', focused = False, urgent = False, visible = True),
Args(name='3: w3', output='HDMI1', focused = False, urgent = True, visible = True),
Args(name='4: w4', output='DVI01', focused = True, urgent = True, visible = True),
])
def test_workspaces(self):
@ -1056,7 +1093,7 @@ class TestI3WM(TestCase):
def get_tree(self):
return self
def descendents(self):
def descendants(self):
nodes_unfocused = [Args(focused = False)]
nodes_focused = [Args(focused = True)]

View File

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

View File

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

View File

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