77
88from __future__ import annotations
99
10+ import contextlib
1011import logging
1112import os
13+ import pathlib
1214import re
1315import shlex
1416import shutil
4143_RUST_SERVER_CONFIG : dict [tuple [str | None , str | None , int | None ], set [str ]] = {}
4244
4345
46+ def _resolve_rust_socket_path (socket_path : str | None , socket_name : str | None ) -> str :
47+ if socket_path :
48+ return socket_path
49+ uid = os .geteuid ()
50+ name = socket_name or "default"
51+ base = (
52+ os .getenv ("TMUX_TMPDIR" )
53+ or os .getenv ("XDG_RUNTIME_DIR" )
54+ or f"/run/user/{ uid } "
55+ or "/tmp"
56+ )
57+ base_path = pathlib .Path (base )
58+ socket_dir = base_path / f"tmux-{ uid } "
59+ socket_dir .mkdir (parents = True , exist_ok = True )
60+ with contextlib .suppress (OSError ):
61+ socket_dir .chmod (0o700 )
62+ return str (socket_dir / name )
63+
64+
65+ def _rust_run_with_config (
66+ socket_path : str | None ,
67+ socket_name : str | None ,
68+ config_file : str ,
69+ cmd_parts : list [str ],
70+ cmd_list : list [str ],
71+ ) -> tuple [list [str ], list [str ], int , list [str ]]:
72+ tmux_bin = shutil .which ("tmux" )
73+ if not tmux_bin :
74+ raise exc .TmuxCommandNotFound
75+ resolved_socket = _resolve_rust_socket_path (socket_path , socket_name )
76+ process = subprocess .Popen (
77+ [tmux_bin , "-S" , resolved_socket , "-f" , config_file , * cmd_parts ],
78+ stdout = subprocess .PIPE ,
79+ stderr = subprocess .PIPE ,
80+ text = True ,
81+ errors = "backslashreplace" ,
82+ )
83+ stdout_raw , stderr_raw = process .communicate ()
84+ stdout_lines = stdout_raw .split ("\n " ) if stdout_raw else []
85+ while stdout_lines and stdout_lines [- 1 ] == "" :
86+ stdout_lines .pop ()
87+ stderr_lines = list (filter (None , stderr_raw .split ("\n " ))) if stderr_raw else []
88+ if "has-session" in cmd_list and stderr_lines and not stdout_lines :
89+ stdout_lines = [stderr_lines [0 ]]
90+ return stdout_lines , stderr_lines , process .returncode , cmd_list
91+
92+
4493def _parse_tmux_args (args : tuple [t .Any , ...]) -> tuple [
4594 str | None , str | None , str | None , int | None , list [str ]
4695]:
@@ -102,7 +151,6 @@ def _rust_server(
102151 socket_name : str | None ,
103152 socket_path : str | None ,
104153 colors : int | None ,
105- config_file : str | None ,
106154) -> t .Any :
107155 key = (socket_name , socket_path , colors )
108156 server = _RUST_SERVER_CACHE .get (key )
@@ -121,17 +169,12 @@ def _rust_server(
121169 _RUST_SERVER_CACHE [key ] = server
122170 _RUST_SERVER_CONFIG [key ] = set ()
123171
124- if config_file :
125- loaded = _RUST_SERVER_CONFIG .setdefault (key , set ())
126- if config_file not in loaded :
127- quoted = shlex .quote (config_file )
128- server .cmd (f"source-file { quoted } " )
129- loaded .add (config_file )
130-
131172 return server
132173
133174
134- def _rust_cmd_result (args : tuple [t .Any , ...]) -> tuple [list [str ], list [str ], int , list [str ]]:
175+ def _rust_cmd_result (
176+ args : tuple [t .Any , ...],
177+ ) -> tuple [list [str ], list [str ], int , list [str ]]:
135178 socket_name , socket_path , config_file , colors , cmd_parts = _parse_tmux_args (args )
136179 cmd_list = [str (c ) for c in args ]
137180 if not cmd_parts :
@@ -159,18 +202,53 @@ def _rust_cmd_result(args: tuple[t.Any, ...]) -> tuple[list[str], list[str], int
159202 cmd_parts = ["-f" , config_file , * cmd_parts ]
160203 config_file = None
161204
162- server = _rust_server (socket_name , socket_path , colors , config_file )
205+ server = _rust_server (socket_name , socket_path , colors )
206+ key = (socket_name , socket_path , colors )
207+ if config_file :
208+ loaded = _RUST_SERVER_CONFIG .setdefault (key , set ())
209+ if config_file not in loaded :
210+ if not server .is_alive ():
211+ stdout_lines , stderr_lines , exit_code , cmd_args = _rust_run_with_config (
212+ socket_path ,
213+ socket_name ,
214+ config_file ,
215+ cmd_parts ,
216+ cmd_list ,
217+ )
218+ if exit_code == 0 :
219+ loaded .add (config_file )
220+ return stdout_lines , stderr_lines , exit_code , cmd_args
221+ quoted = shlex .quote (config_file )
222+ try :
223+ server .cmd (f"source-file { quoted } " )
224+ except Exception as err :
225+ message = str (err )
226+ error_stdout : list [str ] = []
227+ error_stderr = [message ] if message else []
228+ if "has-session" in cmd_list and error_stderr and not error_stdout :
229+ error_stdout = [error_stderr [0 ]]
230+ return error_stdout , error_stderr , 1 , cmd_list
231+ loaded .add (config_file )
163232
164- cmd = " " .join (shlex .quote (part ) for part in cmd_parts )
165- result = server .cmd (cmd )
166- stdout = result .stdout .split ("\n " ) if result .stdout else []
167- while stdout and stdout [- 1 ] == "" :
168- stdout .pop ()
233+ cmd_line = " " .join (shlex .quote (part ) for part in cmd_parts )
234+ try :
235+ result = server .cmd (cmd_line )
236+ except Exception as err :
237+ message = str (err )
238+ error_stdout_lines : list [str ] = []
239+ error_stderr_lines = [message ] if message else []
240+ if "has-session" in cmd_list and error_stderr_lines and not error_stdout_lines :
241+ error_stdout_lines = [error_stderr_lines [0 ]]
242+ return error_stdout_lines , error_stderr_lines , 1 , cmd_list
243+
244+ stdout_lines = result .stdout .split ("\n " ) if result .stdout else []
245+ while stdout_lines and stdout_lines [- 1 ] == "" :
246+ stdout_lines .pop ()
169247 stderr_raw = getattr (result , "exit_message" , None ) or ""
170- stderr = list (filter (None , stderr_raw .split ("\n " ))) if stderr_raw else []
171- if "has-session" in cmd_list and stderr and not stdout :
172- stdout = [stderr [0 ]]
173- return stdout , stderr , result .exit_code , cmd_list
248+ stderr_lines = list (filter (None , stderr_raw .split ("\n " ))) if stderr_raw else []
249+ if "has-session" in cmd_list and stderr_lines and not stdout_lines :
250+ stdout_lines = [stderr_lines [0 ]]
251+ return stdout_lines , stderr_lines , result .exit_code , cmd_list
174252
175253
176254class CmdProtocol (t .Protocol ):
@@ -413,20 +491,20 @@ def __init__(self, *args: t.Any) -> None:
413491 text = True ,
414492 errors = "backslashreplace" ,
415493 )
416- stdout , stderr = self .process .communicate ()
494+ stdout_text , stderr_text = self .process .communicate ()
417495 returncode = self .process .returncode
418496 except Exception :
419497 logger .exception (f"Exception for { subprocess .list2cmdline (cmd )} " )
420498 raise
421499
422500 self .returncode = returncode
423501
424- stdout_split = stdout .split ("\n " )
502+ stdout_split = stdout_text .split ("\n " )
425503 # remove trailing newlines from stdout
426504 while stdout_split and stdout_split [- 1 ] == "" :
427505 stdout_split .pop ()
428506
429- stderr_split = stderr .split ("\n " )
507+ stderr_split = stderr_text .split ("\n " )
430508 self .stderr = list (filter (None , stderr_split )) # filter empty values
431509
432510 if "has-session" in cmd and len (self .stderr ) and not stdout_split :
0 commit comments