Merge remote-tracking branch 'zyx-i/threaded' into develop

This commit is contained in:
Kim Silkebækken 2013-03-18 08:24:54 +01:00
commit f0f1f3f85e
32 changed files with 1811 additions and 374 deletions

2
.local.vimrc Normal file
View File

@ -0,0 +1,2 @@
setlocal noexpandtab
let g:syntastic_python_flake8_args = '--ignore=W191,E501,E121,E122,E123,E128'

View File

@ -3,9 +3,10 @@
import os
import sys
sys.path.insert(0, os.path.abspath('../..'))
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(os.getcwd()))))
sys.path.insert(0, os.path.abspath(os.getcwd()))
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
extensions = ['powerline_autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
source_suffix = '.rst'
master_doc = 'index'
project = u'Powerline'

View File

@ -0,0 +1,78 @@
from sphinx.ext import autodoc
from sphinx.util.inspect import getargspec
from inspect import ArgSpec, getargspec, formatargspec
from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment
from itertools import count
try:
from __builtin__ import unicode
except ImportError:
unicode = lambda s, enc: s
def formatvalue(val):
if type(val) is str:
return '="' + unicode(val, 'utf-8').replace('"', '\\"').replace('\\', '\\\\') + '"'
else:
return '=' + repr(val)
class ThreadedDocumenter(autodoc.FunctionDocumenter):
'''Specialized documenter subclass for ThreadedSegment subclasses.'''
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return (isinstance(member, ThreadedSegment) or
super(ThreadedDocumenter, cls).can_document_member(member, membername, isattr, parent))
def format_args(self):
if isinstance(self.object, ThreadedSegment):
args = ['interval']
defaults = [getattr(self.object, 'interval', 1)]
methods = ['render', 'set_state']
if isinstance(self.object, KwThreadedSegment):
methods += ['key', 'render_one']
for method in methods:
if hasattr(self.object, method):
# Note: on <python-2.6 it may return simple tuple, not
# ArgSpec instance.
argspec = getargspec(getattr(self.object, method))
for i, arg in zip(count(-1, -1), reversed(argspec.args)):
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))):
continue
if argspec.defaults and len(argspec.defaults) >= -i:
default = argspec.defaults[i]
defaults.append(default)
args.append(arg)
else:
args.insert(0, arg)
argspec = ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults))
else:
if hasattr(self.object, 'powerline_origin'):
obj = self.object.powerline_origin
else:
obj = self.object
argspec = getargspec(obj)
args = []
defaults = []
for i, arg in zip(count(-1, -1), reversed(argspec.args)):
if (arg == 'segment_info' and getattr(self.object, 'powerline_requires_segment_info', None)):
continue
if argspec.defaults and len(argspec.defaults) >= -i:
default = argspec.defaults[i]
defaults.append(default)
args.append(arg)
else:
args.insert(0, arg)
argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=tuple(defaults))
return formatargspec(*argspec, formatvalue=formatvalue).replace('\\', '\\\\')
def setup(app):
autodoc.setup(app)
app.add_autodocumenter(ThreadedDocumenter)

View File

