Skip to content

Commit e92d79b

Browse files
committed
Fix type issues, switch to use trio.Path, and more async things in client
1 parent 151e100 commit e92d79b

File tree

2 files changed

+57
-47
lines changed

2 files changed

+57
-47
lines changed

src/idlemypyextension/client.py

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import base64
4444
import contextlib
4545
import json
46-
import os
4746
import sys
4847
from collections import ChainMap
4948
from typing import TYPE_CHECKING, TypedDict, cast
@@ -65,6 +64,7 @@
6564
if TYPE_CHECKING:
6665
import io
6766
from collections.abc import Sequence
67+
from pathlib import Path
6868

6969
from typing_extensions import NotRequired
7070

@@ -115,19 +115,30 @@ class BadStatusError(Exception):
115115
# Client-side infrastructure.
116116

117117

118-
def _read_status(status_file: str) -> dict[str, object]:
118+
def str_maintain_none(obj: str | object | None) -> str | None:
119+
"""Return string or None, maintaining None."""
120+
if obj is None:
121+
return None
122+
return str(obj)
123+
124+
125+
async def _read_status(
126+
status_file: str | Path | trio.Path,
127+
) -> dict[str, object]:
119128
"""Read status file.
120129
121130
Raise BadStatusError if the status file doesn't exist or contains
122131
invalid JSON or the JSON is not a dict.
123132
"""
124-
if not os.path.isfile(status_file):
133+
path = trio.Path(status_file)
134+
if not await path.is_file():
125135
raise BadStatusError("No status file found")
126-
with open(status_file, encoding="utf-8") as file:
127-
try:
128-
data = json.load(file)
129-
except Exception as ex:
130-
raise BadStatusError("Malformed status file (not JSON)") from ex
136+
137+
contents = await path.read_text(encoding="utf-8")
138+
try:
139+
data = json.loads(contents)
140+
except Exception as ex:
141+
raise BadStatusError("Malformed status file (not JSON)") from ex
131142
if not isinstance(data, dict):
132143
raise BadStatusError("Invalid status file (not a dict)")
133144
return data
@@ -155,14 +166,14 @@ def _check_status(data: dict[str, object]) -> tuple[int, str]:
155166
return pid, connection_name
156167

157168

158-
def get_status(status_file: str) -> tuple[int, str]:
169+
async def get_status(status_file: str | Path | trio.Path) -> tuple[int, str]:
159170
"""Read status file and check if the process is alive.
160171
161172
Return (process id, connection_name) on success.
162173
163174
Raise BadStatusError if something's wrong.
164175
"""
165-
data = _read_status(status_file)
176+
data = await _read_status(status_file)
166177
return _check_status(data)
167178

168179

