From 23e38d59bb2ffda1b23773b20c044bc7e2953217 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 19 Jul 2026 12:12:03 -0500 Subject: [PATCH 1/5] git subrepo pull --remote=https://github.com/ccordoba12/python-lsp-server.git --branch=support-jedi-0.20 --update --force external-deps/python-lsp-server subrepo: subdir: "external-deps/python-lsp-server" merged: "78e291530f" upstream: origin: "https://github.com/ccordoba12/python-lsp-server.git" branch: "support-jedi-0.20" commit: "78e291530f" git-subrepo: version: "0.4.9" origin: "https://github.com/ingydotnet/git-subrepo" commit: "ea10886" --- .../.github/workflows/static.yml | 6 +- .../.github/workflows/test-linux.yml | 6 +- .../.github/workflows/test-mac.yml | 10 +- .../.github/workflows/test-win.yml | 6 +- external-deps/python-lsp-server/.gitrepo | 8 +- .../python-lsp-server/CONFIGURATION.md | 2 +- external-deps/python-lsp-server/README.md | 2 +- .../python-lsp-server/pylsp/_utils.py | 29 +++++- .../pylsp/config/schema.json | 7 +- .../pylsp/plugins/definition.py | 5 +- .../pylsp/plugins/rope_autoimport.py | 7 +- .../python-lsp-server/pyproject.toml | 2 +- .../test/plugins/test_autoimport.py | 91 +++++++++++++++++++ .../test/plugins/test_completion.py | 6 +- .../test/plugins/test_definitions.py | 26 ++++++ .../python-lsp-server/test/test_utils.py | 18 ++++ 16 files changed, 202 insertions(+), 29 deletions(-) diff --git a/external-deps/python-lsp-server/.github/workflows/static.yml b/external-deps/python-lsp-server/.github/workflows/static.yml index 881a0aa6022..adae18504f3 100644 --- a/external-deps/python-lsp-server/.github/workflows/static.yml +++ b/external-deps/python-lsp-server/.github/workflows/static.yml @@ -22,13 +22,13 @@ jobs: OS: 'linux' timeout-minutes: 2 steps: - - uses: actions/cache@v4 + - uses: actions/cache@v6 with: path: ~/.cache/pip key: static-pip-${{ hashFiles('pyproject.toml') }} restore-keys: static-pip- - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v7 + - uses: actions/setup-python@v6 with: python-version: '3.9' architecture: 'x64' diff --git a/external-deps/python-lsp-server/.github/workflows/test-linux.yml b/external-deps/python-lsp-server/.github/workflows/test-linux.yml index 543744a6226..86987521b17 100644 --- a/external-deps/python-lsp-server/.github/workflows/test-linux.yml +++ b/external-deps/python-lsp-server/.github/workflows/test-linux.yml @@ -27,13 +27,13 @@ jobs: PYTHON_VERSION: ['3.14', '3.13', '3.12', '3.11', '3.10', '3.9'] timeout-minutes: 10 steps: - - uses: actions/cache@v4 + - uses: actions/cache@v6 with: path: ~/.cache/pip key: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip- - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v7 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.PYTHON_VERSION }} architecture: 'x64' diff --git a/external-deps/python-lsp-server/.github/workflows/test-mac.yml b/external-deps/python-lsp-server/.github/workflows/test-mac.yml index 7b06ad2685e..27b11f4531d 100644 --- a/external-deps/python-lsp-server/.github/workflows/test-mac.yml +++ b/external-deps/python-lsp-server/.github/workflows/test-mac.yml @@ -16,7 +16,7 @@ concurrency: jobs: build: name: Mac Py${{ matrix.PYTHON_VERSION }} - runs-on: macos-13 + runs-on: macos-14 env: CI: 'true' OS: 'macos' @@ -24,16 +24,16 @@ jobs: strategy: fail-fast: false matrix: - PYTHON_VERSION: ['3.14', '3.12', '3.9'] + PYTHON_VERSION: ['3.14', '3.12', '3.11'] timeout-minutes: 10 steps: - - uses: actions/cache@v4 + - uses: actions/cache@v6 with: path: ~/Library/Caches/pip key: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip- - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v7 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.PYTHON_VERSION }} architecture: 'x64' diff --git a/external-deps/python-lsp-server/.github/workflows/test-win.yml b/external-deps/python-lsp-server/.github/workflows/test-win.yml index fa71c5ce4f1..ec072f1832b 100644 --- a/external-deps/python-lsp-server/.github/workflows/test-win.yml +++ b/external-deps/python-lsp-server/.github/workflows/test-win.yml @@ -27,13 +27,13 @@ jobs: PYTHON_VERSION: ['3.14', '3.12', '3.9'] timeout-minutes: 10 steps: - - uses: actions/cache@v4 + - uses: actions/cache@v6 with: path: ~\AppData\Local\pip\Cache key: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip- - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v7 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.PYTHON_VERSION }} architecture: 'x64' diff --git a/external-deps/python-lsp-server/.gitrepo b/external-deps/python-lsp-server/.gitrepo index a25891211ca..ec1cb78f992 100644 --- a/external-deps/python-lsp-server/.gitrepo +++ b/external-deps/python-lsp-server/.gitrepo @@ -4,9 +4,9 @@ ; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme ; [subrepo] - remote = https://github.com/python-lsp/python-lsp-server.git - branch = develop - commit = bc1095f28a975e26eeb208b158a0e88423502785 - parent = b1eb96f846d1564e689ffb1c02ccaa93b1005471 + remote = https://github.com/ccordoba12/python-lsp-server.git + branch = support-jedi-0.20 + commit = 78e291530f82f7aca34fc89af37b0ac8ef648b48 + parent = 0e9c8ced1bbd372cff4cab2d0b0868a306e2591a method = merge cmdver = 0.4.9 diff --git a/external-deps/python-lsp-server/CONFIGURATION.md b/external-deps/python-lsp-server/CONFIGURATION.md index 53d3b2f17f8..5a5a228dad1 100644 --- a/external-deps/python-lsp-server/CONFIGURATION.md +++ b/external-deps/python-lsp-server/CONFIGURATION.md @@ -78,6 +78,6 @@ This server can be configured using the `workspace/didChangeConfiguration` metho | `pylsp.rope.ropeFolder` | `array` of unique `string` items | The name of the folder in which rope stores project configurations and data. Pass `null` for not using such a folder at all. | `null` | | `pylsp.signature.formatter` | `string` (one of: `'black'`, `'ruff'`, `None`) | Formatter to use for reformatting signatures in docstrings. | `"black"` | | `pylsp.signature.include_docstring` | `boolean` | Include signature docstring. | `true` | -| `pylsp.signature.line_length` | `number` | Maximum line length in signatures. | `88` | +| `pylsp.signature.line_length` | `integer` | Maximum line length in signatures. | `88` | This documentation was generated from `pylsp/config/schema.json`. Please do not edit this file directly. diff --git a/external-deps/python-lsp-server/README.md b/external-deps/python-lsp-server/README.md index daca7dc6e29..6f6cb19b8e5 100644 --- a/external-deps/python-lsp-server/README.md +++ b/external-deps/python-lsp-server/README.md @@ -68,7 +68,7 @@ or Arch Linux ``` sudo pacman -S python-lsp-server -```` +``` Only on Alpine Linux the package is named differently. You can install it there by typing this command in your terminal: diff --git a/external-deps/python-lsp-server/pylsp/_utils.py b/external-deps/python-lsp-server/pylsp/_utils.py index c9eb6fb1d3d..0ce0e8cbe07 100644 --- a/external-deps/python-lsp-server/pylsp/_utils.py +++ b/external-deps/python-lsp-server/pylsp/_utils.py @@ -16,6 +16,8 @@ import docstring_to_markdown import jedi +from pylsp import IS_WIN + JEDI_VERSION = jedi.__version__ # Eol chars accepted by the LSP protocol @@ -96,7 +98,13 @@ def find_parents(root, path, names): # Split the relative by directory, generate all the parent directories, then check each of them. # This avoids running a loop that has different base-cases for unix/windows # e.g. /a/b and /a/b/c/d/e.py -> ['/a/b', 'c', 'd'] - dirs = [root] + os.path.relpath(os.path.dirname(path), root).split(os.path.sep) + try: + dirs = [root] + os.path.relpath(os.path.dirname(path), root).split(os.path.sep) + except ValueError: + # On Windows, relpath raises ValueError when path and root are on different mounts + # (e.g. a UNC share root vs a drive-letter path). Nothing to find in this case. + log.warning("Path %r not in %r", path, root) + return [] # Search each of /a/b/c, /a/b, /a while dirs: @@ -133,14 +141,33 @@ def match_uri_to_workspace(uri, workspaces): workspace_parts = pathlib.Path(workspace).parts if len(workspace_parts) > len(path): continue + match_len = 0 + is_parent = True for workspace_part, path_part in zip(workspace_parts, path): + # filename match is case insensitive on windows + # also, uris._normalize_win_path() lowercases the drive letter + if IS_WIN: + workspace_part = workspace_part.lower() + path_part = path_part.lower() + if workspace_part == path_part: match_len += 1 + else: + # give up, any subsequent match is no longer relevant + is_parent = False + break + + # prefer a match that is actually a parent of uri + # otherwise fall back to longest matching non-parent + if is_parent and match_len > 0: + match_len += 1000 + if match_len > 0: if match_len > max_len: max_len = match_len chosen_workspace = workspace + return chosen_workspace diff --git a/external-deps/python-lsp-server/pylsp/config/schema.json b/external-deps/python-lsp-server/pylsp/config/schema.json index 67289d960e4..e14daa207a6 100644 --- a/external-deps/python-lsp-server/pylsp/config/schema.json +++ b/external-deps/python-lsp-server/pylsp/config/schema.json @@ -91,7 +91,10 @@ "description": "List of errors and warnings to ignore (or skip)." }, "pylsp.plugins.flake8.maxComplexity": { - "type": "integer", + "type": [ + "integer", + "null" + ], "default": null, "description": "Maximum allowed complexity threshold." }, @@ -536,7 +539,7 @@ "description": "Include signature docstring." }, "pylsp.signature.line_length": { - "type": "number", + "type": "integer", "default": 88, "description": "Maximum line length in signatures." } diff --git a/external-deps/python-lsp-server/pylsp/plugins/definition.py b/external-deps/python-lsp-server/pylsp/plugins/definition.py index 1ddc03a0099..a40ffbbd30f 100644 --- a/external-deps/python-lsp-server/pylsp/plugins/definition.py +++ b/external-deps/python-lsp-server/pylsp/plugins/definition.py @@ -71,7 +71,10 @@ def pylsp_definitions( }, } for d in definitions - if d.is_definition() and (follow_builtin_defns or _not_internal_definition(d)) + if d.is_definition() + and d.line is not None + and d.column is not None + and (follow_builtin_defns or _not_internal_definition(d)) ] diff --git a/external-deps/python-lsp-server/pylsp/plugins/rope_autoimport.py b/external-deps/python-lsp-server/pylsp/plugins/rope_autoimport.py index 8ba951f7157..be51d20e0ec 100644 --- a/external-deps/python-lsp-server/pylsp/plugins/rope_autoimport.py +++ b/external-deps/python-lsp-server/pylsp/plugins/rope_autoimport.py @@ -337,7 +337,12 @@ def pylsp_code_actions( log.debug(f"textDocument/codeAction: {document} {range} {context}") code_actions = [] for diagnostic in context.get("diagnostics", []): - if "undefined name" not in diagnostic.get("message", "").lower(): + # Provided by flake8 and ruff (F821) + matched_message = "undefined name" in diagnostic.get("message", "").lower() + # Provided by mypy + matched_code = diagnostic.get("code", "") == "name-defined" + + if not matched_message and not matched_code: continue word = get_name_or_module(document, diagnostic) diff --git a/external-deps/python-lsp-server/pyproject.toml b/external-deps/python-lsp-server/pyproject.toml index 8fe9c574ac0..2d21bbcfa4f 100644 --- a/external-deps/python-lsp-server/pyproject.toml +++ b/external-deps/python-lsp-server/pyproject.toml @@ -16,7 +16,7 @@ requires-python = ">=3.9" dependencies = [ "docstring-to-markdown", "importlib_metadata>=4.8.3;python_version<\"3.10\"", - "jedi>=0.17.2,<0.20.0", + "jedi>=0.17.2,<0.21.0", "pluggy>=1.0.0", "python-lsp-jsonrpc>=1.1.0,<2.0.0", "ujson>=3.0.0", diff --git a/external-deps/python-lsp-server/test/plugins/test_autoimport.py b/external-deps/python-lsp-server/test/plugins/test_autoimport.py index cbe3dde1e68..8b7bc1c15c6 100644 --- a/external-deps/python-lsp-server/test/plugins/test_autoimport.py +++ b/external-deps/python-lsp-server/test/plugins/test_autoimport.py @@ -346,3 +346,94 @@ def test_autoimport_code_actions_and_completions_for_notebook_document( context = {"diagnostics": [{"message": "A random message"}]} quick_fixes = server.code_actions("cell_4_uri", {}, context) assert len(quick_fixes) == 0 + + +def make_mypy_context(module_name, line, character_start, character_end): + return { + "diagnostics": [ + { + "range": { + "start": {"line": line, "character": character_start}, + "end": {"line": line, "character": character_end}, + }, + "code": "name-defined", + } + ] + } + + +@pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") +def test_autoimport_code_actions_and_completions_based_on_mypy_error( + client_server_pair, +) -> None: + client, server = client_server_pair + send_initialize_request( + client, + { + "pylsp": { + "plugins": { + "rope_autoimport": { + "memory": True, + "enabled": True, + "completions": {"enabled": True}, + }, + } + } + }, + ) + with patch.object(server._endpoint, "notify") as mock_notify: + # Expectations: + # 1. We receive an autoimport suggestion for "os" in the first cell because + # os is imported after that. + # 2. We don't receive an autoimport suggestion for "os" in the second cell because it's + # already imported in the second cell. + # 3. We don't receive an autoimport suggestion for "os" in the third cell because it's + # already imported in the second cell. + # 4. We receive an autoimport suggestion for "sys" because it's not already imported. + # 5. If diagnostics doesn't contain "undefined name ...", we send empty quick fix suggestions. + send_notebook_did_open(client, ["os", "import os\nos", "os", "sys"]) + wait_for_condition(lambda: mock_notify.call_count >= 4) + # We received diagnostics messages for every cell + assert all( + "textDocument/publishDiagnostics" in c.args + for c in mock_notify.call_args_list + ) + + rope_autoimport_settings = server.workspace._config.plugin_settings( + "rope_autoimport" + ) + assert rope_autoimport_settings.get("completions", {}).get("enabled", False) is True + assert rope_autoimport_settings.get("memory", False) is True + wait_for_condition(lambda: not cache.is_blocked()) + + # 1. + quick_fixes = server.code_actions( + "cell_1_uri", {}, make_mypy_context("os", 0, 0, 2) + ) + assert any(s for s in quick_fixes if contains_autoimport_quickfix(s, "os")) + + completions = server.completions("cell_1_uri", position(0, 2)).get("items") + assert any(s for s in completions if contains_autoimport_completion(s, "os")) + + # 2. + # We don't test code actions here as in this case, there would be no code actions sent bc + # there wouldn't be a diagnostics message. + completions = server.completions("cell_2_uri", position(1, 2)).get("items") + assert not any(s for s in completions if contains_autoimport_completion(s, "os")) + + # 3. + # Same as in 2. + completions = server.completions("cell_3_uri", position(0, 2)).get("items") + assert not any(s for s in completions if contains_autoimport_completion(s, "os")) + + # 4. + quick_fixes = server.code_actions("cell_4_uri", {}, make_context("sys", 0, 0, 3)) + assert any(s for s in quick_fixes if contains_autoimport_quickfix(s, "sys")) + + completions = server.completions("cell_4_uri", position(0, 3)).get("items") + assert any(s for s in completions if contains_autoimport_completion(s, "sys")) + + # 5. + context = {"diagnostics": [{"code": "some-code"}]} + quick_fixes = server.code_actions("cell_4_uri", {}, context) + assert len(quick_fixes) == 0 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 ae5021f571b..bbaeabe0284 100644 --- a/external-deps/python-lsp-server/test/plugins/test_completion.py +++ b/external-deps/python-lsp-server/test/plugins/test_completion.py @@ -322,7 +322,7 @@ def test_matplotlib_completions(config, workspace) -> None: def test_snippets_completion(config, workspace) -> None: - doc_snippets = "from collections import defaultdict \na=defaultdict" + doc_snippets = "from collections import defaultdict \ndef foo(a): pass\nfoo" com_position = {"line": 0, "character": 35} doc = Document(DOC_URI, workspace, doc_snippets) config.capabilities["textDocument"] = { @@ -332,9 +332,9 @@ def test_snippets_completion(config, workspace) -> None: completions = pylsp_jedi_completions(config, doc, com_position) assert completions[0]["insertText"] == "defaultdict" - com_position = {"line": 1, "character": len(doc_snippets)} + com_position = {"line": 2, "character": 3} completions = pylsp_jedi_completions(config, doc, com_position) - assert completions[0]["insertText"] == "defaultdict($0)" + assert completions[0]["insertText"] == "foo($0)" assert completions[0]["insertTextFormat"] == lsp.InsertTextFormat.Snippet diff --git a/external-deps/python-lsp-server/test/plugins/test_definitions.py b/external-deps/python-lsp-server/test/plugins/test_definitions.py index 7923524b2f0..211c4437f2f 100644 --- a/external-deps/python-lsp-server/test/plugins/test_definitions.py +++ b/external-deps/python-lsp-server/test/plugins/test_definitions.py @@ -3,6 +3,8 @@ import os +import jedi + from pylsp import uris from pylsp.plugins.definition import pylsp_definitions from pylsp.workspace import Document @@ -173,3 +175,27 @@ def foo(): assert [{"uri": module_uri, "range": def_range}] == pylsp_definitions( config, doc, cursor_pos ) + + +def test_definition_without_source_position(config, workspace, monkeypatch) -> None: + # A definition jedi resolves without a source position (e.g. a compiled + # builtin, where line/column are None) must be dropped, not crash with + # `TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'`. + # Regression test for #623 and #624. + class _NoPositionName: + line = None + column = None + module_path = None + name = "compiled" + + def is_definition(self) -> bool: + return True + + monkeypatch.setattr( + jedi.api.Script, "goto", lambda *args, **kwargs: [_NoPositionName()] + ) + + doc = Document(DOC_URI, workspace, DOC) + # follow_builtin_definitions defaults to True, so the None-guard in the + # return comprehension is the only thing preventing the crash. + assert pylsp_definitions(config, doc, {"line": 3, "character": 6}) == [] diff --git a/external-deps/python-lsp-server/test/test_utils.py b/external-deps/python-lsp-server/test/test_utils.py index 7ed6214f75a..5d58dc559cd 100644 --- a/external-deps/python-lsp-server/test/test_utils.py +++ b/external-deps/python-lsp-server/test/test_utils.py @@ -197,6 +197,24 @@ def test_find_parents(tmpdir) -> None: ] +def test_find_parents_cross_mount(tmpdir, monkeypatch) -> None: + """find_parents returns [] when root and path are on different mounts (Windows UNC).""" + import unittest.mock as mock + + subsubdir = tmpdir.ensure_dir("subdir", "subsubdir") + path = subsubdir.ensure("path.py") + + with mock.patch( + "os.path.relpath", + side_effect=ValueError( + "path is on mount 'C:', start on mount '\\\\unc\\share'" + ), + ): + result = _utils.find_parents(tmpdir.strpath, path.strpath, ["test.cfg"]) + + assert result == [] + + def test_merge_dicts() -> None: assert _utils.merge_dicts( {"a": True, "b": {"x": 123, "y": {"hello": "world"}}}, From 85ec9a740d63832444c13b84322529dc35f714ef Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 19 Jul 2026 12:15:42 -0500 Subject: [PATCH 2/5] Dependencies: Add support for Jedi 0.20 --- binder/environment.yml | 2 +- requirements/main.yml | 2 +- setup.py | 2 +- spyder/dependencies.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/binder/environment.yml b/binder/environment.yml index e67caa3dcfd..b24ae55863d 100644 --- a/binder/environment.yml +++ b/binder/environment.yml @@ -21,7 +21,7 @@ dependencies: - intervaltree >=3.0.2 - ipython >=9.15.0,<10.0.0 - ipython_pygments_lexers >=1.0 -- jedi >=0.17.2,<0.20.0 +- jedi >=0.17.2,<0.21.0 - jellyfish >=0.7 - jsonschema >=3.2.0 - keyring >=17.0.0 diff --git a/requirements/main.yml b/requirements/main.yml index 75cf0618b5c..17a04e4e6ce 100644 --- a/requirements/main.yml +++ b/requirements/main.yml @@ -18,7 +18,7 @@ dependencies: - intervaltree >=3.0.2 - ipython >=9.15.0,<10.0.0 - ipython_pygments_lexers >=1.0 - - jedi >=0.17.2,<0.20.0 + - jedi >=0.17.2,<0.21.0 - jellyfish >=0.7 - jsonschema >=3.2.0 - keyring >=17.0.0 diff --git a/setup.py b/setup.py index 492afbd6d31..a8c1d04da4b 100644 --- a/setup.py +++ b/setup.py @@ -277,7 +277,7 @@ def run(self): 'intervaltree>=3.0.2', 'ipython>=9.15.0,<10.0.0', 'ipython_pygments_lexers>=1.0', - 'jedi>=0.17.2,<0.20.0', + 'jedi>=0.17.2,<0.21.0', 'jellyfish>=0.7', 'jsonschema>=3.2.0', 'keyring>=17.0.0', diff --git a/spyder/dependencies.py b/spyder/dependencies.py index f1625157b98..7c55f9cd943 100644 --- a/spyder/dependencies.py +++ b/spyder/dependencies.py @@ -42,7 +42,7 @@ INTERVALTREE_REQVER = '>=3.0.2' IPYTHON_REQVER = ">=9.15.0,<10.0.0" IPYTHON_PYGMENTS_LEXERS_REQVER = ">=1.0" -JEDI_REQVER = '>=0.17.2,<0.20.0' +JEDI_REQVER = '>=0.17.2,<0.21.0' JELLYFISH_REQVER = '>=0.7' JSONSCHEMA_REQVER = '>=3.2.0' KEYRING_REQVER = '>=17.0.0' From 595859491e8fe1860a4112f12a8d10dce0b9ea14 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 19 Jul 2026 12:16:44 -0500 Subject: [PATCH 3/5] CI: Force installation of Jedi 0.20 We were using an older version before and without this pin neither conda nor pip install that version (don't know why). --- .github/scripts/install.sh | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/scripts/install.sh b/.github/scripts/install.sh index fd536b85373..53d3928449e 100755 --- a/.github/scripts/install.sh +++ b/.github/scripts/install.sh @@ -47,11 +47,10 @@ if [ "$USE_CONDA" = "true" ]; then # Remove pylsp before installing its subrepo below micromamba remove --force python-lsp-server python-lsp-server-base -y - if [ "$OS" = "linux" ]; then - # Pin Jedi to 0.19.1 because test_update_outline fails frequently with - # 0.19.2, although it passes locally - micromamba install jedi=0.19.1 - elif [ "$OS" = "win" ]; then + # Force installation of Jedi 0.20 + micromamba install jedi=0.20 + + if [ "$OS" = "win" ]; then # Build 8 of this version makes tests fail in odd ways. micromamba install bzip2=1.0.8=h2466b09_7 fi @@ -78,11 +77,8 @@ else # Pin IPykernel to 7.2.0 because version 7.3.0+ causes segfaults pip install ipykernel==7.2.0 - # Pin Jedi to 0.19.1 because test_update_outline fails frequently with - # 0.19.2, although it passes locally - if [ "$OS" = "linux" ]; then - pip install jedi==0.19.1 - fi + # Force installation of Jedi 0.20 + pip install jedi==0.20.0 fi # Install subrepos from source From 2a22d5f90f2e18c71a1d2ea68edd67fc5b3f2ea2 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 19 Jul 2026 22:14:08 -0500 Subject: [PATCH 4/5] Testing: Fix test_get_hints for Jedi 0.20 --- .../editor/widgets/codeeditor/tests/test_hints_and_calltips.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder/plugins/editor/widgets/codeeditor/tests/test_hints_and_calltips.py b/spyder/plugins/editor/widgets/codeeditor/tests/test_hints_and_calltips.py index 02ed7c0c8d5..1f19e04db53 100644 --- a/spyder/plugins/editor/widgets/codeeditor/tests/test_hints_and_calltips.py +++ b/spyder/plugins/editor/widgets/codeeditor/tests/test_hints_and_calltips.py @@ -131,7 +131,7 @@ def test_get_calltips(qtbot, completions_codeeditor, params): @pytest.mark.skipif(not os.name == 'nt', reason='Only works on Windows') @pytest.mark.parametrize('params', [ # Parameter, Expected Output - ('"".format', '-> str'), + ('"".format', '-> LiteralString'), ('import math', 'module'), (TEST_TEXT, TEST_DOCSTRING) ] From bf48ce8da637195a9ae8d0bc57d0c674118056af Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 20 Jul 2026 09:55:04 -0500 Subject: [PATCH 5/5] git subrepo pull --remote=https://github.com/python-lsp/python-lsp-server.git --branch=develop --update --force external-deps/python-lsp-server subrepo: subdir: "external-deps/python-lsp-server" merged: "40e66dba5f" upstream: origin: "https://github.com/python-lsp/python-lsp-server.git" branch: "develop" commit: "40e66dba5f" git-subrepo: version: "0.4.9" origin: "https://github.com/ingydotnet/git-subrepo" commit: "ea10886" --- external-deps/python-lsp-server/.gitrepo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/external-deps/python-lsp-server/.gitrepo b/external-deps/python-lsp-server/.gitrepo index ec1cb78f992..63ad83a8d8b 100644 --- a/external-deps/python-lsp-server/.gitrepo +++ b/external-deps/python-lsp-server/.gitrepo @@ -4,9 +4,9 @@ ; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme ; [subrepo] - remote = https://github.com/ccordoba12/python-lsp-server.git - branch = support-jedi-0.20 - commit = 78e291530f82f7aca34fc89af37b0ac8ef648b48 - parent = 0e9c8ced1bbd372cff4cab2d0b0868a306e2591a + remote = https://github.com/python-lsp/python-lsp-server.git + branch = develop + commit = 40e66dba5f50ef110797c55965b6cd8586e45017 + parent = 2a22d5f90f2e18c71a1d2ea68edd67fc5b3f2ea2 method = merge cmdver = 0.4.9