@ -36,7 +36,7 @@ class Powerline(object):
the package imported like this: ``powerline.renders.{render_module}``.
'''
def __init__(self, ext, renderer_module=None):
def __init__(self, ext, renderer_module=None, run_once=False):
self.config_paths = self.get_config_paths()
# Load main config file
@ -58,6 +58,7 @@ class Powerline(object):
'ext': ext,
'common_config': common_config,
'segment_info': self.get_segment_info(),
'run_once': run_once,
}
local_themes = self.get_local_themes(ext_config.get('local_themes'))
@ -69,11 +70,12 @@ class Powerline(object):
except ImportError as e:
sys.stderr.write('Error while importing renderer module: {0}\n'.format(e))
sys.exit(1)
options = {'term_truecolor': common_config.get('term_truecolor', False),
'ambiwidth': common_config.get('ambiwidth', 1),
'tmux_escape': common_config.get('additional_escapes') == 'tmux',
'screen_escape': common_config.get('additional_escapes') == 'screen',
}
options = {
'term_truecolor': common_config.get('term_truecolor', False),
'ambiwidth': common_config.get('ambiwidth', 1),
'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)
@staticmethod

View File

@ -2,6 +2,7 @@
from powerline.ipython import IpythonPowerline
from IPython.core.prompts import PromptManager
from IPython.core.hooks import TryNext
class PowerlinePromptManager(PromptManager):
@ -41,6 +42,12 @@ def load_ipython_extension(ip):
ip.prompt_manager = PowerlinePromptManager(powerline=powerline,
shell=ip.prompt_manager.shell, config=ip.prompt_manager.config)
def shutdown_hook():
powerline.renderer.shutdown()
raise TryNext()
ip.hooks.shutdown_hook.add(shutdown_hook)
def unload_ipython_extension(ip):
ip.prompt_manager = old_prompt_manager

View File

@ -2,6 +2,7 @@
from powerline.ipython import IpythonPowerline
from IPython.Prompts import BasePrompt
from IPython.ipapi import get as get_ipython
from IPython.ipapi import TryNext
class PowerlinePrompt(BasePrompt):
@ -38,4 +39,11 @@ def setup(prompt='1', **kwargs):
old_prompt = getattr(ip.IP.outputcache, attr)
setattr(ip.IP.outputcache, attr, PowerlinePrompt(powerline,
old_prompt.cache, old_prompt.sep, '', old_prompt.pad_left))
raise TryNext()
def shutdown_hook():
powerline.renderer.shutdown()
raise TryNext()
ip.IP.hooks.late_startup_hook.add(late_startup_hook)
ip.IP.hooks.shutdown_hook.add(shutdown_hook)

View File

@ -92,4 +92,5 @@ 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

View File

@ -2,9 +2,13 @@
from functools import wraps
import json
from powerline.lib.memoize import memoize # NOQA
from powerline.lib.humanize_bytes import humanize_bytes # NOQA
from powerline.lib.url import urllib_read, urllib_urlencode # NOQA
def wraps_saveargs(wrapped):
def dec(wrapper):
r = wraps(wrapped)(wrapper)
r.powerline_origin = getattr(wrapped, 'powerline_origin', wrapped)
return r
return dec
def mergedicts(d1, d2):
@ -19,7 +23,7 @@ def mergedicts(d1, d2):
def add_divider_highlight_group(highlight_group):
def dec(func):
@wraps(func)
@wraps_saveargs(func)
def f(**kwargs):
r = func(**kwargs)
if r:

View File

@ -1,7 +1,7 @@
# vim:fileencoding=utf-8:noet
from functools import wraps
import time
from powerline.lib.time import monotonic
def default_cache_key(**kwargs):
@ -28,10 +28,13 @@ class memoize(object):
cached = self.cache.get(key, None)
except TypeError:
return func(**kwargs)
if cached is None or time.time() - cached['time'] > self.timeout:
# Handle case when time() appears to be less then cached['time'] due
# to clock updates. Not applicable for monotonic clock, but this
# case is currently rare.
if cached is None or not (cached['time'] < monotonic() < cached['time'] + self.timeout):
cached = self.cache[key] = {
'result': func(**kwargs),
'time': time.time(),
'time': monotonic(),
}
return cached['result']
return decorated_function

131
powerline/lib/threaded.py Normal file
View File

@ -0,0 +1,131 @@
# vim:fileencoding=utf-8:noet
from __future__ import absolute_import
from powerline.lib.time import monotonic
from time import sleep
from threading import Thread, Lock
class ThreadedSegment(Thread):
daemon = True
min_sleep_time = 0.1
def __init__(self):
super(ThreadedSegment, self).__init__()
self.update_lock = Lock()
self.write_lock = Lock()
self.keep_going = True
self.run_once = True
self.did_set_interval = False
def __call__(self, **kwargs):
if self.run_once:
self.set_state(**kwargs)
self.update()
elif not self.is_alive():
self.startup(**kwargs)
with self.write_lock:
return self.render(**kwargs)
def sleep(self, adjust_time):
sleep(max(self.interval - adjust_time, self.min_sleep_time))
def run(self):
while self.keep_going:
start_time = monotonic()
with self.update_lock:
self.update()
self.sleep(monotonic() - start_time)
def shutdown(self):
self.keep_going = False
self.update_lock.acquire()
def set_interval(self, interval=None, **kwargs):
# Allowing “interval” keyword in configuration.
# Note: Here **kwargs is needed to support foreign data, in subclasses
# it can be seen in a number of places in order to support
# .set_interval().
interval = interval or getattr(self, 'interval', 1)
self.interval = interval
self.has_set_interval = True
def set_state(self, **kwargs):
if not self.did_set_interval:
self.set_interval(**kwargs)
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”.
self.run_once = False
self.set_state(**kwargs)
if not self.is_alive():
self.start()
def printed(func):
def f(*args, **kwargs):
print(func.__name__)
return func(*args, **kwargs)
return f
class KwThreadedSegment(ThreadedSegment):
drop_interval = 10 * 60
def __init__(self):
super(KwThreadedSegment, self).__init__()
self.queries = {}
self.update_missing = True
@staticmethod
def key(**kwargs):
return frozenset(kwargs.items())
def render(self, **kwargs):
key = self.key(**kwargs)
try:
update_state = self.queries[key][1]
except KeyError:
update_state = self.compute_state(key) if self.update_missing 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)
def update(self):
updates = {}
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))
else:
removes.append(key)
with self.write_lock:
self.queries.update(updates)
for key in removes:
self.queries.pop(key)
def set_state(self, **kwargs):
if not self.did_set_interval or ('interval' in kwargs and self.interval > kwargs['interval']):
self.set_interval(**kwargs)
key = self.key(**kwargs)
self.queries[key] = (monotonic(), None)
@staticmethod
def render_one(update_state, **kwargs):
return update_state
def with_docstring(instance, doc):
instance.__doc__ = doc
return instance

103
powerline/lib/time.py Normal file
View File

@ -0,0 +1,103 @@
# vim:fileencoding=utf-8:noet
from __future__ import division, absolute_import
try:
try:
# >=python-3.3, Unix
from time import clock_gettime
try:
# >={kernel}-sources-2.6.28
from time import CLOCK_MONOTONIC_RAW as CLOCK_ID
except ImportError:
from time import CLOCK_MONOTONIC as CLOCK_ID # NOQA
monotonic = lambda: clock_gettime(CLOCK_ID)
except ImportError:
# >=python-3.3
from time import monotonic # NOQA
except ImportError:
import ctypes
import sys
try:
if sys.platform == 'win32':
# Windows only
GetTickCount64 = ctypes.windll.kernel32.GetTickCount64
GetTickCount64.restype = ctypes.c_ulonglong
def monotonic(): # NOQA
return GetTickCount64() / 1000
elif sys.platform == 'darwin':
# Mac OS X
from ctypes.util import find_library
libc_name = find_library('c')
if not libc_name:
raise OSError
libc = ctypes.CDLL(libc_name, use_errno=True)
mach_absolute_time = libc.mach_absolute_time
mach_absolute_time.argtypes = ()
mach_absolute_time.restype = ctypes.c_uint64
class mach_timebase_info_data_t(ctypes.Structure):
_fields_ = (
('numer', ctypes.c_uint32),
('denom', ctypes.c_uint32),
)
mach_timebase_info_data_p = ctypes.POINTER(mach_timebase_info_data_t)
_mach_timebase_info = libc.mach_timebase_info
_mach_timebase_info.argtypes = (mach_timebase_info_data_p,)
_mach_timebase_info.restype = ctypes.c_int
def mach_timebase_info():
timebase = mach_timebase_info_data_t()
_mach_timebase_info(ctypes.byref(timebase))
return (timebase.numer, timebase.denom)
timebase = mach_timebase_info()
factor = timebase[0] / timebase[1] * 1e-9
def monotonic(): # NOQA
return mach_absolute_time() * factor
else:
# linux only (no librt on OS X)
import os
# See <bits/time.h>
CLOCK_MONOTONIC = 1
CLOCK_MONOTONIC_RAW = 4
class timespec(ctypes.Structure):
_fields_ = (
('tv_sec', ctypes.c_long),
('tv_nsec', ctypes.c_long)
)
tspec = timespec()
librt = ctypes.CDLL('librt.so.1', use_errno=True)
clock_gettime = librt.clock_gettime
clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
if clock_gettime(CLOCK_MONOTONIC_RAW, ctypes.pointer(tspec)) == 0:
# >={kernel}-sources-2.6.28
clock_id = CLOCK_MONOTONIC_RAW
elif clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) == 0:
clock_id = CLOCK_MONOTONIC
else:
raise OSError
def monotonic(): # NOQA
if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) != 0:
errno_ = ctypes.get_errno()
raise OSError(errno_, os.strerror(errno_))
return tspec.tv_sec + tspec.tv_nsec / 1e9
except:
from time import time as monotonic # NOQA

View File

@ -1,26 +1,16 @@
# vim:fileencoding=utf-8:noet
try:
from urllib.error import HTTPError
from urllib.request import urlopen
from urllib.parse import urlencode as urllib_urlencode # NOQA
except ImportError:
from urllib2 import urlopen, HTTPError # NOQA
from urllib import urlencode as urllib_urlencode # NOQA
def urllib_read(url):
try:
import urllib.error
import urllib.request
try:
return urllib.request.urlopen(url, timeout=5).read().decode('utf-8')
except:
return
except ImportError:
import urllib2
try:
return urllib2.urlopen(url, timeout=5).read()
except:
return
def urllib_urlencode(string):
try:
import urllib.parse
return urllib.parse.urlencode(string)
except ImportError:
import urllib
return urllib.urlencode(string)
return urlopen(url, timeout=10).read().decode('utf-8')
except HTTPError:
return

View File

@ -1,7 +1,6 @@
# vim:fileencoding=utf-8:noet
from __future__ import absolute_import
import os
from powerline.lib.memoize import memoize
vcs_props = (
@ -16,12 +15,11 @@ def generate_directories(path):
while True:
old_path = path
path = os.path.dirname(path)
if path == old_path:
if path == old_path or not path:
break
yield path
@memoize(100)
def guess(path):
for directory in generate_directories(path):
for vcs, vcs_dir, check in vcs_props:

View File

@ -97,8 +97,9 @@ except ImportError:
def readlines(cmd, cwd):
p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd)
p.stderr.close()
for line in p.stdout:
yield line[:-1].decode('utf-8')
with p.stdout:
for line in p.stdout:
yield line[:-1].decode('utf-8')
class Repository(object):
__slots__ = ('directory',)

View File

@ -26,12 +26,12 @@ class Renderer(object):
self.theme_kwargs = theme_kwargs
self.colorscheme = colorscheme
self.width_data = {
'N': 1, # Neutral
'Na': 1, # Narrow
'A': getattr(self, 'ambiwidth', 1), # Ambigious
'H': 1, # Half-width
'W': 2, # Wide
'F': 2, # Fullwidth
'N': 1, # Neutral
'Na': 1, # Narrow
'A': getattr(self, 'ambiwidth', 1), # Ambigious
'H': 1, # Half-width
'W': 2, # Wide
'F': 2, # Fullwidth
}
def strwidth(self, string):
@ -40,6 +40,9 @@ class Renderer(object):
def get_theme(self, matcher_info):
return self.theme
def shutdown(self):
self.theme.shutdown()
def get_highlighting(self, segment, mode):
segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level'))
if segment['divider_highlight_group']:

View File

@ -31,6 +31,12 @@ class VimRenderer(Renderer):
super(VimRenderer, self).__init__(*args, **kwargs)
self.hl_groups = {}
def shutdown(self):
self.theme.shutdown()
for match in self.local_themes.values():
if 'theme' in match:
match['theme'].shutdown()
def add_local_theme(self, matcher, theme):
if matcher in self.local_themes:
raise KeyError('There is already a local theme with given matcher')

View File

@ -79,6 +79,8 @@ def gen_segment_getter(ext, path, theme_configs, default_module=None):
'include_modes': segment.get('include_modes', []),
'width': segment.get('width'),
'align': segment.get('align', 'l'),
'shutdown': getattr(contents_func, 'shutdown', None),
'startup': getattr(contents_func, 'startup', None),
'_rendered_raw': '',
'_rendered_hl': '',
'_len': 0,

View File

@ -1,5 +1,7 @@
# vim:fileencoding=utf-8:noet
from __future__ import absolute_import
import os
import sys
@ -7,8 +9,13 @@ from datetime import datetime
import socket
from multiprocessing import cpu_count
from powerline.lib import memoize, urllib_read, urllib_urlencode, add_divider_highlight_group
from powerline.lib import add_divider_highlight_group
from powerline.lib.url import urllib_read, urllib_urlencode
from powerline.lib.vcs import guess
from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment, with_docstring
from powerline.lib.time import monotonic
from powerline.lib.humanize_bytes import humanize_bytes
from collections import namedtuple
def hostname(only_if_ssh=False):
@ -22,27 +29,8 @@ def hostname(only_if_ssh=False):
return socket.gethostname()
def user():
'''Return the current user.
Highlights the user with the ``superuser`` if the effective user ID is 0.
Highlight groups used: ``superuser`` or ``user``. It is recommended to define all highlight groups.
'''
user = os.environ.get('USER')
try:
euid = os.geteuid()
except AttributeError:
# os.geteuid is not available on windows
euid = 1
return [{
'contents': user,
'highlight_group': 'user' if euid != 0 else ['superuser', 'user'],
}]
def branch(status_colors=True):
'''Return the current VCS branch.@
'''Return the current VCS branch.
:param bool status_colors:
determines whether repository status will be used to determine highlighting. Default: True.
@ -180,53 +168,38 @@ def fuzzy_time():
return ' '.join([minute, hour])
@memoize(600)
def _external_ip(query_url='http://ipv4.icanhazip.com/'):
return urllib_read(query_url).strip()
def external_ip(query_url='http://ipv4.icanhazip.com/'):
'''Return external IP address.
class ExternalIpSegment(ThreadedSegment):
def set_state(self, query_url='http://ipv4.icanhazip.com/', **kwargs):
super(ExternalIpSegment, self).set_state(**kwargs)
self.query_url = query_url
Suggested URIs:
def update(self):
ip = _external_ip(query_url=self.query_url)
with self.write_lock:
self.ip = ip
* http://ipv4.icanhazip.com/
* http://ipv6.icanhazip.com/
* http://icanhazip.com/ (returns IPv6 address if available, else IPv4)
:param str query_url:
URI to query for IP address, should return only the IP address as a text string
Divider highlight group used: ``background:divider``.
'''
return [{'contents': _external_ip(query_url=query_url), 'divider_highlight_group': 'background:divider'}]
def render(self):
return [{'contents': self.ip, 'divider_highlight_group': 'background:divider'}]
@add_divider_highlight_group('background:divider')
def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'):
'''Return system uptime.
external_ip = with_docstring(ExternalIpSegment(),
'''Return external IP address.
Uses the ``psutil`` module if available for multi-platform compatibility,
falls back to reading :file:`/proc/uptime`.
Suggested URIs:
:param str format:
format string, will be passed ``days``, ``hours`` and ``minutes`` as arguments
* http://ipv4.icanhazip.com/
* http://ipv6.icanhazip.com/
* http://icanhazip.com/ (returns IPv6 address if available, else IPv4)
Divider highlight group used: ``background:divider``.
'''
try:
import psutil
seconds = int((datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME)).total_seconds())
except ImportError:
try:
with open('/proc/uptime', 'r') as f:
seconds = int(float(f.readline().split()[0]))
except IOError:
return None
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
days, hours = divmod(hours, 24)
return format.format(days=int(days), hours=hours, minutes=minutes)
:param str query_url:
URI to query for IP address, should return only the IP address as a text string
Divider highlight group used: ``background:divider``.
''')
# Weather condition code descriptions available at
@ -304,79 +277,123 @@ weather_conditions_icons = {
'unknown': '',
}
temp_conversions = {
'C': lambda temp: temp,
'F': lambda temp: (temp * 9 / 5) + 32,
'K': lambda temp: temp + 273.15,
}
@memoize(1800)
def weather(unit='c', location_query=None, icons=None):
'''Return weather from Yahoo! Weather.
# Note: there are also unicode characters for units: ℃, ℉ and
temp_units = {
'C': '°C',
'F': '°F',
'K': 'K',
}
Uses GeoIP lookup from http://freegeoip.net/ to automatically determine
your current location. This should be changed if you're in a VPN or if your
IP address is registered at another location.
Returns a list of colorized icon and temperature segments depending on
weather conditions.
class WeatherSegment(ThreadedSegment):
interval = 600
:param str unit:
temperature unit, can be one of ``F``, ``C`` or ``K``
:param str location_query:
location query for your current location, e.g. ``oslo, norway``
:param dict icons:
dict for overriding default icons, e.g. ``{'heavy_snow' : u''}``
def set_state(self, location_query=None, **kwargs):
super(WeatherSegment, self).set_state(**kwargs)
self.location = location_query
self.url = None
self.condition = {}
Divider highlight group used: ``background:divider``.
def update(self):
import json
Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_cold`` or ``weather_temp_hot`` or ``weather_temp`` or ``weather``.
Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo.
'''
import json
if not self.url:
# 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
query_data = {
'q':
'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;'
'select * from we where location="{0}" and unit="c"'.format(self.location).encode('utf-8'),
'format': 'json',
}
self.url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data)
if not location_query:
try:
location = json.loads(urllib_read('http://freegeoip.net/json/' + _external_ip()))
location_query = ','.join([location['city'], location['region_name'], location['country_name']])
except (TypeError, ValueError):
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):
return
try:
icon_names = weather_conditions_codes[condition_code]
except IndexError:
icon_names = (('not_available' if condition_code == 3200 else 'unknown'),)
with self.write_lock:
self.temp = temp
self.icon_names = icon_names
def render(self, icons=None, unit='C', temperature_format=None, **kwargs):
if not hasattr(self, 'icon_names'):
return None
query_data = {
'q':
'use "http://github.com/yql/yql-tables/raw/master/weather/weather.bylocation.xml" as we;'
'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit).encode('utf-8'),
'format': 'json'
}
try:
url = 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data)
response = json.loads(urllib_read(url))
condition = response['query']['results']['weather']['rss']['channel']['item']['condition']
condition_code = int(condition['code'])
except (KeyError, TypeError, ValueError):
return None
try:
icon_names = weather_conditions_codes[condition_code]
except IndexError:
icon_names = (('not_available' if condition_code == 3200 else 'unknown'),)
for icon_name in self.icon_names:
if icons:
if icon_name in icons:
icon = icons[icon_name]
break
else:
icon = weather_conditions_icons[self.icon_names[-1]]
for icon_name in icon_names:
if icons:
if icon_name in icons:
icon = icons[icon_name]
break
else:
icon = weather_conditions_icons[icon_names[-1]]
temperature_format = temperature_format or ('{temp:.0f}' + temp_units[unit])
temp = temp_conversions[unit](self.temp)
groups = ['weather_condition_' + icon_name for icon_name in self.icon_names] + ['weather_conditions', 'weather']
return [
{
'contents': icon + ' ',
'highlight_group': groups,
'divider_highlight_group': 'background:divider',
},
{
'contents': temperature_format.format(temp=temp),
'highlight_group': ['weather_temp_cold' if int(self.temp) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'],
'draw_divider': False,
'divider_highlight_group': 'background:divider',
},
]
groups = ['weather_condition_' + icon_name for icon_name in icon_names] + ['weather_conditions', 'weather']
return [
{
'contents': icon + ' ',
'highlight_group': groups,
'divider_highlight_group': 'background:divider',
},
{
'contents': '{0}°{1}'.format(condition['temp'], unit.upper()),
'highlight_group': ['weather_temp_cold' if int(condition['temp']) < 0 else 'weather_temp_hot', 'weather_temp', 'weather'],
'draw_divider': False,
'divider_highlight_group': 'background:divider',
},
]
weather = with_docstring(WeatherSegment(),
'''Return weather from Yahoo! Weather.
Uses GeoIP lookup from http://freegeoip.net/ to automatically determine
your current location. This should be changed if you're in a VPN or if your
IP address is registered at another location.
Returns a list of colorized icon and temperature segments depending on
weather conditions.
:param str unit:
temperature unit, can be one of ``F``, ``C`` or ``K``
:param str location_query:
location query for your current location, e.g. ``oslo, norway``
:param dict icons:
dict for overriding default icons, e.g. ``{'heavy_snow' : u''}``
:param str temperature_format:
format string, receives ``temp`` as an argument. Should also hold unit.
Divider highlight group used: ``background:divider``.
Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_cold`` or ``weather_temp_hot`` or ``weather_temp`` or ``weather``.
Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo.
''')
def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2):
@ -397,7 +414,11 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2):
Highlight groups used: ``system_load_good`` or ``system_load``, ``system_load_bad`` or ``system_load``, ``system_load_ugly`` or ``system_load``. It is recommended to define all highlight groups.
'''
cpu_num = cpu_count()
global cpu_count
try:
cpu_num = cpu_count()
except NotImplementedError:
return None
ret = []
for avg in os.getloadavg():
normalized = avg / cpu_num
@ -419,69 +440,175 @@ def system_load(format='{avg:.1f}', threshold_good=1, threshold_bad=2):
return ret
def cpu_load_percent(measure_interval=.5):
'''Return the average CPU load as a percentage.
try:
import psutil
Requires the ``psutil`` module.
def _get_bytes(interface):
io_counters = psutil.network_io_counters(pernic=True)
if_io = io_counters.get(interface)
if not if_io:
return None
return if_io.bytes_recv, if_io.bytes_sent
:param float measure_interval:
interval used to measure CPU load (in seconds)
'''
try:
import psutil
except ImportError:
def _get_user():
return psutil.Process(os.getpid()).username
def cpu_load_percent(measure_interval=.5):
'''Return the average CPU load as a percentage.
Requires the ``psutil`` module.
:param float measure_interval:
interval used to measure CPU load (in seconds)
'''
cpu_percent = int(psutil.cpu_percent(interval=measure_interval))
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
def _get_user(): # NOQA
return os.environ.get('USER', None)
def cpu_load_percent(measure_interval=.5): # NOQA
'''Return the average CPU load as a percentage.
Requires the ``psutil`` module.
:param float measure_interval:
interval used to measure CPU load (in seconds)
'''
return None
cpu_percent = int(psutil.cpu_percent(interval=measure_interval))
return '{0}%'.format(cpu_percent)
username = False
def user():
'''Return the current user.
Highlights the user with the ``superuser`` if the effective user ID is 0.
Highlight groups used: ``superuser`` or ``user``. It is recommended to define all highlight groups.
'''
global username
if username is False:
username = _get_user()
if username is None:
return None
try:
euid = os.geteuid()
except AttributeError:
# os.geteuid is not available on windows
euid = 1
return [{
'contents': username,
'highlight_group': 'user' if euid != 0 else ['superuser', 'user'],
}]
if os.path.exists('/proc/uptime'):
def _get_uptime():
with open('/proc/uptime', 'r') as f:
return int(float(f.readline().split()[0]))
elif 'psutil' in globals():
from time import time
def _get_uptime(): # NOQA
# psutil.BOOT_TIME is not subject to clock adjustments, but time() is.
# Thus it is a fallback to /proc/uptime reading and not the reverse.
return int(time() - psutil.BOOT_TIME)
else:
def _get_uptime(): # NOQA
raise NotImplementedError
@add_divider_highlight_group('background:divider')
def network_load(interface='eth0', measure_interval=1, suffix='B/s', si_prefix=False):
'''Return the network load.
def uptime(format='{days}d {hours:02d}h {minutes:02d}m'):
'''Return system uptime.
Uses the ``psutil`` module if available for multi-platform compatibility,
falls back to reading
:file:`/sys/class/net/{interface}/statistics/{rx,tx}_bytes`.
:param str format:
format string, will be passed ``days``, ``hours``, ``minutes`` and
seconds as arguments
:param str interface:
network interface to measure
:param float measure_interval:
interval used to measure the network load (in seconds)
:param str suffix:
string appended to each load string
:param bool si_prefix:
use SI prefix, e.g. MB instead of MiB
Divider highlight group used: ``background:divider``.
'''
import time
from powerline.lib import humanize_bytes
def get_bytes():
try:
import psutil
io_counters = psutil.network_io_counters(pernic=True)
if_io = io_counters.get(interface)
if not if_io:
return None
return (if_io.bytes_recv, if_io.bytes_sent)
except ImportError:
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
b1 = get_bytes()
if b1 is None:
try:
seconds = _get_uptime()
except (IOError, NotImplementedError):
return None
time.sleep(measure_interval)
b2 = get_bytes()
return '{rx_diff}{tx_diff}'.format(
rx_diff=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix).rjust(8),
tx_diff=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix).rjust(8),
)
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
days, hours = divmod(hours, 24)
return format.format(days=int(days), hours=hours, minutes=minutes, seconds=seconds)
class NetworkLoadSegment(KwThreadedSegment):
interfaces = {}
@staticmethod
def key(interface='eth0', **kwargs):
return interface
def compute_state(self, interface):
if interface in self.interfaces:
idata = self.interfaces[interface]
try:
idata['prev'] = idata['last']
except KeyError:
pass
else:
idata = {}
if self.run_once:
idata['prev'] = (monotonic(), _get_bytes(interface))
self.sleep(0)
self.interfaces[interface] = idata
idata['last'] = (monotonic(), _get_bytes(interface))
return idata
def render_one(self, idata, format='{recv:>8}{sent:>8}', suffix='B/s', si_prefix=False, **kwargs):
if not idata or 'prev' not in idata:
return None
t1, b1 = idata['prev']
t2, b2 = idata['last']
measure_interval = t2 - t1
if None in (b1, b2):
return None
return [{
'contents': format.format(
recv=humanize_bytes((b2[0] - b1[0]) / measure_interval, suffix, si_prefix),
sent=humanize_bytes((b2[1] - b1[1]) / measure_interval, suffix, si_prefix),
),
'divider_highlight_group': 'background:divider',
}]
network_load = with_docstring(NetworkLoadSegment(),
'''Return the network load.
Uses the ``psutil`` module if available for multi-platform compatibility,
falls back to reading
:file:`/sys/class/net/{interface}/statistics/{rx,tx}_bytes`.
:param str interface:
network interface to measure
:param str suffix:
string appended to each load string
:param bool si_prefix:
use SI prefix, e.g. MB instead of MiB
:param str format:
format string, receives ``recv`` and ``sent`` as arguments
''')
def virtualenv():
@ -489,44 +616,56 @@ def virtualenv():
return os.path.basename(os.environ.get('VIRTUAL_ENV', '')) or None
@memoize(60)
def email_imap_alert(username, password, server='imap.gmail.com', port=993, folder='INBOX'):
'''Return unread e-mail count for IMAP servers.
_IMAPKey = namedtuple('Key', 'username password server port folder')
:param str username:
login username
:param str password:
login password
:param str server:
e-mail server
:param int port:
e-mail server port
:param str folder:
folder to check for e-mails
Highlight groups used: ``email_alert``.
'''
import imaplib
import re
class EmailIMAPSegment(KwThreadedSegment):
interval = 60
if not username or not password:
return None
try:
mail = imaplib.IMAP4_SSL(server, port)
mail.login(username, password)
rc, message = mail.status(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)
if not unread_count:
return None
return [{
'highlight_group': 'email_alert',
'contents': str(unread_count),
}]
@staticmethod
def key(username, password, server='imap.gmail.com', port=993, folder='INBOX'):
return _IMAPKey(username, password, server, port, folder)
@staticmethod
def compute_state(key):
if not key.username or not key.password:
return None
try:
import imaplib
import re
mail = imaplib.IMAP4_SSL(key.server, key.port)
mail.login(key.username, key.password)
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)
if not unread_count:
return None
return [{
'highlight_group': 'email_alert',
'contents': str(unread_count),
}]
email_imap_alert = with_docstring(EmailIMAPSegment(),
'''Return unread e-mail count for IMAP servers.
:param str username:
login username
:param str password:
login password
:param str server:
e-mail server
:param int port:
e-mail server port
:param str folder:
folder to check for e-mails
Highlight groups used: ``email_alert``.
''')
class NowPlayingSegment(object):

View File

@ -10,9 +10,11 @@ except ImportError:
from powerline.bindings.vim import vim_get_func, getbufvar
from powerline.theme import requires_segment_info
from powerline.lib import memoize, humanize_bytes, add_divider_highlight_group
from powerline.lib import add_divider_highlight_group
from powerline.lib.vcs import guess
from functools import wraps
from powerline.lib.humanize_bytes import humanize_bytes
from powerline.lib.threaded import KwThreadedSegment, with_docstring
from powerline.lib import wraps_saveargs as wraps
from collections import defaultdict
vim_funcs = {
@ -20,6 +22,7 @@ vim_funcs = {
'fnamemodify': vim_get_func('fnamemodify'),
'expand': vim_get_func('expand'),
'bufnr': vim_get_func('bufnr', rettype=int),
'line2byte': vim_get_func('line2byte', rettype=int),
}
vim_modes = {
@ -74,28 +77,18 @@ def launchevent(event):
pass
def bufnr(segment_info, **kwargs):
'''Used for cache key, returns current buffer number'''
return segment_info['bufnr']
def bufname(segment_info, **kwargs):
'''Used for cache key, returns current buffer name'''
return segment_info['buffer'].name
# TODO Remove cache when needed
def window_cached(func):
cache = {}
@requires_segment_info
@wraps(func)
def ret(segment_info, *args, **kwargs):
def ret(segment_info, **kwargs):
window_id = segment_info['window_id']
if segment_info['mode'] == 'nc':
return cache.get(window_id)
else:
r = func(*args, **kwargs)
r = func(**kwargs)
cache[window_id] = r
return r
@ -167,7 +160,7 @@ def file_directory(segment_info, shorten_user=True, shorten_cwd=True, shorten_ho
if not name:
return None
file_directory = vim_funcs['fnamemodify'](name, (':~' if shorten_user else '')
+ (':.' if shorten_home else '') + ':h')
+ (':.' if shorten_cwd else '') + ':h')
if shorten_home and file_directory.startswith('/home/'):
file_directory = '~' + file_directory[6:]
return file_directory + os.sep if file_directory else None
@ -195,10 +188,9 @@ def file_name(segment_info, display_no_file=False, no_file_text='[No file]'):
return file_name
@requires_segment_info
@memoize(2, cache_key=bufname, cache_reg_func=purgebuf_on_shell_and_write)
def file_size(segment_info, suffix='B', si_prefix=False):
'''Return file size.
@window_cached
def file_size(suffix='B', si_prefix=False):
'''Return file size in &encoding.
:param str suffix:
string appended to the file size
@ -206,13 +198,9 @@ def file_size(segment_info, suffix='B', si_prefix=False):
use SI prefix, e.g. MB instead of MiB
:return: file size or None if the file isn't saved or if the size is too big to fit in a number
'''
file_name = segment_info['buffer'].name
if not file_name:
return None
try:
file_size = os.stat(file_name).st_size
except:
return None
# Note: returns file size in &encoding, not in &fileencoding. But returned
# size is updated immediately; and it is valid for any buffer
file_size = vim_funcs['line2byte'](len(vim.current.buffer) + 1) - 1
return humanize_bytes(file_size, suffix, si_prefix)
@ -311,58 +299,135 @@ def modified_buffers(text='+ ', join_str=','):
return None
@requires_segment_info
@memoize(2, cache_key=bufnr, cache_reg_func=purgeall_on_shell)
def branch(segment_info, status_colors=True):
'''Return the current working 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``.
Divider highlight group used: ``branch:divider``.
'''
repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd()))
if repo:
return [{
'contents': repo.branch(),
'highlight_group': (['branch_dirty' if repo.status() else 'branch_clean'] if status_colors else []) + ['branch'],
'divider_highlight_group': 'branch:divider',
}]
return None
class KwWindowThreadedSegment(KwThreadedSegment):
def set_state(self, **kwargs):
for window in vim.windows:
buffer = window.buffer
kwargs['segment_info'] = {'bufnr': buffer.number, 'buffer': buffer}
super(KwWindowThreadedSegment, self).set_state(**kwargs)
@requires_segment_info
@memoize(2, cache_key=bufnr, cache_reg_func=purgebuf_on_shell_and_write)
def file_vcs_status(segment_info):
'''Return the VCS status for this buffer.
class RepositorySegment(KwWindowThreadedSegment):
def __init__(self):
super(RepositorySegment, self).__init__()
self.directories = {}
Highlight groups used: ``file_vcs_status``.
'''
name = segment_info['buffer'].name
if name and not getbufvar(segment_info['bufnr'], '&buftype'):
repo = guess(path=os.path.abspath(name))
@staticmethod
def key(segment_info, **kwargs):
# FIXME os.getcwd() is not a proper variant for non-current buffers
return segment_info['buffer'].name or os.getcwd()
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:
status = repo.status(os.path.relpath(name, repo.directory))
if not status:
return None
status = status.strip()
ret = []
for status in status:
ret.append({
'contents': status,
'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'],
})
return ret
return None
if repo.directory in self.directories:
return self.directories[repo.directory]
else:
r = self.process_repo(repo)
self.directories[repo.directory] = r
return r
@requires_segment_info
@memoize(2, cache_key=bufnr, cache_reg_func=purgeall_on_shell)
def repository_status(segment_info):
'''Return the status for the current repo.'''
repo = guess(path=os.path.abspath(segment_info['buffer'].name or os.getcwd()))
if repo:
return repo.status().strip() or None
return None
class RepositoryStatusSegment(RepositorySegment):
interval = 2
@staticmethod
def process_repo(repo):
return repo.status()
repository_status = with_docstring(RepositoryStatusSegment(),
'''Return the status for the current repo.''')
@requires_segment_info
class BranchSegment(RepositorySegment):
interval = 0.2
started_repository_status = False
@staticmethod
def process_repo(repo):
return repo.branch()
def render_one(self, update_state, segment_info, status_colors=False, **kwargs):
if not update_state:
return None
if status_colors:
self.started_repository_status = True
return [{
'contents': update_state,
'highlight_group': (['branch_dirty' if repository_status(segment_info=segment_info) else 'branch_clean']
if status_colors else []) + ['branch'],
'divider_highlight_group': 'branch:divider',
}]
def startup(self, **kwargs):
super(BranchSegment, self).startup()
if kwargs.get('status_colors', False):
self.started_repository_status = True
repository_status.startup()
def shutdown(self):
if self.started_repository_status:
repository_status.shutdown()
super(BranchSegment, self).shutdown()
branch = with_docstring(BranchSegment(),
'''Return the current working branch.
:param bool status_colors:
determines whether repository status will be used to determine highlighting. Default: False.
Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``.
Divider highlight group used: ``branch:divider``.
''')
@requires_segment_info
class FileVCSStatusSegment(KwWindowThreadedSegment):
interval = 0.2
@staticmethod
def key(segment_info, **kwargs):
name = segment_info['buffer'].name
skip = not (name and (not getbufvar(segment_info['bufnr'], '&buftype')))
return name, skip
@staticmethod
def compute_state(key):
name, skip = key
if not skip:
repo = guess(path=name)
if repo:
status = repo.status(os.path.relpath(name, repo.directory))
if not status:
return None
status = status.strip()
ret = []
for status in status:
ret.append({
'contents': status,
'highlight_group': ['file_vcs_status_' + status, 'file_vcs_status'],
})
return ret
return None
file_vcs_status = with_docstring(FileVCSStatusSegment(),
'''Return the VCS status for this buffer.
Highlight groups used: ``file_vcs_status``.
''')

