Skip to content

Template Sync: Add Jupyter notebook cell support via LSP 3.17 Notebook Document SyncΒ #474

@github-actions

Description

@github-actions

πŸ”„ Template Sync Required

Changes from the upstream vscode-python-tools-extension-template have not yet been incorporated into this repository.

Source PR

Summary

The template now ships with built-in support for linting Python cells inside Jupyter notebooks (.ipynb) and the VS Code Interactive Window via the LSP 3.17 Notebook Document Sync specification. This adds NOTEBOOK_SYNC_OPTIONS to the server initialization, a _get_document_path helper for resolving vscode-notebook-cell: URIs to the parent notebook's filesystem path, and four notebook lifecycle handlers (notebookDocument/didOpen, /didChange, /didSave, /didClose).

vscode-mypy currently explicitly skips notebook cells in _linting_helper with a "Not Supported" log warning. Adopting the template's pattern would enable mypy type checking in Jupyter notebook Python cells.

Files with missing changes

  • bundled/tool/lsp_server.py β€” Missing the notebook infrastructure:
    • import urllib.parse not present
    • NOTEBOOK_SYNC_OPTIONS constant not present
    • LanguageServer constructor does not include notebook_document_sync
    • _get_document_path helper not present
    • No notebook_did_open, notebook_did_change, notebook_did_save, or notebook_did_close handlers
    • _linting_helper explicitly rejects vscode-notebook-cell: URIs (lines 213–216) rather than handling them

Suggested fix

Below are the recommended additions to bundled/tool/lsp_server.py. Mypy-specific logic (e.g., reportingScope, daemon handling) should be preserved inside each new handler, mirroring how _linting_helper works for regular text documents.

1. Add the urllib.parse import (near the top with other stdlib imports):

+import urllib.parse

2. Add NOTEBOOK_SYNC_OPTIONS (before or after MAX_WORKERS):

+import lsprotocol.types as lsp
+
+NOTEBOOK_SYNC_OPTIONS = lsp.NotebookDocumentSyncOptions(
+    notebook_selector=[
+        lsp.NotebookDocumentFilterWithNotebook(
+            notebook="jupyter-notebook",
+            cells=[lsp.NotebookCellLanguage(language="python")],
+        ),
+        lsp.NotebookDocumentFilterWithNotebook(
+            notebook="interactive",
+            cells=[lsp.NotebookCellLanguage(language="python")],
+        ),
+    ],
+    save=True,
+)

3. Add notebook_document_sync to the LanguageServer constructor:

-LSP_SERVER = LanguageServer(name="Mypy", version="v0.1.0", max_workers=MAX_WORKERS)
+LSP_SERVER = LanguageServer(
+    name="Mypy",
+    version="v0.1.0",
+    max_workers=MAX_WORKERS,
+    notebook_document_sync=NOTEBOOK_SYNC_OPTIONS,
+)

4. Add the _get_document_path helper (near other document helpers):

+def _get_document_path(cell_uri: str) -> str:
+    """Resolve a vscode-notebook-cell: URI to the parent notebook's filesystem path."""
+    parsed = urllib.parse.urlparse(cell_uri)
+    if parsed.scheme == "vscode-notebook-cell":
+        nb_uri = parsed._replace(scheme="file", fragment="").geturl()
+        return LSP_SERVER.workspace.get_text_document(nb_uri).path
+    return LSP_SERVER.workspace.get_text_document(cell_uri).path

5. Add notebook lifecycle handlers (alongside the existing did_open, did_save, did_close handlers):

+`@LSP_SERVER`.feature(lsp.NOTEBOOK_DOCUMENT_DID_OPEN)
+def notebook_did_open(params: lsp.DidOpenNotebookDocumentParams) -> None:
+    """LSP handler for notebookDocument/didOpen request."""
+    nb = LSP_SERVER.workspace.get_notebook_document(
+        notebook_uri=params.notebook_document.uri
+    )
+    if nb is None:
+        return
+    for cell in nb.cells:
+        if cell.kind == lsp.NotebookCellKind.Code:
+            doc = LSP_SERVER.workspace.get_text_document(cell.document)
+            _linting_helper(doc)
+
+
+`@LSP_SERVER`.feature(lsp.NOTEBOOK_DOCUMENT_DID_SAVE)
+def notebook_did_save(params: lsp.DidSaveNotebookDocumentParams) -> None:
+    """LSP handler for notebookDocument/didSave request."""
+    nb = LSP_SERVER.workspace.get_notebook_document(
+        notebook_uri=params.notebook_document.uri
+    )
+    if nb is None:
+        return
+    for cell in nb.cells:
+        if cell.kind == lsp.NotebookCellKind.Code:
+            doc = LSP_SERVER.workspace.get_text_document(cell.document)
+            _linting_helper(doc)
+
+
+`@LSP_SERVER`.feature(lsp.NOTEBOOK_DOCUMENT_DID_CLOSE)
+def notebook_did_close(params: lsp.DidCloseNotebookDocumentParams) -> None:
+    """LSP handler for notebookDocument/didClose request."""
+    for cell_doc in params.cell_text_documents:
+        doc = LSP_SERVER.workspace.get_text_document(cell_doc.uri)
+        _clear_diagnostics(doc)
+
+
+`@LSP_SERVER`.feature(lsp.NOTEBOOK_DOCUMENT_DID_CHANGE)
+def notebook_did_change(params: lsp.DidChangeNotebookDocumentParams) -> None:
+    """LSP handler for notebookDocument/didChange request."""
+    if params.change.cells is None:
+        return
+    cell_changes = params.change.cells
+    if cell_changes.text_content:
+        for changed in cell_changes.text_content:
+            doc = LSP_SERVER.workspace.get_text_document(changed.document.uri)
+            _linting_helper(doc)
+    if cell_changes.structure:
+        for removed in (cell_changes.structure.did_close or []):
+            doc = LSP_SERVER.workspace.get_text_document(removed.uri)
+            _clear_diagnostics(doc)

6. Remove the "Not Supported" skip from _linting_helper (lines 213–216):

-        if str(document.uri).startswith("vscode-notebook-cell"):
-            # We don't support running mypy on notebook cells.
-            log_warning(f"Skipping notebook cells [Not Supported]: {str(document.uri)}")
-            _clear_diagnostics(document)
-            return None
-

Note: The mypy tool runs against a file path. For notebook cells, _get_document_path resolves the cell URI to the parent notebook's .ipynb path. You may want to verify how mypy behaves when given a .ipynb file path and adjust the implementation accordingly.

Files skipped

  • README.md β€” Template-specific documentation; vscode-mypy has its own README.
  • src/test/python_tests/test_notebook.py β€” New test file added by the template. vscode-mypy should add its own notebook integration tests covering mypy-specific behavior.

πŸ€– This issue was auto-generated by the extension-template-sync workflow.

Generated by Extension Template Sync

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions