-
Notifications
You must be signed in to change notification settings - Fork 148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tcp mode #636
base: main
Are you sure you want to change the base?
Tcp mode #636
Conversation
Update Master
Update Master
Thank you so much for this! 😍 Despite all the ❌, this is a really good step forward. I guess I'd like to see
I wouldn't go too far overboard with I'm only a casual user of It looks like we will be able to test this fairly robustly with Giving it a pass through Solider on, and let us know if you get hard stuck anywhere! |
Thank you so much for taking the time to review my changes and your input! I hope I will find the time in the close future to fix everything. Regarding anyio: I am not overly proud of the way I managed to get this working with anyio, especially the intersection where non async code (but still running within an existing event loop from jupyter lab) needs to call async code seems much to complicated for my taste. Even more so since the anyio change where I will also try and split the Session class in two. |
While trying to get things running for externally running lsp-servers, I came across following problem (or so I think): It seems that the current implementation never asks the server to shut down properly, i.e. no sequence So my question is: Am I right about this part of the protocol currently not being implemented? If so, I presume one would have to extend the |
Yes. I am not sure when we want to ask servers to close, but at the minimum we need to do so at JupyterLab shutdown, which is also the safest option (but not trivial to detect). It is tricky because user closing the window does not mean that the session ends - they may be just switching to another browser or refreshing the window. So we would probably need to have a hook in the python code using Having said that, there is also a "shut down" option in JupyterLab's "File" menu, though I never use it (I always exit by terminating the server and closing the window) as I think many users do, hence the remark about the need of having
Yes, if we want to emit those from within the frontend (which as described above is complicated and could only reliably happen if and only if the "Shut down" button was pressed in JupyterLab, as any other server disconnect might indicate a temporary network failure, etc...). The next step would be to update We also need to investigate how this would interact with multi-user deployments such as JupyterHub and Real Time Collaboration. In short I do not think that we should implement this in this PR because there are too many unknowns; you are right that we should look into it and thank you for starting this discussion. I think we should create a new issue out of this to allow others to chime in with ideas before deciding on implementation. Edit: there is a |
All right, thanks for clarifying. I will focus on implementing the TCP support for self launched processes for now then and open a separate issue to discuss on how to proceed from this stage, when this PR is done. |
Picked up conflicts with schema. Sadly GitHub Actions do not kick in until conflicts are resolved. I also added a note about |
All right, so I can just pull master and merge it into the tcp_mode branch, thereby resolving the conflict, and then push again, correct? |
or do it through the web ui if you're feeling brave... |
# Conflicts: # python_packages/jupyter_lsp/jupyter_lsp/connection.py # python_packages/jupyter_lsp/jupyter_lsp/manager.py
I think the code should be ready for another review round (I don't think that the smoke installation error was caused by the made changes). To fully take advantage of the structured concurrency offered by anyio, I think more work needs to be done from the jupyterlab extension API side, as the current implementation using Tornado Websockets, while supporting coroutines for |
Looking at the issue of freezes everywhere except for Ubuntu py3.10 on; there is no sane way to debug it on CI/with pytest, but locally I can reproduce it. Starting lab I see: Exception while listening Cannot add child handler, the child watcher does not have a loop attached
Traceback (most recent call last):
File "jupyterlab-lsp/python_packages/jupyter_lsp/jupyter_lsp/session.py", line 162, in run
await self.initialize()
File "jupyterlab-lsp/python_packages/jupyter_lsp/jupyter_lsp/session.py", line 180, in initialize
await self.init_process()
File "jupyterlab-lsp/python_packages/jupyter_lsp/jupyter_lsp/session.py", line 328, in init_process
await self.start_process(self.spec["argv"])
File "jupyterlab-lsp/python_packages/jupyter_lsp/jupyter_lsp/session.py", line 230, in start_process
env=self.substitute_env(self.spec.get("env", {}), os.environ),
File ".pyenv/versions/3.7.15/envs/lsp-lab3.x-py3.7/lib/python3.7/site-packages/anyio/_core/_subprocesses.py", line 135, in open_process
start_new_session=start_new_session,
File ".pyenv/versions/3.7.15/envs/lsp-lab3.x-py3.7/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 1112, in open_process
start_new_session=start_new_session,
File ".pyenv/versions/3.7.15/lib/python3.7/asyncio/subprocess.py", line 217, in create_subprocess_exec
stderr=stderr, **kwds)
File ".pyenv/versions/3.7.15/lib/python3.7/asyncio/base_events.py", line 1544, in subprocess_exec
bufsize, **kwargs)
File ".pyenv/versions/3.7.15/lib/python3.7/asyncio/unix_events.py", line 193, in _make_subprocess_transport
self._child_watcher_callback, transp)
File ".pyenv/versions/3.7.15/lib/python3.7/asyncio/unix_events.py", line 941, in add_child_handler
"Cannot add child handler, "
RuntimeError: Cannot add child handler, the child watcher does not have a loop attached Coming from |
This appears to be https://bugs.python.org/issue35621 which was only fixed in Python 3.8 as the workaround from https://bugs.python.org/msg370355: import asyncio
from .threaded_child_watcher import ThreadedChildWatcher # where ThreadedChildWatcher was copied from CPython
asyncio.set_child_watcher(ThreadedChildWatcher()) does work. This is for Unix only, no idea how to fix this on Windows, and the workaround may have side-effects on tornado. Switching to trio backend gives us:
|
I guess the question becomes: do we really need/want |
So things are getting interesting with krassowski@12bfbd4:
Traceback (most recent call last):
File "/3.7.15/lib/python3.7/threading.py", line 926, in _bootstrap_inner
self.run()
File "/3.7.15/lib/python3.7/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "/lsp-lab3.x-py3.7/lib/python3.7/site-packages/anyio/_core/_eventloop.py", line 70, in run
return asynclib.run(func, *args, **backend_options)
File "/lsp-lab3.x-py3.7/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 292, in run
return native_run(wrapper(), debug=debug)
File "/3.7.15/lib/python3.7/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/3.7.15/lib/python3.7/asyncio/base_events.py", line 587, in run_until_complete
return future.result()
File "/lsp-lab3.x-py3.7/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 287, in wrapper
return await func(*args)
File "jupyterlab-lsp/python_packages/jupyter_lsp/jupyter_lsp/session.py", line 179, in run
await self.cleanup()
File "jupyterlab-lsp/python_packages/jupyter_lsp/jupyter_lsp/session.py", line 205, in cleanup
await self.stop_process(self.stop_timeout)
File "jupyterlab-lsp/python_packages/jupyter_lsp/jupyter_lsp/session.py", line 254, in stop_process
self.process.terminate()
File "/lsp-lab3.x-py3.7/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 1053, in terminate
self._process.terminate()
File "/3.7.15/lib/python3.7/asyncio/subprocess.py", line 131, in terminate
self._transport.terminate()
File "/3.7.15/lib/python3.7/asyncio/base_subprocess.py", line 150, in terminate
self._check_proc()
File "/3.7.15/lib/python3.7/asyncio/base_subprocess.py", line 143, in _check_proc
raise ProcessLookupError()
ProcessLookupError |
References
I could not find any explicit issue that would be solved by this PR. However, the requirement for TCP support has been mentioned in #282 and #184 (The letter also contains some comments regarding design decisions)
Code changes
LanguageServerSession
class and theLspStreamBase
,LspStreamReader
andLspStreamWriter
classes (formerlyLspStdIoBase
,LspStdIoReader
andLspStdIoWriter
)python_packages/jupyter_lsp/jupyter_lsp/schema/schema.json
was extended by amode
property which may take on valuesstdio
ortcp
,stdio
being the default if nomode
is specified, so as to not break existing language server specifications. Ifmode==tcp
, a LS is started in a new process onlocalhost
and on a randomly selected free port. The port is controlled by replacing each occurrence of the placeholder{port}
within theargv
of the server'sspec
with that port.Backwards-incompatible changes
There should be no backward-incompatible changes as the default values for the newly introduced
mode
property isstdio
and thus no changes for the (currently solely used) language servers communicating over stdio is required.TODOS:
Session
intoStdioSession
andTCPSession
Chores