diff --git a/i3pystatus/__init__.py b/i3pystatus/__init__.py index 6d6f9fa..485de11 100644 --- a/i3pystatus/__init__.py +++ b/i3pystatus/__init__.py @@ -7,19 +7,14 @@ from threading import Thread import time from contextlib import contextmanager -class BaseModule: +class Module: output = None + position = 0 def registered(self, status_handler): """Called when this module is registered with a status handler""" - def tick(self): - """Called once per tick""" - -class Module(BaseModule): - pass - -class AsyncModule(BaseModule): +class AsyncModule(Module): def registered(self, status_handler): self.thread = Thread(target=self.mainloop) self.thread.daemon = True @@ -44,35 +39,56 @@ class IOHandler: self.inp = inp self.out = out - def write(self, message): + def write_line(self, message): """Unbuffered printing to stdout.""" self.out.write(message + "\n") self.out.flush() def read(self): - """Interrupted respecting reader for stdin.""" + """Iterate over all input lines (Generator)""" + + while True: + try: + yield self.read_line() + except EOFError: + return + + def read_line(self): + """Interrupted respecting reader for stdin. + + Raises EOFError if the end of stream has been reached""" - # try reading a line, removing any extra whitespace try: line = self.inp.readline().decode("utf-8").strip() - # i3status sends EOF, or an empty line - if not line: - sys.exit(3) - return line - # exit on ctrl-c except KeyboardInterrupt: - sys.exit() + raise EOFError() + + # i3status sends EOF, or an empty line + if not line: + raise EOFError() + return line class JSONIO: def __init__(self, io): self.io = io - self.io.write(self.io.read()) - self.io.write(self.io.read()) + self.io.write_line(self.io.read_line()) + self.io.write_line(self.io.read_line()) + + def read(self): + """Iterate over all JSON input (Generator)""" + + for line in self.io.read(): + with self.parse_line(line) as j: + yield j @contextmanager - def read(self): - line, prefix = self.io.read(), "" + def parse_line(self, line): + """Parse a single line of JSON and write modified JSON back. + + Usage is quite simple using the usual with-Syntax.""" + + prefix = "" # ignore comma at start of lines if line.startswith(","): @@ -80,8 +96,7 @@ class JSONIO: j = json.loads(line) yield j - - self.io.write(prefix + json.dumps(j)) + self.io.write_line(prefix + json.dumps(j)) class I3statusHandler: modules = [] @@ -90,22 +105,16 @@ class I3statusHandler: if fd is None: fd = sys.stdin - self.io = IOHandler(fd) + self.io = JSONIO(IOHandler(fd)) - def register(self, module): + def register(self, module, position=0): """Register a new module.""" self.modules.append(module) + module.position = position module.registered(self) def run(self): - jio = JSONIO(self.io) - - while True: - with jio.read() as j: - for module in self.modules: - module.tick() - - output = module.output - if output: - j.insert(0, output) + for j in self.io.read(): + for module in self.modules: + j.insert(module.position, module.output)