12
12
from inspect import isawaitable
13
13
from json import loads as _json_loads
14
14
from pathlib import Path
15
- from typing import List , Set , Union
15
+ from typing import List , Literal , Optional , Set , Tuple , Union
16
16
17
17
from aiohttp import ClientSession
18
18
from morebuiltins .request import req
@@ -171,7 +171,11 @@ def __init__(
171
171
before_startup = None ,
172
172
after_shutdown = None ,
173
173
clear_after_shutdown = False ,
174
- popen_kwargs : dict = None ,
174
+ popen_kwargs : Optional [dict ] = None ,
175
+ stdout_stderr : Tuple [
176
+ Literal ["" , "/dev/null" , "stdout.log" ],
177
+ Literal ["" , "/dev/null" , "stderr.log" , "stdout.log" ],
178
+ ] = ("stdout.log" , "stdout.log" ),
175
179
):
176
180
if debug :
177
181
logger .setLevel ("DEBUG" )
@@ -192,7 +196,7 @@ def __init__(
192
196
self .headless = headless
193
197
self .proxy = proxy
194
198
self .disable_image = disable_image
195
- self .user_data_dir = user_data_dir
199
+ self .user_data_dir : Optional [ Path ] = user_data_dir
196
200
self .start_url = start_url
197
201
self .extra_config = extra_config
198
202
self .proc_check_interval = proc_check_interval
@@ -206,6 +210,8 @@ def __init__(
206
210
self .DEFAULT_POPEN_ARGS if popen_kwargs is None else popen_kwargs
207
211
)
208
212
self ._use_port_dir = False
213
+ self .stdout_stderr = stdout_stderr
214
+ self .opened_files = [None , None ]
209
215
self .init ()
210
216
211
217
@classmethod
@@ -453,18 +459,57 @@ def cmd(self):
453
459
args .append (self .start_url )
454
460
return args
455
461
456
- def get_cmd_args (self ):
462
+ @property
463
+ def cmd_string (self ):
457
464
# list2cmdline for linux use args list failed...
458
- cmd_string = subprocess .list2cmdline (self .cmd )
459
- logger .debug (f"running with: { cmd_string } " )
460
- kwargs = {"args" : cmd_string , "shell" : True }
461
- if not self .debug :
462
- kwargs ["stdout" ] = subprocess .DEVNULL
463
- kwargs ["stderr" ] = subprocess .DEVNULL
465
+ return subprocess .list2cmdline (self .cmd )
466
+
467
+ def get_cmd_args (self ):
468
+ kwargs = {"args" : self .cmd_string , "shell" : True }
469
+ msg = f"running with: { kwargs ['args' ]} ."
470
+ logger .debug (msg )
471
+ # kwargs["stdout"] = subprocess.DEVNULL
472
+ # kwargs["stderr"] = subprocess.DEVNULL
464
473
kwargs .update (self .popen_kwargs )
474
+ if self .user_data_dir and "text" not in kwargs :
475
+ stdout_path , stderr_path = self .stdout_stderr
476
+ if "stdout" not in kwargs and stdout_path :
477
+ if stdout_path == "/dev/null" :
478
+ kwargs ["stdout" ] = subprocess .DEVNULL
479
+ else :
480
+ need_reopen = (
481
+ not self .opened_files [0 ] or self .opened_files [0 ].closed
482
+ )
483
+ if need_reopen :
484
+ _path = self .user_data_dir / stdout_path
485
+ self .opened_files [0 ] = _path .open ("wb" )
486
+ kwargs ["stdout" ] = self .opened_files [0 ]
487
+ logger .debug (f"stdout_path -> { _path .absolute ().as_posix ()} " )
488
+ if "stderr" not in kwargs and stderr_path :
489
+ if stderr_path == "/dev/null" :
490
+ kwargs ["stderr" ] = subprocess .DEVNULL
491
+ elif stdout_path and stderr_path == stdout_path :
492
+ kwargs ["stderr" ] = subprocess .STDOUT
493
+ else :
494
+ need_reopen = (
495
+ not self .opened_files [1 ] or self .opened_files [1 ].closed
496
+ )
497
+ if need_reopen :
498
+ _path = self .user_data_dir / stderr_path
499
+ self .opened_files [1 ] = _path .open ("wb" )
500
+ kwargs ["stderr" ] = self .opened_files [1 ]
501
+ logger .debug (f"stderr_path -> { _path .absolute ().as_posix ()} " )
465
502
self .cmd_args = kwargs
466
503
return kwargs
467
504
505
+ def close_stdout_stderr (self ):
506
+ for f in self .opened_files :
507
+ try :
508
+ if f and not f .closed :
509
+ f .close ()
510
+ except Exception :
511
+ pass
512
+
468
513
def _start_chrome_process (self ):
469
514
self .chrome_proc_start_time = time .time ()
470
515
self .proc = subprocess .Popen (** self .get_cmd_args ())
@@ -660,6 +705,7 @@ def shutdown(self, reason=None):
660
705
if self .on_shutdown :
661
706
self .on_shutdown (self )
662
707
self .kill (True )
708
+ self .close_stdout_stderr ()
663
709
if self .after_shutdown :
664
710
self .after_shutdown (self )
665
711
if self .clear_after_shutdown :
@@ -755,7 +801,7 @@ async def main():
755
801
asyncio.run(main())
756
802
757
803
'''
758
- __doc__ = ChromeDaemon .__doc__ + _demo
804
+ __doc__ = ( ChromeDaemon .__doc__ or "" ) + _demo
759
805
760
806
def __init__ (
761
807
self ,
@@ -780,7 +826,11 @@ def __init__(
780
826
before_startup = None ,
781
827
after_shutdown = None ,
782
828
clear_after_shutdown = False ,
783
- popen_kwargs : dict = None ,
829
+ popen_kwargs : Optional [dict ] = None ,
830
+ stdout_stderr : Tuple [
831
+ Literal ["" , "/dev/null" , "stdout.log" ],
832
+ Literal ["" , "/dev/null" , "stderr.log" , "stdout.log" ],
833
+ ] = ("stdout.log" , "stdout.log" ),
784
834
):
785
835
super ().__init__ (
786
836
chrome_path = chrome_path ,
@@ -805,6 +855,7 @@ def __init__(
805
855
after_shutdown = after_shutdown ,
806
856
clear_after_shutdown = clear_after_shutdown ,
807
857
popen_kwargs = popen_kwargs ,
858
+ stdout_stderr = stdout_stderr ,
808
859
)
809
860
810
861
def init (self ):
@@ -987,6 +1038,7 @@ async def shutdown(self, reason=None):
987
1038
if self .on_shutdown :
988
1039
await ensure_awaitable (self .on_shutdown (self ))
989
1040
await async_run (self .kill , True )
1041
+ await async_run (self .close_stdout_stderr )
990
1042
if self .after_shutdown :
991
1043
await ensure_awaitable (self .after_shutdown (self ))
992
1044
if self .clear_after_shutdown :
0 commit comments