Merge remote-tracking branch 'zyx-i/pl-argument' into develop

This commit is contained in:
Kim Silkebækken 2013-03-24 16:52:44 +01:00
commit 5cebad9428
22 changed files with 705 additions and 387 deletions

@ -155,6 +155,17 @@ Common configuration is a subdictionary that is a value of ``common`` key in
:ref:`module segment option <config-themes-seg-module>`. Paths defined here
have priority when searching for modules.
``log_file``
Defines path which will hold powerline logs. If not present, logging will be
done to stderr.
``log_level``
String, determines logging level. Defaults to ``WARNING``.
``log_format``
String, determines format of the log messages. Defaults to
``'%(asctime)s:%(level)s:%(message)s'``.
Extension-specific configuration
--------------------------------

@ -28,6 +28,9 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter):
if isinstance(self.object, ThreadedSegment):
args = ['interval']
defaults = [getattr(self.object, 'interval', 1)]
if self.object.update_first:
args.append('update_first')
defaults.append(True)
methods = ['render', 'set_state']
if isinstance(self.object, KwThreadedSegment):
methods += ['key', 'render_one']
@ -41,7 +44,8 @@ class ThreadedDocumenter(autodoc.FunctionDocumenter):
if (arg == 'self' or
(arg == 'segment_info' and
getattr(self.object, 'powerline_requires_segment_info', None)) or
(method == 'render_one' and -i == len(argspec.args))):
(method == 'render_one' and -i == len(argspec.args)) or
arg in args):
continue
if argspec.defaults and len(argspec.defaults) >= -i:
default = argspec.defaults[i]

@ -1 +1 @@
EBUILD powerline-9999.ebuild 3916 SHA256 754f8750aa5c6a455871d44c9478b81278b71318e591a13ef30f763ddb1fbda5 SHA512 48dd9d3ac737417b6a072397a4a50fefc04c0cb4a3c6249b5bf11ee201986f7b25a94b83e6901e8099ae3ea733dc0e650f5ce2234b5143bdbafd906af56916e7 WHIRLPOOL 803298b97adaeb2e3a6cd31c4bd12897373d7ce0722df22a991c684ecd5146fcd87addfc6e428374d924d6cb950dee6bbcf2ea0a262904cc4d04e3eac0b2dcb8
EBUILD powerline-9999.ebuild 4091 SHA256 d1d13b09e3ebefdaa1b90c50ccceb563bb444ca0f484e6448a993d2612dfbb78 SHA512 47f211249bb85608cb9c5b8e72daba5c36971e294e27843396cbf517bc792db64a4f80e843f5c883f8411ed4c9cef5618f9d617b305303a31a4590636879dcd3 WHIRLPOOL 2fc50e1da46d56d160140d3a87a4d9421bf63d67c792bfd88486e0dc72d379045ed79d152aa060a014e7f6bf4eb232642463014de9523909946bd8b2cbf83371

