diff --git a/external-deps/python-lsp-server/.gitignore b/external-deps/python-lsp-server/.gitignore index 3c4093d1128..fe35f067046 100644 --- a/external-deps/python-lsp-server/.gitignore +++ b/external-deps/python-lsp-server/.gitignore @@ -100,6 +100,7 @@ ENV/ # Spyder project settings .spyderproject +.spyproject # Rope project settings .ropeproject diff --git a/external-deps/python-lsp-server/.gitrepo b/external-deps/python-lsp-server/.gitrepo index f119dac2a13..79cdf195323 100644 --- a/external-deps/python-lsp-server/.gitrepo +++ b/external-deps/python-lsp-server/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/python-lsp/python-lsp-server.git branch = develop - commit = 8c96441745876a104554450d779a3d01ac4e76dd - parent = 3a670e0e326ca226e8e21d931fc65b53863e9bbd + commit = 6a24326890964b56b8349b605ed76727f3502326 + parent = fb1fdbcc1ba6327231c395286a17e980d3c2c8ba method = merge - cmdver = 0.4.3 + cmdver = 0.4.9 diff --git a/external-deps/python-lsp-server/CHANGELOG.md b/external-deps/python-lsp-server/CHANGELOG.md index 6ec4506267d..d32c2d473fb 100644 --- a/external-deps/python-lsp-server/CHANGELOG.md +++ b/external-deps/python-lsp-server/CHANGELOG.md @@ -1,5 +1,17 @@ # History of changes +## Version 1.13.1 (2025/08/26) + +### Pull Requests Merged + +* [PR 667](https://github.com/python-lsp/python-lsp-server/pull/667) - Use PyQt6 for testing, by [@WhyNotHugo](https://github.com/WhyNotHugo) +* [PR 666](https://github.com/python-lsp/python-lsp-server/pull/666) - Expose a shutdown hook, by [@dlax](https://github.com/dlax) +* [PR 663](https://github.com/python-lsp/python-lsp-server/pull/663) - Copy `LAST_JEDI_COMPLETIONS` to cell document so that `completionItem/resolve` will work, by [@hjr265](https://github.com/hjr265) + +In this release 3 pull requests were closed. + +---- + ## Version 1.13.0 (2025/07/07) ### New features diff --git a/external-deps/python-lsp-server/README.md b/external-deps/python-lsp-server/README.md index 4cf305cc79e..daca7dc6e29 100644 --- a/external-deps/python-lsp-server/README.md +++ b/external-deps/python-lsp-server/README.md @@ -2,8 +2,7 @@ [![image](https://github.com/python-ls/python-ls/workflows/Linux%20tests/badge.svg)](https://github.com/python-ls/python-ls/actions?query=workflow%3A%22Linux+tests%22) [![image](https://github.com/python-ls/python-ls/workflows/Mac%20tests/badge.svg)](https://github.com/python-ls/python-ls/actions?query=workflow%3A%22Mac+tests%22) [![image](https://github.com/python-ls/python-ls/workflows/Windows%20tests/badge.svg)](https://github.com/python-ls/python-ls/actions?query=workflow%3A%22Windows+tests%22) [![image](https://img.shields.io/github/license/python-ls/python-ls.svg)](https://github.com/python-ls/python-ls/blob/master/LICENSE) -A Python 3.8+ implementation of the [Language Server Protocol](https://github.com/Microsoft/language-server-protocol). -(Note: versions <1.4 should still work with Python 3.6) +A Python 3.9+ implementation of the [Language Server Protocol](https://github.com/Microsoft/language-server-protocol). ## Installation diff --git a/external-deps/python-lsp-server/pylsp/hookspecs.py b/external-deps/python-lsp-server/pylsp/hookspecs.py index e7e7ce4276f..cf97c7458bb 100644 --- a/external-deps/python-lsp-server/pylsp/hookspecs.py +++ b/external-deps/python-lsp-server/pylsp/hookspecs.py @@ -138,3 +138,8 @@ def pylsp_signature_help(config, workspace, document, position) -> None: @hookspec def pylsp_workspace_configuration_changed(config, workspace) -> None: pass + + +@hookspec +def pylsp_shutdown(config, workspace) -> None: + pass diff --git a/external-deps/python-lsp-server/pylsp/plugins/flake8_lint.py b/external-deps/python-lsp-server/pylsp/plugins/flake8_lint.py index 0ac91855c08..b0a71b88a6a 100644 --- a/external-deps/python-lsp-server/pylsp/plugins/flake8_lint.py +++ b/external-deps/python-lsp-server/pylsp/plugins/flake8_lint.py @@ -34,6 +34,13 @@ | {"E999"} ) +if sys.platform == "win32": + from subprocess import CREATE_NO_WINDOW +else: + # CREATE_NO_WINDOW flag only available on Windows. + # Set constant as default `Popen` `creationflag` kwarg value (`0`) + CREATE_NO_WINDOW = 0 + @hookimpl def pylsp_settings(): @@ -128,7 +135,7 @@ def run_flake8(flake8_executable, args, document, source): ) log.debug("Calling %s with args: '%s'", flake8_executable, args) - popen_kwargs = {} + popen_kwargs = {"creationflags": CREATE_NO_WINDOW} if cwd := document._workspace.root_path: popen_kwargs["cwd"] = cwd try: diff --git a/external-deps/python-lsp-server/pylsp/python_lsp.py b/external-deps/python-lsp-server/pylsp/python_lsp.py index 36265890743..a26406668fb 100644 --- a/external-deps/python-lsp-server/pylsp/python_lsp.py +++ b/external-deps/python-lsp-server/pylsp/python_lsp.py @@ -237,6 +237,7 @@ def __getitem__(self, item): def m_shutdown(self, **_kwargs) -> None: for workspace in self.workspaces.values(): workspace.close() + self._hook("pylsp_shutdown") self._shutdown = True def m_invalid_request_after_shutdown(self, **_kwargs): @@ -722,6 +723,12 @@ def _cell_document__completion(self, cellDocument, position=None, **_kwargs): if item.get("data", {}).get("doc_uri") == temp_uri: item["data"]["doc_uri"] = cellDocument.uri + # Copy LAST_JEDI_COMPLETIONS to cell document so that completionItem/resolve will work + tempDocument = workspace.get_document(temp_uri) + cellDocument.shared_data["LAST_JEDI_COMPLETIONS"] = ( + tempDocument.shared_data.get("LAST_JEDI_COMPLETIONS", None) + ) + return completions def m_text_document__completion(self, textDocument=None, position=None, **_kwargs): diff --git a/external-deps/python-lsp-server/pyproject.toml b/external-deps/python-lsp-server/pyproject.toml index 7b0c205b1e5..0be4035c470 100644 --- a/external-deps/python-lsp-server/pyproject.toml +++ b/external-deps/python-lsp-server/pyproject.toml @@ -10,7 +10,8 @@ name = "python-lsp-server" authors = [{name = "Python Language Server Contributors"}] description = "Python Language Server for the Language Server Protocol" readme = "README.md" -license = {text = "MIT"} +license = "MIT" +license-files = ["LICENSE"] requires-python = ">=3.9" dependencies = [ "docstring-to-markdown", @@ -57,7 +58,7 @@ test = [ "numpy", "pandas", "matplotlib", - "pyqt5", + "pyqt6", "flaky", "websockets>=10.3", ] @@ -170,7 +171,6 @@ docstring-code-format = false docstring-code-line-length = "dynamic" [tool.setuptools] -license-files = ["LICENSE"] include-package-data = false [tool.setuptools.packages.find] diff --git a/external-deps/python-lsp-server/test/plugins/test_completion.py b/external-deps/python-lsp-server/test/plugins/test_completion.py index 3ba8dbdd096..015d0c434f3 100644 --- a/external-deps/python-lsp-server/test/plugins/test_completion.py +++ b/external-deps/python-lsp-server/test/plugins/test_completion.py @@ -282,8 +282,8 @@ def test_jedi_method_completion(config, workspace) -> None: reason="Test in Python 3 and not on CIs on Linux because wheels don't work on them.", ) def test_pyqt_completion(config, workspace) -> None: - # Over 'QA' in 'from PyQt5.QtWidgets import QApplication' - doc_pyqt = "from PyQt5.QtWidgets import QA" + # Over 'QA' in 'from PyQt6.QtWidgets import QApplication' + doc_pyqt = "from PyQt6.QtWidgets import QA" com_position = {"line": 0, "character": len(doc_pyqt)} doc = Document(DOC_URI, workspace, doc_pyqt) completions = pylsp_jedi_completions(config, doc, com_position) diff --git a/external-deps/python-lsp-server/test/test_notebook_document.py b/external-deps/python-lsp-server/test/test_notebook_document.py index ca0d477db37..215258e119e 100644 --- a/external-deps/python-lsp-server/test/test_notebook_document.py +++ b/external-deps/python-lsp-server/test/test_notebook_document.py @@ -530,3 +530,71 @@ def test_notebook_completion(client_server_pair) -> None: }, ], } + + +@pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") +def test_notebook_completion_resolve(client_server_pair) -> None: + """ + Tests that completion item resolve works correctly + """ + client, server = client_server_pair + send_initialize_request(client) + + # Open notebook + with patch.object(server._endpoint, "notify") as mock_notify: + send_notebook_did_open( + client, + [ + "def answer():\n\t'''Returns an important number.'''\n\treturn 42", + "ans", + ], + ) + # wait for expected diagnostics messages + wait_for_condition(lambda: mock_notify.call_count >= 2) + assert len(server.workspace.documents) == 3 + for uri in ["cell_1_uri", "cell_2_uri", "notebook_uri"]: + assert uri in server.workspace.documents + + future = client._endpoint.request( + "textDocument/completion", + { + "textDocument": { + "uri": "cell_2_uri", + }, + "position": {"line": 0, "character": 3}, + }, + ) + result = future.result(CALL_TIMEOUT_IN_SECONDS) + assert result == { + "isIncomplete": False, + "items": [ + { + "data": {"doc_uri": "cell_2_uri"}, + "insertText": "answer", + "kind": 3, + "label": "answer()", + "sortText": "aanswer", + }, + ], + } + + future = client._endpoint.request( + "completionItem/resolve", + { + "data": {"doc_uri": "cell_2_uri"}, + "label": "answer()", + }, + ) + result = future.result(CALL_TIMEOUT_IN_SECONDS) + del result["detail"] # The value of this is unpredictable. + assert result == { + "data": {"doc_uri": "cell_2_uri"}, + "insertText": "answer", + "kind": 3, + "label": "answer()", + "sortText": "aanswer", + "documentation": { + "kind": "markdown", + "value": "```python\nanswer()\n```\n\n\nReturns an important number.", + }, + }