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
8892 def _parse_lines_from_buffer (buf ):
8993 line = 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 ()
166179 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 ))
171186 t .setDaemon (True )
172187 t .start ()
173188 threads .append (t )
@@ -177,8 +192,8 @@ def _handle_lines(fd, handler, decode):
177192 else :
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
183198 READ_ONLY = select .POLLIN | select .POLLPRI | select .POLLHUP | select .POLLERR # @UndefinedVariable
184199 CLOSED = select .POLLHUP | select .POLLERR # @UndefinedVariable
@@ -334,7 +349,8 @@ def __del__(self):
334349 try :
335350 proc .terminate ()
336351 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 )
338354 pass # ignore error when process already died
339355 except AttributeError :
340356 # try windows
@@ -638,12 +654,12 @@ def execute(self, command,
638654 env .update (self ._environment )
639655
640656 if is_win :
641- cmd_not_found_exception = WindowsError
657+ cmd_not_found_exception = OSError
642658 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.' )
644660 else :
645661 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
647663 else :
648664 cmd_not_found_exception = OSError
649665 # end handle
@@ -663,7 +679,7 @@ def execute(self, command,
663679 ** subprocess_kwargs
664680 )
665681 except cmd_not_found_exception as err :
666- raise GitCommandNotFound ('%s: %s' % ( command [ 0 ] , err ) )
682+ raise GitCommandNotFound (command , err )
667683
668684 if as_process :
669685 return self .AutoInterrupt (proc , command )
0 commit comments