Repository navigation
Add opt-in Pyright language server for Python - #168
Grammy Jiang (grammy-jiang) wants to merge 8 commits into
Conversation
There was a problem hiding this comment.
🔵 Needs a closer look
It adds a new external language-server integration whose real runtime behavior is only covered by an environment-gated test and modifies shared definition/reference parsing used by every backend, so human verification is warranted.
No open issues 🎉
What changed in this PR
This PR adds an opt-in Pyright language server backend for Python to multilspy, keeping Jedi as the default. It introduces a PyrightServer adapter that reuses multilspy's existing LanguageServer/LanguageServerHandler infrastructure (launching pyright-langserver --stdio), adds a python_server config option to select the backend, and makes two small, backward-compatible robustness fixes to shared response parsing so that LSP-valid results from Pyright (and other servers) are handled correctly.
Changes:
- Add
python_serverconfig ("jedi"default or"pyright") and route it in theLanguageServer.create()factory, raisingMultilspyExceptionfor unknown values. - Add
PyrightServeradapter with read-onlyworkspace/configuration/applyEdithandlers, boundedpublishDiagnosticsdelivery, UTF-16 initialization, and shutdown/stop lifecycle handling. - Relax
request_definitionto acceptLocationLinkresults without the optionaloriginSelectionRange, and treat anullreferences response as an empty list; add synthetic + env-gated integration tests and README docs.
| File | Description |
|---|---|
src/multilspy/multilspy_config.py |
Adds python_server Literal field (defaults to "jedi"). |
src/multilspy/language_server.py |
Routes Pyright in the factory; relaxes LocationLink parsing and guards None references. |
src/multilspy/language_servers/pyright_language_server/pyright_server.py |
New Pyright adapter with init, diagnostics queue, and lifecycle handling. |
src/multilspy/language_servers/pyright_language_server/__init__.py |
New empty package init. |
tests/multilspy/test_multilspy_pyright.py |
Synthetic backend-selection/parsing tests plus env-gated real Pyright test. |
README.md |
Documents Pyright as an optional Python backend and how to select it. |
Review effort: Balanced
|
@microsoft-github-policy-service agree |
… request shapes Add a binary version check, an advertised-capability matrix test, one data-driven corpus of exact low-level payloads (declaration, type definition, highlights, prepare rename, signature help, call hierarchy, UTF-16 non-BMP columns, null results, code action, rename proposal, completion resolve, MethodNotFound for unadvertised requests), and push diagnostics for every typeCheckingMode. Document the capability matrix in the README. 74 passed on Linux ARM64 with real Pyright 1.1.413. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rage Run pinned real Pyright in CI, preserve latest diagnostics across queue overflow, reset stale state on restart, validate advertised capabilities and bound shutdown. Correct shared UTF-16 document edits and stable completion ranking; add Pyright/Jedi protocol, Unicode, Windows CLI and lifecycle regressions across Python 3.10-3.14.
Support explicit, bounded workspace/applyEdit authorization with fail-closed LSP response validation. Cover genuine organize-import edits, create-type-stub direct writes, and documented process/workspace isolation boundaries. Fix Python 3.14 Windows file URI drive casing and strengthen portable LSP tests. Verify pinned Pyright 1.1.413 across Windows 3.10/3.12/3.13/3.14, Ubuntu full suite 3.10/3.12/3.14, and native macOS/Linux regression suites.
|
Maintainer review requested for the finalized Pyright adapter at |
Clear pending requests on cancellation, stop and EOF; ignore late, duplicate and malformed replies. Serialize concurrent stdin drains for Python 3.10/3.11, fence stale readers after restart, and reap finished dispatch tasks. Add concurrent, stalled-writer, late-response, malformed-JSON-RPC, interrupted-frame, process-crash and real Pyright restart regression tests across Mac and Linux.
|
Follow-up fix pushed on the existing PR #168 branch: |
Summary
Introduce Pyright as an opt-in Python language server for
multilspy, preserving Jedi as the default. Follow existingLanguageServer,LanguageServerHandler, LSP initialization and adapter conventions; fix protocol response shapes shared by existing language backends.Current contributor revision:
9203732e4e5f65cc1374b109110f7b6354d3d43b(JSON-RPC request lifecycle fix over previously verifiedb5325f1). Real Pyright version pinned for tests: 1.1.413 (pyright-langserver --stdio). This is an upstream library contribution; it is not a production security sandbox or a Binnacle MCP release.Features and how they are accessed
multilspyaccessrequest_definition,request_references,request_hoverrequest_completionsand low-level resolveserver.send.*server.send.*server.send.prepare_rename,server.send.renameWorkspaceEditproposal; no automatic disk modificationserver.send.code_actionCreate Type Stubquick fix from an isolated untyped dependencyserver.send.execute_command+ optional editor-side handlerworkspace/applyEditapplied to a disposable fileserver.send.execute_command.pyifiles directly in a disposable environment; does not needworkspace/applyEditPyrightServer.next_diagnostics()SyncLanguageServerThe adapter loads a packaged
initialize_params.json, explicitly chooses UTF-16, checks essential server capabilities and tears down the child process after successful exit, initialization failure, cancellation or graceful-shutdown timeout. Config appends thepython_serveroption so older positional callers and Jedi defaults remain unchanged.Workspace writes and security boundaries
workspace/applyEditand disables organize-imports execution. It does not apply returned rename proposals.PyrightServer.set_workspace_edit_handler(async_callback, timeout_seconds=...)before startup. The adapter advertises edit support only if configured, validates LSPApplyWorkspaceEditResult, restricts the callback lifetime, bounds its execution time, and reports failures without echoing exception paths. The caller remains responsible for edit authorization, document versions,changesanddocumentChanges, atomic application, filesystem operations and synchronizing any open document buffers throughdidChange.pyright.createtypestubcommand can write.pyifiles directly, without asking the client to apply aWorkspaceEdit. Never pass unrestricted command execution to untrusted callers. Process/workspace permissions must be enforced by the embedding application.Testing and CI
9203732, including actual Pyright, Unicode paths, Windows URI spelling, editor callbacks, process lifecycle, and shared JSON-RPC cleanup regressions.9203732, including real Pyright 1.1.413, Jedi compatibility, edit proposals, path and request/process lifecycle regressions.b5325f1passed 126 targeted tests per host before this shared lifecycle fix.9203732, each 197 passed, 16 skipped, 1 xfailed, 2 xpassed. The skipped tests are existing environment-dependent upstream cases, not hidden Pyright binary/version mismatches. Real Pyright 1.1.413 is installed and verified during CI.Historical
b5325f1fork verification: native Windows four-version matrix + Linux full suite. The new9203732run is linked above. These fork CI results are independent of Microsoft-hosted workflow approval.Shared JSON-RPC request lifecycle fix (
9203732, 2026-10-22)Follow-up from an independent Binnacle integration acceptance run against the real patched Pyright adapter found that a timed-out/cancelled
LanguageServerHandler.send_request()retained an entry in_response_handlerseven after the provider subprocess stopped. This reproduced natively on macOS and Raspberry Pi Linux. The original failure was preserved as an explicit private Binnacle release gate; with this patch it turns green (37/37 related real-provider/policy tests on each tested host).The fix lives in multilspy's shared
lsp_protocol_handler/server.py, not in the Binnacle server or a second Pyright adapter. It now cleans requests infinally, uses predicate-based condition waits (no lost wakeup), wakes pending callers on EOF/stop, drops late/duplicate replies safely, rejects malformed reply envelopes, serializes concurrent stdin writes/drains across Python versions, isolates prior-reader cleanup from a restarted session, and releases completed background tasks. Cancellation continues to propagate rather than being swallowed. Tests cover 100 repeated cancellations, 20 concurrent out-of-order results, blocked writes, malformed and incomplete frames, crash/EOF, restart, errors, null results and actual Pyright requests after cancellation.Contributor native targeted regressions, including real Pyright 1.1.413: 152/152 each on macOS ARM64 Python 3.10/3.13/3.14 and Linux ARM64 Python 3.10/3.13/3.14. Ruff, source compilation, wheel build/resource inclusion, and macOS-vs-Linux patch hashes also passed. An independent read-only code review was performed and its substantive concurrency/race findings were addressed with tests.
Current revision fork CI: 7/7 jobs SUCCESS for
9203732— 105/105 native Windows targeted tests per Python 3.10/3.12/3.13/3.14 and Ubuntu full-suite 197 passed per Python 3.10/3.12/3.14. The previous 7/7 fork CI belongs to historicalb5325f1. Microsoft-hosted Test Multilspy and CodeQL currently remainaction_requireduntil authorized maintainers approve them; human review and merge are also external gates.Limits / maintained compatibility
Features not advertised to this specific client profile by Pyright 1.1.413 (such as
implementationProvider, selected formatting and pull-diagnostics features) are not claimed; LSPMethodNotFoundbehavior is tested. All non-automatically-applied edit results remain proposals until the embedding client handles them. A newly released Pyright version must be revalidated against the version-pinned capability and wire-format tests.Shared LSP fixes include optional
originSelectionRangehandling inLocationLink, nullable references, LSP-valid completion items, bounded incomplete-completion retry, UTF-16 document buffer indexing, stable completion candidate ordering, and Windows drive-case portability. The changes are covered by the Python backend and shared cross-adapter regressions.Related: historical basedpyright proposal #39. This contribution targets official
pyright-langserverand current multilspy main (baseline 0.1.1), not basedpyright or a privately maintained fork.Upstream release status: pending Microsoft human review and merge. Microsoft-hosted CI approvals are controlled by repository maintainers; fork CI is reported separately and is not presented as Microsoft acceptance.