1818from pynvim .msgpack_rpc .event_loop .base import BaseEventLoop , TTransportType
1919
2020logger = logging .getLogger (__name__ )
21- debug , info , warn = (logger .debug , logger .info , logger .warning , )
21+ debug , info , warn = (logger .debug , logger .info , logger .warning )
2222
2323loop_cls = asyncio .SelectorEventLoop
2424
@@ -47,13 +47,14 @@ def __init__(self, on_data, on_error):
4747 @override
4848 def connection_made (self , transport ):
4949 """Used to signal `asyncio.Protocol` of a successful connection."""
50- del transport # no-op
50+ self . _transport = transport
5151
5252 @override
5353 def connection_lost (self , exc : Optional [Exception ]) -> None :
5454 """Used to signal `asyncio.Protocol` of a lost connection."""
55- debug (f"connection_lost: exc = { exc } " )
56- self ._on_error (exc if exc else EOFError ())
55+ warn (f"connection_lost: exc = { exc } " )
56+
57+ self ._on_error (exc if exc else EOFError ("connection_lost" ))
5758
5859 @override
5960 def data_received (self , data : bytes ) -> None :
@@ -63,11 +64,19 @@ def data_received(self, data: bytes) -> None:
6364 @override
6465 def pipe_connection_lost (self , fd : int , exc : Optional [Exception ]) -> None :
6566 """Used to signal `asyncio.SubprocessProtocol` of a lost connection."""
66- debug ("pipe_connection_lost: fd = %s, exc = %s" , fd , exc )
67+
68+ assert isinstance (self ._transport , asyncio .SubprocessTransport )
69+ debug_info = {'fd' : fd , 'exc' : exc , 'pid' : self ._transport .get_pid ()}
70+ warn (f"pipe_connection_lost { debug_info } " )
71+
6772 if os .name == 'nt' and fd == 2 : # stderr
6873 # On windows, ignore piped stderr being closed immediately (#505)
6974 return
70- self ._on_error (exc if exc else EOFError ())
75+
76+ # pipe_connection_lost() *may* be called before process_exited() is
77+ # called, when a Nvim subprocess crashes (SIGABRT). Do not handle
78+ # errors here, as errors will be handled somewhere else
79+ # self._on_error(exc if exc else EOFError("pipe_connection_lost"))
7180
7281 @override
7382 def pipe_data_received (self , fd , data ):
@@ -81,8 +90,13 @@ def pipe_data_received(self, fd, data):
8190 @override
8291 def process_exited (self ) -> None :
8392 """Used to signal `asyncio.SubprocessProtocol` when the child exits."""
84- debug ("process_exited" )
85- self ._on_error (EOFError ())
93+ assert isinstance (self ._transport , asyncio .SubprocessTransport )
94+ pid = self ._transport .get_pid ()
95+ return_code = self ._transport .get_returncode ()
96+
97+ warn ("process_exited, pid = %s, return_code = %s" , pid , return_code )
98+ err = EOFError (f"process_exited: pid = { pid } , return_code = { return_code } " )
99+ self ._on_error (err )
86100
87101
88102class AsyncioEventLoop (BaseEventLoop ):
@@ -131,7 +145,8 @@ def _on_data(data: bytes) -> None:
131145 def _connect_tcp (self , address : str , port : int ) -> None :
132146 async def connect_tcp ():
133147 transport , protocol = await self ._loop .create_connection (
134- self ._protocol_factory , address , port )
148+ self ._protocol_factory , address , port
149+ )
135150 debug (f"tcp connection successful: { address } :{ port } " )
136151 self ._transport = transport
137152 self ._protocol = protocol
@@ -141,13 +156,12 @@ async def connect_tcp():
141156 @override
142157 def _connect_socket (self , path : str ) -> None :
143158 async def connect_socket ():
144- if os .name == 'nt' :
159+ if os .name == "nt" :
145160 _create_connection = self ._loop .create_pipe_connection
146161 else :
147162 _create_connection = self ._loop .create_unix_connection
148163
149- transport , protocol = await _create_connection (
150- self ._protocol_factory , path )
164+ transport , protocol = await _create_connection (self ._protocol_factory , path )
151165 debug ("socket connection successful: %s" , self ._transport )
152166 self ._transport = transport
153167 self ._protocol = protocol
@@ -157,15 +171,17 @@ async def connect_socket():
157171 @override
158172 def _connect_stdio (self ) -> None :
159173 async def connect_stdin ():
160- if os .name == 'nt' :
174+ if os .name == "nt" :
161175 pipe = PipeHandle (msvcrt .get_osfhandle (sys .stdin .fileno ()))
162176 else :
163177 pipe = sys .stdin
164178 transport , protocol = await self ._loop .connect_read_pipe (
165- self ._protocol_factory , pipe )
179+ self ._protocol_factory , pipe
180+ )
166181 debug ("native stdin connection successful" )
167182 self ._to_close .append (transport )
168183 del protocol
184+
169185 self ._loop .run_until_complete (connect_stdin ())
170186
171187 # Make sure subprocesses don't clobber stdout,
@@ -174,34 +190,39 @@ async def connect_stdin():
174190 os .dup2 (sys .stderr .fileno (), sys .stdout .fileno ())
175191
176192 async def connect_stdout ():
177- if os .name == 'nt' :
193+ if os .name == "nt" :
178194 pipe = PipeHandle (msvcrt .get_osfhandle (rename_stdout ))
179195 else :
180- pipe = os .fdopen (rename_stdout , 'wb' )
196+ pipe = os .fdopen (rename_stdout , "wb" )
181197
182198 transport , protocol = await self ._loop .connect_write_pipe (
183- self ._protocol_factory , pipe )
199+ self ._protocol_factory , pipe
200+ )
184201 debug ("native stdout connection successful" )
185202 self ._transport = transport
186203 self ._protocol = protocol
204+
187205 self ._loop .run_until_complete (connect_stdout ())
188206
189207 @override
190208 def _connect_child (self , argv : List [str ]) -> None :
191- if os .name != 'nt' :
209+ if os .name != "nt" :
192210 # see #238, #241
193211 self ._child_watcher = asyncio .get_child_watcher ()
194212 self ._child_watcher .attach_loop (self ._loop )
195213
196214 async def create_subprocess ():
197215 transport : asyncio .SubprocessTransport # type: ignore
198216 transport , protocol = await self ._loop .subprocess_exec (
199- self ._protocol_factory , * argv )
217+ self ._protocol_factory , * argv
218+ )
200219 pid = transport .get_pid ()
220+ # print(f"============ PID: {pid}")
201221 debug ("child subprocess_exec successful, PID = %s" , pid )
202222
203- self ._transport = cast (asyncio .WriteTransport ,
204- transport .get_pipe_transport (0 )) # stdin
223+ self ._transport = cast (
224+ asyncio .WriteTransport , transport .get_pipe_transport (0 )
225+ ) # stdin
205226 self ._protocol = protocol
206227
207228 # proactor transport implementations do not close the pipes
@@ -250,11 +271,13 @@ def _close_transport(transport):
250271 # Windows: for ProactorBasePipeTransport, close() doesn't take in
251272 # effect immediately (closing happens asynchronously inside the
252273 # event loop), need to wait a bit for completing graceful shutdown.
253- if os .name == 'nt' and hasattr (transport , '_sock' ):
274+ if os .name == "nt" and hasattr (transport , "_sock" ):
275+
254276 async def wait_until_closed ():
255277 # pylint: disable-next=protected-access
256278 while transport ._sock is not None :
257279 await asyncio .sleep (0.01 )
280+
258281 self ._loop .run_until_complete (wait_until_closed ())
259282
260283 if self ._transport :
0 commit comments