8000 Make checking parent process aliveness behind a flag (#455) · lcheylus/python-lsp-server@ab3e5ea · GitHub
[go: up one dir, main page]

Skip to content

Commit ab3e5ea

Browse files
author
JiahuiJiang
authored
Make checking parent process aliveness behind a flag (python-lsp#455)
Closes python-lsp#445
1 parent d51edb5 commit ab3e5ea

File tree

3 files changed

+31
-11
lines changed

3 files changed

+31
-11
lines changed

pyls/__main__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ def add_arguments(parser):
2424
"--port", type=int, default=2087,
2525
help="Bind to this port"
2626
)
27+
parser.add_argument(
28+
'--check-parent-process', action="store_true",
29+
help="Check whether parent process is still alive using os.kill(ppid, 0) "
30+
"and auto shut down language server process when parent process is not alive."
31+
"Note that this may not work on a Windows machine."
32+
)
2733

2834
log_group = parser.add_mutually_exclusive_group()
2935
log_group.add_argument(
@@ -52,7 +58,7 @@ def main():
5258
start_tcp_lang_server(args.host, args.port, PythonLanguageServer)
5359
else:
5460
stdin, stdout = _binary_stdio()
55-
start_io_lang_server(stdin, stdout, PythonLanguageServer)
61+
start_io_lang_server(stdin, stdout, args.check_parent_process, PythonLanguageServer)
5662

5763

5864
def _binary_stdio():

pyls/python_ls.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ def start_tcp_lang_server(bind_addr, port, handler_class):
5353
server.server_close()
5454

5555

56-
def start_io_lang_server(rfile, wfile, handler_class):
56+
def start_io_lang_server(rfile, wfile, check_parent_process, handler_class):
5757
if not issubclass(handler_class, PythonLanguageServer):
5858
raise ValueError('Handler class must be an instance of PythonLanguageServer')
5959
log.info('Starting %s IO language server', handler_class.__name__)
60-
server = handler_class(rfile, wfile)
60+
server = handler_class(rfile, wfile, check_parent_process)
6161
server.start()
6262

6363

@@ -68,12 +68,13 @@ class PythonLanguageServer(MethodDispatcher):
6868

6969
# pylint: disable=too-many-public-methods,redefined-builtin
7070

71-
def __init__(self, rx, tx):
71+
def __init__(self, rx, tx, check_parent_process=False):
7272
self.workspace = None
7373
self.config = None
7474

7575
self._jsonrpc_stream_reader = JsonRpcStreamReader(rx)
7676
self._jsonrpc_stream_writer = JsonRpcStreamWriter(tx)
77+
self._check_parent_process = check_parent_process
7778
self._endpoint = Endpoint(self, self._jsonrpc_stream_writer.write, max_workers=MAX_WORKERS)
7879
self._dispatchers = []
7980
self._shutdown = False
@@ -156,7 +157,7 @@ def m_initialize(self, processId=None, rootUri=None, rootPath=None, initializati
156157
self._dispatchers = self._hook('pyls_dispatchers')
157158
self._hook('pyls_initialize')
158159

159-
if processId is not None:
160+
if self._check_parent_process and processId is not None:
160161
def watch_parent_process(pid):
161162
# exist when the given pid is not alive
162163
if not _utils.is_process_alive(pid):

test/test_language_server.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,29 @@ def start_client(client):
1616

1717
class _ClientServer(object):
1818
""" A class to setup a client/server pair """
19-
def __init__(self):
19+
def __init__(self, check_parent_process=False):
2020
# Client to Server pipe
2121
csr, csw = os.pipe()
2222
# Server to client pipe
2323
scr, scw = os.pipe()
2424

2525
self.server_thread = Thread(target=start_io_lang_server, args=(
26-
os.fdopen(csr, 'rb'), os.fdopen(scw, 'wb'), PythonLanguageServer
26+
os.fdopen(csr, 'rb'), os.fdopen(scw, 'wb'), check_parent_process, PythonLanguageServer
2727
))
2828
self.server_thread.daemon = True
2929
self.server_thread.start()
3030

31-
self.client = PythonLanguageServer(os.fdopen(scr, 'rb'), os.fdopen(csw, 'wb'))
31+
self.client = PythonLanguageServer(os.fdopen(scr, 'rb'), os.fdopen(csw, 'wb'), start_io_lang_server)
3232
self.client_thread = Thread(target=start_client, args=[self.client])
3333
self.client_thread.daemon = True
3434
self.client_thread.start()
3535

3636

3737
@pytest.fixture
3838
def client_server():
39-
""" A fixture that sets up a client/server pair and shuts down the server """
39+
""" A fixture that sets up a client/server pair and shuts down the server
40+
This client/server pair does not support checking parent process aliveness
41+
"""
4042
client_server_pair = _ClientServer()
4143

4244
yield client_server_pair.client
@@ -48,8 +50,10 @@ def client_server():
4850

4951
@pytest.fixture
5052
def client_exited_server():
51-
""" A fixture that sets up a client/server pair and assert the server has already exited """
52-
client_server_pair = _ClientServer()
53+
""" A fixture that sets up a client/server pair that support checking parent process aliveness
54+
and assert the server has already exited
55+
"""
56+
client_server_pair = _ClientServer(True)
5357

5458
yield client_server_pair.client
5559

@@ -74,6 +78,15 @@ def test_exit_with_parent_process_died(client_exited_server): # pylint: disable
7478
}).result(timeout=CALL_TIMEOUT)
7579

7680

81+
def test_not_exit_without_check_parent_process_flag(client_server): # pylint: disable=redefined-outer-name
82+
response = client_server._endpoint.request('initialize', {
83+
'processId': 1234,
84+
'rootPath': os.path.dirname(__file__),
85+
'initializationOptions': {}
86+
}).result(timeout=CALL_TIMEOUT)
87+
assert 'capabilities' in response
88+
89+
7790
def test_missing_message(client_server): # pylint: disable=redefined-outer-name
7891
with pytest.raises(JsonRpcMethodNotFound):
7992
client_server._endpoint.request('unknown_method').result(timeout=CALL_TIMEOUT)

0 commit comments

Comments
 (0)
0