View File

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

View File

@ -19,12 +19,12 @@ def u(s):
def requires_segment_info(func):
func.requires_powerline_segment_info = True
func.powerline_requires_segment_info = True
return func
class Theme(object):
def __init__(self, ext, theme_config, common_config, top_theme_config=None, segment_info=None):
def __init__(self, ext, theme_config, common_config, top_theme_config=None, segment_info=None, run_once=False):
self.dividers = theme_config.get('dividers', common_config['dividers'])
self.spaces = theme_config.get('spaces', common_config['spaces'])
self.segments = {
@ -42,6 +42,18 @@ class Theme(object):
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]:
if segment['startup']:
segment['startup'](**segment['args'])
def shutdown(self):
for segments in self.segments.values():
for segment in segments:
try:
segment['shutdown']()
except TypeError:
pass
def get_divider(self, side='left', type='soft'):
'''Return segment divider.'''
@ -60,8 +72,8 @@ class Theme(object):
parsed_segments = []
for segment in self.segments[side]:
if segment['type'] == 'function':
if (hasattr(segment['contents_func'], 'requires_powerline_segment_info')
and segment['contents_func'].requires_powerline_segment_info):
if (hasattr(segment['contents_func'], 'powerline_requires_segment_info')
and segment['contents_func'].powerline_requires_segment_info):
contents = segment['contents_func'](segment_info=self.segment_info, **segment['args'])
else:
contents = segment['contents_func'](**segment['args'])

