Skip to content

Commit bdcd755

Browse files
do not modify interfaces
1 parent ca34372 commit bdcd755

File tree

2 files changed

+48
-46
lines changed

2 files changed

+48
-46
lines changed

plugin/core/transports.py

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ def on_stderr_message(self, message: str) -> None:
4949

5050
class AbstractProcessor(Generic[T]):
5151

52-
def write_data(self, data: T) -> None:
52+
def write_data(self, writer: IO[bytes], data: T) -> None:
5353
raise NotImplementedError()
5454

55-
def read_data(self) -> Optional[T]:
55+
def read_data(self, reader: IO[bytes]) -> Optional[T]:
5656
raise NotImplementedError()
5757

5858

@@ -74,20 +74,15 @@ def decode_payload(message: bytes) -> Optional[Dict[str, Any]]:
7474

7575

7676
class StandardProcessor(AbstractProcessor[Dict[str, Any]]):
77-
78-
def __init__(self, reader: IO[bytes], writer: IO[bytes]):
79-
self._reader = reader
80-
self._writer = writer
81-
82-
def write_data(self, data: Dict[str, Any]) -> None:
77+
def write_data(self, writer: IO[bytes], data: Dict[str, Any]) -> None:
8378
body = encode_payload(data)
84-
self._writer.writelines(("Content-Length: {}\r\n\r\n".format(len(body)).encode('ascii'), body))
85-
self._writer.flush()
79+
writer.writelines(("Content-Length: {}\r\n\r\n".format(len(body)).encode('ascii'), body))
80+
writer.flush()
8681

87-
def read_data(self) -> Optional[Dict[str, Any]]:
88-
headers = http.client.parse_headers(self._reader) # type: ignore
82+
def read_data(self, reader: IO[bytes]) -> Optional[Dict[str, Any]]:
83+
headers = http.client.parse_headers(reader) # type: ignore
8984
try:
90-
body = self._reader.read(int(headers.get("Content-Length")))
85+
body = reader.read(int(headers.get("Content-Length")))
9186
except TypeError:
9287
# Expected error on process stopping. Stop the read loop.
9388
raise StopLoopError()
@@ -98,18 +93,15 @@ class NodeIpcProcessor(AbstractProcessor[Dict[str, Any]]):
9893
_buf = bytearray()
9994
_lines = 0
10095

101-
def __init__(self, conn: multiprocessing.connection._ConnectionBase):
102-
self._conn = conn
103-
104-
def write_data(self, data: Dict[str, Any]) -> None:
96+
def write_data(self, connection: multiprocessing.connection._ConnectionBase, data: Dict[str, Any]) -> None:
10597
body = encode_payload(data) + b"\n"
10698
while len(body):
107-
n = self._conn._write(self._conn.fileno(), body) # type: ignore
99+
n = connection._write(connection.fileno(), body) # type: ignore
108100
body = body[n:]
109101

110-
def read_data(self) -> Optional[Dict[str, Any]]:
102+
def read_data(self, connection: multiprocessing.connection._ConnectionBase) -> Optional[Dict[str, Any]]:
111103
while self._lines == 0:
112-
chunk = self._conn._read(self._conn.fileno(), 65536) # type: ignore
104+
chunk = connection._read(connection.fileno(), 65536) # type: ignore
113105
if len(chunk) == 0:
114106
# EOF reached: https://docs.python.org/3/library/os.html#os.read
115107
raise StopLoopError()
@@ -124,16 +116,14 @@ def read_data(self) -> Optional[Dict[str, Any]]:
124116

125117
class ProcessTransport(Transport[T]):
126118

