Skip to content

Commit e30a4d1

Browse files
committed
Fix pylsp-rope compatibility with Jupyter Notebooks
Implement guard clauses to handle virtual documents (notebook cells) safely: - Add is_virtual_document() helper to detect non-file URIs - Update get_resource() to return None for virtual documents - Add early returns in pylsp_code_actions() and pylsp_rename() hooks - Prevent rope operations on virtual documents that don't correspond to files This fixes the python-lsp-server notebook test failure where pylsp-rope was interfering with notebook cell handling by trying to treat virtual URIs as filesystem paths. Signed-off-by: Matěj Cepl <mcepl@cepl.eu>
1 parent aa7f929 commit e30a4d1

2 files changed

Lines changed: 36 additions & 3 deletions

File tree

pylsp_rope/plugin.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
get_resources,
1414
rope_changeset_to_workspace_edit,
1515
new_project,
16+
is_virtual_document,
1617
)
1718

1819

@@ -68,6 +69,11 @@ def pylsp_code_actions(
6869
) -> List[typing.CodeAction]:
6970
logger.info("textDocument/codeAction: %s %s %s", document, range, context)
7071

72+
# Skip virtual documents (e.g., notebook cells)
73+
if is_virtual_document(document.uri):
74+
logger.debug("Skipping code actions for virtual document: %s", document.uri)
75+
return []
76+
7177
class info:
7278
current_document, resource = get_resource(workspace, document.uri)
7379
position = range["start"]
@@ -185,7 +191,9 @@ def pylsp_rename(
185191
return None
186192

187193
logger.info("textDocument/rename: %s %s %s", document, position, new_name)
188-
project = new_project(workspace) # FIXME: we shouldn't have to always keep creating new projects here
194+
project = new_project(
195+
workspace
196+
) # FIXME: we shouldn't have to always keep creating new projects here
189197
document, resource = get_resource(workspace, document.uri, project=project)
190198

191199
rename = Rename(

pylsp_rope/project.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@
2323
logger = logging.getLogger(__name__)
2424

2525

26+
def is_virtual_document(document_uri: DocumentUri) -> bool:
27+
"""
28+
Check if a document URI represents a virtual document (e.g., notebook cell).
29+
30+
Virtual documents have URI schemes other than 'file' (including empty schemes)
31+
and don't correspond to actual files on the filesystem.
32+
"""
33+
try:
34+
parsed_uri = uris.urlparse(document_uri)
35+
scheme = parsed_uri[0] if len(parsed_uri) > 0 else ""
36+
return scheme != "file"
37+
except Exception:
38+
# If we can't parse URI, treat it as virtual for safety
39+
return True
40+
41+
2642
@lru_cache(maxsize=None)
2743
def get_project(workspace) -> rope.Project:
2844
"""Get a cached rope Project or create one if it doesn't exist yet"""
@@ -43,20 +59,29 @@ def get_resource(
4359
document_uri: DocumentUri,
4460
*,
4561
project: rope.Project = None,
46-
) -> Tuple[workspace.Document, rope.Resource]:
62+
) -> Tuple[workspace.Document, Optional[rope.Resource]]:
4763
"""
4864
Return a Document and Resource related to an LSP Document.
4965
5066
`project` must be provided if not using instances of rope Project from
5167
`pylsp_rope.project.get_project()`.
68+
69+
Returns None as resource if the document is virtual (e.g., notebook cell).
5270
"""
5371
document = workspace.get_document(document_uri)
72+
73+
# Handle virtual documents (e.g., notebook cells)
74+
if is_virtual_document(document_uri):
75+
return document, None
76+
5477
project = project or get_project(workspace)
5578
resource = libutils.path_to_resource(project, document.path)
5679
return document, resource
5780

5881

59-
def get_resources(workspace, documents: List[DocumentUri]) -> List[rope.Resource]:
82+
def get_resources(
83+
workspace, documents: List[DocumentUri]
84+
) -> List[Optional[rope.Resource]]:
6085
if documents is None:
6186
return None
6287
return [get_resource(workspace, document_uri)[1] for document_uri in documents]

0 commit comments

Comments
 (0)