diff --git a/src/execo/process.py b/src/execo/process.py index 38ed5a7533dfcbf0b0f9fbde94c4c80c71f650a1..fcd19aaee0bd74ab37bc6fe1b14169a89ca0ebac 100644 --- a/src/execo/process.py +++ b/src/execo/process.py @@ -216,6 +216,7 @@ class ExpectOutputHandler(ProcessOutputHandler): self.last_pos = {} def expect(self, + process, regexes, callback = None, condition = None, @@ -267,8 +268,10 @@ class ExpectOutputHandler(ProcessOutputHandler): self.condition = condition self.backtrack_size = backtrack_size self.start_from_current = start_from_current + self._scan(process, STDOUT, '', False, False) + self._scan(process, STDERR, '', False, False) - def read(self, process, stream, string, eof, error): + def _scan(self, process, stream, string, eof, error): """When there is a match, the match position in the process stream becomes the new position from which subsequent searches on the same process / stream. @@ -283,6 +286,7 @@ class ExpectOutputHandler(ProcessOutputHandler): self.last_pos[k] = 0 elif self.backtrack_size != None: self.last_pos[k] = max(self.last_pos[k], len(streamdata) - len(string) - self.backtrack_size) + logger.debug("ExpectOuputHandler: scan stream %s at position %s of process %s" % (stream, self.last_pos[k], process)) for re_index, r in enumerate(self.regexes): mo = r.search(streamdata, self.last_pos[k]) if mo != None: @@ -298,9 +302,15 @@ class ExpectOutputHandler(ProcessOutputHandler): del self.last_pos[k] if mo != None: if stream == STDOUT: - process.stdout_handlers.remove(self) + try: + process.stdout_handlers.remove(self) + except ValueError: + pass if stream == STDERR: - process.stderr_handlers.remove(self) + try: + process.stderr_handlers.remove(self) + except ValueError: + pass if mo != None or eof or error: if self.condition != None: with self.condition: @@ -308,6 +318,9 @@ class ExpectOutputHandler(ProcessOutputHandler): if self.callback: self.callback(process, stream, re_index, mo) + def read(self, process, stream, string, eof, error): + self._scan(process, stream, string, eof, error) + class ProcessBase(object): """An almost abstract base class for all kinds of processes. @@ -834,7 +847,7 @@ class ProcessBase(object): else: logger.warning(s) - def expect(self, regexes, timeout = False, stream = STDOUT, backtrack_size = 2000, start_from_current = False): + def expect(self, regexes, timeout = False, stream = STDOUT, backtrack_size = 2000, start_from_current = False, expect_output_handler = None): """searches the process output stream(s) for some regex. It mimics/takes ideas from Don Libes expect, or python-pexpect. It is an easier-to-use frontend for @@ -875,6 +888,11 @@ class ProcessBase(object): False: when a process is monitored by this handler for the first time, the regex matching is started from the beginning of the stream. + + :param expect_output_handler: ExpectOutputHandler. If not + None, a specific ExpectOutputHandler instance to + use. Otherwise, a thread local ExpectOutputHandler is + instanciated. """ if timeout == False: timeout = self.default_expect_timeout countdown = Timer(timeout) @@ -885,17 +903,20 @@ class ProcessBase(object): re_index_and_match_object[1] = match_object with cond: cond.notify_all() - if self._thread_local_storage.expect_handler == None: - self._thread_local_storage.expect_handler = ExpectOutputHandler() - self._thread_local_storage.expect_handler.expect(regexes, - callback = internal_callback, - backtrack_size = backtrack_size, - start_from_current = start_from_current) + if expect_output_handler == None: + if self._thread_local_storage.__dict__.setdefault('expect_handler') == None: + self._thread_local_storage.expect_handler = ExpectOutputHandler() + expect_output_handler = self._thread_local_storage.expect_handler + expect_output_handler.expect(self, + regexes, + callback = internal_callback, + backtrack_size = backtrack_size, + start_from_current = start_from_current) with cond: if stream == STDOUT: - self.stdout_handlers.append(self._thread_local_storage.expect_handler) + self.stdout_handlers.append(expect_output_handler) else: - self.stderr_handlers.append(self._thread_local_storage.expect_handler) + self.stderr_handlers.append(expect_output_handler) while (countdown.remaining() == None or countdown.remaining() > 0) and re_index_and_match_object[0] == None: non_retrying_intr_cond_wait(cond, countdown.remaining()) if re_index_and_match_object[0] == None: