@@ -28,41 +28,71 @@ def run_before_script(
28
28
script_file : str | pathlib .Path ,
29
29
cwd : pathlib .Path | None = None ,
30
30
) -> int :
31
- """Execute a shell script, wraps :meth:`subprocess.check_call()` in a try/catch."""
32
- try :
33
- proc = subprocess .Popen (
34
- shlex .split (str (script_file )),
35
- stderr = subprocess .PIPE ,
36
- stdout = subprocess .PIPE ,
37
- cwd = cwd ,
38
- text = True ,
39
- errors = "backslashreplace" ,
40
- encoding = "utf-8" ,
31
+ """Execute shell script, ``tee``-ing output to both terminal (if TTY) and buffer."""
32
+ script_cmd = shlex .split (str (script_file ))
33
+ proc = subprocess .Popen (
34
+ script_cmd ,
35
+ cwd = cwd ,
36
+ stdout = subprocess .PIPE ,
37
+ stderr = subprocess .PIPE ,
38
+ text = True , # decode to str
39
+ errors = "backslashreplace" ,
40
+ )
41
+
42
+ out_buffer = []
43
+ err_buffer = []
44
+
45
+ # While process is running, read lines from stdout/stderr
46
+ # and write them to this process's stdout/stderr if isatty
47
+ is_out_tty = sys .stdout .isatty ()
48
+ is_err_tty = sys .stderr .isatty ()
49
+
50
+ # You can do a simple loop reading in real-time:
51
+ while True :
52
+ # Use .poll() to check if the child has exited
53
+ return_code = proc .poll ()
54
+
55
+ # Read one line from stdout, if available
56
+ if proc .stdout :
57
+ line_out = proc .stdout .readline ()
58
+ else :
59
+ line_out = ""
60
+
61
+ # Read one line from stderr, if available
62
+ if proc .stderr :
63
+ line_err = proc .stderr .readline ()
64
+ else :
65
+ line_err = ""
66
+
67
+ if line_out :
68
+ out_buffer .append (line_out )
69
+ if is_out_tty :
70
+ sys .stdout .write (line_out )
71
+ sys .stdout .flush ()
72
+
73
+ if line_err :
74
+ err_buffer .append (line_err )
75
+ if is_err_tty :
76
+ sys .stderr .write (line_err )
77
+ sys .stderr .flush ()
78
+
79
+ # If no more data from pipes and process ended, break
80
+ if not line_out and not line_err and return_code is not None :
81
+ break
82
+
83
+ # At this point, the process has finished
84
+ return_code = proc .wait ()
85
+
86
+ if return_code != 0 :
87
+ # Join captured stderr lines for your exception
88
+ stderr_str = "" .join (err_buffer ).strip ()
89
+ raise exc .BeforeLoadScriptError (
90
+ return_code ,
91
+ os .path .abspath (script_file ), # NOQA: PTH100
92
+ stderr_str ,
41
93
)
42
- if proc .stdout is not None :
43
- for line in iter (proc .stdout .readline , "" ):
44
- sys .stdout .write (line )
45
- proc .wait ()
46
-
47
- if proc .returncode and proc .stderr is not None :
48
- stderr = proc .stderr .read ()
49
- proc .stderr .close ()
50
- stderr_strlist = stderr .split ("\n " )
51
- stderr_str = "\n " .join (list (filter (None , stderr_strlist ))) # filter empty
52
-
53
- raise exc .BeforeLoadScriptError (
54
- proc .returncode ,
55
- os .path .abspath (script_file ), # NOQA: PTH100
56
- stderr_str ,
57
- )
58
- except OSError as e :
59
- if e .errno == 2 :
60
- raise exc .BeforeLoadScriptNotExists (
61
- e ,
62
- os .path .abspath (script_file ), # NOQA: PTH100
63
- ) from e
64
- raise
65
- return proc .returncode
94
+
95
+ return return_code
66
96
67
97
68
98
def oh_my_zsh_auto_title () -> None :
0 commit comments