View File

@ -12,7 +12,7 @@ except ImportError:
if __name__ == '__main__':
args = get_argparser(description=__doc__).parse_args()
powerline = ShellPowerline(args)
powerline = ShellPowerline(args, run_once=True)
rendered = powerline.renderer.render(width=args.width, side=args.side)
try:
sys.stdout.write(rendered)

View File

@ -1,5 +1,6 @@
#!/bin/sh
pip install .
pip install psutil
if python -c 'import sys; sys.exit(1 * (sys.version_info[0] != 2))' ; then
# Python 2
pip install mercurial bzr

View File

@ -24,7 +24,7 @@ def urllib_read(query_url):
elif query_url.startswith('http://freegeoip.net/json/'):
return '{"city": "Meppen", "region_code": "06", "region_name": "Niedersachsen", "areacode": "", "ip": "82.145.55.16", "zipcode": "49716", "longitude": 7.3167, "country_name": "Germany", "country_code": "DE", "metrocode": "", "latitude": 52.6833}'
elif query_url.startswith('http://query.yahooapis.com/v1/public/'):
return '{"query":{"count":1,"created":"2013-03-02T13:20:22Z","lang":"en-US","results":{"weather":{"rss":{"version":"2.0","geo":"http://www.w3.org/2003/01/geo/wgs84_pos#","yweather":"http://xml.weather.yahoo.com/ns/rss/1.0","channel":{"title":"Yahoo! Weather - Russia, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","description":"Yahoo! Weather for Russia, RU","language":"en-us","lastBuildDate":"Sat, 02 Mar 2013 4:58 pm MSK","ttl":"60","location":{"city":"Russia","country":"Russia","region":""},"units":{"distance":"km","pressure":"mb","speed":"km/h","temperature":"C"},"wind":{"chill":"-11","direction":"0","speed":""},"atmosphere":{"humidity":"94","pressure":"1006.1","rising":"0","visibility":""},"astronomy":{"sunrise":"10:04 am","sunset":"7:57 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 Russia, RU at 4:58 pm MSK","lat":"59.45","long":"108.83","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","pubDate":"Sat, 02 Mar 2013 4:58 pm MSK","condition":{"code":"30","date":"Sat, 02 Mar 2013 4:58 pm MSK","temp":"-11","text":"Partly Cloudy"},"description":"<img src=\"http://l.yimg.com/a/i/us/we/52/30.gif\"/><br />\n<b>Current Conditions:</b><br />\nPartly Cloudy, -11 C<BR />\n<BR /><b>Forecast:</b><BR />\nSat - Partly Cloudy. High: -9 Low: -19<br />\nSun - Partly Cloudy. High: -12 Low: -18<br />\n<br />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html\">Full Forecast at Yahoo! Weather</a><BR/><BR/>\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)<br/>","forecast":[{"code":"29","date":"2 Mar 2013","day":"Sat","high":"-9","low":"-19","text":"Partly Cloudy"},{"code":"30","date":"3 Mar 2013","day":"Sun","high":"-12","low":"-18","text":"Partly Cloudy"}],"guid":{"isPermaLink":"false","content":"RSXX1511_2013_03_03_7_00_MSK"}}}}}}}}'
return r'{"query":{"count":1,"created":"2013-03-02T13:20:22Z","lang":"en-US","results":{"weather":{"rss":{"version":"2.0","geo":"http://www.w3.org/2003/01/geo/wgs84_pos#","yweather":"http://xml.weather.yahoo.com/ns/rss/1.0","channel":{"title":"Yahoo! Weather - Russia, RU","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","description":"Yahoo! Weather for Russia, RU","language":"en-us","lastBuildDate":"Sat, 02 Mar 2013 4:58 pm MSK","ttl":"60","location":{"city":"Russia","country":"Russia","region":""},"units":{"distance":"km","pressure":"mb","speed":"km/h","temperature":"C"},"wind":{"chill":"-11","direction":"0","speed":""},"atmosphere":{"humidity":"94","pressure":"1006.1","rising":"0","visibility":""},"astronomy":{"sunrise":"10:04 am","sunset":"7:57 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 Russia, RU at 4:58 pm MSK","lat":"59.45","long":"108.83","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html","pubDate":"Sat, 02 Mar 2013 4:58 pm MSK","condition":{"code":"30","date":"Sat, 02 Mar 2013 4:58 pm MSK","temp":"-11","text":"Partly Cloudy"},"description":"<img src=\"http://l.yimg.com/a/i/us/we/52/30.gif\"/><br />\n<b>Current Conditions:</b><br />\nPartly Cloudy, -11 C<BR />\n<BR /><b>Forecast:</b><BR />\nSat - Partly Cloudy. High: -9 Low: -19<br />\nSun - Partly Cloudy. High: -12 Low: -18<br />\n<br />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Russia__RU/*http://weather.yahoo.com/forecast/RSXX1511_c.html\">Full Forecast at Yahoo! Weather</a><BR/><BR/>\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)<br/>","forecast":[{"code":"29","date":"2 Mar 2013","day":"Sat","high":"-9","low":"-19","text":"Partly Cloudy"},{"code":"30","date":"3 Mar 2013","day":"Sun","high":"-12","low":"-18","text":"Partly Cloudy"}],"guid":{"isPermaLink":"false","content":"RSXX1511_2013_03_03_7_00_MSK"}}}}}}}}'
else:
raise NotImplementedError

