Skip to content

Commit 15e933f

Browse files
committed
run_before_script: Use chunk-based output
1 parent 001b85c commit 15e933f

File tree

1 file changed

+68
-1
lines changed

1 file changed

+68
-1
lines changed

src/tmuxp/util.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
PY2 = sys.version_info[0] == 2
2525

2626

27-
def run_before_script(
27+
def run_before_script2(
2828
script_file: str | pathlib.Path,
2929
cwd: pathlib.Path | None = None,
3030
) -> int:
@@ -96,6 +96,73 @@ def run_before_script(
9696
return return_code
9797

9898

99+
def run_before_script(
100+
script_file: str | pathlib.Path,
101+
cwd: pathlib.Path | None = None,
102+
) -> int:
103+
script_cmd = shlex.split(str(script_file))
104+
105+
try:
106+
proc = subprocess.Popen(
107+
script_cmd,
108+
cwd=cwd,
109+
stdout=subprocess.PIPE,
110+
stderr=subprocess.PIPE,
111+
text=True, # still decode to str
112+
errors="backslashreplace",
113+
bufsize=0, # unbuffered in Python, best effort
114+
)
115+
except FileNotFoundError as e:
116+
raise exc.BeforeLoadScriptNotExists(
117+
e,
118+
os.path.abspath(script_file), # NOQA: PTH100
119+
) from e
120+
121+
out_buffer = []
122+
err_buffer = []
123+
is_out_tty = sys.stdout.isatty()
124+
is_err_tty = sys.stderr.isatty()
125+
126+
# We'll read in chunks from both stdout and stderr in a loop.
127+
# This example uses a single loop that tries to read both streams
128+
# repeatedly. For large outputs, consider threading or select().
129+
130+
while True:
131+
return_code = proc.poll()
132+
133+
# Read some data from stdout (if available)
134+
chunk_out = proc.stdout.read(1024) if proc.stdout else ""
135+
if chunk_out:
136+
out_buffer.append(chunk_out)
137+
if is_out_tty:
138+
sys.stdout.write(chunk_out)
139+
sys.stdout.flush()
140+
141+
# Read some data from stderr (if available)
142+
chunk_err = proc.stderr.read(1024) if proc.stderr else ""
143+
if chunk_err:
144+
err_buffer.append(chunk_err)
145+
if is_err_tty:
146+
sys.stderr.write(chunk_err)
147+
sys.stderr.flush()
148+
149+
if return_code is not None and not chunk_out and not chunk_err:
150+
# No more data from pipes, process ended
151+
break
152+
153+
return_code = proc.wait()
154+
155+
if return_code != 0:
156+
stderr_str = "".join(err_buffer).strip()
157+
raise exc.BeforeLoadScriptError(
158+
return_code,
159+
os.path.abspath(script_file), # NOQA: PTH100
160+
stderr_str,
161+
)
162+
163+
return return_code
164+
165+
99166
def oh_my_zsh_auto_title() -> None:
100167
"""Give warning and offer to fix ``DISABLE_AUTO_TITLE``.
101168

0 commit comments

Comments
 (0)