def __init__(self, args=['coqtop', '-ideslave', '-main-channel', 'stdfds', '-async-proofs', 'on'], extra_args_file=None, handler=lambda x: None): if extra_args_file is not None: with open(extra_args_file, 'r') as f: args = args + shlex.split(f.read()) from subprocess import PIPE coro = asyncio.create_subprocess_exec(*args, stdin=PIPE, stdout=PIPE, stderr=open('fifo', 'w')) self._proc = await(coro) self._to_coq = self._proc.stdin self._from_coq = self._proc.stdout self._do_recv() self._parser = XMLStreamParser(self._handle_response, encoding='utf-8') self._response_waiters = deque([]) self._message_handler = None
class Coqtop(object): # __init__ must run in a greenlet context so it can wait for the subprocess # to start up. def __init__(self, args=['coqtop', '-ideslave', '-main-channel', 'stdfds', '-async-proofs', 'on'], extra_args_file=None, handler=lambda x: None): if extra_args_file is not None: with open(extra_args_file, 'r') as f: args = args + shlex.split(f.read()) from subprocess import PIPE coro = asyncio.create_subprocess_exec(*args, stdin=PIPE, stdout=PIPE, stderr=open('fifo', 'w')) self._proc = await(coro) self._to_coq = self._proc.stdin self._from_coq = self._proc.stdout self._do_recv() self._parser = XMLStreamParser(self._handle_response, encoding='utf-8') self._response_waiters = deque([]) self._message_handler = None # The low-level receiver code runs directly on the event loop greenlet, # inside an ordinary async callback. def _do_recv(self): coro = self._from_coq.read(4096) task = asyncio.async(coro) task.add_done_callback(self._handle_recv) def _handle_recv(self, task): msg = task.result() assert len(msg) > 0 self._parser.feed(msg) self._do_recv() def _handle_response(self, xml): if xml.tag == 'value': self._handle_response_value(xml) elif xml.tag == 'message': self._handle_response_message(xml) else: print('unhandled: %r' % ET.tostring(xml)) def _handle_response_value(self, xml): resp = parse_response(xml) gr = self._response_waiters.popleft() def callback(): gr.switch(resp) asyncio.get_event_loop().call_soon_threadsafe(callback) def _handle_response_message(self, xml): if self._message_handler is None: return assert xml[0].tag == 'message_level' level = xml[0].get('val') msg = parse_value(xml[1]) def task(): self._message_handler(level, msg) gr = greenlet.greenlet(task) def callback(): gr.switch() asyncio.get_event_loop().call_soon_threadsafe(callback) def set_message_handler(self, f): self._message_handler = f # The higher-level code runs in a greenlet, so it can block for responses. def _get_response(self): gr = greenlet.getcurrent() self._response_waiters.append(gr) return gr.parent.switch() def call(self, name, arg): xml = encode_call(name, arg) msg = ET.tostring(xml, encoding='utf-8') self._to_coq.write(msg) self._to_coq.drain() resp = self._get_response() return resp