View File

@ -1,19 +1,12 @@
#!/bin/sh
: ${PYTHON:=python}
FAILED=0
if ${PYTHON} -c 'import sys; sys.exit(1 * (sys.version_info >= (2, 7)))' ; then
# Python 2.6
export PYTHONPATH="${PYTHONPATH}:`realpath .`"
for file in tests/test_*.py ; do
if ! ${PYTHON} $file ; then
exit 1
fi
done
else
if ! ${PYTHON} setup.py test ; then
export PYTHONPATH="${PYTHONPATH}:`realpath .`"
for file in tests/test_*.py ; do
if ! ${PYTHON} $file ; then
FAILED=1
fi
fi
done
if ! ${PYTHON} scripts/powerline-lint ; then
FAILED=1
fi

View File

@ -49,6 +49,7 @@ class TestConfig(TestCase):
check_output(1, 0)
finally:
vim_module._start_mode('n')
powerline.renderer.shutdown()
def test_tmux(self):
from powerline.segments import common
@ -56,16 +57,16 @@ class TestConfig(TestCase):
reload(common)
from powerline.shell import ShellPowerline
with replace_module_attr(common, 'urllib_read', urllib_read):
ShellPowerline(Args(ext=['tmux'])).renderer.render()
ShellPowerline(Args(ext=['tmux']), run_once=True).renderer.render()
reload(common)
def test_zsh(self):
from powerline.shell import ShellPowerline
ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt')).renderer.render()
ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt'), run_once=True).renderer.render()
def test_bash(self):
from powerline.shell import ShellPowerline
ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})])).renderer.render()
ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]), run_once=True).renderer.render()
def test_ipython(self):
from powerline.ipython import IpythonPowerline
@ -75,7 +76,9 @@ class TestConfig(TestCase):
config_overrides = None
theme_overrides = {}
IpyPowerline().renderer.render()
powerline = IpyPowerline()
powerline.renderer.render()
powerline.renderer.shutdown()
def test_wm(self):
from powerline.segments import common
@ -83,7 +86,7 @@ class TestConfig(TestCase):
reload(common)
from powerline import Powerline
with replace_module_attr(common, 'urllib_read', urllib_read):
Powerline(ext='wm', renderer_module='pango_markup').renderer.render()
Powerline(ext='wm', renderer_module='pango_markup', run_once=True).renderer.render()
reload(common)

