Merge pull request #1642 from ZyX-I/wm-daemon
Use powerline daemon for running awesome bindings
This commit is contained in:
commit
5fa504118e
|
@ -215,6 +215,13 @@ Common configuration is a subdictionary that is a value of ``ext`` key in
|
|||
|
||||
All components are enabled by default.
|
||||
|
||||
.. _config-ext-update_interval:
|
||||
|
||||
``update_interval``
|
||||
Determines how often WM status bars need to be updated, in seconds. Only
|
||||
valid for WM extensions which use ``powerline-daemon``. Defaults to
|
||||
2 seconds.
|
||||
|
||||
.. _config-colors:
|
||||
|
||||
Color definitions
|
||||
|
|
|
@ -419,6 +419,11 @@ def _get_log_keys(common_config):
|
|||
))
|
||||
|
||||
|
||||
DEFAULT_UPDATE_INTERVAL = 2
|
||||
'''Default value for :ref:`update_interval <config-ext-update_interval>`
|
||||
'''
|
||||
|
||||
|
||||
class Powerline(object):
|
||||
'''Main powerline class, entrance point for all powerline uses. Sets
|
||||
powerline up and loads the configuration.
|
||||
|
@ -514,6 +519,7 @@ class Powerline(object):
|
|||
self.setup_args = ()
|
||||
self.setup_kwargs = {}
|
||||
self.imported_modules = set()
|
||||
self.update_interval = DEFAULT_UPDATE_INTERVAL
|
||||
|
||||
get_encoding = staticmethod(get_preferred_output_encoding)
|
||||
'''Get encoding used by the current application
|
||||
|
@ -638,6 +644,7 @@ class Powerline(object):
|
|||
or self.ext_config.get('local_themes') != self.prev_ext_config.get('local_themes')
|
||||
):
|
||||
self.renderer_options['local_themes'] = self.get_local_themes(self.ext_config.get('local_themes'))
|
||||
self.update_interval = self.ext_config.get('update_interval', 2)
|
||||
load_colorscheme = (
|
||||
load_colorscheme
|
||||
or not self.prev_ext_config
|
||||
|
|
|
@ -4,38 +4,17 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
|
|||
|
||||
import sys
|
||||
|
||||
from time import sleep
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from powerline import Powerline
|
||||
from powerline.lib.monotonic import monotonic
|
||||
|
||||
powerline = Powerline('wm', renderer_module='pango_markup')
|
||||
powerline.update_renderer()
|
||||
|
||||
try:
|
||||
interval = float(sys.argv[1])
|
||||
except IndexError:
|
||||
interval = 2
|
||||
from powerline.bindings.wm import DEFAULT_UPDATE_INTERVAL
|
||||
from powerline.bindings.wm.awesome import run
|
||||
|
||||
|
||||
def read_to_log(pl, client):
|
||||
for line in client.stdout:
|
||||
if line:
|
||||
pl.info(line, prefix='awesome-client')
|
||||
for line in client.stderr:
|
||||
if line:
|
||||
pl.error(line, prefix='awesome-client')
|
||||
if client.wait():
|
||||
pl.error('Client exited with {0}', client.returncode, prefix='awesome')
|
||||
def main():
|
||||
try:
|
||||
interval = float(sys.argv[1])
|
||||
except IndexError:
|
||||
interval = DEFAULT_UPDATE_INTERVAL
|
||||
run(interval=interval)
|
||||
|
||||
|
||||
while True:
|
||||
start_time = monotonic()
|
||||
s = powerline.render(side='right')
|
||||
request = 'powerline_widget:set_markup(\'' + s.replace('\\', '\\\\').replace('\'', '\\\'') + '\')\n'
|
||||
client = Popen(['awesome-client'], shell=False, stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
||||
client.stdin.write(request.encode('utf-8'))
|
||||
client.stdin.close()
|
||||
read_to_log(powerline.pl, client)
|
||||
sleep(max(interval - (monotonic() - start_time), 0.1))
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -6,6 +6,5 @@ powerline_widget:set_align('right')
|
|||
|
||||
function powerline(mode, widget) end
|
||||
|
||||
bindings_path = string.gsub(debug.getinfo(1).source:match('@(.*)$'), '/[^/]+$', '')
|
||||
powerline_cmd = bindings_path .. '/powerline-awesome.py'
|
||||
awful.util.spawn_with_shell('ps -C powerline-awesome.py || ' .. powerline_cmd)
|
||||
awful.util.spawn_with_shell('powerline-daemon -q')
|
||||
awful.util.spawn_with_shell('powerline wm.awesome')
|
||||
|
|
|
@ -11,6 +11,7 @@ from argparse import ArgumentParser
|
|||
|
||||
from powerline.lemonbar import LemonbarPowerline
|
||||
from powerline.lib.encoding import get_unicode_writer
|
||||
from powerline.bindings.wm import DEFAULT_UPDATE_INTERVAL
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -29,7 +30,7 @@ if __name__ == '__main__':
|
|||
|
||||
def render(reschedule=False):
|
||||
if reschedule:
|
||||
Timer(0.5, render, kwargs={'reschedule': True}).start()
|
||||
Timer(DEFAULT_UPDATE_INTERVAL, render, kwargs={'reschedule': True}).start()
|
||||
|
||||
global lock
|
||||
with lock:
|
||||
|
|
|
@ -99,7 +99,7 @@ class EmptyArgs(object):
|
|||
def init_tmux_environment(pl, args, set_tmux_environment=set_tmux_environment):
|
||||
'''Initialize tmux environment from tmux configuration
|
||||
'''
|
||||
powerline = ShellPowerline(finish_args(os.environ, EmptyArgs('tmux', args.config_path)))
|
||||
powerline = ShellPowerline(finish_args(None, os.environ, EmptyArgs('tmux', args.config_path)))
|
||||
# TODO Move configuration files loading out of Powerline object and use it
|
||||
# directly
|
||||
powerline.update_renderer()
|
||||
|
|
|
@ -5,6 +5,10 @@ import re
|
|||
|
||||
from powerline.theme import requires_segment_info
|
||||
from powerline.lib.shell import run_cmd
|
||||
from powerline.bindings.wm.awesome import AwesomeThread
|
||||
|
||||
|
||||
DEFAULT_UPDATE_INTERVAL = 0.5
|
||||
|
||||
|
||||
conn = None
|
||||
|
@ -36,3 +40,8 @@ def get_connected_xrandr_outputs(pl):
|
|||
return (match.groupdict() for match in XRANDR_OUTPUT_RE.finditer(
|
||||
run_cmd(pl, ['xrandr', '-q'])
|
||||
))
|
||||
|
||||
|
||||
wm_threads = {
|
||||
'awesome': AwesomeThread,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# vim:fileencoding=utf-8:noet
|
||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||
|
||||
import sys
|
||||
|
||||
from threading import Thread, Event
|
||||
from time import sleep
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from powerline import Powerline
|
||||
from powerline.lib.monotonic import monotonic
|
||||
|
||||
|
||||
def read_to_log(pl, client):
|
||||
for line in client.stdout:
|
||||
if line:
|
||||
pl.info(line, prefix='awesome-client')
|
||||
for line in client.stderr:
|
||||
if line:
|
||||
pl.error(line, prefix='awesome-client')
|
||||
if client.wait():
|
||||
pl.error('Client exited with {0}', client.returncode, prefix='awesome')
|
||||
|
||||
|
||||
def run(thread_shutdown_event=None, pl_shutdown_event=None, pl_config_loader=None,
|
||||
interval=None):
|
||||
powerline = Powerline(
|
||||
'wm',
|
||||
renderer_module='pango_markup',
|
||||
shutdown_event=pl_shutdown_event,
|
||||
config_loader=pl_config_loader,
|
||||
)
|
||||
powerline.update_renderer()
|
||||
|
||||
if not thread_shutdown_event:
|
||||
thread_shutdown_event = powerline.shutdown_event
|
||||
|
||||
while not thread_shutdown_event.is_set():
|
||||
# powerline.update_interval may change over time
|
||||
used_interval = interval or powerline.update_interval
|
||||
start_time = monotonic()
|
||||
s = powerline.render(side='right')
|
||||
request = 'powerline_widget:set_markup(\'' + s.translate({'\'': '\\\'', '\\': '\\\\'}) + '\')\n'
|
||||
client = Popen(['awesome-client'], shell=False, stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
||||
client.stdin.write(request.encode('utf-8'))
|
||||
client.stdin.close()
|
||||
read_to_log(powerline.pl, client)
|
||||
thread_shutdown_event.wait(max(used_interval - (monotonic() - start_time), 0.1))
|
||||
|
||||
|
||||
class AwesomeThread(Thread):
|
||||
__slots__ = ('powerline_shutdown_event',)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(AwesomeThread, self).__init__()
|
||||
self.powerline_run_kwargs = kwargs
|
||||
|
||||
def run(self):
|
||||
run(**self.powerline_run_kwargs)
|
|
@ -25,6 +25,11 @@ _powerline_tmux_pane() {
|
|||
echo "${TMUX_PANE:-`tmux display -p "#D"`}" | tr -d ' %'
|
||||
}
|
||||
|
||||
_powerline_tmux_pane() {
|
||||
local -x TMUX="$_POWERLINE_TMUX"
|
||||
echo "${TMUX_PANE:-`tmux display -p "#D"`}" | tr -d ' %'
|
||||
}
|
||||
|
||||
_powerline_init_tmux_support() {
|
||||
emulate -L zsh
|
||||
if test -n "$TMUX" && tmux refresh -S &>/dev/null ; then
|
||||
|
|
|
@ -11,6 +11,7 @@ from powerline.lib.overrides import parsedotval, parse_override_var
|
|||
from powerline.lib.dict import mergeargs
|
||||
from powerline.lib.encoding import get_preferred_arguments_encoding
|
||||
from powerline.lib.unicode import u, unicode
|
||||
from powerline.bindings.wm import wm_threads
|
||||
|
||||
|
||||
if sys.version_info < (3,):
|
||||
|
@ -23,7 +24,7 @@ else:
|
|||
return s
|
||||
|
||||
|
||||
def finish_args(environ, args):
|
||||
def finish_args(parser, environ, args, is_daemon=False):
|
||||
'''Do some final transformations
|
||||
|
||||
Transforms ``*_override`` arguments into dictionaries, adding overrides from
|
||||
|
@ -61,7 +62,13 @@ def finish_args(environ, args):
|
|||
[path for path in environ.get('POWERLINE_CONFIG_PATHS', '').split(':') if path]
|
||||
+ (args.config_path or [])
|
||||
)
|
||||
args.side = args.side[0]
|
||||
if args.ext[0].startswith('wm.'):
|
||||
if not is_daemon:
|
||||
parser.error('WM bindings must be used with daemon only')
|
||||
elif args.ext[0][3:] not in wm_threads:
|
||||
parser.error('WM binding not found')
|
||||
elif not args.side:
|
||||
parser.error('expected one argument')
|
||||
return args
|
||||
|
||||
|
||||
|
@ -77,15 +84,16 @@ def get_argparser(ArgumentParser=argparse.ArgumentParser):
|
|||
parser.add_argument(
|
||||
'ext', nargs=1,
|
||||
help='Extension: application for which powerline command is launched '
|
||||
'(usually `shell\' or `tmux\').'
|
||||
'(usually `shell\' or `tmux\'). Also supports `wm.\' extensions: '
|
||||
+ ', '.join(('`wm.' + key + '\'' for key in wm_threads.keys())) + '.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'side', nargs=1, choices=('left', 'right', 'above', 'aboveleft'),
|
||||
'side', nargs='?', choices=('left', 'right', 'above', 'aboveleft'),
|
||||
help='Side: `left\' and `right\' represent left and right side '
|
||||
'respectively, `above\' emits lines that are supposed to be printed '
|
||||
'just above the prompt and `aboveleft\' is like concatenating '
|
||||
'`above\' with `left\' with the exception that only one Python '
|
||||
'instance is used in this case.'
|
||||
'instance is used in this case. May be omitted for `wm.*\' extensions.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-r', '--renderer-module', metavar='MODULE', type=str,
|
||||
|
|
|
@ -46,7 +46,8 @@
|
|||
},
|
||||
"wm": {
|
||||
"colorscheme": "default",
|
||||
"theme": "default"
|
||||
"theme": "default",
|
||||
"update_interval": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,7 +130,8 @@ main_spec = (Spec(
|
|||
local_themes=Spec().unknown_spec(
|
||||
Spec().re('^[0-9A-Za-z-]+$'),
|
||||
ext_theme_spec()
|
||||
).optional()
|
||||
).optional(),
|
||||
update_interval=Spec().cmp('gt', 0.0).optional(),
|
||||
).optional(),
|
||||
).unknown_spec(
|
||||
check_ext,
|
||||
|
|
|
@ -6,6 +6,9 @@ import socket
|
|||
import os
|
||||
import errno
|
||||
import sys
|
||||
import fcntl
|
||||
import atexit
|
||||
import stat
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from select import select
|
||||
|
@ -13,21 +16,21 @@ from signal import signal, SIGTERM
|
|||
from time import sleep
|
||||
from functools import partial
|
||||
from io import BytesIO
|
||||
from threading import Event
|
||||
from itertools import chain
|
||||
from logging import StreamHandler
|
||||
|
||||
from powerline.shell import ShellPowerline
|
||||
from powerline.commands.main import finish_args, write_output
|
||||
from powerline.lib.monotonic import monotonic
|
||||
from powerline.lib.encoding import get_preferred_output_encoding, get_preferred_arguments_encoding, get_unicode_writer
|
||||
from powerline.bindings.wm import wm_threads
|
||||
|
||||
from powerline.commands.main import get_argparser as get_main_argparser
|
||||
from powerline.commands.daemon import get_argparser as get_daemon_argparser
|
||||
|
||||
|
||||
is_daemon = False
|
||||
use_filesystem = not sys.platform.lower().startswith('linux')
|
||||
|
||||
address = None
|
||||
pidfile = None
|
||||
USE_FILESYSTEM = not sys.platform.lower().startswith('linux')
|
||||
|
||||
|
||||
class NonInteractiveArgParser(ArgumentParser):
|
||||
|
@ -44,31 +47,48 @@ class NonInteractiveArgParser(ArgumentParser):
|
|||
raise Exception(self.format_usage())
|
||||
|
||||
|
||||
parser = get_main_argparser(NonInteractiveArgParser)
|
||||
|
||||
EOF = b'EOF\0\0'
|
||||
|
||||
powerlines = {}
|
||||
logger = None
|
||||
config_loader = None
|
||||
home = os.path.expanduser('~')
|
||||
|
||||
class State(object):
|
||||
__slots__ = ('powerlines', 'logger', 'config_loader', 'started_wm_threads',
|
||||
'ts_shutdown_event')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.logger = None
|
||||
self.config_loader = None
|
||||
self.started_wm_threads = {}
|
||||
self.powerlines = {}
|
||||
self.ts_shutdown_event = Event()
|
||||
|
||||
|
||||
class PowerlineDaemon(ShellPowerline):
|
||||
HOME = os.path.expanduser('~')
|
||||
|
||||
|
||||
class NonDaemonShellPowerline(ShellPowerline):
|
||||
def get_log_handler(self):
|
||||
if not is_daemon:
|
||||
import logging
|
||||
return logging.StreamHandler()
|
||||
return super(PowerlineDaemon, self).get_log_handler()
|
||||
return StreamHandler()
|
||||
|
||||
|
||||
def render(args, environ, cwd):
|
||||
global logger
|
||||
global config_loader
|
||||
cwd = cwd or environ.get('PWD', '/')
|
||||
def start_wm(args, environ, cwd, is_daemon, state):
|
||||
wm_name = args.ext[0][3:]
|
||||
if wm_name in state.started_wm_threads:
|
||||
return b''
|
||||
thread_shutdown_event = Event()
|
||||
thread = wm_threads[wm_name](
|
||||
thread_shutdown_event=thread_shutdown_event,
|
||||
pl_shutdown_event=state.ts_shutdown_event,
|
||||
pl_config_loader=state.config_loader,
|
||||
)
|
||||
thread.start()
|
||||
state.started_wm_threads[wm_name] = (thread, thread_shutdown_event)
|
||||
return b''
|
||||
|
||||
|
||||
def render(args, environ, cwd, is_daemon, state):
|
||||
segment_info = {
|
||||
'getcwd': lambda: cwd,
|
||||
'home': environ.get('HOME', home),
|
||||
'home': environ.get('HOME', HOME),
|
||||
'environ': environ,
|
||||
'args': args,
|
||||
}
|
||||
|
@ -82,22 +102,24 @@ def render(args, environ, cwd):
|
|||
environ.get('POWERLINE_CONFIG_OVERRIDES', ''),
|
||||
environ.get('POWERLINE_CONFIG_PATHS', ''),
|
||||
)
|
||||
finish_args(environ, args)
|
||||
|
||||
PowerlineClass = ShellPowerline if is_daemon else NonDaemonShellPowerline
|
||||
powerline = None
|
||||
try:
|
||||
powerline = powerlines[key]
|
||||
powerline = state.powerlines[key]
|
||||
except KeyError:
|
||||
try:
|
||||
powerline = powerlines[key] = PowerlineDaemon(
|
||||
powerline = state.powerlines[key] = PowerlineClass(
|
||||
args,
|
||||
logger=logger,
|
||||
config_loader=config_loader,
|
||||
logger=state.logger,
|
||||
config_loader=state.config_loader,
|
||||
run_once=False,
|
||||
shutdown_event=state.ts_shutdown_event,
|
||||
)
|
||||
if logger is None:
|
||||
logger = powerline.logger
|
||||
if config_loader is None:
|
||||
config_loader = powerline.config_loader
|
||||
if state.logger is None:
|
||||
state.logger = powerline.logger
|
||||
if state.config_loader is None:
|
||||
state.config_loader = powerline.config_loader
|
||||
except SystemExit:
|
||||
# Somebody thought raising system exit was a good idea,
|
||||
return ''
|
||||
|
@ -168,23 +190,30 @@ def safe_bytes(o, encoding=get_preferred_output_encoding()):
|
|||
return safe_bytes(str(e), encoding)
|
||||
|
||||
|
||||
def parse_args(req, encoding=get_preferred_arguments_encoding()):
|
||||
def parse_args(req, parser, encoding=get_preferred_arguments_encoding()):
|
||||
args = [x.decode(encoding) for x in req.split(b'\0') if x]
|
||||
numargs = int(args[0], 16)
|
||||
shell_args = parser.parse_args(args[1:numargs + 1])
|
||||
cwd = args[numargs + 1]
|
||||
environ = dict(((k, v) for k, v in (x.partition('=')[0::2] for x in args[numargs + 2:])))
|
||||
cwd = cwd or environ.get('PWD', '/')
|
||||
return shell_args, environ, cwd
|
||||
|
||||
|
||||
def do_render(req):
|
||||
def get_answer(req, is_daemon, argparser, state):
|
||||
try:
|
||||
return safe_bytes(render(*parse_args(req)))
|
||||
args, environ, cwd = parse_args(req, argparser)
|
||||
finish_args(argparser, environ, args, is_daemon=True)
|
||||
if args.ext[0].startswith('wm.'):
|
||||
return safe_bytes(start_wm(args, environ, cwd, is_daemon, state))
|
||||
else:
|
||||
return safe_bytes(render(args, environ, cwd, is_daemon, state))
|
||||
except Exception as e:
|
||||
return safe_bytes(str(e))
|
||||
|
||||
|
||||
def do_one(sock, read_sockets, write_sockets, result_map):
|
||||
def do_one(sock, read_sockets, write_sockets, result_map, is_daemon, argparser,
|
||||
state):
|
||||
r, w, e = select(
|
||||
tuple(read_sockets) + (sock,),
|
||||
tuple(write_sockets),
|
||||
|
@ -214,7 +243,7 @@ def do_one(sock, read_sockets, write_sockets, result_map):
|
|||
if req == EOF:
|
||||
raise SystemExit(0)
|
||||
elif req:
|
||||
ans = do_render(req)
|
||||
ans = get_answer(req, is_daemon, argparser, state)
|
||||
result_map[s] = ans
|
||||
write_sockets.add(s)
|
||||
else:
|
||||
|
@ -230,17 +259,61 @@ def do_one(sock, read_sockets, write_sockets, result_map):
|
|||
s.close()
|
||||
|
||||
|
||||
def main_loop(sock):
|
||||
def shutdown(sock, read_sockets, write_sockets, state):
|
||||
'''Perform operations necessary for nicely shutting down daemon
|
||||
|
||||
Specifically it
|
||||
|
||||
#. Closes all sockets.
|
||||
#. Notifies segments based on
|
||||
:py:class:`powerline.lib.threaded.ThreadedSegment` and WM-specific
|
||||
threads that daemon is shutting down.
|
||||
#. Waits for threads to finish, but no more then 2 seconds total.
|
||||
#. Waits so that total execution time of this function is 2 seconds in order
|
||||
to allow ThreadedSegments to finish.
|
||||
'''
|
||||
total_wait_time = 2
|
||||
shutdown_start_time = monotonic()
|
||||
|
||||
for s in chain((sock,), read_sockets, write_sockets):
|
||||
s.close()
|
||||
|
||||
# Notify ThreadedSegments
|
||||
state.ts_shutdown_event.set()
|
||||
for thread, shutdown_event in state.started_wm_threads.values():
|
||||
shutdown_event.set()
|
||||
|
||||
for thread, shutdown_event in state.started_wm_threads.values():
|
||||
wait_time = total_wait_time - (monotonic() - shutdown_start_time)
|
||||
if wait_time > 0:
|
||||
thread.join(wait_time)
|
||||
|
||||
wait_time = total_wait_time - (monotonic() - shutdown_start_time)
|
||||
sleep(wait_time)
|
||||
|
||||
|
||||
def main_loop(sock, is_daemon):
|
||||
sock.listen(128)
|
||||
sock.setblocking(0)
|
||||
|
||||
read_sockets, write_sockets = set(), set()
|
||||
result_map = {}
|
||||
parser = get_main_argparser(NonInteractiveArgParser)
|
||||
state = State()
|
||||
try:
|
||||
while True:
|
||||
do_one(sock, read_sockets, write_sockets, result_map)
|
||||
except KeyboardInterrupt:
|
||||
raise SystemExit(0)
|
||||
try:
|
||||
while True:
|
||||
do_one(
|
||||
sock, read_sockets, write_sockets, result_map,
|
||||
is_daemon=is_daemon,
|
||||
argparser=parser,
|
||||
state=state,
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
raise SystemExit(0)
|
||||
except SystemExit as e:
|
||||
shutdown(sock, read_sockets, write_sockets, state)
|
||||
raise e
|
||||
return 0
|
||||
|
||||
|
||||
|
@ -249,10 +322,10 @@ def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull):
|
|||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit first parent
|
||||
sys.exit(0)
|
||||
raise SystemExit(0)
|
||||
except OSError as e:
|
||||
sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||
sys.exit(1)
|
||||
raise SystemExit(1)
|
||||
|
||||
# decouple from parent environment
|
||||
os.chdir("/")
|
||||
|
@ -264,10 +337,10 @@ def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull):
|
|||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit from second parent
|
||||
sys.exit(0)
|
||||
raise SystemExit(0)
|
||||
except OSError as e:
|
||||
sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||
sys.exit(1)
|
||||
raise SystemExit(1)
|
||||
|
||||
# Redirect standard file descriptors.
|
||||
si = open(stdin, 'rb')
|
||||
|
@ -276,12 +349,11 @@ def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull):
|
|||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
global is_daemon
|
||||
is_daemon = True
|
||||
return True
|
||||
|
||||
|
||||
def check_existing():
|
||||
if use_filesystem:
|
||||
def check_existing(address):
|
||||
if USE_FILESYSTEM:
|
||||
# We cannot bind if the socket file already exists so remove it, we
|
||||
# already have a lock on pidfile, so this should be safe.
|
||||
try:
|
||||
|
@ -299,7 +371,7 @@ def check_existing():
|
|||
return sock
|
||||
|
||||
|
||||
def kill_daemon():
|
||||
def kill_daemon(address):
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
try:
|
||||
try:
|
||||
|
@ -313,7 +385,7 @@ def kill_daemon():
|
|||
return True
|
||||
|
||||
|
||||
def cleanup_lockfile(fd, *args):
|
||||
def cleanup_lockfile(pidfile, fd, *args):
|
||||
try:
|
||||
# Remove the directory entry for the lock file
|
||||
os.unlink(pidfile)
|
||||
|
@ -326,10 +398,7 @@ def cleanup_lockfile(fd, *args):
|
|||
raise SystemExit(1)
|
||||
|
||||
|
||||
def lockpidfile():
|
||||
import fcntl
|
||||
import atexit
|
||||
import stat
|
||||
def lockpidfile(pidfile):
|
||||
fd = os.open(
|
||||
pidfile,
|
||||
os.O_WRONLY | os.O_CREAT,
|
||||
|
@ -344,24 +413,25 @@ def lockpidfile():
|
|||
os.ftruncate(fd, 0)
|
||||
os.write(fd, ('%d' % os.getpid()).encode('ascii'))
|
||||
os.fsync(fd)
|
||||
cleanup = partial(cleanup_lockfile, fd)
|
||||
cleanup = partial(cleanup_lockfile, pidfile, fd)
|
||||
signal(SIGTERM, cleanup)
|
||||
atexit.register(cleanup)
|
||||
return fd
|
||||
|
||||
|
||||
def main():
|
||||
global address
|
||||
global pidfile
|
||||
parser = get_daemon_argparser()
|
||||
args = parser.parse_args()
|
||||
is_daemon = False
|
||||
address = None
|
||||
pidfile = None
|
||||
|
||||
if args.socket:
|
||||
address = args.socket
|
||||
if not use_filesystem:
|
||||
if not USE_FILESYSTEM:
|
||||
address = '\0' + address
|
||||
else:
|
||||
if use_filesystem:
|
||||
if USE_FILESYSTEM:
|
||||
address = '/tmp/powerline-ipc-%d'
|
||||
else:
|
||||
# Use the abstract namespace for sockets rather than the filesystem
|
||||
|
@ -370,13 +440,13 @@ def main():
|
|||
|
||||
address = address % os.getuid()
|
||||
|
||||
if use_filesystem:
|
||||
if USE_FILESYSTEM:
|
||||
pidfile = address + '.pid'
|
||||
|
||||
if args.kill:
|
||||
if args.foreground or args.replace:
|
||||
parser.error('--kill and --foreground/--replace cannot be used together')
|
||||
if kill_daemon():
|
||||
if kill_daemon(address):
|
||||
if not args.quiet:
|
||||
print ('Kill command sent to daemon, if it does not die in a couple of seconds use kill to kill it')
|
||||
raise SystemExit(0)
|
||||
|
@ -386,19 +456,19 @@ def main():
|
|||
raise SystemExit(1)
|
||||
|
||||
if args.replace:
|
||||
while kill_daemon():
|
||||
while kill_daemon(address):
|
||||
if not args.quiet:
|
||||
print ('Kill command sent to daemon, waiting for daemon to exit, press Ctrl-C to terminate wait and exit')
|
||||
sleep(2)
|
||||
|
||||
if use_filesystem and not args.foreground:
|
||||
if USE_FILESYSTEM and not args.foreground:
|
||||
# We must daemonize before creating the locked pidfile, unfortunately,
|
||||
# this means further print statements are discarded
|
||||
daemonize()
|
||||
is_daemon = daemonize()
|
||||
|
||||
if use_filesystem:
|
||||
if USE_FILESYSTEM:
|
||||
# Create a locked pid file containing the daemon’s PID
|
||||
if lockpidfile() is None:
|
||||
if lockpidfile(pidfile) is None:
|
||||
if not args.quiet:
|
||||
sys.stderr.write(
|
||||
'The daemon is already running. Use %s -k to kill it.\n' % (
|
||||
|
@ -406,7 +476,7 @@ def main():
|
|||
raise SystemExit(1)
|
||||
|
||||
# Bind to address or bail if we cannot bind
|
||||
sock = check_existing()
|
||||
sock = check_existing(address)
|
||||
if sock is None:
|
||||
if not args.quiet:
|
||||
sys.stderr.write(
|
||||
|
@ -414,14 +484,11 @@ def main():
|
|||
os.path.basename(sys.argv[0])))
|
||||
raise SystemExit(1)
|
||||
|
||||
if args.foreground:
|
||||
return main_loop(sock)
|
||||
|
||||
if not use_filesystem:
|
||||
if not USE_FILESYSTEM and not args.foreground:
|
||||
# We daemonize on linux
|
||||
daemonize()
|
||||
is_daemon = daemonize()
|
||||
|
||||
main_loop(sock)
|
||||
return main_loop(sock, is_daemon)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -23,8 +23,9 @@ else:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = get_argparser().parse_args()
|
||||
finish_args(os.environ, args)
|
||||
parser = get_argparser()
|
||||
args = parser.parse_args()
|
||||
finish_args(parser, os.environ, args)
|
||||
powerline = ShellPowerline(args, run_once=True)
|
||||
segment_info = {'args': args, 'environ': os.environ}
|
||||
write_output(args, powerline, segment_info, get_unicode_writer())
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
#!/bin/sh
|
||||
. tests/common.sh
|
||||
|
||||
enter_suite awesome
|
||||
|
||||
TEST_ROOT="$ROOT/tests/awesome"
|
||||
TEST_PATH="$TEST_ROOT/path"
|
||||
TEST_STATIC_ROOT="$ROOT/tests/test_awesome"
|
||||
|
||||
test -d "$TEST_ROOT" && rm -r "$TEST_ROOT"
|
||||
mkdir "$TEST_ROOT"
|
||||
cp -r "$TEST_STATIC_ROOT/path" "$TEST_ROOT"
|
||||
cp -r "$TEST_STATIC_ROOT/powerline" "$TEST_ROOT"
|
||||
|
||||
export PYTHONPATH="$ROOT${PYTHONPATH:+:}$PYTHONPATH"
|
||||
|
||||
ln -s "$(which "${PYTHON}")" "$TEST_PATH"/python
|
||||
ln -s "$(which cat)" "$TEST_PATH"
|
||||
ln -s "$(which sh)" "$TEST_PATH"
|
||||
ln -s "$(which env)" "$TEST_PATH"
|
||||
if which socat ; then
|
||||
ln -s "$(which socat)" "$TEST_PATH"
|
||||
fi
|
||||
for pexe in powerline powerline.sh powerline.py ; do
|
||||
if test -e scripts/$pexe ; then
|
||||
ln -s "$PWD/scripts/$pexe" $TEST_ROOT/path
|
||||
elif test -e client/$pexe ; then
|
||||
ln -s "$PWD/client/$pexe" $TEST_ROOT/path
|
||||
elif which $pexe ; then
|
||||
ln -s "$(which $pexe)" $TEST_ROOT/path
|
||||
else
|
||||
continue
|
||||
fi
|
||||
if test "x$pexe" != 'xpowerline.sh' || test -e "$TEST_PATH/socat" ; then
|
||||
POWERLINE_COMMAND="$pexe"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
DEPRECATED_SCRIPT="$ROOT/powerline/bindings/awesome/powerline-awesome.py"
|
||||
POWERLINE_DAEMON="scripts/powerline-daemon"
|
||||
|
||||
run() {
|
||||
env -i \
|
||||
LANG=C \
|
||||
PATH="$TEST_PATH" \
|
||||
XDG_CONFIG_HOME="$TEST_ROOT" \
|
||||
XDG_CONFIG_DIRS="$TEST_ROOT/dummy" \
|
||||
PYTHONPATH="$PYTHONPATH" \
|
||||
TEST_ROOT="$TEST_ROOT" \
|
||||
LD_LIBRARY_PATH="$LD_LIBRARY_PATH" \
|
||||
"$@" || true
|
||||
}
|
||||
|
||||
display_log() {
|
||||
local log_file="$1"
|
||||
echo "$log_file:"
|
||||
echo '============================================================'
|
||||
cat -v "$log_file"
|
||||
echo
|
||||
echo '____________________________________________________________'
|
||||
}
|
||||
|
||||
check_log() {
|
||||
local args_file="$TEST_ROOT/results/args"
|
||||
local log_file="$TEST_ROOT/results/requests"
|
||||
local line="$(head -n1 "$log_file")"
|
||||
local linenum="$(cat "$log_file" | wc -l)"
|
||||
echo "Number of runs: $linenum (expected approx 5 / 0.5 = 10 runs)"
|
||||
if test $linenum -lt 5 ; then
|
||||
fail "log:lt" F "Script was run not enough times: $linenum < 5"
|
||||
return 1
|
||||
elif test $linenum -gt 15 ; then
|
||||
fail "log:gt" E "Script was run too many times: $linenum > 15"
|
||||
return 1
|
||||
fi
|
||||
local expline="powerline_widget:set_markup('<span foreground=\"#303030\"> </span><span foreground=\"#d0d0d0\" background=\"#303030\" font_weight=\"bold\"> default-right </span>')"
|
||||
if test "x$expline" != "x$line" ; then
|
||||
echo "Line: '$line'"
|
||||
echo "Expected: '$expline'"
|
||||
fail "log:line" F "Unexpected line"
|
||||
return 1
|
||||
fi
|
||||
local ret=0
|
||||
while test $linenum -gt 0 ; do
|
||||
echo "$line" >> "$TEST_ROOT/ok"
|
||||
linenum=$(( linenum - 1 ))
|
||||
done
|
||||
if ! diff "$TEST_ROOT/ok" "$log_file" ; then
|
||||
fail "log:diff" F "Unexpected output"
|
||||
ret=1
|
||||
fi
|
||||
rm "$TEST_ROOT/ok"
|
||||
return $ret
|
||||
}
|
||||
|
||||
killscript() {
|
||||
kill -KILL $1 || true
|
||||
}
|
||||
|
||||
if ! test -e "$DEPRECATED_SCRIPT" ; then
|
||||
# TODO: uncomment when skip is available
|
||||
# skip "deprecated" "Missing deprecated bar bindings script"
|
||||
:
|
||||
else
|
||||
enter_suite "deprecated"
|
||||
for args in "" "0.5"; do
|
||||
rm -rf "$TEST_ROOT/results"
|
||||
mkdir "$TEST_ROOT/results"
|
||||
DEPRECATED_LOG="$TEST_ROOT/deprecated.log"
|
||||
run env \
|
||||
DEPRECATED_SCRIPT="$DEPRECATED_SCRIPT" \
|
||||
args="$args" \
|
||||
DEPRECATED_LOG="$DEPRECATED_LOG" \
|
||||
TEST_ROOT="$TEST_ROOT" \
|
||||
sh -c '
|
||||
echo $$ > "$TEST_ROOT/$args-pid"
|
||||
exec "$DEPRECATED_SCRIPT" $args > "$DEPRECATED_LOG" 2>&1
|
||||
' &
|
||||
sleep 5
|
||||
killscript "$(cat "$TEST_ROOT/$args-pid")"
|
||||
rm "$TEST_ROOT/$args-pid"
|
||||
if test "x$(cat "$DEPRECATED_LOG")" != "x" ; then
|
||||
display_log "$DEPRECATED_LOG"
|
||||
fail "output" E "Nonempty $DEPRECATED_SCRIPT output"
|
||||
fi
|
||||
rm "$DEPRECATED_LOG"
|
||||
if ! check_log ; then
|
||||
display_log "$TEST_ROOT/results/args"
|
||||
fail "log" F "Checking log failed"
|
||||
fi
|
||||
done
|
||||
exit_suite --continue
|
||||
fi
|
||||
|
||||
enter_suite "awesome"
|
||||
ADDRESS="powerline-ipc-test-$$"
|
||||
echo "Powerline address: $ADDRESS"
|
||||
rm -rf "$TEST_ROOT/results"
|
||||
mkdir "$TEST_ROOT/results"
|
||||
run env \
|
||||
POWERLINE_DAEMON="$POWERLINE_DAEMON" \
|
||||
TEST_ROOT="$TEST_ROOT" \
|
||||
ADDRESS="$ADDRESS" \
|
||||
sh -c '
|
||||
echo $$ > "$TEST_ROOT/dpid"
|
||||
exec python "$POWERLINE_DAEMON" --socket $ADDRESS --foreground > "$TEST_ROOT/daemon.log" 2>&1
|
||||
' &
|
||||
DPID=$!
|
||||
sleep 2
|
||||
run "$POWERLINE_COMMAND" --socket $ADDRESS wm.awesome > "$TEST_ROOT/output.log.1" 2>&1
|
||||
run "$POWERLINE_COMMAND" --socket $ADDRESS wm.awesome > "$TEST_ROOT/output.log.2" 2>&1
|
||||
run "$POWERLINE_COMMAND" --socket $ADDRESS wm.awesome > "$TEST_ROOT/output.log.3" 2>&1
|
||||
run "$POWERLINE_COMMAND" --socket $ADDRESS wm.awesome > "$TEST_ROOT/output.log.4" 2>&1
|
||||
run "$POWERLINE_COMMAND" --socket $ADDRESS wm.awesome > "$TEST_ROOT/output.log.5" 2>&1
|
||||
for log_file in "$TEST_ROOT"/output.log.* ; do
|
||||
if test "x$(cat "$log_file")" != "x" ; then
|
||||
display_log "$log_file"
|
||||
fail "output" E "Nonempty $POWERLINE_COMMAND output at run ${log_file#*.}"
|
||||
fi
|
||||
rm "$log_file"
|
||||
done
|
||||
sleep 5
|
||||
run python "$POWERLINE_DAEMON" --socket $ADDRESS --quiet --kill > "$TEST_ROOT/kill.log" 2>&1
|
||||
if test "x$(cat "$TEST_ROOT/kill.log")" != "x" ; then
|
||||
display_log "$TEST_ROOT/kill.log"
|
||||
fail "daemonlog" E "Nonempty kill log"
|
||||
fi
|
||||
rm "$TEST_ROOT/kill.log"
|
||||
wait $DPID
|
||||
if test "x$(cat "$TEST_ROOT/daemon.log")" != "x" ; then
|
||||
display_log "$TEST_ROOT/daemon.log"
|
||||
fail "daemonlog" E "Nonempty daemon log"
|
||||
fi
|
||||
rm "$TEST_ROOT/daemon.log"
|
||||
if ! check_log ; then
|
||||
display_log "$TEST_ROOT/results/args"
|
||||
fail "log" F "Checking log failed"
|
||||
fi
|
||||
exit_suite --continue
|
||||
|
||||
if ! powerline-lint \
|
||||
-p "$ROOT/powerline/config_files" \
|
||||
-p "$TEST_STATIC_ROOT/powerline"
|
||||
then
|
||||
fail "lint" F "Checking test config failed"
|
||||
fi
|
||||
|
||||
if test $FAILED -eq 0 ; then
|
||||
rm -r "$TEST_ROOT"
|
||||
fi
|
||||
|
||||
exit_suite
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
echo "$@" >> "$TEST_ROOT/results/args"
|
||||
cat >> "$TEST_ROOT/results/requests"
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"ext": {
|
||||
"wm": {
|
||||
"update_interval": 0.5
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"segments": {
|
||||
"left": [
|
||||
{
|
||||
"type": "string",
|
||||
"highlight_groups": ["time"],
|
||||
"contents": "default-left"
|
||||
}
|
||||
],
|
||||
"right": [
|
||||
{
|
||||
"type": "string",
|
||||
"highlight_groups": ["time"],
|
||||
"contents": "default-right"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"segments": {
|
||||
"left": [
|
||||
{
|
||||
"type": "string",
|
||||
"highlight_groups": ["time"],
|
||||
"contents": "dvi-left"
|
||||
}
|
||||
],
|
||||
"right": [
|
||||
{
|
||||
"type": "string",
|
||||
"highlight_groups": ["time"],
|
||||
"contents": "dvi-right"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -127,7 +127,7 @@ class TestParser(TestCase):
|
|||
}),
|
||||
]:
|
||||
args = parser.parse_args(argv)
|
||||
finish_args({}, args)
|
||||
finish_args(parser, {}, args)
|
||||
for key, val in expargs.items():
|
||||
self.assertEqual(getattr(args, key), val)
|
||||
for key, val in args.__dict__.items():
|
||||
|
|
|
@ -311,7 +311,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te
|
|||
if test $TEST_TYPE = daemon ; then
|
||||
sh -c '
|
||||
echo $$ > tests/shell/daemon_pid
|
||||
$PYTHON ./scripts/powerline-daemon -s$ADDRESS -f >tests/shell/daemon_log 2>&1
|
||||
exec $PYTHON ./scripts/powerline-daemon -s$ADDRESS -f >tests/shell/daemon_log 2>&1
|
||||
' &
|
||||
fi
|
||||
echo "> Testing $TEST_TYPE"
|
||||
|
|
Loading…
Reference in New Issue