Skip to content

Commit 5bc7b5f

Browse files
committed
also make shadow file ops async
1 parent 61e633d commit 5bc7b5f

File tree

2 files changed

+54
-22
lines changed

2 files changed

+54
-22
lines changed

py_src/jupyter_lsp/tests/test_virtual_documents_shadow.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,46 @@
1111
)
1212

1313

14-
def test_read(tmp_path):
14+
@pytest.mark.asyncio
15+
async def test_read(tmp_path):
1516
path = tmp_path / "existing.py"
1617
path.write_text("a\ntest")
17-
file = EditableFile(path)
18-
assert file.lines == ["a", "test"]
1918

19+
editable_file = EditableFile(path)
20+
21+
await editable_file.read()
22+
23+
assert editable_file.lines == ["a", "test"]
24+
25+
26+
@pytest.mark.asyncio
27+
async def test_read_missing(tmp_path):
2028
path = tmp_path / "missing.py"
21-
file = EditableFile(path)
22-
assert file.lines == [""]
29+
missing_file = EditableFile(path)
30+
31+
await missing_file.read()
32+
33+
assert missing_file.lines == [""]
2334

2435

25-
def test_apply_change(tmp_path):
36+
@pytest.mark.asyncio
37+
async def test_apply_change(tmp_path):
2638
# inserting text
2739
path = tmp_path / "test.py"
28-
file = EditableFile(path)
29-
file.apply_change("new\ntext", **file.full_range)
30-
assert file.lines == ["new", "text"]
40+
editable_file = EditableFile(path)
41+
await editable_file.read()
42+
43+
editable_file.apply_change("new\ntext", **editable_file.full_range)
44+
assert editable_file.lines == ["new", "text"]
3145

3246
# modifying a range
33-
file.apply_change(
47+
editable_file.apply_change(
3448
"ves", start={"line": 1, "character": 0}, end={"line": 1, "character": 3}
3549
)
36-
assert file.lines == ["new", "vest"]
50+
assert editable_file.lines == ["new", "vest"]
3751

38-
file.apply_change("", **file.full_range)
39-
assert file.lines == [""]
52+
editable_file.apply_change("", **editable_file.full_range)
53+
assert editable_file.lines == [""]
4054

4155

4256
def test_extract_or_none():

py_src/jupyter_lsp/virtual_documents_shadow.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
# flake8: noqa: W503
2+
from concurrent.futures import ThreadPoolExecutor
23
from pathlib import Path
34
from shutil import rmtree
45

6+
from tornado.concurrent import run_on_executor
7+
from tornado.gen import convert_yielded
8+
59
from .manager import lsp_message_listener
610
from .paths import file_uri_to_path
711

12+
# TODO: make configurable
13+
MAX_WORKERS = 4
14+
815

916
def extract_or_none(obj, path):
1017
for crumb in path:
@@ -16,11 +23,19 @@ def extract_or_none(obj, path):
1623

1724

1825
class EditableFile:
26+
executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)
27+
1928
def __init__(self, path):
2029
# Python 3.5 relict:
2130
self.path = Path(path) if isinstance(path, str) else path
22-
self.lines = self.read_lines()
2331

32+
async def read(self):
33+
self.lines = await convert_yielded(self.read_lines())
34+
35+
async def write(self):
36+
return await convert_yielded(self.write_lines())
37+
38+
@run_on_executor
2439
def read_lines(self):
2540
# empty string required by the assumptions of the gluing algorithm
2641
lines = [""]
@@ -30,6 +45,11 @@ def read_lines(self):
3045
pass
3146
return lines
3247

48+
@run_on_executor
49+
def write_lines(self):
50+
self.path.parent.mkdir(parents=True, exist_ok=True)
51+
self.path.write_text("\n".join(self.lines))
52+
3353
@staticmethod
3454
def trim(lines: list, character: int, side: int):
3555
needs_glue = False
@@ -63,10 +83,6 @@ def apply_change(self, text: str, start, end):
6383
+ after[1 if needs_glue_right else None :]
6484
) or [""]
6585

66-
def write(self):
67-
self.path.parent.mkdir(parents=True, exist_ok=True)
68-
self.path.write_text("\n".join(self.lines))
69-
7086
@property
7187
def full_range(self):
7288
start = {"line": 0, "character": 0}
@@ -125,7 +141,9 @@ async def shadow_virtual_documents(scope, message, language_server, manager):
125141
return
126142

127143
path = file_uri_to_path(uri)
128-
file = EditableFile(path)
144+
editable_file = EditableFile(path)
145+
146+
await editable_file.read()
129147

130148
text = extract_or_none(document, ["text"])
131149

@@ -148,10 +166,10 @@ async def shadow_virtual_documents(scope, message, language_server, manager):
148166
)
149167

150168
for change in changes[:1]:
151-
change_range = change.get("range", file.full_range)
152-
file.apply_change(change["text"], **change_range)
169+
change_range = change.get("range", editable_file.full_range)
170+
editable_file.apply_change(change["text"], **change_range)
153171

154-
file.write()
172+
await editable_file.write()
155173

156174
return path
157175

0 commit comments

Comments
 (0)