View File

@ -1,5 +1,6 @@
# vim:fileencoding=utf-8:noet
from powerline.lib import mergedicts, add_divider_highlight_group, humanize_bytes
from powerline.lib import mergedicts, add_divider_highlight_group
from powerline.lib.humanize_bytes import humanize_bytes
from powerline.lib.vcs import guess
from subprocess import call, PIPE
import os

View File

@ -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, replace_module_attr, new_module, replace_module_module, replace_env
from tests.lib import Args, urllib_read, replace_module_attr, new_module, replace_module_module, replace_env
from tests import TestCase
@ -37,13 +37,15 @@ class TestCommon(TestCase):
self.assertEqual(common.hostname(only_if_ssh=True), None)
def test_user(self):
new_os = new_module('os', environ={'USER': 'def'})
new_os = new_module('os', environ={'USER': 'def'}, getpid=lambda: 1)
new_psutil = new_module('psutil', Process=lambda pid: Args(username='def'))
with replace_module_attr(common, 'os', new_os):
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_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']}])
def test_branch(self):
with replace_module_attr(common, 'guess', lambda path: Args(branch=lambda: os.path.basename(path), status=lambda: None)):
@ -128,12 +130,41 @@ class TestCommon(TestCase):
self.assertEqual(common.external_ip(), [{'contents': '127.0.0.1', 'divider_highlight_group': 'background:divider'}])
def test_uptime(self):
# TODO
pass
with replace_module_attr(common, '_get_uptime', lambda: 65536):
self.assertEqual(common.uptime(), [{'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)
def test_weather(self):
# TODO
pass
with replace_module_attr(common, 'urllib_read', urllib_read):
self.assertEqual(common.weather(), [
{'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_cold', 'weather_temp', 'weather'], 'contents': '-11°C'}
])
self.assertEqual(common.weather(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_cold', 'weather_temp', 'weather'], 'contents': '-11°C'}
])
self.assertEqual(common.weather(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_cold', 'weather_temp', 'weather'], 'contents': '-11°C'}
])
self.assertEqual(common.weather(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_cold', 'weather_temp', 'weather'], 'contents': '12°F'}
])
self.assertEqual(common.weather(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_cold', 'weather_temp', 'weather'], 'contents': '262K'}
])
self.assertEqual(common.weather(temperature_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_cold', 'weather_temp', 'weather'], 'contents': '-1.1e+01C'}
])
def test_system_load(self):
with replace_module_module(common, 'os', getloadavg=lambda: (7.5, 3.5, 1.5)):
@ -148,12 +179,39 @@ class TestCommon(TestCase):
{'contents': '2', 'highlight_group': ['system_load_bad', 'system_load'], 'draw_divider': False, 'divider_highlight_group': 'background:divider'}])
def test_cpu_load_percent(self):
with replace_module('psutil', cpu_percent=lambda **kwargs: 52.3):
with replace_module_module(common, 'psutil', cpu_percent=lambda **kwargs: 52.3):
self.assertEqual(common.cpu_load_percent(), '52%')
def test_network_load(self):
# TODO
pass
def _get_bytes(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)
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(), [
{'divider_highlight_group': 'background:divider', 'contents': '⬇ 1 KiB/s ⬆ 2 KiB/s'}
])
self.assertEqual(common.network_load(format='r {recv} s {sent}'), [
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s s 2 KiB/s'}
])
self.assertEqual(common.network_load(format='r {recv} s {sent}', suffix='bps'), [
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 Kibps s 2 Kibps'}
])
self.assertEqual(common.network_load(format='r {recv} s {sent}', si_prefix=True), [
{'divider_highlight_group': 'background:divider', 'contents': 'r 1 kB/s s 2 kB/s'}
])
def test_virtualenv(self):
with replace_env('VIRTUAL_ENV', '/abc/def/ghi'):
@ -229,7 +287,7 @@ class TestVim(TestCase):
def test_file_size(self):
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.file_size(segment_info=segment_info), None)
self.assertEqual(vim.file_size(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')
@ -267,16 +325,36 @@ class TestVim(TestCase):
self.assertEqual(vim.modified_buffers(), None)
def test_branch(self):
# TODO
pass
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),
[{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}])
self.assertEqual(vim.branch(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),
[{'divider_highlight_group': 'branch:divider', 'highlight_group': ['branch'], 'contents': 'foo'}])
self.assertEqual(vim.branch(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):
# TODO
pass
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),
[{'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 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)
def test_repository_status(self):
# TODO
pass
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')
old_cwd = None

View File

@ -150,6 +150,13 @@ def _emul_exists(varname):
raise NotImplementedError
@_logged
def _emul_line2byte(line):
buflines = _buf_lines[_buffer()]
if line == len(buflines) + 1:
return sum((len(s) for s in buflines)) + 1
raise NotImplementedError
_window_ids = [None]
_window_id = 0
_win_scopes = [None]
@ -242,6 +249,15 @@ class _Buffer(object):
_buf_scopes.pop(bufnr)
class _Current(object):
@property
def buffer(self):
return buffers[_buffer()]
current = _Current()
_dict = None

646
tools/colors.map Normal file
View File

@ -0,0 +1,646 @@
Grey 545454
Grey, Silver C0C0C0
grey BEBEBE
LightGray D3D3D3
LightSlateGrey 778899
SlateGray 708090
SlateGray1 C6E2FF
SlateGray2 B9D3EE
SlateGray3 9FB6CD
SlateGray4 6C7B8B
black 000000
grey0 000000
grey1 030303
grey2 050505
grey3 080808
grey4 0A0A0A
grey5 0D0D0D
grey6 0F0F0F
grey7 121212
grey8 141414
grey9 171717
grey10 1A1A1A
grey11 1C1C1C
grey12 1F1F1F
grey13 212121
grey14 242424
grey15 262626
grey16 292929
grey17 2B2B2B
grey18 2E2E2E
grey19 303030
grey20 333333
grey21 363636
grey22 383838
grey23 3B3B3B
grey24 3D3D3D
grey25 404040
grey26 424242
grey27 454545
grey28 474747
grey29 4A4A4A
grey30 4D4D4D
grey31 4F4F4F
grey32 525252
grey33 545454
grey34 575757
grey35 595959
grey36 5C5C5C
grey37 5E5E5E
grey38 616161
grey39 636363
grey40 666666
grey41, DimGrey 696969
grey42 6B6B6B
grey43 6E6E6E
grey44 707070
grey45 737373
grey46 757575
grey47 787878
grey48 7A7A7A
grey49 7D7D7D
grey50 7F7F7F
grey51 828282
grey52 858585
grey53 878787
grey54 8A8A8A
grey55 8C8C8C
grey56 8F8F8F
grey57 919191
grey58 949494
grey59 969696
grey60 999999
grey61 9C9C9C
grey62 9E9E9E
grey63 A1A1A1
grey64 A3A3A3
grey65 A6A6A6
grey66 A8A8A8
grey67 ABABAB
grey68 ADADAD
grey69 B0B0B0
grey70 B3B3B3
grey71 B5B5B5
grey72 B8B8B8
grey73 BABABA
grey74 BDBDBD
grey75 BFBFBF
grey76 C2C2C2
grey77 C4C4C4
grey78 C7C7C7
grey79 C9C9C9
grey80 CCCCCC
grey81 CFCFCF
grey82 D1D1D1
grey83 D4D4D4
grey84 D6D6D6
grey85 D9D9D9
grey86 DBDBDB
grey87 DEDEDE
grey88 E0E0E0
grey89 E3E3E3
grey90 E5E5E5
grey91 E8E8E8
grey92 EBEBEB
grey93 EDEDED
grey94 F0F0F0
grey95 F2F2F2
grey96 F5F5F5
grey97 F7F7F7
grey98 FAFAFA
grey99 FCFCFC
grey100, White FFFFFF
Dark Slate Grey 2F4F4F
Dim Grey 545454
Very Light Grey CDCDCD
Free Speech Grey 635688
AliceBlue F0F8FF
BlueViolet 8A2BE2
Cadet Blue 5F9F9F
CadetBlue 5F9EA0
CadetBlue 5F9EA0
CadetBlue1 98F5FF
CadetBlue2 8EE5EE
CadetBlue3 7AC5CD
CadetBlue4 53868B
Corn Flower Blue 42426F
CornflowerBlue 6495ED
DarkSlateBlue 483D8B
DarkTurquoise 00CED1
DeepSkyBlue 00BFFF
DeepSkyBlue1 00BFFF
DeepSkyBlue2 00B2EE
DeepSkyBlue3 009ACD
DeepSkyBlue4 00688B
DodgerBlue 1E90FF
DodgerBlue1 1E90FF
DodgerBlue2 1C86EE
DodgerBlue3 1874CD
DodgerBlue4 104E8B
LightBlue ADD8E6
LightBlue1 BFEFFF
LightBlue2 B2DFEE
LightBlue3 9AC0CD
LightBlue4 68838B
LightCyan E0FFFF
LightCyan1 E0FFFF
LightCyan2 D1EEEE
LightCyan3 B4CDCD
LightCyan4 7A8B8B
LightSkyBlue 87CEFA
LightSkyBlue1 B0E2FF
LightSkyBlue2 A4D3EE
LightSkyBlue3 8DB6CD
LightSkyBlue4 607B8B
LightSlateBlue 8470FF
LightSteelBlue B0C4DE
LightSteelBlue1 CAE1FF
LightSteelBlue2 BCD2EE
LightSteelBlue3 A2B5CD
LightSteelBlue4 6E7B8B
Aquamarine 70DB93
MediumBlue 0000CD
MediumSlateBlue 7B68EE
MediumTurquoise 48D1CC
MidnightBlue 191970
NavyBlue 000080
PaleTurquoise AFEEEE
PaleTurquoise1 BBFFFF
PaleTurquoise2 AEEEEE
PaleTurquoise3 96CDCD
PaleTurquoise4 668B8B
PowderBlue B0E0E6
RoyalBlue 4169E1
RoyalBlue1 4876FF
RoyalBlue2 436EEE
RoyalBlue3 3A5FCD
RoyalBlue4 27408B
RoyalBlue5 002266
SkyBlue 87CEEB
SkyBlue1 87CEFF
SkyBlue2 7EC0EE
SkyBlue3 6CA6CD
SkyBlue4 4A708B
SlateBlue 6A5ACD
SlateBlue1 836FFF
SlateBlue2 7A67EE
SlateBlue3 6959CD
SlateBlue4 473C8B
SteelBlue 4682B4
SteelBlue1 63B8FF
SteelBlue2 5CACEE
SteelBlue3 4F94CD
SteelBlue4 36648B
aquamarine 7FFFD4
aquamarine1 7FFFD4
aquamarine2 76EEC6
aquamarine3, MediumAquamarine 66CDAA
aquamarine4 458B74
azure F0FFFF
azure1 F0FFFF
azure2 E0EEEE
azure3 C1CDCD
azure4 838B8B
blue 0000FF
blue1 0000FF
blue2 0000EE
blue3 0000CD
blue4 00008B
aqua 00FFFF
True Iris Blue 03B4CC
cyan 00FFFF
cyan1 00FFFF
cyan2 00EEEE
cyan3 00CDCD
cyan4 008B8B
navy 000080
teal 008080
turquoise 40E0D0
turquoise1 00F5FF
turquoise2 00E5EE
turquoise3 00C5CD
turquoise4 00868B
DarkSlateGray 2F4F4F
DarkSlateGray1 97FFFF
DarkSlateGray2 8DEEEE
DarkSlateGray3 79CDCD
DarkSlateGray4 528B8B
Dark Slate Blue 241882
Dark Turquoise 7093DB
Medium Slate Blue 7F00FF
Medium Turquoise 70DBDB
Midnight Blue 2F2F4F
Navy Blue 23238E
Neon Blue 4D4DFF
New Midnight Blue 00009C
Rich Blue 5959AB
Sky Blue 3299CC
Slate Blue 007FFF
Summer Sky 38B0DE
Iris Blue 03B4C8
Free Speech Blue 4156C5
RosyBrown BC8F8F
RosyBrown1 FFC1C1
RosyBrown2 EEB4B4
RosyBrown3 CD9B9B
RosyBrown4 8B6969
SaddleBrown 8B4513
SandyBrown F4A460
beige F5F5DC
brown A52A2A
brown A62A2A
brown1 FF4040
brown2 EE3B3B
brown3 CD3333
brown4 8B2323
dark brown 5C4033
burlywood DEB887
burlywood1 FFD39B
burlywood2 EEC591
burlywood3 CDAA7D
burlywood4 8B7355
baker's chocolate 5C3317
chocolate D2691E
chocolate1 FF7F24
chocolate2 EE7621
chocolate3 CD661D
chocolate4 8B4513
peru CD853F
tan D2B48C
tan1 FFA54F
tan2 EE9A49
tan3 CD853F
tan4 8B5A2B
Dark Tan 97694F
Dark Wood 855E42
Light Wood 856363
Medium Wood A68064
New Tan EBC79E
Semi-Sweet Chocolate 6B4226
Sienna 8E6B23
Tan DB9370
Very Dark Brown 5C4033
Dark Green 2F4F2F
DarkGreen 006400
dark green copper 4A766E
DarkKhaki BDB76B
DarkOliveGreen 556B2F
DarkOliveGreen1 CAFF70
DarkOliveGreen2 BCEE68
DarkOliveGreen3 A2CD5A
DarkOliveGreen4 6E8B3D
olive 808000
DarkSeaGreen 8FBC8F
DarkSeaGreen1 C1FFC1
DarkSeaGreen2 B4EEB4
DarkSeaGreen3 9BCD9B
DarkSeaGreen4 698B69
ForestGreen 228B22
GreenYellow ADFF2F
LawnGreen 7CFC00
LightSeaGreen 20B2AA
LimeGreen 32CD32
MediumSeaGreen 3CB371
MediumSpringGreen 00FA9A
MintCream F5FFFA
OliveDrab 6B8E23
OliveDrab1 C0FF3E
OliveDrab2 B3EE3A
OliveDrab3 9ACD32
OliveDrab4 698B22
PaleGreen 98FB98
PaleGreen1 9AFF9A
PaleGreen2 90EE90
PaleGreen3 7CCD7C
PaleGreen4 548B54
SeaGreen, SeaGreen4 2E8B57
SeaGreen1 54FF9F
SeaGreen2 4EEE94
SeaGreen3 43CD80
SpringGreen 00FF7F
SpringGreen1 00FF7F
SpringGreen2 00EE76
SpringGreen3 00CD66
SpringGreen4 008B45
YellowGreen 9ACD32
chartreuse 7FFF00
chartreuse1 7FFF00
chartreuse2 76EE00
chartreuse3 66CD00
chartreuse4 458B00
green 00FF00
green 008000
lime 00FF00
green1 00FF00
green2 00EE00
green3 00CD00
green4 008B00
khaki F0E68C
khaki1 FFF68F
khaki2 EEE685
khaki3 CDC673
khaki4 8B864E
Dark Olive Green 4F4F2F
Green Yellow <a href=#sic>[sic]</a> D19275
Hunter Green <a href=#sic>[sic]</a> 8E2323
Forest Green, Khaki, Medium Aquamarine 238E23
Medium Forest Green DBDB70
Medium Sea Green 426F42
Medium Spring Green 7FFF00
Pale Green 8FBC8F
Sea Green 238E68
Spring Green 00FF7F
Free Speech Green 09F911
Free Speech Aquamarine 029D74
DarkOrange FF8C00
DarkOrange1 FF7F00
DarkOrange2 EE7600
DarkOrange3 CD6600
DarkOrange4 8B4500
DarkSalmon E9967A
LightCoral F08080
LightSalmon FFA07A
LightSalmon1 FFA07A
LightSalmon2 EE9572
LightSalmon3 CD8162
LightSalmon4 8B5742
PeachPuff FFDAB9
PeachPuff1 FFDAB9
PeachPuff2 EECBAD
PeachPuff3 CDAF95
PeachPuff4 8B7765
bisque FFE4C4
bisque1 FFE4C4
bisque2 EED5B7
bisque3 CDB79E
bisque4 8B7D6B
coral FF7F00
coral FF7F50
coral1 FF7256
coral2 EE6A50
coral3 CD5B45
coral4 8B3E2F
honeydew F0FFF0
honeydew1 F0FFF0
honeydew2 E0EEE0
honeydew3 C1CDC1
honeydew4 838B83
orange FFA500
orange1 FFA500
orange2 EE9A00
orange3 CD8500
orange4 8B5A00
salmon FA8072
salmon1 FF8C69
salmon2 EE8262
salmon3 CD7054
salmon4 8B4C39
sienna A0522D
sienna1 FF8247
sienna2 EE7942
sienna3 CD6839
sienna4 8B4726
Mandarian Orange 8E2323
Orange FF7F00
Orange Red FF2400
DeepPink FF1493
DeepPink1 FF1493
DeepPink2 EE1289
DeepPink3 CD1076
DeepPink4 8B0A50
HotPink FF69B4
HotPink1 FF6EB4
HotPink2 EE6AA7
HotPink3 CD6090
HotPink4 8B3A62
IndianRed CD5C5C
IndianRed1 FF6A6A
IndianRed2 EE6363
IndianRed3 CD5555
IndianRed4 8B3A3A
LightPink FFB6C1
LightPink1 FFAEB9
LightPink2 EEA2AD
LightPink3 CD8C95
LightPink4 8B5F65
MediumVioletRed C71585
MistyRose FFE4E1
MistyRose1 FFE4E1
MistyRose2 EED5D2
MistyRose3 CDB7B5
MistyRose4 8B7D7B
OrangeRed FF4500
OrangeRed1 FF4500
OrangeRed2 EE4000
OrangeRed3 CD3700
OrangeRed4 8B2500
PaleVioletRed DB7093
PaleVioletRed1 FF82AB
PaleVioletRed2 EE799F
PaleVioletRed3 CD6889
PaleVioletRed4 8B475D
VioletRed D02090
VioletRed1 FF3E96
VioletRed2 EE3A8C
VioletRed3 CD3278
VioletRed4 8B2252
firebrick B22222
firebrick1 FF3030
firebrick2 EE2C2C
firebrick3 CD2626
firebrick4 8B1A1A
pink FFC0CB
pink1 FFB5C5
pink2 EEA9B8
pink3 CD919E
pink4 8B636C
Flesh F5CCB0
Feldspar D19275
red FF0000
red1 FF0000
red2 EE0000
red3 CD0000
red4 8B0000
tomato FF6347
tomato1 FF6347
tomato2 EE5C42
tomato3 CD4F39
tomato4 8B3626
Dusty Rose 856363
Firebrick 8E2323
Indian Red F5CCB0
Pink BC8F8F
Salmon 6F4242
Scarlet 8C1717
Spicy Pink FF1CAE
Free Speech Magenta E35BD8
Free Speech Red C00000
DarkOrchid 9932CC
DarkOrchid1 BF3EFF
DarkOrchid2 B23AEE
DarkOrchid3 9A32CD
DarkOrchid4 68228B
DarkViolet 9400D3
LavenderBlush FFF0F5
LavenderBlush1 FFF0F5
LavenderBlush2 EEE0E5
LavenderBlush3 CDC1C5
LavenderBlush4 8B8386
MediumOrchid BA55D3
MediumOrchid1 E066FF
MediumOrchid2 D15FEE
MediumOrchid3 B452CD
MediumOrchid4 7A378B
MediumPurple 9370DB
Medium Orchid 9370DB
MediumPurple1 AB82FF
Dark Orchid 9932CD
MediumPurple2 9F79EE
MediumPurple3 8968CD
MediumPurple4 5D478B
lavender E6E6FA
magenta FF00FF
fuchsia FF00FF
magenta1 FF00FF
magenta2 EE00EE
magenta3 CD00CD
magenta4 8B008B
maroon B03060
maroon1 FF34B3
maroon2 EE30A7
maroon3 CD2990
maroon4 8B1C62
orchid DA70D6
Orchid DB70DB
orchid1 FF83FA
orchid2 EE7AE9
orchid3 CD69C9
orchid4 8B4789
plum DDA0DD
plum1 FFBBFF
plum2 EEAEEE
plum3 CD96CD
plum4 8B668B
purple A020F0
purple 800080
purple1 9B30FF
purple2 912CEE
purple3 7D26CD
purple4 551A8B
thistle D8BFD8
thistle1 FFE1FF
thistle2 EED2EE
thistle3 CDB5CD
thistle4 8B7B8B
violet EE82EE
violet blue 9F5F9F
Dark Purple 871F78
Maroon 800000
Medium Violet Red DB7093
Neon Pink FF6EC7
Plum EAADEA
Thistle D8BFD8
Turquoise ADEAEA
Violet 4F2F4F
Violet Red CC3299
AntiqueWhite FAEBD7
AntiqueWhite1 FFEFDB
AntiqueWhite2 EEDFCC
AntiqueWhite3 CDC0B0
AntiqueWhite4 8B8378
FloralWhite FFFAF0
GhostWhite F8F8FF
NavajoWhite FFDEAD
NavajoWhite1 FFDEAD
NavajoWhite2 EECFA1
NavajoWhite3 CDB38B
NavajoWhite4 8B795E
OldLace FDF5E6
WhiteSmoke F5F5F5
gainsboro DCDCDC
ivory FFFFF0
ivory1 FFFFF0
ivory2 EEEEE0
ivory3 CDCDC1
ivory4 8B8B83
linen FAF0E6
seashell FFF5EE
seashell1 FFF5EE
seashell2 EEE5DE
seashell3 CDC5BF
seashell4 8B8682
snow FFFAFA
snow1 FFFAFA
snow2 EEE9E9
snow3 CDC9C9
snow4 8B8989
wheat F5DEB3
wheat1 FFE7BA
wheat2 EED8AE
wheat3 CDBA96
wheat4 8B7E66
white FFFFFF
Quartz D9D9F3
Wheat D8D8BF
BlanchedAlmond FFEBCD
DarkGoldenrod B8860B
DarkGoldenrod1 FFB90F
DarkGoldenrod2 EEAD0E
DarkGoldenrod3 CD950C
DarkGoldenrod4 8B6508
LemonChiffon FFFACD
LemonChiffon1 FFFACD
LemonChiffon2 EEE9BF
LemonChiffon3 CDC9A5
LemonChiffon4 8B8970
LightGoldenrod EEDD82
LightGoldenrod1 FFEC8B
LightGoldenrod2 EEDC82
LightGoldenrod3 CDBE70
LightGoldenrod4 8B814C
LightGoldenrodYellow FAFAD2
LightYellow FFFFE0
LightYellow1 FFFFE0
LightYellow2 EEEED1
LightYellow3 CDCDB4
LightYellow4 8B8B7A
PaleGoldenrod EEE8AA
PapayaWhip FFEFD5
cornsilk FFF8DC
cornsilk1 FFF8DC
cornsilk2 EEE8CD
cornsilk3 CDC8B1
cornsilk4 8B8878
goldenrod DAA520
goldenrod1 FFC125
goldenrod2 EEB422
goldenrod3 CD9B1D
goldenrod4 8B6914
moccasin FFE4B5
yellow FFFF00
yellow1 FFFF00
yellow2 EEEE00
yellow3 CDCD00
yellow4 8B8B00
gold FFD700
gold1 FFD700
gold2 EEC900
gold3 CDAD00
gold4 8B7500
Goldenrod DBDB70
Medium Goldenrod EAEAAE
Yellow Green 99CC32
copper B87333
cool copper D98719
Green Copper 856363
brass B5A642
bronze 8C7853
bronze II A67D3D
bright gold D9D919
Old Gold CFB53B
CSS Gold CC9900
gold CD7F32
silver E6E8FA
Silver, Grey C0C0C0
Light Steel Blue 545454
Steel Blue 236B8E

38
tools/colors_find.py Normal file
View File

@ -0,0 +1,38 @@
import sys
import os
def get_color(name, rgb):
return name, (int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16))
with open(os.path.join(os.path.dirname(__file__), 'colors.map'), 'r') as f:
colors = [get_color(*line.split('\t')) for line in f]
urgb = get_color(None, sys.argv[1])[1]
def col_distance(rgb1, rgb2):
return sum(((rgb1[i] - rgb2[i]) ** 2 for i in range(3)))
def find_color(urgb, colors):
cur_distance = 3 * (255 ** 2 + 1)
cur_color = None
for color, crgb in colors:
dist = col_distance(urgb, crgb)
if dist < cur_distance:
cur_distance = dist
cur_color = (color, crgb)
return cur_color
cur_color = find_color(urgb, colors)
print urgb, ':', cur_color
col_1 = ';2;' + ';'.join((str(i) for i in urgb)) + 'm'
col_2 = ';2;' + ';'.join((str(i) for i in cur_color[1])) + 'm'
sys.stdout.write('\033[48' + col_1 + '\033[38' + col_2 + 'abc\033[0m <-- bg:urgb, fg:crgb\n')
sys.stdout.write('\033[48' + col_2 + '\033[38' + col_1 + 'abc\033[0m <-- bg:crgb, fg:urgb\n')

105
tools/generate_gradients.py Normal file
View File

@ -0,0 +1,105 @@
import sys
import json
from powerline.colorscheme import cterm_to_hex
from itertools import groupby
if len(sys.argv) == 1:
sys.stderr.write('''
Usage: generate_gradients.py colors itemnum[ "show"]
colors: JSON list with either cterm ([200, 42, 6]) or RGB (["abcdef",
"feffef"]) colors.
itemnum: number of items in generated gradient.
"show": static string, determines whether gradient sample should be
printed to stdout as well.
''')
def linear_gradient(start_value, stop_value, start_offset, stop_offset, offset):
return start_value + ((offset - start_offset) * (stop_value - start_value) / (stop_offset - start_offset))
def gradient(DATA):
def gradient_function(y):
initial_offset = 0
for offset, start, end in DATA:
if y <= offset:
return [linear_gradient(start[i], end[i], initial_offset, offset, y) for i in range(3)]
initial_offset = offset
return gradient_function
def get_color(rgb):
if type(rgb) is unicode:
return int(rgb[:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16)
else:
return rgbint_to_rgb(cterm_to_hex[rgb])
def get_rgb(*args):
return "%02x%02x%02x" % args
def col_distance(rgb1, rgb2):
return sum(((rgb1[i] - rgb2[i]) ** 2 for i in range(3)))
def rgbint_to_rgb(rgbint):
return ((rgbint >> 16) & 0xFF, (rgbint >> 8) & 0xFF, rgbint & 0xFF)
def find_color(urgb, colors):
cur_distance = 3 * (255 ** 2 + 1)
cur_color = None
i = 0
for crgbint in colors:
crgb = rgbint_to_rgb(crgbint)
dist = col_distance(urgb, crgb)
if dist < cur_distance:
cur_distance = dist
cur_color = (i, crgb)
i += 1
return cur_color
def print_color(color):
if type(color) is int:
colstr = '5;' + str(color)
else:
colstr = '2;' + ';'.join((str(i) for i in color))
sys.stdout.write('\033[48;' + colstr + 'm ')
def print_colors(colors):
for i in range(101):
color = colors[int(round(i * (len(colors) - 1) / 100))]
print_color(color)
sys.stdout.write('\033[0m\n')
c = [get_color(color) for color in json.loads(sys.argv[1])]
m = int(sys.argv[2]) if len(sys.argv) > 2 else 100
m += m % (len(c) - 1)
step = m / (len(c) - 1)
data = [(i * step, c[i - 1], c[i]) for i in range(1, len(c))]
gr_func = gradient(data)
gradient = [gr_func(y) for y in range(0, m - 1)]
r = [get_rgb(*color) for color in gradient]
r2 = [find_color(color, cterm_to_hex)[0] for color in gradient]
r3 = [i[0] for i in groupby(r2)]
print json.dumps(r)
print json.dumps(r2)
print json.dumps(r3)
if len(sys.argv) > 3 and sys.argv[3] == 'show':
print_colors(gradient)
print_colors(r2)
print_colors(r3)
sys.stdout.write('0')
sys.stdout.write(''.join(('%10u' % (i * 10) for i in range(1, 11))))
sys.stdout.write('\n')
nums = (''.join((str(i) for i in range(10))))
sys.stdout.write(''.join(((('\033[1m' if j % 2 else '\033[0m') + nums) for j in range(10))))
sys.stdout.write('\033[0m0\n')