def get_answer(self) -> Tuple[Result, str]: """Read from 'out_q' and wait until a full response is received.""" assert self.xml is not None data = [] poll_sec = 1 while True: # Abort if an error is printed to stderr, but ignore warnings. # NOTE: If `warnings_wf` is False because this version of Coq does # not follow the pattern expected by `partition_warnings` then # pretend everything is a warning and hope for the best. err = self.collect_err() if self.xml.warnings_wf and partition_warnings(err)[1] != "": return UNEXPECTED_ERR, err try: data.append(self.out_q.get(timeout=poll_sec)) except Empty: continue xml = b"".join(data) if not self.xml.worth_parsing(xml): continue response = self.xml.raw_response(xml) if response is None: continue # Don't bother doing prettyxml if debugging isn't on if self.logger.isEnabledFor(logging.DEBUG): self.logger.debug(prettyxml(b"<response>" + xml + b"</response>")) return response, err
def get_answer(self) -> Result: """Read from 'out_q' and wait until a full response is received.""" assert self.xml is not None data = [] while True: data.append(self.out_q.get()) xml = b"".join(data) if not self.xml.worth_parsing(xml): continue response = self.xml.raw_response(xml) if response is None: continue # Don't bother doing prettyxml if debugging isn't on if self.logger.isEnabledFor(logging.DEBUG): self.logger.debug( prettyxml(b"<response>" + xml + b"</response>")) return response
def get_answer(self, res_ref): # type: (Ref) -> None """Read from 'out_q' and wait until a full response is received.""" data = [] while True: data.append(self.out_q.get()) if not self.xml.worth_parsing(data[-1]): continue xml = b"".join(data) response = self.xml.raw_response(xml) if response is None: continue # Don't bother doing prettyxml if debugging isn't on if self.logger.isEnabledFor(logging.DEBUG): self.logger.debug(prettyxml(b"<response>" + xml + b"</response>")) res_ref.val = response # Notify the caller that Coqtop is done self.done_callback() break
def call( self, cmdtype_msg: Tuple[str, Optional[bytes]], timeout: Optional[int] = None, ) -> Tuple[Result, str]: """Send 'msg' to the Coqtop process and wait for the response.""" assert self.xml is not None # Check if Coqtop has stopped if not self.running(): raise CoqtopError("Coqtop is not running.") # Throw away any unread messages self.empty_out() # 'msg' can be None if a command does not exist for a particular # version and is being faked. # NOTE: It is important that the '_standardize' function being called # does not depend on the value it is passed since it is None cmd, msg = cmdtype_msg if msg is None: return self.xml.standardize(cmd, Ok(None)), self.collect_err() # Don't bother doing prettyxml if debugging isn't on if self.logger.isEnabledFor(logging.DEBUG): self.logger.debug(prettyxml(msg)) self.send_cmd(msg) with futures.ThreadPoolExecutor(1) as pool: try: timeout = timeout if timeout != 0 else None response = pool.submit(self.get_answer).result(timeout) except futures.TimeoutError: self.interrupt() response = TIMEOUT_ERR return self.xml.standardize(cmd, response), self.collect_err()
def call( self, cmdtype_msg, # type: Tuple[Text, Optional[bytes]] timeout=None, # type: Optional[int] ): # type: (...) -> Generator[Union[Ok, Err], bool, None] """Send 'msg' to the Coqtop process and wait for the response.""" # Check if Coqtop has stopped if not self.running(): raise CoqtopError("Coqtop is not running.") # Throw away any unread messages self.empty_out() cmd, msg = cmdtype_msg # 'msg' can be None if a command does not exist for a particular # version and is being faked. # N.B. It is important that the '_standardize' function being called # does not depend on the value it is passed since it is None if msg is None: self.done_callback() yield # type: ignore[misc] # (see comment above start()) yield self.xml.standardize(cmd, Ok(None)) return # Don't bother doing prettyxml if debugging isn't on if self.logger.isEnabledFor(logging.DEBUG): self.logger.debug(prettyxml(msg)) self.send_cmd(msg) if timeout == 0: timeout = None # The got_response event tells the timeout_thread that get_answer() # returned normally, while timed_out will be set by timeout_thread if # time runs out without receiving a response got_response = threading.Event() timed_out = threading.Event() timeout_thread = threading.Thread(target=self.timeout_thread, args=(timeout, got_response, timed_out)) timeout_thread.daemon = True # Start a thread to get Coqtop's response res_ref = Ref() answer_thread = threading.Thread(target=self.get_answer, args=(res_ref, )) answer_thread.daemon = True # Start threads and yield back to caller to wait for Coqtop to finish timeout_thread.start() answer_thread.start() stopped = yield # type: ignore[misc] # (see comment above start()) # Notify timeout_thread that a response is received and wait for # threads to finish got_response.set() timeout_thread.join() answer_thread.join() response = res_ref.val # Check for user interrupt or timeout if isinstance(response, Err): if stopped: response = STOPPED_ERR elif timed_out.is_set(): response = TIMEOUT_ERR yield self.xml.standardize(cmd, response)