#! /bin/env python3 # encoding: utf-8 import queue import subprocess import sys import threading import time import over # FIXME # -------------------------------------------------- def capture_output(stream, fifo): while True: chunk = stream.read1(100) if chunk: fifo.put(chunk) else: break stream.close() class Command: def __init__(self, sequence): self.__dict__["sequence"] = list(sequence) self.__dict__["thread"] = None self.__dict__["fifo"] = None def __setattr__(self, name, value): found = False for i, word in enumerate(self.sequence): if word == name: self.sequence[i] = value found = True if not found: raise AttributeError("Command has no attribute \'%s\'" %(name)) def dump(self, sequence=None): out = [] if not sequence: sequence = self.sequence for item in sequence: if type(item) is list: out += self.dump(item) else: out.append(str(item)) return out def run(self, async=False, stderr=False): """ Executes the command in the current environment. async return immediatelly, use poll_output to get output stderr capture stderr instead of stdout """ self.__dict__["process"] = subprocess.Popen(self.dump(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1) self.__dict__["fifo"] = queue.Queue() self.__dict__["thread"] = threading.Thread( target=capture_output, args=(self.process.stderr if stderr else self.process.stdout, self.fifo) ) self.thread.daemon = True # thread dies with the program self.thread.start() if not async: buffer = [] probably_dead = False while True: probably_dead = self.process.poll() is not None chunk = self.poll_output() if chunk: buffer.append(chunk) else: time.sleep(0.01) if not chunk and self.process.poll() is not None and probably_dead: break return b"".join(buffer) else: return None def poll_output(self): """ Returns the output of a currently running process and clears the buffer. Returns None if no process is running. Blocking - always returns at least one char. """ if self.fifo.empty(): return None else: buffer = [] while not self.fifo.empty(): buffer.append(self.fifo.get_nowait()) return b"".join(buffer) @property def running(self): return self.process.poll() is None # -------------------------------------------------- def parse_fps(raw): if "/" in raw: num, den = (int(x) for x in raw.split("/")) return float(num) / float(den) return float(raw)