diff --git a/pcl_expect.py b/pcl_expect.py index 4265bf55718fb42d31891d216695b9ae4cc39437..bff35fe78ba56a4d39cc76c06e6ad5a0483d0fb3 100644 --- a/pcl_expect.py +++ b/pcl_expect.py @@ -1,6 +1,9 @@ -import sets -import select import os +import re +import select +import sets +import sys +import types # # Global variables. The user are supposed to change these. @@ -21,24 +24,33 @@ timeout_raises_exception = True class Timeout(Exception): pass class expectable: - def __init__(self): + def __init__(self, fileno): + self.__fileno = fileno + self.__buffer = "" self.__eof_seen = False def fileno(self): return self.__fileno def fill_buffer(self): - s = os.read(self.fileno(), 8192) - if s == "": - self.__eof_seen = True + if not self.__eof_seen: + s = os.read(self.fileno(), 8192) + if s == "": + debug("got eof from fd %d" % self.fileno()) + self.__eof_seen = True + else: + debug("got %d bytes from fd %d" % (len(s), self.fileno())) + self.__buffer = self.__buffer + s else: - self.__buffer = self.__buffer + s + debug("eof already seen on fd %d; not reading" % self.fileno()) + return self.__eof_seen def eof(self): """Return True if end-of-file has been seen. There might still be pending data in the buffer, though. """ + return self.__eof_seen def buffer(self): return self.__buffer @@ -46,6 +58,14 @@ class expectable: def remove_first(self, n): self.__buffer = self.__buffer[n:] +class popen(expectable): + def __init__(self, cmd): + self.__cmd = os.popen(cmd, "r") + expectable.__init__(self, self.__cmd.fileno()) + + def close(self): + return self.__cmd.close() + class impl: def __init__(self, timeout = None): self.__first = True @@ -56,21 +76,29 @@ class impl: self.__timeout_active = False self.__acted = False self.__all_inputs_seen = False + debug("START") def loop(self): + debug("at top of loop") if self.__done: + debug("done -- exiting expect") return False if self.__first: self.__first = False - if __expect_before(): + debug("first time round") + self.__acted = False + if _expect_before(): return False return True elif not self.__acted: + debug("no action taken during previous loop") self.__all_inputs_seen = True - if __expect_after(): + if _expect_after(): return False + self.__acted = False + # Nothing handled the timeout event. if self.__timeout_active: debug("Unhandled timeout") @@ -79,6 +107,7 @@ class impl: else: return False + r = ", ".join([str(x.fileno()) for x in self.__inputs]) if not self.__all_inputs_seen: t = 0 elif self.__timeout == None: @@ -86,7 +115,9 @@ class impl: else: t = self.__timeout if t > 0: - debug("Waiting for input") + debug("Waiting for input on %s" % r) + else: + debug("Polling for input on %s" % r) (r, w, e) = select.select([x for x in self.__inputs], [], [], t) self.__readable = sets.Set(r) if len(self.__readable) == 0: @@ -95,11 +126,19 @@ class impl: self.__timeout_active = True else: debug("Input available") - if __expect_before(): + if _expect_before(): return False return True + def __fill_buffer(self, exp): + if exp in self.__readable: + debug("reading from fd %d" % exp.fileno()) + self.__readable.remove(exp) + if exp.fill_buffer(): + debug("got eof on fd %d" % exp.fileno()) + self.__inputs.discard(exp) + def re(self, exp, regexp): """Implement "when re foo, foo_re". @@ -119,9 +158,10 @@ class impl: if self.__done: raise "foo! me bad!" - if exp in self.__readable: - self.__readable.remove(exp) - exp.fill_buffer() + self.__fill_buffer(exp) + + debug("does \"%s\" match \"%s\" (fd %d)?" % ( + exp.buffer(), regexp, exp.fileno())) # Doing this compilation again and again could be a problem. # I rely on the cache in module re. I hope it exists... @@ -130,16 +170,43 @@ class impl: match = regexp.search(exp.buffer()) if match != None: + debug("yes") exp.match = match exp.remove_first(match.end()) self.__done = True self.__acted = True return True else: + debug("no") if not exp.eof(): + debug("adding fd %d to rd-set" % exp.fileno()) self.__inputs.add(exp) return False + def eof(self, exp): + """Implement "when eof foo". + """ + if self.__acted: + return False + + if self.__done: + raise "foo! me bad!" + + self.__fill_buffer(exp) + + if exp.eof(): + debug("eof seen on fd %d" % exp.fileno()) + exp.match = exp.buffer() + exp.remove_first(len(exp.match)) + self.__done = True + self.__acted = True + return True + else: + debug("no eof on fd %d" % exp.fileno()) + debug("adding fd %d to rd-set" % exp.fileno()) + self.__inputs.add(exp) + return False + def timeout(self): """Implement "when timeout". @@ -153,14 +220,56 @@ class impl: raise "foo! me bad!" if self.__timeout_active: + debug("eof pending") self.__timeout_active = False self.__done = True self.__acted = True return True else: + debug("no eof pending") return False def cont(self): + debug("cont called") self.__done = False self.__first = True # Don't touch self.__acted + +def _expect_before(): + return False + +def _expect_after(): + return False + +def debug(s): + if 0: + sys.stderr.write("pcl-expect: %s\n" % s) + +if __name__ == '__main__': + df = popen("df -k") + + x = impl() + while x.loop(): + if x.re(df, "Filesystem.*\n"): + print "Header:", df.match.group() + + x = impl() + while x.loop(): + if x.re(df, "^/.* .*\n"): + print "Normal line:", df.match.group() + x.cont() + continue + elif x.re(df, "^/.*\n"): + print "Mount point only:", df.match.group() + x.cont() + continue + elif x.re(df, "^ .*\n"): + print "Info only:", df.match.group() + x.cont() + continue + elif x.re(df, "^.*\n"): + print "Unexpected line", df.match.group() + x.cont() + continue + elif x.eof(df): + print "And that's all, folks!"