@ -130,6 +130,10 @@ src_install() {
fi
rm powerline/bindings/bash/powerline.sh
elog ""
insinto /etc/xdg/powerline
doins -r powerline/config_files/*
rm -r powerline/config_files
sed -i -e "/DEFAULT_SYSTEM_CONFIG_DIR/ s@None@'/etc/xdg'@" powerline/__init__.py
distutils-r1_src_install
use doc && dohtml -r docs_output/*
}

@ -4,10 +4,14 @@ from __future__ import absolute_import
import json
import os
import sys
import logging
from powerline.colorscheme import Colorscheme
DEFAULT_SYSTEM_CONFIG_DIR = None
def open_file(path):
return open(path, 'r')
@ -22,6 +26,42 @@ def load_json_config(search_paths, config_file, load=json.load, open_file=open_f
raise IOError('Config file not found in search path: {0}'.format(config_file))
class PowerlineState(object):
def __init__(self, logger, environ, getcwd, home):
self.environ = environ
self.getcwd = getcwd
self.home = home or environ.get('HOME', None)
self.logger = logger
self.prefix = None
self.last_msgs = {}
def _log(self, attr, msg, *args, **kwargs):
prefix = kwargs.get('prefix') or self.prefix
msg = ((prefix + ':') if prefix else '') + msg.format(*args, **kwargs)
key = attr+':'+prefix
if msg != self.last_msgs.get(key):
getattr(self.logger, attr)(msg)
self.last_msgs[key] = msg
def critical(self, msg, *args, **kwargs):
self._log('critical', msg, *args, **kwargs)
def exception(self, msg, *args, **kwargs):
self._log('exception', msg, *args, **kwargs)
def info(self, msg, *args, **kwargs):
self._log('info', msg, *args, **kwargs)
def error(self, msg, *args, **kwargs):
self._log('error', msg, *args, **kwargs)
def warn(self, msg, *args, **kwargs):
self._log('warning', msg, *args, **kwargs)
def debug(self, msg, *args, **kwargs):
self._log('debug', msg, *args, **kwargs)
class Powerline(object):
'''Main powerline class, entrance point for all powerline uses. Sets
powerline up and loads the configuration.
@ -34,9 +74,29 @@ class Powerline(object):
:param str renderer_module:
Overrides renderer module (defaults to ``ext``). Should be the name of
the package imported like this: ``powerline.renders.{render_module}``.
:param bool run_once:
Determines whether .renderer.render() method will be run only once
during python session.
:param Logger logger:
If present, no new logger will be created and this logger will be used.
:param dict environ:
Object with ``.__getitem__`` and ``.get`` methods used to obtain
environment variables. Defaults to ``os.environ``.
:param func getcwd:
Function used to get current working directory. Defaults to
``os.getcwdu`` or ``os.getcwd``.
:param str home:
Home directory. Defaults to ``environ.get('HOME')``.
'''
def __init__(self, ext, renderer_module=None, run_once=False):
def __init__(self,
ext,
renderer_module=None,
run_once=False,
logger=None,
environ=os.environ,
getcwd=getattr(os, 'getcwdu', os.getcwd),
home=None):
self.config_paths = self.get_config_paths()
# Load main config file
@ -75,7 +135,40 @@ class Powerline(object):
'tmux_escape': common_config.get('additional_escapes') == 'tmux',
'screen_escape': common_config.get('additional_escapes') == 'screen',
}
self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, **options)
# Create logger
if not logger:
log_format = common_config.get('format', '%(asctime)s:%(level)s:%(message)s')
formatter = logging.Formatter(log_format)
level = getattr(logging, common_config.get('log_level', 'WARNING'))
handler = self.get_log_handler(common_config)
handler.setLevel(level)
logger = logging.getLogger('powerline')
logger.setLevel(level)
logger.addHandler(handler)
pl = PowerlineState(logger=logger, environ=environ, getcwd=getcwd, home=home)
self.renderer = Renderer(theme_config, local_themes, theme_kwargs, colorscheme, pl, **options)
def get_log_handler(self, common_config):
'''Get log handler.
:param dict common_config:
Common configuration.
:return: logging.Handler subclass.
'''
log_file = common_config.get('file', None)
if log_file:
log_dir = os.path.dirname(log_file)
if not os.path.isdir(log_dir):
os.mkdir(log_dir)
return logging.FileHandler(log_file)
else:
return logging.StreamHandler()
@staticmethod
def get_config_paths():
@ -86,7 +179,7 @@ class Powerline(object):
config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config'))
config_path = os.path.join(config_home, 'powerline')
config_paths = [config_path]
config_dirs = os.environ.get('XDG_CONFIG_DIRS', None)
config_dirs = os.environ.get('XDG_CONFIG_DIRS', DEFAULT_SYSTEM_CONFIG_DIR)
if config_dirs is not None:
config_paths.extend([os.path.join(d, 'powerline') for d in config_dirs.split(':')])
plugin_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'config_files')

@ -21,27 +21,24 @@ let s:powerline_pyeval = get(g:, 'powerline_pyeval', s:powerline_pycmd.'eval')
let s:import_cmd = 'from powerline.vim import VimPowerline'
try
exec s:powerline_pycmd s:import_cmd
catch
" An error occurred while importing the module, it could be installed
" outside of Python's module search paths. Update sys.path and try again.
exec s:powerline_pycmd 'import sys, vim'
exec s:powerline_pycmd 'sys.path.append(vim.eval(''expand("<sfile>:h:h:h:h:h")''))'
try
exec s:powerline_pycmd s:import_cmd
let s:launched = 1
finally
if !exists('s:launched')
call s:CriticalError('An error occurred while importing the Powerline package.
\ This could be caused by an invalid sys.path setting, or by an incompatible
\ Python version (Powerline requires Python 2.6+ or 3.2+ to work). Please consult
\ the troubleshooting section in the documentation for possible solutions.')
finish
endif
endtry
exec s:powerline_pycmd "try:\n"
\ ." ".s:import_cmd."\n"
\ ."except ImportError:\n"
\ ." import sys, vim\n"
\ ." sys.path.append(vim.eval('expand(\"<sfile>:h:h:h:h:h\")'))\n"
\ ." ".s:import_cmd
let s:launched = 1
finally
if !exists('s:launched')
call s:CriticalError('An error occurred while importing the Powerline package.
\ This could be caused by an invalid sys.path setting, or by an incompatible
\ Python version (Powerline requires Python 2.6+ or 3.2+ to work). Please consult
\ the troubleshooting section in the documentation for possible solutions.')
finish
else
unlet s:launched
endif
endtry
exec s:powerline_pycmd 'powerline = VimPowerline()'
exec s:powerline_pycmd 'del VimPowerline'
if !get(g:, 'powerline_debugging_pyeval') && exists('*'. s:powerline_pyeval)
let s:pyeval = function(s:powerline_pyeval)
@ -80,18 +77,19 @@ endfunction
function! PowerlineRegisterCachePurgerEvent(event)
exec s:powerline_pycmd 'from powerline.segments.vim import launchevent as powerline_launchevent'
augroup Powerline
exec 'autocmd!' a:event '*' s:powerline_pycmd.' powerline_launchevent("'.a:event.'")'
exec 'autocmd' a:event '*' s:powerline_pycmd.' powerline_launchevent("'.a:event.'")'
augroup END
endfunction
augroup Powerline
autocmd! ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()'
autocmd! VimEnter * :redrawstatus!
autocmd! VimLeave * :exec s:powerline_pycmd 'powerline.renderer.shutdown()'
augroup END
exec s:powerline_pycmd 'powerline = VimPowerline()'
exec s:powerline_pycmd 'del VimPowerline'
" Is immediately changed when PowerlineNew() function is run. Good for global
" value.
set statusline=%!PowerlineNew()
call PowerlineNew()
augroup Powerline
autocmd!
autocmd ColorScheme * :exec s:powerline_pycmd 'powerline.renderer.reset_highlight()'
autocmd VimEnter * :redrawstatus!
autocmd VimLeave * :exec s:powerline_pycmd 'powerline.renderer.shutdown()'
augroup END

@ -45,17 +45,47 @@ class Args(object):
return None
def string(s):
if type(s) is bytes:
return s.decode('utf-8', errors='replace')
else:
return str(s)
class Environment(object):
@staticmethod
def __getitem__(key):
try:
return string(zsh.getvalue(key))
except IndexError as e:
raise KeyError(*e.args)
@staticmethod
def get(key, default=None):
try:
return string(zsh.getvalue(key))
except IndexError:
return default
class Prompt(object):
__slots__ = ('render', 'side', 'savedpsvar', 'savedps')
__slots__ = ('render', 'side', 'savedpsvar', 'savedps', 'args')
def __init__(self, powerline, side, savedpsvar=None, savedps=None):
self.render = powerline.renderer.render
self.side = side
self.savedpsvar = savedpsvar
self.savedps = savedps
self.args = powerline.args
def __str__(self):
return self.render(width=zsh.columns(), side=self.side).encode('utf-8')
r = self.render(width=zsh.columns(), side=self.side, segment_info=self.args)
if type(r) is not str:
if type(r) is bytes:
return r.decode('utf-8')
else:
return r.encode('utf-8')
return r
def __del__(self):
if self.savedps:
@ -71,6 +101,7 @@ def set_prompt(powerline, psvar, side):
def setup():
powerline = ShellPowerline(Args())
environ = Environment()
powerline = ShellPowerline(Args(), environ=environ, getcwd=lambda: environ['PWD'])
set_prompt(powerline, 'PS1', 'left')
set_prompt(powerline, 'RPS1', 'right')

@ -8,7 +8,7 @@ from time import sleep
from threading import Thread, Lock
class ThreadedSegment(Thread):
class ThreadedSegment(object):
daemon = True
min_sleep_time = 0.1
update_first = True
@ -21,16 +21,37 @@ class ThreadedSegment(Thread):
self.keep_going = True
self.run_once = True
self.did_set_interval = False
self.thread = None
self.skip = False
self.crashed_value = None
def __call__(self, **kwargs):
def __call__(self, pl, update_first=True, **kwargs):
if self.run_once:
self.pl = pl
self.set_state(**kwargs)
self.update()
elif not self.is_alive():
self.startup(**kwargs)
# Without this we will not have to wait long until receiving bug “I
# opened vim, but branch information is only shown after I move
# cursor”.
#
# If running once .update() is called in __call__.
if update_first and self.update_first:
self.update()
self.start()
if self.skip:
return self.crashed_value
with self.write_lock:
return self.render(**kwargs)
return self.render(update_first=update_first, pl=pl, **kwargs)
def is_alive(self):
return self.thread and self.thread.is_alive()
def start(self):
self.thread = Thread(target=self.run)
self.thread.daemon = self.daemon
self.thread.start()
def sleep(self, adjust_time):
sleep(max(self.interval - adjust_time, self.min_sleep_time))
@ -39,14 +60,29 @@ class ThreadedSegment(Thread):
while self.keep_going:
start_time = monotonic()
with self.update_lock:
self.update()
try:
if self.update_lock.acquire(False):
try:
self.update()
except Exception as e:
self.error('Exception while updating: {0}', str(e))
self.skip = True
else:
self.skip = False
else:
return
finally:
# Release lock in any case. If it is not locked in this thread,
# it was done in main thread in .shutdown method, and the lock
# will never be released.
self.update_lock.release()
self.sleep(monotonic() - start_time)
def shutdown(self):
self.keep_going = False
self.update_lock.acquire()
if self.keep_going:
self.keep_going = False
self.update_lock.acquire()
def set_interval(self, interval=None):
# Allowing “interval” keyword in configuration.
@ -60,23 +96,24 @@ class ThreadedSegment(Thread):
def set_state(self, interval=None, **kwargs):
if not self.did_set_interval or interval:
self.set_interval(interval)
# Without this we will not have to wait long until receiving bug “I
# opened vim, but branch information is only shown after I move cursor”.
if self.update_first:
self.update_first = False
self.update()
def startup(self, **kwargs):
# Normally .update() succeeds to run before value is requested, meaning
# that user is getting values he needs directly at vim startup. Without
# .startup() we will not have to wait long until receiving bug “I opened
# vim, but branch information is only shown after I move cursor”.
def startup(self, pl, **kwargs):
self.run_once = False
self.pl = pl
if not self.is_alive():
self.set_state(**kwargs)
self.start()
def error(self, *args, **kwargs):
self.pl.error(prefix=self.__class__.__name__, *args, **kwargs)
def warn(self, *args, **kwargs):
self.pl.warn(prefix=self.__class__.__name__, *args, **kwargs)
def debug(self, *args, **kwargs):
self.pl.debug(prefix=self.__class__.__name__, *args, **kwargs)
def printed(func):
def f(*args, **kwargs):
@ -87,25 +124,29 @@ def printed(func):
class KwThreadedSegment(ThreadedSegment):
drop_interval = 10 * 60
update_missing = True
update_first = False
def __init__(self):
super(KwThreadedSegment, self).__init__()
self.queries = {}
self.crashed = set()
@staticmethod
def key(**kwargs):
return frozenset(kwargs.items())
def render(self, **kwargs):
def render(self, update_first, **kwargs):
key = self.key(**kwargs)
if key in self.crashed:
return self.crashed_value
try:
update_state = self.queries[key][1]
except KeyError:
# self.update_missing has the same reasoning as self.update_first in
# parent class
update_state = self.compute_state(key) if self.update_missing else None
# Allow only to forbid to compute missing values: in either user
# configuration or in subclasses.
update_state = self.compute_state(key) if update_first and self.update_first or self.run_once else None
# No locks: render method is already running with write_lock acquired.
self.queries[key] = (monotonic(), update_state)
return self.render_one(update_state, **kwargs)
@ -115,11 +156,17 @@ class KwThreadedSegment(ThreadedSegment):
removes = []
for key, (last_query_time, state) in list(self.queries.items()):
if last_query_time < monotonic() < last_query_time + self.drop_interval:
updates[key] = (last_query_time, self.compute_state(key))
try:
updates[key] = (last_query_time, self.compute_state(key))
except Exception as e:
self.exception('Exception while computing state for {0}: {1}', repr(key), str(e))
with self.write_lock:
self.crashed.add(key)
else:
removes.append(key)
with self.write_lock:
self.queries.update(updates)
self.crashed -= set(updates)
for key in removes:
self.queries.pop(key)
@ -127,9 +174,8 @@ class KwThreadedSegment(ThreadedSegment):
if not self.did_set_interval or (interval < self.interval):
self.set_interval(interval)
key = self.key(**kwargs)
if key not in self.queries:
self.queries[key] = (monotonic(), self.compute_state(key) if self.update_missing else None)
if self.update_first:
self.update_first = update_first
@staticmethod
def render_one(update_state, **kwargs):

@ -8,6 +8,7 @@ import os
import re
from collections import defaultdict
from copy import copy
import logging
try:
@ -408,6 +409,11 @@ main_spec = (Spec(
# only for existence of the path, not for it being a directory
paths=Spec().list((lambda value, *args: (True, True, not os.path.exists(value.value))),
lambda value: 'path does not exist: {0}'.format(value)).optional(),
log_file=Spec().type(str).func(lambda value, *args: (True, True, not os.path.isdir(os.path.dirname(value))),
lambda value: 'directory does not exist: {0}'.format(os.path.dirname(value))).optional(),
log_level=Spec().re('^[A-Z]+$').func(lambda value, *args: (True, True, not hasattr(logging, value)),
lambda value: 'unknown debugging level {0}'.format(value)).optional(),
log_format=Spec().type(str).optional(),
).context_message('Error while loading common configuration (key {key})'),
ext=Spec(
vim=Spec(
@ -786,6 +792,11 @@ def check_segment_data_key(key, data, context, echoerr):
return True, False, False
# FIXME More checks, limit existing to ThreadedSegment instances only
args_spec = Spec(
interval=Spec().either(Spec().type(float), Spec().type(int)).optional(),
update_first=Spec().type(bool).optional(),
).unknown_spec(Spec(), Spec()).optional().copy
highlight_group_spec = Spec().type(unicode).copy
segment_module_spec = Spec().type(unicode).func(check_segment_module).optional().copy
segments_spec = Spec().optional().list(
@ -801,8 +812,7 @@ segments_spec = Spec().optional().list(
before=Spec().type(unicode).optional(),
width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(),
align=Spec().oneof(set('lr')).optional(),
# FIXME Check args
args=Spec().type(dict).optional(),
args=args_spec(),
contents=Spec().type(unicode).optional(),
highlight_group=Spec().list(
highlight_group_spec().re('^(?:(?!:divider$).)+$',
@ -819,8 +829,7 @@ theme_spec = (Spec(
Spec(
after=Spec().type(unicode).optional(),
before=Spec().type(unicode).optional(),
# FIXME Check args
args=Spec().type(dict).optional(),
args=args_spec(),
contents=Spec().type(unicode).optional(),
),
).optional().context_message('Error while loading segment data (key {key})'),

@ -18,9 +18,11 @@ def construct_returned_value(rendered_highlighted, segments, output_raw):
class Renderer(object):
def __init__(self, theme_config, local_themes, theme_kwargs, colorscheme, **options):
def __init__(self, theme_config, local_themes, theme_kwargs, colorscheme, pl, **options):
self.__dict__.update(options)
self.theme_config = theme_config
theme_kwargs['pl'] = pl
self.pl = pl
self.theme = Theme(theme_config=theme_config, **theme_kwargs)
self.local_themes = local_themes
self.theme_kwargs = theme_kwargs

@ -66,12 +66,7 @@ class VimRenderer(Renderer):
return vim.strwidth(string)
def render(self, window_id, winidx, current):
'''Render all segments.
This method handles replacing of the percent placeholder for vim
statuslines, and it caches segment contents which are retrieved and
used in non-current windows.
'''
'''Render all segments.'''
if current:
mode = vim_mode(1)
mode = mode_translations.get(mode, mode)

@ -64,6 +64,7 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None):
contents, contents_func, module = get_segment_info(data, segment)
highlight_group = segment_type != 'function' and segment.get('highlight_group') or segment.get('name')
return {
'name': segment.get('name'),
'type': segment_type,
'highlight_group': highlight_group,
'divider_highlight_group': None,

@ -18,39 +18,98 @@ from powerline.lib.humanize_bytes import humanize_bytes
from collections import namedtuple
def hostname(only_if_ssh=False):
def hostname(pl, only_if_ssh=False):
'''Return the current hostname.
:param bool only_if_ssh:
only return the hostname if currently in an SSH session
'''
if only_if_ssh and not os.environ.get('SSH_CLIENT'):
if only_if_ssh and not pl.environ.get('SSH_CLIENT'):
return None
return socket.gethostname()
def branch(status_colors=True):
'''Return the current VCS branch.
class RepositorySegment(KwThreadedSegment):
def __init__(self):
super(RepositorySegment, self).__init__()
self.directories = {}
:param bool status_colors:
determines whether repository status will be used to determine highlighting. Default: True.
@staticmethod
def key(pl, **kwargs):
return os.path.abspath(pl.getcwd())
Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``.
'''
repo = guess(path=os.path.abspath(os.getcwd()))
if repo:
branch = repo.branch()
if status_colors:
def update(self):
# .compute_state() is running only in this method, and only in one
# thread, thus operations with .directories do not need write locks
# (.render() method is not using .directories). If this is changed
# .directories needs redesigning
self.directories.clear()
super(RepositorySegment, self).update()
def compute_state(self, path):
repo = guess(path=path)
if repo:
if repo.directory in self.directories:
return self.directories[repo.directory]
else:
r = self.process_repo(repo)
self.directories[repo.directory] = r
return r
class RepositoryStatusSegment(RepositorySegment):
interval = 2
@staticmethod
def process_repo(repo):
return repo.status()
repository_status = with_docstring(RepositoryStatusSegment(),
'''Return the status for the current VCS repository.''')
class BranchSegment(RepositorySegment):
interval = 0.2
started_repository_status = False
@staticmethod
def process_repo(repo):
return repo.branch()
@staticmethod
def render_one(branch, status_colors=False, **kwargs):
if branch and status_colors:
return [{
'contents': branch,
'highlight_group': ['branch_dirty' if repo.status() else 'branch_clean', 'branch'],
'highlight_group': ['branch_dirty' if repository_status(**kwargs) else 'branch_clean', 'branch'],
}]
else:
return branch
return None
def startup(self, status_colors=False, **kwargs):
super(BranchSegment, self).startup(**kwargs)
if status_colors:
self.started_repository_status = True
repository_status.startup(**kwargs)
def shutdown(self):
if self.started_repository_status:
repository_status.shutdown()
super(BranchSegment, self).shutdown()
def cwd(dir_shorten_len=None, dir_limit_depth=None):
branch = with_docstring(BranchSegment(),
'''Return the current VCS branch.
:param bool status_colors:
determines whether repository status will be used to determine highlighting. Default: True.
Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``.
''')
def cwd(pl, dir_shorten_len=None, dir_limit_depth=None):
'''Return the current working directory.
Returns a segment list to create a breadcrumb-like effect.
@ -67,18 +126,16 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None):
'''
import re
try:
try:
cwd = os.getcwdu()
except AttributeError:
cwd = os.getcwd()
cwd = pl.getcwd()
except OSError as e:
if e.errno == 2:
# user most probably deleted the directory
# this happens when removing files from Mercurial repos for example
pl.warn('Current directory not found')
cwd = "[not found]"
else:
raise
home = os.environ.get('HOME')
home = pl.home
if home:
cwd = re.sub('^' + re.escape(home), '~', cwd, 1)
cwd_split = cwd.split(os.sep)
@ -101,7 +158,7 @@ def cwd(dir_shorten_len=None, dir_limit_depth=None):
return ret
def date(format='%Y-%m-%d', istime=False):
def date(pl, format='%Y-%m-%d', istime=False):
'''Return the current date.
:param str format:
@ -118,7 +175,7 @@ def date(format='%Y-%m-%d', istime=False):
}]
def fuzzy_time():
def fuzzy_time(pl):
'''Display the current time as fuzzy time, e.g. "quarter past six".'''
hour_str = ['twelve', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven']
minute_str = {
@ -182,7 +239,7 @@ class ExternalIpSegment(ThreadedSegment):
with self.write_lock:
self.ip = ip
def render(self):
def render(self, **kwargs):
if not hasattr(self, 'ip'):
return None
return [{'contents': self.ip, 'divider_highlight_group': 'background:divider'}]
@ -309,13 +366,10 @@ class WeatherSegment(ThreadedSegment):
# Do not lock attribute assignments in this branch: they are used
# only in .update()
if not self.location:
try:
location_data = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip()))
self.location = ','.join([location_data['city'],
location_data['region_name'],
location_data['country_name']])
except (TypeError, ValueError):
return
location_data = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip()))
self.location = ','.join([location_data['city'],
location_data['region_name'],
location_data['country_name']])
query_data = {
'q':
'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;'
@ -324,19 +378,24 @@ class WeatherSegment(ThreadedSegment):
}
self.url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data)
try:
raw_response = urllib_read(self.url)
response = json.loads(raw_response)
condition = response['query']['results']['weather']['rss']['channel']['item']['condition']
condition_code = int(condition['code'])
temp = float(condition['temp'])
except (KeyError, TypeError, ValueError):
raw_response = urllib_read(self.url)
if not raw_response:
self.error('Failed to get response')
return
response = json.loads(raw_response)
condition = response['query']['results']['weather']['rss']['channel']['item']['condition']
condition_code = int(condition['code'])
temp = float(condition['temp'])
try:
icon_names = weather_conditions_codes[condition_code]
except IndexError:
icon_names = (('not_available' if condition_code == 3200 else 'unknown'),)
except IndexError as e:
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)
with self.write_lock:
self.temp = temp
@ -413,7 +472,7 @@ Also uses ``weather_conditions_{condition}`` for all weather conditions supporte
''')
def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2):
def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2):
'''Return system load average.
Highlights using ``system_load_good``, ``system_load_bad`` and
@ -441,6 +500,7 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2):
try:
cpu_num = cpu_count()
except NotImplementedError:
pl.warn('Unable to get CPU count: method is not implemented')
return None
ret = []
for avg in os.getloadavg():
@ -480,10 +540,10 @@ try:
if data:
yield interface, data.bytes_recv, data.bytes_sent
def _get_user():
def _get_user(pl):
return psutil.Process(os.getpid()).username
def cpu_load_percent(measure_interval=.5):
def cpu_load_percent(pl, measure_interval=.5):
'''Return the average CPU load as a percentage.
Requires the ``psutil`` module.
@ -495,14 +555,11 @@ try:
return '{0}%'.format(cpu_percent)
except ImportError:
def _get_bytes(interface): # NOQA
try:
with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj:
rx = int(file_obj.read())
with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj:
tx = int(file_obj.read())
return (rx, tx)
except IOError:
return None
with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj:
rx = int(file_obj.read())
with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj:
tx = int(file_obj.read())
return (rx, tx)
def _get_interfaces():
for interface in os.listdir('/sys/class/net'):
@ -510,10 +567,10 @@ except ImportError:
if x is not None:
yield interface, x[0], x[1]
def _get_user(): # NOQA
return os.environ.get('USER', None)
def _get_user(pl): # NOQA
return pl.environ.get('USER', None)
def cpu_load_percent(measure_interval=.5): # NOQA
def cpu_load_percent(pl, measure_interval=.5): # NOQA
'''Return the average CPU load as a percentage.
Requires the ``psutil`` module.
@ -521,13 +578,16 @@ except ImportError:
:param float measure_interval:
interval used to measure CPU load (in seconds)
'''
pl.warn('psutil package is not installed, thus CPU load is not available')
return None
username = False
# os.geteuid is not available on windows
_geteuid = getattr(os, 'geteuid', lambda: 1)
def user():
def user(pl):
'''Return the current user.
Highlights the user with the ``superuser`` if the effective user ID is 0.
@ -536,14 +596,11 @@ def user():
'''
global username
if username is False:
username = _get_user()
username = _get_user(pl)
if username is None:
pl.warn('Failed to get username')
return None
try:
euid = os.geteuid()
except AttributeError:
# os.geteuid is not available on windows
euid = 1
euid = _geteuid()
return [{
'contents': username,
'highlight_group': 'user' if euid != 0 else ['superuser', 'user'],
@ -566,7 +623,7 @@ else:
@add_divider_highlight_group('background:divider')
def uptime(format='{days}d {hours:02d}h {minutes:02d}m'):
def uptime(pl, format='{days}d {hours:02d}h {minutes:02d}m'):
'''Return system uptime.
:param str format:
@ -577,7 +634,8 @@ def uptime(format='{days}d {hours:02d}h {minutes:02d}m'):
'''
try:
seconds = _get_uptime()
except (IOError, NotImplementedError):
except NotImplementedError:
pl.warn('Unable to get uptime. You should install psutil package')
return None
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
@ -646,7 +704,10 @@ class NetworkLoadSegment(KwThreadedSegment):
t2, b2 = idata['last']
measure_interval = t2 - t1
if None in (b1, b2) or measure_interval == 0:
if None in (b1, b2):
return None
if measure_interval == 0:
self.error('Measure interval is zero. This should not happen')
return None
r = []
@ -703,9 +764,9 @@ Highlight groups used: ``network_load_sent_gradient`` (gradient) or ``network_lo
''')
def virtualenv():
def virtualenv(pl):
'''Return the name of the current Python virtualenv.'''
return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None
return os.path.basename(pl.environ.get('VIRTUAL_ENV', '')) or None
_IMAPKey = namedtuple('Key', 'username password server port folder')
@ -715,12 +776,12 @@ class EmailIMAPSegment(KwThreadedSegment):
interval = 60
@staticmethod
def key(username, password, server='imap.gmail.com', port=993, folder='INBOX'):
def key(username, password, server='imap.gmail.com', port=993, folder='INBOX', **kwargs):
return _IMAPKey(username, password, server, port, folder)
@staticmethod
def compute_state(key):
def compute_state(self, key):
if not key.username or not key.password:
self.warn('Username and password are not configured')
return None
try:
import imaplib
@ -730,8 +791,6 @@ class EmailIMAPSegment(KwThreadedSegment):
rc, message = mail.status(key.folder, '(UNSEEN)')
unread_str = message[0].decode('utf-8')
unread_count = int(re.search('UNSEEN (\d+)', unread_str).group(1))
except socket.gaierror:
return None
except imaplib.IMAP4.error as e:
unread_count = str(e)
return unread_count
@ -783,7 +842,7 @@ class NowPlayingSegment(object):
'stop': '',
}
def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', *args, **kwargs):
def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', **kwargs):
player_func = getattr(self, 'player_{0}'.format(player))
stats = {
'state': None,
@ -794,7 +853,7 @@ class NowPlayingSegment(object):
'elapsed': None,
'total': None,
}
func_stats = player_func(*args, **kwargs)
func_stats = player_func(**kwargs)
if not func_stats:
return None
stats.update(func_stats)
@ -825,7 +884,7 @@ class NowPlayingSegment(object):
def _convert_seconds(seconds):
return '{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60))
def player_cmus(self):
def player_cmus(self, pl):
'''Return cmus player information.
cmus-remote -Q returns data with multi-level information i.e.
@ -863,7 +922,7 @@ class NowPlayingSegment(object):
'total': self._convert_seconds(now_playing.get('duration', 0)),
}
def player_mpd(self, host='localhost', port=6600):
def player_mpd(self, pl, host='localhost', port=6600):
try:
import mpd
client = mpd.MPDClient()
@ -895,7 +954,7 @@ class NowPlayingSegment(object):
'total': now_playing[3],
}
def player_spotify(self):
def player_spotify(self, pl):
try:
import dbus
except ImportError:
@ -923,7 +982,7 @@ class NowPlayingSegment(object):
'total': self._convert_seconds(info.get('mpris:length') / 1e6),
}
def player_rhythmbox(self):
def player_rhythmbox(self, pl):
now_playing = self._run_cmd(['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td'])
if not now_playing:
return

@ -3,5 +3,5 @@
from powerline.theme import requires_segment_info
@requires_segment_info
def prompt_count(segment_info):
def prompt_count(pl, segment_info):
return str(segment_info.prompt_count)

@ -4,7 +4,7 @@ from powerline.theme import requires_segment_info
@requires_segment_info
def last_status(segment_info):
def last_status(pl, segment_info):
'''Return last exit code.
Highlight groups used: ``exit_fail``
@ -15,7 +15,7 @@ def last_status(segment_info):
@requires_segment_info
def last_pipe_status(segment_info):
def last_pipe_status(pl, segment_info):
'''Return last pipe status.
Highlight groups used: ``exit_fail``, ``exit_success``

@ -47,34 +47,32 @@ vim_modes = {
}
eventcaches = defaultdict(lambda: [])
bufeventcaches = defaultdict(lambda: [])
eventfuncs = defaultdict(lambda: [])
bufeventfuncs = defaultdict(lambda: [])
defined_events = set()
def purgeonevents_reg(events, eventcaches=bufeventcaches):
def cache_reg_func(cache):
for event in events:
if event not in eventcaches:
vim.eval('PowerlineRegisterCachePurgerEvent("' + event + '")')
eventcaches[event].append(cache)
return cache_reg_func
purgeall_on_shell = purgeonevents_reg(('ShellCmdPost', 'ShellFilterPost', 'FocusGained'), eventcaches=eventcaches)
purgebuf_on_shell_and_write = purgeonevents_reg(('BufWritePost', 'ShellCmdPost', 'ShellFilterPost', 'FocusGained'))
def purgeonevents_reg(func, events, is_buffer_event=False):
if is_buffer_event:
cureventfuncs = bufeventfuncs
else:
cureventfuncs = eventfuncs
for event in events:
if event not in defined_events:
vim.eval('PowerlineRegisterCachePurgerEvent("' + event + '")')
defined_events.add(event)
cureventfuncs[event].append(func)
def launchevent(event):
global eventcaches
global bufeventcaches
for cache in eventcaches[event]:
cache.clear()
if bufeventcaches[event]:
buf = int(vim_funcs['expand']('<abuf>'))
for cache in bufeventcaches[event]:
try:
cache.pop(buf)
except KeyError:
pass
global eventfuncs
global bufeventfuncs
for func in eventfuncs[event]:
func()
if bufeventfuncs[event]:
buffer = vim.buffers[int(vim_funcs['expand']('<abuf>')) - 1]
for func in bufeventfuncs[event]:
func(buffer)
# TODO Remove cache when needed
@ -96,7 +94,7 @@ def window_cached(func):
@requires_segment_info
def mode(segment_info, override=None):
def mode(pl, segment_info, override=None):
'''Return the current vim mode.
:param dict override:
@ -114,7 +112,7 @@ def mode(segment_info, override=None):
@requires_segment_info
def modified_indicator(segment_info, text='+'):
def modified_indicator(pl, segment_info, text='+'):
'''Return a file modified indicator.
:param string text:
@ -124,7 +122,7 @@ def modified_indicator(segment_info, text='+'):
@requires_segment_info
def paste_indicator(segment_info, text='PASTE'):
def paste_indicator(pl, segment_info, text='PASTE'):
'''Return a paste mode indicator.
:param string text:
@ -134,7 +132,7 @@ def paste_indicator(segment_info, text='PASTE'):
@requires_segment_info
def readonly_indicator(segment_info, text=''):
def readonly_indicator(pl, segment_info, text=''):
'''Return a read-only indicator.
:param string text:
@ -144,7 +142,7 @@ def readonly_indicator(segment_info, text=''):
@requires_segment_info
def file_directory(segment_info, shorten_user=True, shorten_cwd=True, shorten_home=False):
def file_directory(pl, segment_info, shorten_user=True, shorten_cwd=True, shorten_home=False):
'''Return file directory (head component of the file path).
:param bool shorten_user:
@ -167,13 +165,15 @@ def file_directory(segment_info, shorten_user=True, shorten_cwd=True, shorten_ho
@requires_segment_info
def file_name(segment_info, display_no_file=False, no_file_text='[No file]'):
def file_name(pl, segment_info, display_no_file=False, no_file_text='[No file]'):
'''Return file name (tail component of the file path).
:param bool display_no_file:
display a string if the buffer is missing a file name
:param str no_file_text:
the string to display if the buffer is missing a file name
Highlight groups used: ``file_name_no_file`` or ``file_name``, ``file_name``.
'''
name = segment_info['buffer'].name
if not name:
@ -189,7 +189,7 @@ def file_name(segment_info, display_no_file=False, no_file_text='[No file]'):
@window_cached
def file_size(suffix='B', si_prefix=False):
def file_size(pl, suffix='B', si_prefix=False):
'''Return file size in &encoding.
:param str suffix:
@ -206,7 +206,7 @@ def file_size(suffix='B', si_prefix=False):
@requires_segment_info
@add_divider_highlight_group('background:divider')
def file_format(segment_info):
def file_format(pl, segment_info):
'''Return file format (i.e. line ending type).
:return: file format or None if unknown or missing file format
@ -218,7 +218,7 @@ def file_format(segment_info):
@requires_segment_info
@add_divider_highlight_group('background:divider')
def file_encoding(segment_info):
def file_encoding(pl, segment_info):
'''Return file encoding/character set.
:return: file encoding/character set or None if unknown or missing file encoding
@ -230,7 +230,7 @@ def file_encoding(segment_info):
@requires_segment_info
@add_divider_highlight_group('background:divider')
def file_type(segment_info):
def file_type(pl, segment_info):
'''Return file type.
:return: file type or None if unknown file type
@ -241,7 +241,7 @@ def file_type(segment_info):
@requires_segment_info
def line_percent(segment_info, gradient=False):
def line_percent(pl, segment_info, gradient=False):
'''Return the cursor position in the file as a percentage.
:param bool gradient:
@ -262,20 +262,20 @@ def line_percent(segment_info, gradient=False):
@requires_segment_info
def line_current(segment_info):
def line_current(pl, segment_info):
'''Return the current cursor line.'''
return str(segment_info['window'].cursor[0])
@requires_segment_info
def col_current(segment_info):
def col_current(pl, segment_info):
'''Return the current cursor column.
'''
return str(segment_info['window'].cursor[1] + 1)
@window_cached
def virtcol_current():
def virtcol_current(pl):
'''Return current visual column with concealed characters ingored
Highlight groups used: ``virtcol_current`` or ``col_current``.
@ -284,7 +284,7 @@ def virtcol_current():
'highlight_group': ['virtcol_current', 'col_current']}]
def modified_buffers(text='+ ', join_str=','):
def modified_buffers(pl, text='+ ', join_str=','):
'''Return a comma-separated list of modified buffers.
:param str text:
@ -368,16 +368,16 @@ class BranchSegment(RepositorySegment):
return [{
'contents': update_state,
'highlight_group': (['branch_dirty' if repository_status(segment_info=segment_info) else 'branch_clean']
'highlight_group': (['branch_dirty' if repository_status(segment_info=segment_info, **kwargs) else 'branch_clean']
if status_colors else []) + ['branch'],
'divider_highlight_group': 'branch:divider',
}]
def startup(self, status_colors=False, **kwargs):
super(BranchSegment, self).startup()
super(BranchSegment, self).startup(**kwargs)
if status_colors:
self.started_repository_status = True
repository_status.startup()
repository_status.startup(**kwargs)
def shutdown(self):
if self.started_repository_status:

@ -14,13 +14,10 @@ def mergeargs(argvalue):
class ShellPowerline(Powerline):
def __init__(self, args, run_once=False):
def __init__(self, args, **kwargs):
self.args = args
self.theme_option = mergeargs(args.theme_option) or {}
super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, run_once=run_once)
def get_segment_info(self):
return self.args
super(ShellPowerline, self).__init__(args.ext[0], args.renderer_module, **kwargs)
def load_main_config(self):
r = super(ShellPowerline, self).load_main_config()

@ -24,7 +24,7 @@ def requires_segment_info(func):
class Theme(object):
def __init__(self, ext, theme_config, common_config, top_theme_config=None, run_once=False):
def __init__(self, ext, theme_config, common_config, pl, top_theme_config=None, run_once=False):
self.dividers = theme_config.get('dividers', common_config['dividers'])
self.spaces = theme_config.get('spaces', common_config['spaces'])
self.segments = {
@ -35,16 +35,22 @@ class Theme(object):
'contents': None,
'highlight': {'fg': False, 'bg': False, 'attr': 0}
}
self.pl = pl
theme_configs = [theme_config]
if top_theme_config:
theme_configs.append(top_theme_config)
get_segment = gen_segment_getter(ext, common_config['paths'], theme_configs, theme_config.get('default_module'))
for side in ['left', 'right']:
self.segments[side].extend((get_segment(segment, side) for segment in theme_config['segments'].get(side, [])))
if not run_once:
for segment in self.segments[side]:
for segment in theme_config['segments'].get(side, []):
segment = get_segment(segment, side)
if not run_once:
if segment['startup']:
segment['startup'](**segment['args'])
try:
segment['startup'](pl=pl, **segment['args'])
except Exception as e:
pl.error('Exception during {0} startup: {1}', segment['name'], str(e))
continue
self.segments[side].append(segment)
def shutdown(self):
for segments in self.segments.values():
@ -71,11 +77,17 @@ class Theme(object):
parsed_segments = []
for segment in self.segments[side]:
if segment['type'] == 'function':
if (hasattr(segment['contents_func'], 'powerline_requires_segment_info')
and segment['contents_func'].powerline_requires_segment_info):
contents = segment['contents_func'](segment_info=segment_info, **segment['args'])
else:
contents = segment['contents_func'](**segment['args'])
self.pl.prefix = segment['name']
try:
if (hasattr(segment['contents_func'], 'powerline_requires_segment_info')
and segment['contents_func'].powerline_requires_segment_info):
contents = segment['contents_func'](pl=self.pl, segment_info=segment_info, **segment['args'])
else:
contents = segment['contents_func'](pl=self.pl, **segment['args'])
except Exception as e:
self.pl.exception('Exception while computing segment: {0}', str(e))
continue
if contents is None:
continue
if isinstance(contents, list):

@ -13,7 +13,10 @@ except ImportError:
if __name__ == '__main__':
args = get_argparser(description=__doc__).parse_args()
powerline = ShellPowerline(args, run_once=True)
kwargs = {}
if 'PWD' in os.environ:
kwargs['getcwd'] = lambda: os.environ['PWD']
powerline = ShellPowerline(args, run_once=True, **kwargs)
rendered = powerline.renderer.render(width=args.width, side=args.side, segment_info=args)
try:
sys.stdout.write(rendered)

@ -4,6 +4,28 @@ import sys
import os
class Pl(object):
def __init__(self):
self.errors = []
self.warns = []
self.debugs = []
self._cwd = None
self.prefix = None
self.environ = {}
self.home = None
def getcwd(self):
if isinstance(self._cwd, Exception):
raise self._cwd
else:
return self._cwd
for meth in ('error', 'warn', 'debug'):
exec ('''def {0}(self, msg, *args, **kwargs):
self.{0}s.append((kwargs.get('prefix') or self.prefix, msg, args, kwargs))
''').format(meth)
class Args(object):
theme_option = None
config = None
@ -63,50 +85,58 @@ def new_module(name, **kwargs):
return module
class ModuleAttrReplace(object):
def __init__(self, module, attr, new):
self.module = module
class AttrReplace(object):
def __init__(self, obj, attr, new):
self.obj = obj
self.attr = attr
self.new = new
def __enter__(self):
try:
self.old = getattr(self.module, self.attr)
self.old = getattr(self.obj, self.attr)
except AttributeError:
pass
setattr(self.module, self.attr, self.new)
setattr(self.obj, self.attr, self.new)
def __exit__(self, *args):
try:
setattr(self.module, self.attr, self.old)
setattr(self.obj, self.attr, self.old)
except AttributeError:
delattr(self.module, self.attr)
delattr(self.obj, self.attr)
replace_module_attr = ModuleAttrReplace
replace_attr = AttrReplace
def replace_module_module(module, name, **kwargs):
return replace_module_attr(module, name, new_module(name, **kwargs))
return replace_attr(module, name, new_module(name, **kwargs))
class EnvReplace(object):
def __init__(self, name, new):
self.name = name
class ItemReplace(object):
def __init__(self, d, key, new, r=None):
self.key = key
self.new = new
self.d = d
self.r = r
def __enter__(self):
self.old = os.environ.get(self.name)
os.environ[self.name] = self.new
self.old = self.d.get(self.key)
self.d[self.key] = self.new
return self.r
def __exit__(self, *args):
if self.old is None:
try:
os.environ.pop(self.name)
self.d.pop(self.key)
except KeyError:
pass
else:
os.environ[self.name] = self.old
self.d[self.key] = self.old
replace_env = EnvReplace
def replace_env(key, new, d=None):
r = None
if not d:
r = Pl()
d = r.environ
return ItemReplace(d, key, new, r)

@ -7,7 +7,7 @@ import tests.vim as vim_module
import sys
import os
import json
from tests.lib import Args, urllib_read, replace_module_attr
from tests.lib import Args, urllib_read, replace_attr
from tests import TestCase
@ -67,7 +67,7 @@ class TestConfig(TestCase):
from imp import reload
reload(common)
from powerline.shell import ShellPowerline
with replace_module_attr(common, 'urllib_read', urllib_read):
with replace_attr(common, 'urllib_read', urllib_read):
powerline = ShellPowerline(Args(ext=['tmux']), run_once=False)
powerline.renderer.render()
powerline = ShellPowerline(Args(ext=['tmux']), run_once=False)
@ -112,7 +112,7 @@ class TestConfig(TestCase):
from imp import reload
reload(common)
from powerline import Powerline
with replace_module_attr(common, 'urllib_read', urllib_read):
with replace_attr(common, 'urllib_read', urllib_read):
Powerline(ext='wm', renderer_module='pango_markup', run_once=True).renderer.render()
reload(common)

@ -4,7 +4,7 @@ from powerline.segments import shell, common
import tests.vim as vim_module
import sys
import os
from tests.lib import Args, urllib_read, replace_module_attr, new_module, replace_module_module, replace_env
from tests.lib import Args, urllib_read, replace_attr, new_module, replace_module_module, replace_env, Pl
from tests import TestCase
@ -13,14 +13,16 @@ vim = None
class TestShell(TestCase):
def test_last_status(self):
self.assertEqual(shell.last_status(Args(last_exit_code=10)),
pl = Pl()
self.assertEqual(shell.last_status(pl=pl, segment_info=Args(last_exit_code=10)),
[{'contents': '10', 'highlight_group': 'exit_fail'}])
self.assertEqual(shell.last_status(Args(last_exit_code=None)), None)
self.assertEqual(shell.last_status(pl=pl, segment_info=Args(last_exit_code=None)), None)
def test_last_pipe_status(self):
self.assertEqual(shell.last_pipe_status(Args(last_pipe_status=[])), None)
self.assertEqual(shell.last_pipe_status(Args(last_pipe_status=[0, 0, 0])), None)
self.assertEqual(shell.last_pipe_status(Args(last_pipe_status=[0, 2, 0])),
pl = Pl()
self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=Args(last_pipe_status=[])), None)
self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=Args(last_pipe_status=[0, 0, 0])), None)
self.assertEqual(shell.last_pipe_status(pl=pl, segment_info=Args(last_pipe_status=[0, 2, 0])),
[{'contents': '0', 'highlight_group': 'exit_success'},
{'contents': '2', 'highlight_group': 'exit_fail'},
{'contents': '0', 'highlight_group': 'exit_success'}])
@ -28,219 +30,226 @@ class TestShell(TestCase):
class TestCommon(TestCase):
def test_hostname(self):
with replace_env('SSH_CLIENT', '192.168.0.12 40921 22'):
with replace_env('SSH_CLIENT', '192.168.0.12 40921 22') as pl:
with replace_module_module(common, 'socket', gethostname=lambda: 'abc'):
self.assertEqual(common.hostname(), 'abc')
self.assertEqual(common.hostname(only_if_ssh=True), 'abc')
os.environ.pop('SSH_CLIENT')
self.assertEqual(common.hostname(), 'abc')
self.assertEqual(common.hostname(only_if_ssh=True), None)
self.assertEqual(common.hostname(pl=pl), 'abc')
self.assertEqual(common.hostname(pl=pl, only_if_ssh=True), 'abc')
pl.environ.pop('SSH_CLIENT')
self.assertEqual(common.hostname(pl=pl), 'abc')
self.assertEqual(common.hostname(pl=pl, only_if_ssh=True), None)
def test_user(self):
new_os = new_module('os', environ={'USER': 'def'}, getpid=lambda: 1)
new_os = new_module('os', getpid=lambda: 1)
new_psutil = new_module('psutil', Process=lambda pid: Args(username='def'))
with replace_module_attr(common, 'os', new_os):
with replace_module_attr(common, 'psutil', new_psutil):
self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}])
new_os.geteuid = lambda: 1
self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': 'user'}])
new_os.geteuid = lambda: 0
self.assertEqual(common.user(), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}])
with replace_env('USER', 'def') as pl:
with replace_attr(common, 'os', new_os):
with replace_attr(common, 'psutil', new_psutil):
with replace_attr(common, '_geteuid', lambda: 5):
self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': 'user'}])
with replace_attr(common, '_geteuid', lambda: 0):
self.assertEqual(common.user(pl=pl), [{'contents': 'def', 'highlight_group': ['superuser', 'user']}])
def test_branch(self):
with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None)):
self.assertEqual(common.branch(status_colors=False), 'tests')
self.assertEqual(common.branch(status_colors=True),
pl = Pl()
pl._cwd = os.getcwd()
with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory='/tmp/tests')):
self.assertEqual(common.branch(pl=pl, status_colors=False), 'tests')
self.assertEqual(common.branch(pl=pl, status_colors=True),
[{'contents': 'tests', 'highlight_group': ['branch_clean', 'branch']}])
with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ')):
self.assertEqual(common.branch(status_colors=False), 'tests')
self.assertEqual(common.branch(),
with replace_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'D ', directory='/tmp/tests')):
self.assertEqual(common.branch(pl=pl, status_colors=False), 'tests')
self.assertEqual(common.branch(pl=pl, status_colors=True),
[{'contents': 'tests', 'highlight_group': ['branch_dirty', 'branch']}])
with replace_module_attr(common, 'guess', lambda path: None):
self.assertEqual(common.branch(), None)
self.assertEqual(common.branch(pl=pl), 'tests')
with replace_attr(common, 'guess', lambda path: None):
self.assertEqual(common.branch(pl=pl), None)
def test_cwd(self):
new_os = new_module('os', path=os.path, environ={}, sep='/')
new_os.getcwd = lambda: '/abc/def/ghi/foo/bar'
with replace_module_attr(common, 'os', new_os):
self.assertEqual(common.cwd(),
new_os = new_module('os', path=os.path, sep='/')
pl = Pl()
with replace_attr(common, 'os', new_os):
pl._cwd = '/abc/def/ghi/foo/bar'
self.assertEqual(common.cwd(pl=pl),
[{'contents': '/', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'abc', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'def', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'ghi', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'foo', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}])
new_os.getcwdu = lambda: '/abc/def/ghi/foo/bar'
self.assertEqual(common.cwd(),
[{'contents': '/', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'abc', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'def', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'ghi', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'foo', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}])
new_os.environ['HOME'] = '/abc/def/ghi'
self.assertEqual(common.cwd(),
pl.home = '/abc/def/ghi'
self.assertEqual(common.cwd(pl=pl),
[{'contents': '~', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'foo', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}])
self.assertEqual(common.cwd(dir_limit_depth=3),
self.assertEqual(common.cwd(pl=pl, dir_limit_depth=3),
[{'contents': '~', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'foo', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}])
self.assertEqual(common.cwd(dir_limit_depth=1),
self.assertEqual(common.cwd(pl=pl, dir_limit_depth=1),
[{'contents': '', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}])
self.assertEqual(common.cwd(dir_limit_depth=2, dir_shorten_len=2),
self.assertEqual(common.cwd(pl=pl, dir_limit_depth=2, dir_shorten_len=2),
[{'contents': '~', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'fo', 'divider_highlight_group': 'cwd:divider'},
{'contents': 'bar', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}])
ose = OSError()
ose.errno = 2
def raises(exc):
raise exc
new_os.getcwdu = lambda: raises(ose)
self.assertEqual(common.cwd(dir_limit_depth=2, dir_shorten_len=2),
pl._cwd = ose
self.assertEqual(common.cwd(pl=pl, dir_limit_depth=2, dir_shorten_len=2),
[{'contents': '[not found]', 'divider_highlight_group': 'cwd:divider', 'highlight_group': ['cwd:current_folder', 'cwd']}])
new_os.getcwdu = lambda: raises(OSError())
self.assertRaises(OSError, common.cwd, tuple(), {'dir_limit_depth': 2, 'dir_shorten_len': 2})
new_os.getcwdu = lambda: raises(ValueError())
self.assertRaises(ValueError, common.cwd, tuple(), {'dir_limit_depth': 2, 'dir_shorten_len': 2})
pl._cwd = OSError()
self.assertRaises(OSError, common.cwd, pl=pl, dir_limit_depth=2, dir_shorten_len=2)
pl._cwd = ValueError()
self.assertRaises(ValueError, common.cwd, pl=pl, dir_limit_depth=2, dir_shorten_len=2)
def test_date(self):
with replace_module_attr(common, 'datetime', Args(now=lambda: Args(strftime=lambda fmt: fmt))):
self.assertEqual(common.date(), [{'contents': '%Y-%m-%d', 'highlight_group': ['date'], 'divider_highlight_group': None}])
self.assertEqual(common.date(format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_group': ['time', 'date'], 'divider_highlight_group': 'time:divider'}])
pl = Pl()
with replace_attr(common, 'datetime', Args(now=lambda: Args(strftime=lambda fmt: fmt))):
self.assertEqual(common.date(pl=pl), [{'contents': '%Y-%m-%d', 'highlight_group': ['date'], 'divider_highlight_group': None}])
self.assertEqual(common.date(pl=pl, format='%H:%M', istime=True), [{'contents': '%H:%M', 'highlight_group': ['time', 'date'], 'divider_highlight_group': 'time:divider'}])
def test_fuzzy_time(self):
time = Args(hour=0, minute=45)
with replace_module_attr(common, 'datetime', Args(now=lambda: time)):
self.assertEqual(common.fuzzy_time(), 'quarter to one')
pl = Pl()
with replace_attr(common, 'datetime', Args(now=lambda: time)):
self.assertEqual(common.fuzzy_time(pl=pl), 'quarter to one')
time.hour = 23
time.minute = 59
self.assertEqual(common.fuzzy_time(), 'round about midnight')
self.assertEqual(common.fuzzy_time(pl=pl), 'round about midnight')
time.minute = 33
self.assertEqual(common.fuzzy_time(), 'twenty-five to twelve')
self.assertEqual(common.fuzzy_time(pl=pl), 'twenty-five to twelve')
time.minute = 60
self.assertEqual(common.fuzzy_time(), 'twelve o\'clock')
self.assertEqual(common.fuzzy_time(pl=pl), 'twelve o\'clock')
def test_external_ip(self):
with replace_module_attr(common, 'urllib_read', urllib_read):
self.assertEqual(common.external_ip(), [{'contents': '127.0.0.1', 'divider_highlight_group': 'background:divider'}])
pl = Pl()
with replace_attr(common, 'urllib_read', urllib_read):
self.assertEqual(common.external_ip(pl=pl), [{'contents': '127.0.0.1', 'divider_highlight_group': 'background:divider'}])
def test_uptime(self):
with replace_module_attr(common, '_get_uptime', lambda: 65536):
self.assertEqual(common.uptime(), [{'contents': '0d 18h 12m', 'divider_highlight_group': 'background:divider'}])
pl = Pl()
with replace_attr(common, '_get_uptime', lambda: 65536):
self.assertEqual(common.uptime(pl=pl), [{'contents': '0d 18h 12m', 'divider_highlight_group': 'background:divider'}])
def _get_uptime():
raise NotImplementedError
with replace_module_attr(common, '_get_uptime', _get_uptime):
self.assertEqual(common.uptime(), None)
with replace_attr(common, '_get_uptime', _get_uptime):
self.assertEqual(common.uptime(pl=pl), None)
def test_weather(self):
with replace_module_attr(common, 'urllib_read', urllib_read):
self.assertEqual(common.weather(), [
pl = Pl()
with replace_attr(common, 'urllib_read', urllib_read):
self.assertEqual(common.weather(pl=pl), [
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': ''},
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0}
])
self.assertEqual(common.weather(temp_coldest=0, temp_hottest=100), [
self.assertEqual(common.weather(pl=pl, temp_coldest=0, temp_hottest=100), [
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': ''},
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 0}
])
self.assertEqual(common.weather(temp_coldest=-100, temp_hottest=-50), [
self.assertEqual(common.weather(pl=pl, temp_coldest=-100, temp_hottest=-50), [
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': ''},
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 100}
])
self.assertEqual(common.weather(icons={'cloudy': 'o'}), [
self.assertEqual(common.weather(pl=pl, icons={'cloudy': 'o'}), [
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'o '},
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0}
])
self.assertEqual(common.weather(icons={'partly_cloudy_day': 'x'}), [
self.assertEqual(common.weather(pl=pl, icons={'partly_cloudy_day': 'x'}), [
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': 'x '},
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9°C', 'gradient_level': 30.0}
])
self.assertEqual(common.weather(unit='F'), [
self.assertEqual(common.weather(pl=pl, unit='F'), [
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': ''},
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '16°F', 'gradient_level': 30.0}
])
self.assertEqual(common.weather(unit='K'), [
self.assertEqual(common.weather(pl=pl, unit='K'), [
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': ''},
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '264K', 'gradient_level': 30.0}
])
self.assertEqual(common.weather(temp_format='{temp:.1e}C'), [
self.assertEqual(common.weather(pl=pl, temp_format='{temp:.1e}C'), [
{'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_condition_partly_cloudy_day', 'weather_condition_cloudy', 'weather_conditions', 'weather'], 'contents': ''},
{'draw_divider': False, 'divider_highlight_group': 'background:divider', 'highlight_group': ['weather_temp_gradient', 'weather_temp', 'weather'], 'contents': '-9.0e+00C', 'gradient_level': 30.0}
])
def test_system_load(self):
pl = Pl()
with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)):
with replace_module_attr(common, 'cpu_count', lambda: 2):
self.assertEqual(common.system_load(),
with replace_attr(common, 'cpu_count', lambda: 2):
self.assertEqual(common.system_load(pl=pl),
[{'contents': '7.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100},
{'contents': '3.5 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0},
{'contents': '1.5', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 0}])
self.assertEqual(common.system_load(format='{avg:.0f}', threshold_good=0, threshold_bad=1),
self.assertEqual(common.system_load(pl=pl, format='{avg:.0f}', threshold_good=0, threshold_bad=1),
[{'contents': '8 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': True, 'divider_highlight_group': 'background:divider', 'gradient_level': 100},
{'contents': '4 ', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 100},
{'contents': '2', 'highlight_group': ['system_load_gradient', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider', 'gradient_level': 75.0}])
def test_cpu_load_percent(self):
pl = Pl()
with replace_module_module(common, 'psutil', cpu_percent=lambda **kwargs: 52.3):
self.assertEqual(common.cpu_load_percent(), '52%')
self.assertEqual(common.cpu_load_percent(pl=pl), '52%')
def test_network_load(self):
def _get_bytes(interface):
def gb(interface):
return None
with replace_module_attr(common, '_get_bytes', _get_bytes):
self.assertEqual(common.network_load(), None)
l = [0, 0]
def _get_bytes2(interface):
l[0] += 1200
l[1] += 2400
return tuple(l)
f = [gb]
def _get_bytes(interface):
return f[0](interface)
pl = Pl()
with replace_attr(common, '_get_bytes', _get_bytes):
common.network_load.startup(pl=pl)
self.assertEqual(common.network_load(pl=pl), None)
common.network_load.sleep(0)
self.assertEqual(common.network_load(pl=pl), None)
l = [0, 0]
def gb2(interface):
l[0] += 1200
l[1] += 2400
return tuple(l)
f[0] = gb2
from imp import reload
reload(common)
with replace_module_attr(common, '_get_bytes', _get_bytes2):
common.network_load.startup()
common.network_load.sleep(0)
common.network_load.sleep(0)
self.assertEqual(common.network_load(), [
self.assertEqual(common.network_load(pl=pl), [
{'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'background:divider', 'contents': '⬆ 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']},
])
self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}'), [
self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}'), [
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']},
])
self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', suffix='bps'), [
self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', suffix='bps'), [
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps', 'highlight_group': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'background:divider', 'contents': 's 2 Kibps', 'highlight_group': ['network_load_sent', 'network_load']},
])
self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', si_prefix=True), [
self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', si_prefix=True), [
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s', 'highlight_group': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'background:divider', 'contents': 's 2 kB/s', 'highlight_group': ['network_load_sent', 'network_load']},
])
self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', recv_max=0), [
self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', recv_max=0), [
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv_gradient', 'network_load_gradient', 'network_load_recv', 'network_load'], 'gradient_level': 100},
{'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent', 'network_load']},
])
class ApproxEqual(object):
def __eq__(self, i):
return abs(i - 50.0) < 1
self.assertEqual(common.network_load(recv_format='r {value}', sent_format='s {value}', sent_max=4800), [
self.assertEqual(common.network_load(pl=pl, recv_format='r {value}', sent_format='s {value}', sent_max=4800), [
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']},
{'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()},
])
def test_virtualenv(self):
with replace_env('VIRTUAL_ENV', '/abc/def/ghi'):
self.assertEqual(common.virtualenv(), 'ghi')
os.environ.pop('VIRTUAL_ENV')
self.assertEqual(common.virtualenv(), None)
with replace_env('VIRTUAL_ENV', '/abc/def/ghi') as pl:
self.assertEqual(common.virtualenv(pl=pl), 'ghi')
pl.environ.pop('VIRTUAL_ENV')
self.assertEqual(common.virtualenv(pl=pl), None)
def test_email_imap_alert(self):
# TODO
@ -253,131 +262,145 @@ class TestCommon(TestCase):
class TestVim(TestCase):
def test_mode(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.mode(segment_info=segment_info), 'NORMAL')
self.assertEqual(vim.mode(segment_info=segment_info, override={'i': 'INS'}), 'NORMAL')
self.assertEqual(vim.mode(segment_info=segment_info, override={'n': 'NORM'}), 'NORM')
self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'NORMAL')
self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'i': 'INS'}), 'NORMAL')
self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'n': 'NORM'}), 'NORM')
with vim_module._with('mode', 'i') as segment_info:
self.assertEqual(vim.mode(segment_info=segment_info), 'INSERT')
self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'INSERT')
with vim_module._with('mode', chr(ord('V') - 0x40)) as segment_info:
self.assertEqual(vim.mode(segment_info=segment_info), 'V·BLCK')
self.assertEqual(vim.mode(segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK')
self.assertEqual(vim.mode(pl=pl, segment_info=segment_info), 'V·BLCK')
self.assertEqual(vim.mode(pl=pl, segment_info=segment_info, override={'^V': 'VBLK'}), 'VBLK')
def test_modified_indicator(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.modified_indicator(segment_info=segment_info), None)
self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info), None)
segment_info['buffer'][0] = 'abc'
try:
self.assertEqual(vim.modified_indicator(segment_info=segment_info), '+')
self.assertEqual(vim.modified_indicator(segment_info=segment_info, text='-'), '-')
self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info), '+')
self.assertEqual(vim.modified_indicator(pl=pl, segment_info=segment_info, text='-'), '-')
finally:
vim_module._bw(segment_info['bufnr'])
def test_paste_indicator(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.paste_indicator(segment_info=segment_info), None)
self.assertEqual(vim.paste_indicator(pl=pl, segment_info=segment_info), None)
with vim_module._with('options', paste=1):
self.assertEqual(vim.paste_indicator(segment_info=segment_info), 'PASTE')
self.assertEqual(vim.paste_indicator(segment_info=segment_info, text='P'), 'P')
self.assertEqual(vim.paste_indicator(pl=pl, segment_info=segment_info), 'PASTE')
self.assertEqual(vim.paste_indicator(pl=pl, segment_info=segment_info, text='P'), 'P')
def test_readonly_indicator(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.readonly_indicator(segment_info=segment_info), None)
self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info), None)
with vim_module._with('bufoptions', readonly=1):
self.assertEqual(vim.readonly_indicator(segment_info=segment_info), '')
self.assertEqual(vim.readonly_indicator(segment_info=segment_info, text='L'), 'L')
self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info), '')
self.assertEqual(vim.readonly_indicator(pl=pl, segment_info=segment_info, text='L'), 'L')
def test_file_directory(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.file_directory(segment_info=segment_info), None)
with replace_env('HOME', '/home/foo'):
self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), None)
with replace_env('HOME', '/home/foo', os.environ):
with vim_module._with('buffer', '/tmp/abc') as segment_info:
self.assertEqual(vim.file_directory(segment_info=segment_info), '/tmp/')
self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '/tmp/')
os.environ['HOME'] = '/tmp'
self.assertEqual(vim.file_directory(segment_info=segment_info), '~/')
self.assertEqual(vim.file_directory(pl=pl, segment_info=segment_info), '~/')
def test_file_name(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.file_name(segment_info=segment_info), None)
self.assertEqual(vim.file_name(segment_info=segment_info, display_no_file=True),
self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), None)
self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True),
[{'contents': '[No file]', 'highlight_group': ['file_name_no_file', 'file_name']}])
self.assertEqual(vim.file_name(segment_info=segment_info, display_no_file=True, no_file_text='X'),
self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info, display_no_file=True, no_file_text='X'),
[{'contents': 'X', 'highlight_group': ['file_name_no_file', 'file_name']}])
with vim_module._with('buffer', '/tmp/abc') as segment_info:
self.assertEqual(vim.file_name(segment_info=segment_info), 'abc')
self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), 'abc')
with vim_module._with('buffer', '/tmp/') as segment_info:
self.assertEqual(vim.file_name(segment_info=segment_info), '')
self.assertEqual(vim.file_name(pl=pl, segment_info=segment_info), '')
def test_file_size(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.file_size(segment_info=segment_info), '0 B')
self.assertEqual(vim.file_size(pl=pl, segment_info=segment_info), '0 B')
with vim_module._with('buffer', os.path.join(os.path.dirname(__file__), 'empty')) as segment_info:
self.assertEqual(vim.file_size(segment_info=segment_info), '0 B')
self.assertEqual(vim.file_size(pl=pl, segment_info=segment_info), '0 B')
def test_file_opts(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.file_format(segment_info=segment_info),
self.assertEqual(vim.file_format(pl=pl, segment_info=segment_info),
[{'divider_highlight_group': 'background:divider', 'contents': 'unix'}])
self.assertEqual(vim.file_encoding(segment_info=segment_info),
self.assertEqual(vim.file_encoding(pl=pl, segment_info=segment_info),
[{'divider_highlight_group': 'background:divider', 'contents': 'utf-8'}])
self.assertEqual(vim.file_type(segment_info=segment_info), None)
self.assertEqual(vim.file_type(pl=pl, segment_info=segment_info), None)
with vim_module._with('bufoptions', filetype='python'):
self.assertEqual(vim.file_type(segment_info=segment_info),
self.assertEqual(vim.file_type(pl=pl, segment_info=segment_info),
[{'divider_highlight_group': 'background:divider', 'contents': 'python'}])
def test_line_percent(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
segment_info['buffer'][0:-1] = [str(i) for i in range(100)]
try:
self.assertEqual(vim.line_percent(segment_info=segment_info), '1')
self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info), '1')
vim_module._set_cursor(50, 0)
self.assertEqual(vim.line_percent(segment_info=segment_info), '50')
self.assertEqual(vim.line_percent(segment_info=segment_info, gradient=True),
self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info), '50')
self.assertEqual(vim.line_percent(pl=pl, segment_info=segment_info, gradient=True),
[{'contents': '50', 'highlight_group': ['line_percent_gradient', 'line_percent'], 'gradient_level': 50 * 100.0 / 101}])
finally:
vim_module._bw(segment_info['bufnr'])
def test_cursor_current(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.line_current(segment_info=segment_info), '1')
self.assertEqual(vim.col_current(segment_info=segment_info), '1')
self.assertEqual(vim.virtcol_current(segment_info=segment_info),
self.assertEqual(vim.line_current(pl=pl, segment_info=segment_info), '1')
self.assertEqual(vim.col_current(pl=pl, segment_info=segment_info), '1')
self.assertEqual(vim.virtcol_current(pl=pl, segment_info=segment_info),
[{'highlight_group': ['virtcol_current', 'col_current'], 'contents': '1'}])
def test_modified_buffers(self):
self.assertEqual(vim.modified_buffers(), None)
pl = Pl()
self.assertEqual(vim.modified_buffers(pl=pl), None)
def test_branch(self):
pl = Pl()
with vim_module._with('buffer', '/foo') as segment_info:
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)):
self.assertEqual(vim.branch(segment_info=segment_info),
with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)):
self.assertEqual(vim.branch(pl=pl, segment_info=segment_info),
[{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}])
self.assertEqual(vim.branch(segment_info=segment_info, status_colors=True),
self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True),
[{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_clean', 'branch'], 'contents': 'foo'}])
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)):
self.assertEqual(vim.branch(segment_info=segment_info),
with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)):
self.assertEqual(vim.branch(pl=pl, segment_info=segment_info),
[{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}])
self.assertEqual(vim.branch(segment_info=segment_info, status_colors=True),
self.assertEqual(vim.branch(pl=pl, segment_info=segment_info, status_colors=True),
[{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch_dirty', 'branch'], 'contents': 'foo'}])
def test_file_vcs_status(self):
pl = Pl()
with vim_module._with('buffer', '/foo') as segment_info:
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)):
self.assertEqual(vim.file_vcs_status(segment_info=segment_info),
with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)):
self.assertEqual(vim.file_vcs_status(pl=pl, segment_info=segment_info),
[{'highlight_group': ['file_vcs_status_M', 'file_vcs_status'], 'contents': 'M'}])
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: None, directory=path)):
self.assertEqual(vim.file_vcs_status(segment_info=segment_info), None)
with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: None, directory=path)):
self.assertEqual(vim.file_vcs_status(pl=pl, segment_info=segment_info), None)
with vim_module._with('buffer', '/bar') as segment_info:
with vim_module._with('bufoptions', buftype='nofile'):
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)):
self.assertEqual(vim.file_vcs_status(segment_info=segment_info), None)
with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda file: 'M', directory=path)):
self.assertEqual(vim.file_vcs_status(pl=pl, segment_info=segment_info), None)
def test_repository_status(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)):
self.assertEqual(vim.repository_status(segment_info=segment_info), None)
with replace_module_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)):
self.assertEqual(vim.repository_status(segment_info=segment_info), 'DU')
with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None, directory=path)):
self.assertEqual(vim.repository_status(pl=pl, segment_info=segment_info), None)
with replace_attr(vim, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: 'DU', directory=path)):
self.assertEqual(vim.repository_status(pl=pl, segment_info=segment_info), 'DU')
old_cwd = None