43
43
import base64
44
44
import contextlib
45
45
import json
46
- import os
47
46
import sys
48
47
from collections import ChainMap
49
48
from typing import TYPE_CHECKING , TypedDict , cast
65
64
if TYPE_CHECKING :
66
65
import io
67
66
from collections .abc import Sequence
67
+ from pathlib import Path
68
68
69
69
from typing_extensions import NotRequired
70
70
@@ -115,19 +115,30 @@ class BadStatusError(Exception):
115
115
# Client-side infrastructure.
116
116
117
117
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 ]:
119
128
"""Read status file.
120
129
121
130
Raise BadStatusError if the status file doesn't exist or contains
122
131
invalid JSON or the JSON is not a dict.
123
132
"""
124
- if not os .path .isfile (status_file ):
133
+ path = trio .Path (status_file )
134
+ if not await path .is_file ():
125
135
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
131
142
if not isinstance (data , dict ):
132
143
raise BadStatusError ("Invalid status file (not a dict)" )
133
144
return data
@@ -155,14 +166,14 @@ def _check_status(data: dict[str, object]) -> tuple[int, str]:
155
166
return pid , connection_name
156
167
157
168
158
- def get_status (status_file : str ) -> tuple [int , str ]:
169
+ async def get_status (status_file : str | Path | trio . Path ) -> tuple [int , str ]:
159
170
"""Read status file and check if the process is alive.
160
171
161
172
Return (process id, connection_name) on success.
162
173
163
174
Raise BadStatusError if something's wrong.
164
175
"""
165
- data = _read_status (status_file )
176
+ data = await _read_status (status_file )
166
177
return _check_status (data )
167
178
168
179
@@ -278,7 +289,7 @@ def find_frame_in_buffer(
278
289
279
290
280
291
async def request (
281
- status_file : str ,
292
+ status_file : str | Path | trio . Path ,
282
293
command : str ,
283
294
* ,
284
295
timeout : int | None = None , # noqa: ASYNC109
@@ -303,7 +314,7 @@ async def request(
303
314
args ["is_tty" ] = False
304
315
args ["terminal_width" ] = 80
305
316
request_arguments = json .dumps (args )
306
- _ , name = get_status (status_file )
317
+ _ , name = await get_status (status_file )
307
318
308
319
async with REQUEST_LOCK :
309
320
if sys .platform == "win32" or FORCE_BASE_REQUEST :
@@ -316,30 +327,30 @@ async def request(
316
327
)
317
328
318
329
319
- def is_running (status_file : str ) -> bool :
330
+ async def is_running (status_file : str | Path | trio . Path ) -> bool :
320
331
"""Check if the server is running cleanly."""
321
332
try :
322
- get_status (status_file )
333
+ await get_status (status_file )
323
334
except BadStatusError :
324
335
return False
325
336
return True
326
337
327
338
328
- async def stop (status_file : str ) -> Response :
339
+ async def stop (status_file : str | Path | trio . Path ) -> Response :
329
340
"""Stop daemon via a 'stop' request."""
330
341
return await request (status_file , "stop" , timeout = 5 )
331
342
332
343
333
344
async def _wait_for_server (
334
- status_file : str ,
345
+ status_file : str | Path | trio . Path ,
335
346
timeout : float = 5.0 , # noqa: ASYNC109
336
347
) -> bool :
337
348
"""Wait until the server is up. Return False if timed out."""
338
349
try :
339
350
with trio .fail_after (timeout ):
340
351
while True :
341
352
try :
342
- data = _read_status (status_file )
353
+ data = await _read_status (status_file )
343
354
except BadStatusError :
344
355
# If the file isn't there yet, retry later.
345
356
await trio .sleep (0.1 )
@@ -356,21 +367,21 @@ async def _wait_for_server(
356
367
357
368
358
369
async def _start_server (
359
- status_file : str ,
370
+ status_file : str | Path | trio . Path ,
360
371
* ,
361
372
flags : list [str ],
362
373
daemon_timeout : int | None = None ,
363
374
allow_sources : bool = False ,
364
- log_file : str | None = None ,
375
+ log_file : str | Path | trio . Path | None = None ,
365
376
) -> bool :
366
377
"""Start the server and wait for it. Return False if error starting."""
367
378
start_options = _process_start_options (flags , allow_sources )
368
379
if (
369
380
_daemonize (
370
381
start_options ,
371
- status_file ,
382
+ str ( status_file ) ,
372
383
timeout = daemon_timeout ,
373
- log_file = log_file ,
384
+ log_file = str_maintain_none ( log_file ) ,
374
385
)
375
386
!= 0
376
387
):
@@ -379,12 +390,12 @@ async def _start_server(
379
390
380
391
381
392
async def restart (
382
- status_file : str ,
393
+ status_file : str | Path | trio . Path ,
383
394
* ,
384
395
flags : list [str ],
385
396
daemon_timeout : int | None = 0 ,
386
397
allow_sources : bool = False ,
387
- log_file : str | None = None ,
398
+ log_file : str | Path | trio . Path | None = None ,
388
399
) -> bool :
389
400
"""Restart daemon (it may or may not be running; but not hanging).
390
401
@@ -406,18 +417,18 @@ async def restart(
406
417
407
418
408
419
async def start (
409
- status_file : str ,
420
+ status_file : str | Path | trio . Path ,
410
421
* ,
411
422
flags : list [str ],
412
423
daemon_timeout : int = 0 ,
413
424
allow_sources : bool = False ,
414
- log_file : str | None = None ,
425
+ log_file : str | Path | trio . Path | None = None ,
415
426
) -> bool :
416
427
"""Start daemon if not already running.
417
428
418
429
Returns False if error starting / already running.
419
430
"""
420
- if not is_running (status_file ):
431
+ if not await is_running (status_file ):
421
432
# Bad or missing status file or dead process; good to start.
422
433
return await _start_server (
423
434
status_file ,
@@ -430,7 +441,7 @@ async def start(
430
441
431
442
432
443
async def status (
433
- status_file : str ,
444
+ status_file : str | Path | trio . Path ,
434
445
* ,
435
446
timeout : int = 5 , # noqa: ASYNC109
436
447
fswatcher_dump_file : str | None = None ,
@@ -445,12 +456,12 @@ async def status(
445
456
446
457
447
458
async def run (
448
- status_file : str ,
459
+ status_file : str | Path | trio . Path ,
449
460
* ,
450
461
flags : list [str ],
451
462
timeout : int | None = None , # noqa: ASYNC109
452
463
daemon_timeout : int = 0 ,
453
- log_file : str | None = None ,
464
+ log_file : str | Path | trio . Path | None = None ,
454
465
export_types : bool = False ,
455
466
) -> Response :
456
467
"""Do a check, starting (or restarting) the daemon as necessary.
@@ -495,9 +506,9 @@ async def run(
495
506
return response
496
507
497
508
498
- def kill (status_file : str ) -> bool :
509
+ async def kill (status_file : str | Path | trio . Path ) -> bool :
499
510
"""Kill daemon process with SIGKILL. Return True if killed."""
500
- pid = get_status (status_file )[0 ]
511
+ pid = ( await get_status (status_file ) )[0 ]
501
512
try :
502
513
_kill (pid )
503
514
except OSError as ex :
@@ -507,7 +518,7 @@ def kill(status_file: str) -> bool:
507
518
508
519
509
520
async def check (
510
- status_file : str ,
521
+ status_file : str | Path | trio . Path ,
511
522
files : Sequence [str ],
512
523
* ,
513
524
timeout : int | None = None , # noqa: ASYNC109
@@ -524,7 +535,7 @@ async def check(
524
535
525
536
526
537
async def recheck (
527
- status_file : str ,
538
+ status_file : str | Path | trio . Path ,
528
539
export_types : bool ,
529
540
* ,
530
541
timeout : int | None = None , # noqa: ASYNC109
@@ -562,7 +573,7 @@ async def recheck(
562
573
563
574
564
575
async def inspect (
565
- status_file : str ,
576
+ status_file : str | Path | trio . Path ,
566
577
location : str , # line:col
567
578
show : str = "type" , # type, attrs, definition
568
579
* ,
@@ -593,7 +604,7 @@ async def inspect(
593
604
594
605
595
606
async def suggest (
596
- status_file : str ,
607
+ status_file : str | Path | trio . Path ,
597
608
function : str ,
598
609
do_json : bool ,
599
610
* ,
@@ -622,7 +633,7 @@ async def suggest(
622
633
623
634
624
635
async def hang (
625
- status_file : str ,
636
+ status_file : str | Path | trio . Path ,
626
637
* ,
627
638
timeout : int = 1 , # noqa: ASYNC109
628
639
) -> Response :
@@ -633,10 +644,10 @@ async def hang(
633
644
634
645
635
646
def do_daemon (
636
- status_file : str ,
647
+ status_file : str | Path | trio . Path ,
637
648
flags : list [str ],
638
649
daemon_timeout : int | None = None ,
639
650
) -> None :
640
651
"""Serve requests in the foreground."""
641
652
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 ()
0 commit comments