45
45
)
46
46
import io
47
47
from _io import UnsupportedOperation
48
+ from git .exc import CommandError
48
49
49
50
execute_kwargs = set (('istream' , 'with_keep_cwd' , 'with_extended_output' ,
50
51
'with_exceptions' , 'as_process' , 'stdout_as_string' ,
56
57
57
58
__all__ = ('Git' ,)
58
59
59
- if is_win :
60
- WindowsError = OSError # @ReservedAssignment
61
-
62
60
if PY3 :
63
61
_bchr = bchr
64
62
else :
@@ -73,17 +71,23 @@ def _bchr(c):
73
71
# Documentation
74
72
## @{
75
73
76
- def handle_process_output (process , stdout_handler , stderr_handler , finalizer ,
77
- decode_stdout = True , decode_stderr = True ):
74
+ def handle_process_output (process , stdout_handler , stderr_handler , finalizer , decode_streams = True ):
78
75
"""Registers for notifications to lean that process output is ready to read, and dispatches lines to
79
76
the respective line handlers. We are able to handle carriage returns in case progress is sent by that
80
77
mean. For performance reasons, we only apply this to stderr.
81
78
This function returns once the finalizer returns
79
+
82
80
:return: result of finalizer
83
81
:param process: subprocess.Popen instance
84
82
:param stdout_handler: f(stdout_line_string), or None
85
83
:param stderr_hanlder: f(stderr_line_string), or None
86
- :param finalizer: f(proc) - wait for proc to finish"""
84
+ :param finalizer: f(proc) - wait for proc to finish
85
+ :param decode_streams:
86
+ Assume stdout/stderr streams are binary and decode them vefore pushing \
87
+ their contents to handlers.
88
+ Set it to False if `universal_newline == True` (then streams are in text-mode)
89
+ or if decoding must happen later (i.e. for Diffs).
90
+ """
87
91
88
92
def _parse_lines_from_buffer (buf ):
89
93
line = b''
@@ -156,18 +160,29 @@ def _deplete_buffer(fno, handler, buf_list, decode):
156
160
# Oh ... probably we are on windows. or TC mockap provided for streams.
157
161
# Anyhow, select.select() can only handle sockets, we have files
158
162
# The only reliable way to do this now is to use threads and wait for both to finish
159
- def _handle_lines (fd , handler , decode ):
160
- for line in fd :
161
- if handler :
162
- if decode :
163
- line = line .decode (defenc )
164
- handler (line )
165
-
163
+ def pump_stream (cmdline , name , stream , is_decode , handler ):
164
+ try :
165
+ for line in stream :
166
+ if handler :
167
+ if is_decode :
168
+ line = line .decode (defenc )
169
+ handler (line )
170
+ except Exception as ex :
171
+ log .error ("Pumping %r of cmd(%s) failed due to: %r" , name , cmdline , ex )
172
+ raise CommandError (['<%s-pump>' % name ] + cmdline , ex )
173
+ finally :
174
+ stream .close ()
175
+
176
+ cmdline = getattr (process , 'args' , '' ) # PY3+ only
177
+ if not isinstance (cmdline , (tuple , list )):
178
+ cmdline = cmdline .split ()
166
179
threads = []
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 ))
180
+ for name , stream , handler in (
181
+ ('stdout' , process .stdout , stdout_handler ),
182
+ ('stderr' , process .stderr , stderr_handler ),
183
+ ):
184
+ t = threading .Thread (target = pump_stream ,
185
+ args = (cmdline , name , stream , decode_streams , handler ))
171
186
t .setDaemon (True )
172
187
t .start ()
173
188
threads .append (t )
@@ -177,8 +192,8 @@ def _handle_lines(fd, handler, decode):
177
192
else :
178
193
# poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be
179
194
# 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 )}
195
+ fdmap = {outfn : (stdout_handler , [b'' ], decode_streams ),
196
+ errfn : (stderr_handler , [b'' ], decode_streams )}
182
197
183
198
READ_ONLY = select .POLLIN | select .POLLPRI | select .POLLHUP | select .POLLERR # @UndefinedVariable
184
199
CLOSED = select .POLLHUP | select .POLLERR # @UndefinedVariable
@@ -334,7 +349,8 @@ def __del__(self):
334
349
try :
335
350
proc .terminate ()
336
351
proc .wait () # ensure process goes away
337
- except (OSError , WindowsError ):
352
+ except OSError as ex :
353
+ log .info ("Ignored error after process has dies: %r" , ex )
338
354
pass # ignore error when process already died
339
355
except AttributeError :
340
356
# try windows
@@ -638,12 +654,12 @@ def execute(self, command,
638
654
env .update (self ._environment )
639
655
640
656
if is_win :
641
- cmd_not_found_exception = WindowsError
657
+ cmd_not_found_exception = OSError
642
658
if kill_after_timeout :
643
- raise GitCommandError ('"kill_after_timeout" feature is not supported on Windows.' )
659
+ raise GitCommandError (command , '"kill_after_timeout" feature is not supported on Windows.' )
644
660
else :
645
661
if sys .version_info [0 ] > 2 :
646
- cmd_not_found_exception = FileNotFoundError # NOQA # this is defined, but flake8 doesn't know
662
+ cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable
647
663
else :
648
664
cmd_not_found_exception = OSError
649
665
# end handle
@@ -663,7 +679,7 @@ def execute(self, command,
663
679
** subprocess_kwargs
664
680
)
665
681
except cmd_not_found_exception as err :
666
- raise GitCommandNotFound ('%s: %s' % ( command [ 0 ] , err ) )
682
+ raise GitCommandNotFound (command , err )
667
683
668
684
if as_process :
669
685
return self .AutoInterrupt (proc , command )
0 commit comments