diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index ff82a5af..420f0e86 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -1,9 +1,18 @@ # vim:fileencoding=utf-8:noet import zsh +import atexit from powerline.shell import ShellPowerline from powerline.lib import parsedotval +used_powerlines = [] + + +def shutdown(): + for powerline in used_powerlines: + powerline.renderer.shutdown() + + def get_var_config(var): try: return [parsedotval(i) for i in zsh.getvalue(var).items()] @@ -69,17 +78,17 @@ class Environment(object): class Prompt(object): - __slots__ = ('render', 'side', 'savedpsvar', 'savedps', 'args') + __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args') def __init__(self, powerline, side, savedpsvar=None, savedps=None): - self.render = powerline.renderer.render + self.powerline = powerline self.side = side self.savedpsvar = savedpsvar self.savedps = savedps self.args = powerline.args def __str__(self): - r = self.render(width=zsh.columns(), side=self.side, segment_info=self.args) + r = self.powerline.renderer.render(width=zsh.columns(), side=self.side, segment_info=self.args) if type(r) is not str: if type(r) is bytes: return r.decode('utf-8') @@ -90,6 +99,9 @@ class Prompt(object): def __del__(self): if self.savedps: zsh.setvalue(self.savedpsvar, self.savedps) + used_powerlines.remove(self.powerline) + if self.powerline not in used_powerlines: + self.powerline.renderer.shutdown() def set_prompt(powerline, psvar, side): @@ -103,5 +115,8 @@ def set_prompt(powerline, psvar, side): def setup(): environ = Environment() powerline = ShellPowerline(Args(), environ=environ, getcwd=lambda: environ['PWD']) + used_powerlines.append(powerline) + used_powerlines.append(powerline) set_prompt(powerline, 'PS1', 'left') set_prompt(powerline, 'RPS1', 'right') + atexit.register(shutdown) diff --git a/powerline/lib/threaded.py b/powerline/lib/threaded.py index 147de23b..50d7895e 100644 --- a/powerline/lib/threaded.py +++ b/powerline/lib/threaded.py @@ -4,21 +4,18 @@ from __future__ import absolute_import from powerline.lib.time import monotonic -from time import sleep -from threading import Thread, Lock +from threading import Thread, Lock, Event class ThreadedSegment(object): - daemon = True min_sleep_time = 0.1 update_first = True interval = 1 def __init__(self): super(ThreadedSegment, self).__init__() - self.update_lock = Lock() + self.shutdown_event = Event() self.write_lock = Lock() - self.keep_going = True self.run_once = True self.did_set_interval = False self.thread = None @@ -39,6 +36,8 @@ class ThreadedSegment(object): if update_first and self.update_first: self.update() self.start() + elif not self.updated: + self.update() if self.skip: return self.crashed_value @@ -49,40 +48,29 @@ class ThreadedSegment(object): return self.thread and self.thread.is_alive() def start(self): + self.keep_going = True self.thread = Thread(target=self.run) - self.thread.daemon = self.daemon self.thread.start() def sleep(self, adjust_time): - sleep(max(self.interval - adjust_time, self.min_sleep_time)) + self.shutdown_event.wait(max(self.interval - adjust_time, self.min_sleep_time)) + if self.shutdown_event.is_set(): + self.keep_going = False def run(self): while self.keep_going: start_time = monotonic() - try: - if self.update_lock.acquire(False): - try: - self.update() - except Exception as e: - self.error('Exception while updating: {0}', str(e)) - self.skip = True - else: - self.skip = False - else: - return - finally: - # Release lock in any case. If it is not locked in this thread, - # it was done in main thread in .shutdown method, and the lock - # will never be released. - self.update_lock.release() - + self.update() + except Exception as e: + self.error('Exception while updating: {0}', str(e)) + self.skip = True + else: + self.skip = False self.sleep(monotonic() - start_time) def shutdown(self): - if self.keep_going: - self.keep_going = False - self.update_lock.acquire() + self.shutdown_event.set() def set_interval(self, interval=None): # Allowing “interval” keyword in configuration. @@ -93,9 +81,10 @@ class ThreadedSegment(object): self.interval = interval self.has_set_interval = True - def set_state(self, interval=None, **kwargs): + def set_state(self, interval=None, update_first=True, **kwargs): if not self.did_set_interval or interval: self.set_interval(interval) + self.updated = not (update_first and self.update_first) def startup(self, pl, **kwargs): self.run_once = False @@ -124,12 +113,13 @@ def printed(func): class KwThreadedSegment(ThreadedSegment): drop_interval = 10 * 60 - update_first = False + update_first = True def __init__(self): super(KwThreadedSegment, self).__init__() self.queries = {} self.crashed = set() + self.updated = True @staticmethod def key(**kwargs): @@ -170,7 +160,7 @@ class KwThreadedSegment(ThreadedSegment): for key in removes: self.queries.pop(key) - def set_state(self, interval=None, **kwargs): + def set_state(self, interval=None, update_first=True, **kwargs): if not self.did_set_interval or (interval < self.interval): self.set_interval(interval) diff --git a/tests/test_segments.py b/tests/test_segments.py index 9ce53e27..87c460f1 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -193,6 +193,7 @@ class TestCommon(TestCase): def test_network_load(self): from time import sleep + def gb(interface): return None @@ -251,6 +252,7 @@ class TestCommon(TestCase): {'divider_highlight_group': 'background:divider', 'contents': 'r 1 KiB/s', 'highlight_group': ['network_load_recv', 'network_load']}, {'divider_highlight_group': 'background:divider', 'contents': 's 2 KiB/s', 'highlight_group': ['network_load_sent_gradient', 'network_load_gradient', 'network_load_sent', 'network_load'], 'gradient_level': ApproxEqual()}, ]) + common.network_load.shutdown() def test_virtualenv(self): with replace_env('VIRTUAL_ENV', '/abc/def/ghi') as pl: