Skip to content

Commit 79e8b33

Browse files
committed
WIP: coroutine and subprocess support
1 parent 5e57718 commit 79e8b33

File tree

3 files changed

+72
-3
lines changed

3 files changed

+72
-3
lines changed

pynvim/api/nvim.py

+50
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,56 @@ def handler():
453453
raise
454454
self._session.threadsafe_call(handler)
455455

456+
if IS_PYTHON3:
457+
458+
def run_coroutine(self, coroutine):
459+
""" Run a coroutine inside a response handler (or setup_cb)"""
460+
461+
return self._session.run_coroutine(coroutine)
462+
463+
def start_subprocess(self, cmd, on_data, on_exit, **args):
464+
coro = self.loop.subprocess_exec(partial(NvimAsyncioProcess, self, on_data, on_exit),
465+
*cmd, **args)
466+
(transport, protocol) = self.run_coroutine(coro)
467+
return transport
468+
469+
if IS_PYTHON3:
470+
471+
import asyncio
472+
473+
474+
class NvimAsyncioProcess(asyncio.SubprocessProtocol):
475+
476+
def __init__(self, session, on_data, on_exit):
477+
self.session = session
478+
self.on_data = on_data
479+
self.on_exit = on_exit
480+
481+
self.call_point = ''.join(format_stack(None, 6)[:-2])
482+
483+
def _callback(self, cb, *args):
484+
485+
def handler():
486+
try:
487+
cb(*args)
488+
except Exception as err:
489+
msg = ("error caught while executing subprocess callback:\n"
490+
"{!r}\n{}\n \nthe process was created at\n{}"
491+
.format(err, format_exc_skip(1), self.call_point))
492+
self.session._err_cb(msg)
493+
raise
494+
495+
self.session._session.threadsafe_call(handler)
496+
497+
498+
def connection_made(self, transport):
499+
pass
500+
501+
def pipe_data_received(self, fd, data):
502+
self._callback(self.on_data, fd, data)
503+
504+
def process_exited(self):
505+
self._callback(self.on_exit)
456506

457507
class Buffers(object):
458508

pynvim/msgpack_rpc/event_loop/asyncio.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ def _init(self):
8383
self._queued_data = deque()
8484
self._fact = lambda: self
8585
self._raw_transport = None
86+
if os.name != 'nt':
87+
self._child_watcher = asyncio.get_child_watcher()
88+
self._child_watcher.attach_loop(self._loop)
8689

8790
def _connect_tcp(self, address, port):
8891
coroutine = self._loop.create_connection(self._fact, address, port)
@@ -118,9 +121,6 @@ def _connect_stdio(self):
118121
debug("native stdout connection successful")
119122

120123
def _connect_child(self, argv):
121-
if os.name != 'nt':
122-
self._child_watcher = asyncio.get_child_watcher()
123-
self._child_watcher.attach_loop(self._loop)
124124
coroutine = self._loop.subprocess_exec(self._fact, *argv)
125125
self._loop.run_until_complete(coroutine)
126126

pynvim/msgpack_rpc/session.py

+19
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,25 @@ def request(self, method, *args, **kwargs):
102102
raise self.error_wrapper(err)
103103
return rv
104104

105+
def run_coroutine(self, coroutine):
106+
if not self._is_running:
107+
# TODO: can has return value?
108+
return self.loop._loop.run_until_complete(coroutine)
109+
gr = greenlet.getcurrent()
110+
parent = gr.parent
111+
112+
def result_cb(future):
113+
debug('coroutine result is available for greenlet %s, switching back', gr)
114+
gr.switch(future)
115+
116+
task = self.loop._loop.create_task(coroutine)
117+
task.add_done_callback(result_cb)
118+
119+
debug('yielding from greenlet %s to wait for coroutine', gr)
120+
future = parent.switch()
121+
return future.result() # should re-raise any exception
122+
123+
105124
def run(self, request_cb, notification_cb, setup_cb=None):
106125
"""Run the event loop to receive requests and notifications from Nvim.
107126

0 commit comments

Comments
 (0)