Skip to content

Commit 2a7126f

Browse files
committed
Delay creation of .virtual_documents until actually needed
because deployments may primarily use a well-behaved server that does not need virtual documents folder together with secondary servers that do require it but are only used from time to time when opening specific file types. This can also marginally improve startup times as there is no need to wait on file system operation of purging the directory which is now only done on first relevant LSP message.
1 parent e1d65e2 commit 2a7126f

File tree

3 files changed

+45
-10
lines changed

3 files changed

+45
-10
lines changed

python_packages/jupyter_lsp/jupyter_lsp/serverextension.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,15 @@ async def initialize(nbapp, virtual_documents_uri): # pragma: no cover
2929

3030
if any(servers_requiring_disk_access):
3131
nbapp.log.debug(
32-
"[lsp] servers that requested virtual documents on disk: %s",
32+
"[lsp] Servers that requested virtual documents on disk: %s",
3333
servers_requiring_disk_access,
3434
)
3535
setup_shadow_filesystem(virtual_documents_uri=virtual_documents_uri)
36+
else:
37+
nbapp.log.debug(
38+
"[lsp] None of the installed servers require virtual documents"
39+
" disabling shadow filesystem."
40+
)
3641

3742
nbapp.log.debug(
3843
"[lsp] The following Language Servers will be available: {}".format(

python_packages/jupyter_lsp/jupyter_lsp/tests/test_virtual_documents_shadow.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,20 +120,44 @@ async def test_shadow(shadow_path, message_func, content, expected_content, mana
120120

121121

122122
@pytest.mark.asyncio
123-
async def test_short_circuits_for_well_behaved_server(
123+
async def test_no_shadow_for_well_behaved_server(
124124
shadow_path,
125125
):
126126
"""We call server well behaved when it does not require a disk copy"""
127-
shadow = setup_shadow_filesystem(Path(shadow_path).as_uri())
128-
ok_file_path = Path(shadow_path) / "test.py"
127+
shadow_path_for_well = Path(shadow_path) / "well"
128+
shadow = setup_shadow_filesystem(Path(shadow_path_for_well).as_uri())
129+
ok_file_path = Path(shadow_path_for_well) / "test.py"
129130

130131
manager = SimpleNamespace(
131132
language_servers={"python-lsp-server": {"requires_documents_on_disk": False}}
132133
)
133134

134135
message = did_open(ok_file_path.as_uri(), "content\nof\nopened\nfile")
135136
result = await shadow("client", message, "python-lsp-server", manager)
137+
# should short-circuit for well behaved server
136138
assert result is None
139+
# should not create the directory
140+
assert not shadow_path_for_well.exists()
141+
142+
143+
@pytest.mark.asyncio
144+
async def test_shadow_created_for_ill_behaved_server(
145+
shadow_path,
146+
):
147+
shadow_path_for_ill = Path(shadow_path) / "ill"
148+
shadow = setup_shadow_filesystem(shadow_path_for_ill.as_uri())
149+
ok_file_path = Path(shadow_path_for_ill) / "test.py"
150+
151+
manager = SimpleNamespace(
152+
language_servers={"python-lsp-server": {"requires_documents_on_disk": True}}
153+
)
154+
155+
message = did_open(ok_file_path.as_uri(), "content\nof\nopened\nfile")
156+
result = await shadow("client", message, "python-lsp-server", manager)
157+
assert result is not None
158+
# should create the directory at given path
159+
assert shadow_path_for_ill.exists()
160+
assert shadow_path_for_ill.is_dir()
137161

138162

139163
@pytest.mark.asyncio

python_packages/jupyter_lsp/jupyter_lsp/virtual_documents_shadow.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,8 @@ def setup_shadow_filesystem(virtual_documents_uri: str):
110110
+ virtual_documents_uri
111111
)
112112

113+
initialized = False
113114
shadow_filesystem = Path(file_uri_to_path(virtual_documents_uri))
114-
# create if does no exist (so that removal does not raise)
115-
shadow_filesystem.mkdir(parents=True, exist_ok=True)
116-
# remove with contents
117-
rmtree(str(shadow_filesystem))
118-
# create again
119-
shadow_filesystem.mkdir(parents=True, exist_ok=True)
120115

121116
@lsp_message_listener("client")
122117
async def shadow_virtual_documents(scope, message, language_server, manager):
@@ -125,6 +120,7 @@ async def shadow_virtual_documents(scope, message, language_server, manager):
125120
Only create the shadow file if the URI matches the virtual documents URI.
126121
Returns the path on filesystem where the content was stored.
127122
"""
123+
nonlocal initialized
128124

129125
# short-circut if language server does not require documents on disk
130126
server_spec = manager.language_servers[language_server]
@@ -147,6 +143,16 @@ async def shadow_virtual_documents(scope, message, language_server, manager):
147143
if not uri.startswith(virtual_documents_uri):
148144
return
149145

146+
# initialization (/any file system operations) delayed until needed
147+
if not initialized:
148+
# create if does no exist (so that removal does not raise)
149+
shadow_filesystem.mkdir(parents=True, exist_ok=True)
150+
# remove with contents
151+
rmtree(str(shadow_filesystem))
152+
# create again
153+
shadow_filesystem.mkdir(parents=True, exist_ok=True)
154+
initialized = True
155+
150156
path = file_uri_to_path(uri)
151157
editable_file = EditableFile(path)
152158

0 commit comments

Comments
 (0)