127-
def __init__(self,
128-
name: str,
129-
process: subprocess.Popen,
130-
socket: Optional[socket.socket],
131-
stderr: Optional[IO[bytes]],
132-
processor: AbstractProcessor[T],
119+
def __init__(self, name: str, process: subprocess.Popen, socket: Optional[socket.socket], reader: Any,
120+
writer: Any, stderr: Optional[IO[bytes]], processor: AbstractProcessor[T],
133121
callback_object: TransportCallbacks[T]) -> None:
134122
self._closed = False
135123
self._process = process
136124
self._socket = socket
125+
self._reader = reader
126+
self._writer = writer
137127
self._stderr = stderr
138128
self._processor = processor
139129
self._reader_thread = threading.Thread(target=self._read_loop, name='{}-reader'.format(name))
@@ -171,8 +161,8 @@ def __del__(self) -> None:
171161

172162
def _read_loop(self) -> None:
173163
try:
174-
while True:
175-
payload = self._processor.read_data()
164+
while self._reader:
165+
payload = self._processor.read_data(self._reader)
176166
if payload is None:
177167
continue
178168

@@ -221,11 +211,11 @@ def invoke() -> None:
221211
def _write_loop(self) -> None:
222212
exception = None # type: Optional[Exception]
223213
try:
224-
while True:
214+
while self._writer:
225215
d = self._send_queue.get()
226216
if d is None:
227217
break
228-
self._processor.write_data(d)
218+
self._processor.write_data(self._writer, d)
229219
except (BrokenPipeError, AttributeError):
230220
pass
231221
except Exception as ex:
@@ -252,6 +242,10 @@ def _stderr_loop(self) -> None:
252242
exception_log('unexpected exception type in stderr loop', ex)
253243
self._send_queue.put_nowait(None)
254244

245+
# Can be a singleton since it doesn't hold any state.
246+
standard_processor = StandardProcessor()
247+
node_ipc_processor = NodeIpcProcessor()
248+
255249

256250
def create_transport(config: TransportConfig, cwd: Optional[str],
257251
callback_object: TransportCallbacks) -> Transport[Dict[str, Any]]:
@@ -264,14 +258,14 @@ def create_transport(config: TransportConfig, cwd: Optional[str],
264258
else:
265259
stdout = subprocess.DEVNULL
266260
stdin = subprocess.DEVNULL
267-
elif not config.node_ipc:
268-
stdout = subprocess.PIPE
269-
stdin = subprocess.PIPE
270-
else:
261+
elif config.node_ipc:
271262
stdout = subprocess.PIPE
272263
stdin = subprocess.DEVNULL
273264
stderr = subprocess.STDOUT
274-
pass_fds = (config.node_ipc.child_conn.fileno(),)
265+
pass_fds = (config.node_ipc.child_connection.fileno(),)
266+
else:
267+
stdout = subprocess.PIPE
268+
stdin = subprocess.PIPE
275269

276270
startupinfo = _fixup_startup_args(config.command)
277271
sock = None # type: Optional[socket.socket]
@@ -288,27 +282,31 @@ def start_subprocess() -> subprocess.Popen:
288282
config.listener_socket,
289283
start_subprocess
290284
)
291-
processor = StandardProcessor(reader, writer) # type: AbstractProcessor
292285
else:
293286
process = start_subprocess()
294287
if config.tcp_port:
295288
sock = _connect_tcp(config.tcp_port)
296289
if sock is None:
297290
raise RuntimeError("Failed to connect on port {}".format(config.tcp_port))
298291
reader = writer = sock.makefile('rwb')
299-
processor = StandardProcessor(reader, writer)
300-
elif not config.node_ipc:
292+
elif config.node_ipc:
293+
reader = writer = config.node_ipc.parent_connection
294+
else:
301295
if not process.stdout or not process.stdin:
302296
raise RuntimeError(
303297
'Failed initializing transport: reader: {}, writer: {}'
304298
.format(process.stdout, process.stdin)
305299
)
306-
processor = StandardProcessor(process.stdout, process.stdin)
307-
else:
308-
processor = NodeIpcProcessor(config.node_ipc.parent_conn)
309-
300+
reader = process.stdout
301+
writer = process.stdin
310302
stderr_reader = process.stdout if config.node_ipc else process.stderr
311-
return ProcessTransport(config.name, process, sock, stderr_reader, processor, callback_object)
303+
processor = node_ipc_processor if config.node_ipc else standard_processor
304+
305+
if not reader or not writer:
306+
raise RuntimeError('Failed initializing transport: reader: {}, writer: {}'.format(reader, writer))
307+
308+
return ProcessTransport(config.name, process, sock, reader, writer, stderr_reader, processor,
309+
callback_object)
312310

313311

314312
_subprocesses = weakref.WeakSet() # type: weakref.WeakSet[subprocess.Popen]

plugin/core/types.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,11 @@ def map_from_remote_to_local(self, uri: str) -> Tuple[str, bool]:
607607
return _translate_path(uri, self._remote, self._local)
608608

609609

610-
NodeIpcPipe = collections.namedtuple('NodeIpcPipe', 'parent_conn,child_conn')
610+
class NodeIpcPipe():
611+
def __init__(self) -> None:
612+
parent_connection, child_connection = multiprocessing.Pipe()
613+
self.parent_connection = parent_connection
614+
self.child_connection = child_connection
611615

612616

613617
class TransportConfig:
@@ -804,8 +808,8 @@ def resolve_transport_config(self, variables: Dict[str, str]) -> TransportConfig
804808
env[key] = sublime.expand_variables(value, variables)
805809
node_ipc = None
806810
if self.use_node_ipc:
807-
node_ipc = NodeIpcPipe(*multiprocessing.Pipe())
808-
env["NODE_CHANNEL_FD"] = str(node_ipc.child_conn.fileno())
811+
node_ipc = NodeIpcPipe()
812+
env["NODE_CHANNEL_FD"] = str(node_ipc.child_connection.fileno())
809813
return TransportConfig(self.name, command, tcp_port, env, listener_socket, node_ipc)
810814

811815
def set_view_status(self, view: sublime.View, message: str) -> None:

0 commit comments

Comments
 (0)