@@ -278,7 +289,7 @@ def find_frame_in_buffer(
278289

279290

280291
async def request(
281-
status_file: str,
292+
status_file: str | Path | trio.Path,
282293
command: str,
283294
*,
284295
timeout: int | None = None, # noqa: ASYNC109
@@ -303,7 +314,7 @@ async def request(
303314
args["is_tty"] = False
304315
args["terminal_width"] = 80
305316
request_arguments = json.dumps(args)
306-
_, name = get_status(status_file)
317+
_, name = await get_status(status_file)
307318

308319
async with REQUEST_LOCK:
309320
if sys.platform == "win32" or FORCE_BASE_REQUEST:
@@ -316,30 +327,30 @@ async def request(
316327
)
317328

318329

319-
def is_running(status_file: str) -> bool:
330+
async def is_running(status_file: str | Path | trio.Path) -> bool:
320331
"""Check if the server is running cleanly."""
321332
try:
322-
get_status(status_file)
333+
await get_status(status_file)
323334
except BadStatusError:
324335
return False
325336
return True
326337

327338

328-
async def stop(status_file: str) -> Response:
339+
async def stop(status_file: str | Path | trio.Path) -> Response:
329340
"""Stop daemon via a 'stop' request."""
330341
return await request(status_file, "stop", timeout=5)
331342

332343

333344
async def _wait_for_server(
334-
status_file: str,
345+
status_file: str | Path | trio.Path,
335346
timeout: float = 5.0, # noqa: ASYNC109
336347
) -> bool:
337348
"""Wait until the server is up. Return False if timed out."""
338349
try:
339350
with trio.fail_after(timeout):
340351
while True:
341352
try:
342-
data = _read_status(status_file)
353+
data = await _read_status(status_file)
343354
except BadStatusError:
344355
# If the file isn't there yet, retry later.
345356
await trio.sleep(0.1)
@@ -356,21 +367,21 @@ async def _wait_for_server(
356367

357368

358369
async def _start_server(
359-
status_file: str,
370+
status_file: str | Path | trio.Path,
360371
*,
361372
flags: list[str],
362373
daemon_timeout: int | None = None,
363374
allow_sources: bool = False,
364-
log_file: str | None = None,
375+
log_file: str | Path | trio.Path | None = None,
365376
) -> bool:
366377
"""Start the server and wait for it. Return False if error starting."""
367378
start_options = _process_start_options(flags, allow_sources)
368379
if (
369380
_daemonize(
370381
start_options,
371-
status_file,
382+
str(status_file),
372383
timeout=daemon_timeout,
373-
log_file=log_file,
384+
log_file=str_maintain_none(log_file),
374385
)
375386
!= 0
376387
):
@@ -379,12 +390,12 @@ async def _start_server(
379390

380391

381392
async def restart(
382-
status_file: str,
393+
status_file: str | Path | trio.Path,
383394
*,
384395
flags: list[str],
385396
daemon_timeout: int | None = 0,
386397
allow_sources: bool = False,
387-
log_file: str | None = None,
398+
log_file: str | Path | trio.Path | None = None,
388399
) -> bool:
389400
"""Restart daemon (it may or may not be running; but not hanging).
390401
@@ -406,18 +417,18 @@ async def restart(
406417

407418

408419
async def start(
409-
status_file: str,
420+
status_file: str | Path | trio.Path,
410421
*,
411422
flags: list[str],
412423
daemon_timeout: int = 0,
413424
allow_sources: bool = False,
414-
log_file: str | None = None,
425+
log_file: str | Path | trio.Path | None = None,
415426
) -> bool:
416427
"""Start daemon if not already running.
417428
418429
Returns False if error starting / already running.
419430
"""
420-
if not is_running(status_file):
431+
if not await is_running(status_file):
421432
# Bad or missing status file or dead process; good to start.
422433
return await _start_server(
423434
status_file,
@@ -430,7 +441,7 @@ async def start(
430441

431442

432443
async def status(
433-
status_file: str,
444+
status_file: str | Path | trio.Path,
434445
*,
435446
timeout: int = 5, # noqa: ASYNC109
436447
fswatcher_dump_file: str | None = None,
@@ -445,12 +456,12 @@ async def status(
445456

446457

447458
async def run(
448-
status_file: str,
459+
status_file: str | Path | trio.Path,
449460
*,
450461
flags: list[str],
451462
timeout: int | None = None, # noqa: ASYNC109
452463
daemon_timeout: int = 0,
453-
log_file: str | None = None,
464+
log_file: str | Path | trio.Path | None = None,
454465
export_types: bool = False,
455466
) -> Response:
456467
"""Do a check, starting (or restarting) the daemon as necessary.
@@ -495,9 +506,9 @@ async def run(
495506
return response
496507

497508

498-
def kill(status_file: str) -> bool:
509+
async def kill(status_file: str | Path | trio.Path) -> bool:
499510
"""Kill daemon process with SIGKILL. Return True if killed."""
500-
pid = get_status(status_file)[0]
511+
pid = (await get_status(status_file))[0]
501512
try:
502513
_kill(pid)
503514
except OSError as ex:
@@ -507,7 +518,7 @@ def kill(status_file: str) -> bool:
507518

508519

509520
async def check(
510-
status_file: str,
521+
status_file: str | Path | trio.Path,
511522
files: Sequence[str],
512523
*,
513524
timeout: int | None = None, # noqa: ASYNC109
@@ -524,7 +535,7 @@ async def check(
524535

525536

526537
async def recheck(
527-
status_file: str,
538+
status_file: str | Path | trio.Path,
528539
export_types: bool,
529540
*,
530541
timeout: int | None = None, # noqa: ASYNC109
@@ -562,7 +573,7 @@ async def recheck(
562573

563574

564575
async def inspect(
565-
status_file: str,
576+
status_file: str | Path | trio.Path,
566577
location: str, # line:col
567578
show: str = "type", # type, attrs, definition
568579
*,
@@ -593,7 +604,7 @@ async def inspect(
593604

594605

595606
async def suggest(
596-
status_file: str,
607+
status_file: str | Path | trio.Path,
597608
function: str,
598609
do_json: bool,
599610
*,
@@ -622,7 +633,7 @@ async def suggest(
622633

623634

624635
async def hang(
625-
status_file: str,
636+
status_file: str | Path | trio.Path,
626637
*,
627638
timeout: int = 1, # noqa: ASYNC109
628639
) -> Response:
@@ -633,10 +644,10 @@ async def hang(
633644

634645

635646
def do_daemon(
636-
status_file: str,
647+
status_file: str | Path | trio.Path,
637648
flags: list[str],
638649
daemon_timeout: int | None = None,
639650
) -> None:
640651
"""Serve requests in the foreground."""
641652
options = _process_start_options(flags, allow_sources=False)
642-
_Server(options, status_file, timeout=daemon_timeout).serve()
653+
_Server(options, str(status_file), timeout=daemon_timeout).serve()

src/idlemypyextension/extension.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,11 @@ def __init__(self, editwin: PyShellEditorWindow) -> None:
201201

202202
def __getattr__(self, attr_name: str) -> object:
203203
"""Transform event async sync calls to sync wrappers."""
204-
if not attr_name.endswith("_event"):
205-
return super.__getattr__(attr_name)
206-
as_async = f"{attr_name}_async"
207-
if not hasattr(self, as_async):
208-
return super.__getattr__(attr_name)
209-
return self.get_async(as_async)
204+
if attr_name.endswith("_event"):
205+
as_async = f"{attr_name}_async"
206+
if hasattr(self, as_async):
207+
return self.get_async(as_async)
208+
return super().__getattribute__(attr_name)
210209

211210
def get_async(
212211
self,
@@ -423,7 +422,7 @@ def add_errors(
423422

424423
async def ensure_daemon_running(self) -> bool:
425424
"""Make sure daemon is running. Return False if cannot continue."""
426-
if not client.is_running(self.status_file):
425+
if not await client.is_running(str(self.status_file)):
427426
command = " ".join(
428427
x
429428
for x in [
@@ -456,7 +455,7 @@ async def shutdown_dmypy_daemon_event_async(
456455
) -> str:
457456
"""Shutdown dmypy daemon event handler."""
458457
# pylint: disable=unused-argument
459-
if not client.is_running(self.status_file):
458+
if not await client.is_running(self.status_file):
460459
self.text.bell()
461460
return "break"
462461

@@ -466,7 +465,7 @@ async def shutdown_dmypy_daemon_event_async(
466465
response = await client.stop(self.status_file)
467466
if response.get("err") or response.get("error"):
468467
# Kill
469-
client.kill(self.status_file)
468+
await client.kill(self.status_file)
470469

471470
return "break"
472471

0 commit comments

Comments
 (0)