4545)
4646import io
4747from _io import UnsupportedOperation
48+ from git .exc import CommandError
4849
4950execute_kwargs = set (('istream' ,'with_keep_cwd' ,'with_extended_output' ,
5051'with_exceptions' ,'as_process' ,'stdout_as_string' ,
5657
5758__all__ = ('Git' ,)
5859
59- if is_win :
60- WindowsError = OSError # @ReservedAssignment
61-
6260if PY3 :
6361_bchr = bchr
6462else :
@@ -73,17 +71,23 @@ def _bchr(c):
7371# Documentation
7472## @{
7573
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 ):
7875"""Registers for notifications to lean that process output is ready to read, and dispatches lines to
7976 the respective line handlers. We are able to handle carriage returns in case progress is sent by that
8077 mean. For performance reasons, we only apply this to stderr.
8178 This function returns once the finalizer returns
79+
8280 :return: result of finalizer
8381 :param process: subprocess.Popen instance
8482 :param stdout_handler: f(stdout_line_string), or None
8583 :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+ """
8791
8892def _parse_lines_from_buffer (buf ):
8993line = b''
@@ -156,18 +160,29 @@ def _deplete_buffer(fno, handler, buf_list, decode):
156160# Oh ... probably we are on windows. or TC mockap provided for streams.
157161# Anyhow, select.select() can only handle sockets, we have files
158162# 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 ()
166179threads = []
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 ))
171186t .setDaemon (True )
172187t .start ()
173188threads .append (t )
@@ -177,8 +192,8 @@ def _handle_lines(fd, handler, decode):
177192else :
178193# poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be
179194# 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 )}
182197
183198READ_ONLY = select .POLLIN | select .POLLPRI | select .POLLHUP | select .POLLERR # @UndefinedVariable
184199CLOSED = select .POLLHUP | select .POLLERR # @UndefinedVariable
@@ -334,7 +349,8 @@ def __del__(self):
334349try :
335350proc .terminate ()
336351proc .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 )
338354pass # ignore error when process already died
339355except AttributeError :
340356# try windows
@@ -638,12 +654,12 @@ def execute(self, command,
638654env .update (self ._environment )
639655
640656if is_win :
641- cmd_not_found_exception = WindowsError
657+ cmd_not_found_exception = OSError
642658if 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.' )
644660else :
645661if sys .version_info [0 ]> 2 :
646- cmd_not_found_exception = FileNotFoundError # NOQA #this is defined, but flake8doesn't know
662+ cmd_not_found_exception = FileNotFoundError # NOQA #exists, flake8unknown @UndefinedVariable
647663else :
648664cmd_not_found_exception = OSError
649665# end handle
@@ -663,7 +679,7 @@ def execute(self, command,
663679** subprocess_kwargs
664680 )
665681except cmd_not_found_exception as err :
666- raise GitCommandNotFound ('%s: %s' % ( command [ 0 ] ,err ) )
682+ raise GitCommandNotFound (command ,err )
667683
668684if as_process :
669685return self .AutoInterrupt (proc ,command )