44
44
is_win ,
45
45
)
46
46
import io
47
+ from _io import UnsupportedOperation
47
48
48
49
execute_kwargs = set (('istream' , 'with_keep_cwd' , 'with_extended_output' ,
49
50
'with_exceptions' , 'as_process' , 'stdout_as_string' ,
56
57
__all__ = ('Git' ,)
57
58
58
59
if is_win :
59
- WindowsError = OSError
60
+ WindowsError = OSError # @ReservedAssignment
60
61
61
62
if PY3 :
62
63
_bchr = bchr
@@ -72,7 +73,8 @@ def _bchr(c):
72
73
# Documentation
73
74
## @{
74
75
75
- def handle_process_output (process , stdout_handler , stderr_handler , finalizer ):
76
+ def handle_process_output (process , stdout_handler , stderr_handler , finalizer ,
77
+ decode_stdout = True , decode_stderr = True ):
76
78
"""Registers for notifications to lean that process output is ready to read, and dispatches lines to
77
79
the respective line handlers. We are able to handle carriage returns in case progress is sent by that
78
80
mean. For performance reasons, we only apply this to stderr.
@@ -82,8 +84,6 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer):
82
84
:param stdout_handler: f(stdout_line_string), or None
83
85
:param stderr_hanlder: f(stderr_line_string), or None
84
86
:param finalizer: f(proc) - wait for proc to finish"""
85
- fdmap = {process .stdout .fileno (): (stdout_handler , [b'' ]),
86
- process .stderr .fileno (): (stderr_handler , [b'' ])}
87
87
88
88
def _parse_lines_from_buffer (buf ):
89
89
line = b''
@@ -94,7 +94,7 @@ def _parse_lines_from_buffer(buf):
94
94
bi += 1
95
95
96
96
if char in (b'\r ' , b'\n ' ) and line :
97
- yield bi , line
97
+ yield bi , line + b' \n '
98
98
line = b''
99
99
else :
100
100
line += char
@@ -114,105 +114,111 @@ def _read_lines_from_fno(fno, last_buf_list):
114
114
# keep remainder
115
115
last_buf_list [0 ] = buf [bi :]
116
116
117
- def _dispatch_single_line (line , handler ):
118
- line = line .decode (defenc )
117
+ def _dispatch_single_line (line , handler , decode ):
118
+ if decode :
119
+ line = line .decode (defenc )
119
120
if line and handler :
120
121
handler (line )
121
122
# end dispatch helper
122
123
# end single line helper
123
124
124
- def _dispatch_lines (fno , handler , buf_list ):
125
+ def _dispatch_lines (fno , handler , buf_list , decode ):
125
126
lc = 0
126
127
for line in _read_lines_from_fno (fno , buf_list ):
127
- _dispatch_single_line (line , handler )
128
+ _dispatch_single_line (line , handler , decode )
128
129
lc += 1
129
130
# for each line
130
131
return lc
131
132
# end
132
133
133
- def _deplete_buffer (fno , handler , buf_list , wg = None ):
134
+ def _deplete_buffer (fno , handler , buf_list , decode ):
134
135
lc = 0
135
136
while True :
136
- line_count = _dispatch_lines (fno , handler , buf_list )
137
+ line_count = _dispatch_lines (fno , handler , buf_list , decode )
137
138
lc += line_count
138
139
if line_count == 0 :
139
140
break
140
141
# end deplete buffer
141
142
142
143
if buf_list [0 ]:
143
- _dispatch_single_line (buf_list [0 ], handler )
144
+ _dispatch_single_line (buf_list [0 ], handler , decode )
144
145
lc += 1
145
146
# end
146
147
147
- if wg :
148
- wg .done ()
149
-
150
148
return lc
151
149
# end
152
150
153
- if hasattr (select , 'poll' ):
154
- # poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be
155
- # an issue for us, as it matters how many handles our own process has
156
- poll = select .poll ()
157
- READ_ONLY = select .POLLIN | select .POLLPRI | select .POLLHUP | select .POLLERR
158
- CLOSED = select .POLLHUP | select .POLLERR
159
-
160
- poll .register (process .stdout , READ_ONLY )
161
- poll .register (process .stderr , READ_ONLY )
162
-
163
- closed_streams = set ()
164
- while True :
165
- # no timeout
166
-
167
- try :
168
- poll_result = poll .poll ()
169
- except select .error as e :
170
- if e .args [0 ] == errno .EINTR :
171
- continue
172
- raise
173
- # end handle poll exception
174
-
175
- for fd , result in poll_result :
176
- if result & CLOSED :
177
- closed_streams .add (fd )
178
- else :
179
- _dispatch_lines (fd , * fdmap [fd ])
180
- # end handle closed stream
181
- # end for each poll-result tuple
182
-
183
- if len (closed_streams ) == len (fdmap ):
184
- break
185
- # end its all done
186
- # end endless loop
187
-
188
- # Depelete all remaining buffers
189
- for fno , (handler , buf_list ) in fdmap .items ():
190
- _deplete_buffer (fno , handler , buf_list )
191
- # end for each file handle
192
-
193
- for fno in fdmap .keys ():
194
- poll .unregister (fno )
195
- # end don't forget to unregister !
196
- else :
197
- # Oh ... probably we are on windows. select.select() can only handle sockets, we have files
151
+ try :
152
+ outfn = process .stdout .fileno ()
153
+ errfn = process .stderr .fileno ()
154
+ poll = select .poll () # @UndefinedVariable
155
+ except (UnsupportedOperation , AttributeError ):
156
+ # Oh ... probably we are on windows. or TC mockap provided for streams.
157
+ # Anyhow, select.select() can only handle sockets, we have files
198
158
# The only reliable way to do this now is to use threads and wait for both to finish
199
- def _handle_lines (fd , handler ):
159
+ def _handle_lines (fd , handler , decode ):
200
160
for line in fd :
201
- line = line .decode (defenc )
202
- if line and handler :
161
+ if handler :
162
+ if decode :
163
+ line = line .decode (defenc )
203
164
handler (line )
204
165
205
166
threads = []
206
- for fd , handler in zip ((process .stdout , process .stderr ),
207
- (stdout_handler , stderr_handler )):
208
- t = threading .Thread (target = _handle_lines , args = (fd , handler ))
167
+ for fd , handler , decode in zip ((process .stdout , process .stderr ),
168
+ (stdout_handler , stderr_handler ),
169
+ (decode_stdout , decode_stderr ),):
170
+ t = threading .Thread (target = _handle_lines , args = (fd , handler , decode ))
209
171
t .setDaemon (True )
210
172
t .start ()
211
173
threads .append (t )
212
174
213
175
for t in threads :
214
176
t .join ()
215
- # end
177
+ else :
178
+ # poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be
179
+ # an issue for us, as it matters how many handles our own process has
180
+ fdmap = {outfn : (stdout_handler , [b'' ], decode_stdout ),
181
+ errfn : (stderr_handler , [b'' ], decode_stderr )}
182
+
183
+ READ_ONLY = select .POLLIN | select .POLLPRI | select .POLLHUP | select .POLLERR # @UndefinedVariable
184
+ CLOSED = select .POLLHUP | select .POLLERR # @UndefinedVariable
185
+
186
+ poll .register (process .stdout , READ_ONLY )
187
+ poll .register (process .stderr , READ_ONLY )
188
+
189
+ closed_streams = set ()
190
+ while True :
191
+ # no timeout
192
+
193
+ try :
194
+ poll_result = poll .poll ()
195
+ except select .error as e :
196
+ if e .args [0 ] == errno .EINTR :
197
+ continue
198
+ raise
199
+ # end handle poll exception
200
+
201
+ for fd , result in poll_result :
202
+ if result & CLOSED :
203
+ closed_streams .add (fd )
204
+ else :
205
+ _dispatch_lines (fd , * fdmap [fd ])
206
+ # end handle closed stream
207
+ # end for each poll-result tuple
208
+
209
+ if len (closed_streams ) == len (fdmap ):
210
+ break
211
+ # end its all done
212
+ # end endless loop
213
+
214
+ # Depelete all remaining buffers
215
+ for fno , (handler , buf_list , decode ) in fdmap .items ():
216
+ _deplete_buffer (fno , handler , buf_list , decode )
217
+ # end for each file handle
218
+
219
+ for fno in fdmap .keys ():
220
+ poll .unregister (fno )
221
+ # end don't forget to unregister !
216
222
217
223
return finalizer (process )
218
224
@@ -458,6 +464,7 @@ def next(self):
458
464
line = self .readline ()
459
465
if not line :
460
466
raise StopIteration
467
+
461
468
return line
462
469
463
470
def __del__ (self ):
0 commit comments