1919import threading
2020from collections import OrderedDict
2121from textwrap import dedent
22- from typing import Any , Dict , List , Optional
22+ import warnings
2323
2424from git .compat import (
2525 defenc ,
2929 is_win ,
3030)
3131from git .exc import CommandError
32- from git .util import is_cygwin_git , cygpath , expand_path
32+ from git .util import is_cygwin_git , cygpath , expand_path , remove_password_if_present
3333
3434from .exc import (
3535 GitCommandError ,
4040 stream_copy ,
4141)
4242
43- from .types import PathLike
44-
4543execute_kwargs = {'istream' , 'with_extended_output' ,
4644 'with_exceptions' , 'as_process' , 'stdout_as_string' ,
4745 'output_stream' , 'with_stdout' , 'kill_after_timeout' ,
@@ -85,8 +83,8 @@ def pump_stream(cmdline, name, stream, is_decode, handler):
8583 line = line .decode (defenc )
8684 handler (line )
8785 except Exception as ex :
88- log .error ("Pumping %r of cmd(%s) failed due to: %r" , name , cmdline , ex )
89- raise CommandError (['<%s-pump>' % name ] + cmdline , ex ) from ex
86+ log .error ("Pumping %r of cmd(%s) failed due to: %r" , name , remove_password_if_present ( cmdline ) , ex )
87+ raise CommandError (['<%s-pump>' % name ] + remove_password_if_present ( cmdline ) , ex ) from ex
9088 finally :
9189 stream .close ()
9290
@@ -105,7 +103,7 @@ def pump_stream(cmdline, name, stream, is_decode, handler):
105103 for name , stream , handler in pumps :
106104 t = threading .Thread (target = pump_stream ,
107105 args = (cmdline , name , stream , decode_streams , handler ))
108- t .setDaemon ( True )
106+ t .daemon = True
109107 t .start ()
110108 threads .append (t )
111109
@@ -140,7 +138,7 @@ def dict_to_slots_and__excluded_are_none(self, d, excluded=()):
140138
141139## CREATE_NEW_PROCESS_GROUP is needed to allow killing it afterwards,
142140# see https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal
143- PROC_CREATIONFLAGS = (CREATE_NO_WINDOW | subprocess .CREATE_NEW_PROCESS_GROUP
141+ PROC_CREATIONFLAGS = (CREATE_NO_WINDOW | subprocess .CREATE_NEW_PROCESS_GROUP # type: ignore[attr-defined]
144142 if is_win else 0 )
145143
146144
@@ -212,7 +210,7 @@ def refresh(cls, path=None):
212210 # - a GitCommandNotFound error is spawned by ourselves
213211 # - a PermissionError is spawned if the git executable provided
214212 # cannot be executed for whatever reason
215-
213+
216214 has_git = False
217215 try :
218216 cls ().version ()
@@ -408,7 +406,7 @@ def read_all_from_possibly_closed_stream(stream):
408406 if status != 0 :
409407 errstr = read_all_from_possibly_closed_stream (self .proc .stderr )
410408 log .debug ('AutoInterrupt wait stderr: %r' % (errstr ,))
411- raise GitCommandError (self .args , status , errstr )
409+ raise GitCommandError (remove_password_if_present ( self .args ) , status , errstr )
412410 # END status handling
413411 return status
414412 # END auto interrupt
@@ -500,7 +498,7 @@ def readlines(self, size=-1):
500498 # skipcq: PYL-E0301
501499 def __iter__ (self ):
502500 return self
503-
501+
504502 def __next__ (self ):
505503 return self .next ()
506504
@@ -519,7 +517,7 @@ def __del__(self):
519517 self ._stream .read (bytes_left + 1 )
520518 # END handle incomplete read
521519
522- def __init__ (self , working_dir : Optional [ PathLike ] = None ) -> None :
520+ def __init__ (self , working_dir = None ):
523521 """Initialize this instance with:
524522
525523 :param working_dir:
@@ -528,12 +526,12 @@ def __init__(self, working_dir: Optional[PathLike]=None) -> None:
528526 It is meant to be the working tree directory if available, or the
529527 .git directory in case of bare repositories."""
530528 super (Git , self ).__init__ ()
531- self ._working_dir = expand_path (working_dir ) if working_dir is not None else None
529+ self ._working_dir = expand_path (working_dir )
532530 self ._git_options = ()
533- self ._persistent_git_options = [] # type: List[str]
531+ self ._persistent_git_options = []
534532
535533 # Extra environment variables to pass to git commands
536- self ._environment = {} # type: Dict[str, Any]
534+ self ._environment = {}
537535
538536 # cached command slots
539537 self .cat_file_header = None
@@ -547,7 +545,7 @@ def __getattr__(self, name):
547545 return LazyMixin .__getattr__ (self , name )
548546 return lambda * args , ** kwargs : self ._call_process (name , * args , ** kwargs )
549547
550- def set_persistent_git_options (self , ** kwargs ) -> None :
548+ def set_persistent_git_options (self , ** kwargs ):
551549 """Specify command line options to the git executable
552550 for subsequent subcommand calls
553551
@@ -641,7 +639,7 @@ def execute(self, command,
641639
642640 :param env:
643641 A dictionary of environment variables to be passed to `subprocess.Popen`.
644-
642+
645643 :param max_chunk_size:
646644 Maximum number of bytes in one chunk of data passed to the output_stream in
647645 one invocation of write() method. If the given number is not positive then
@@ -685,8 +683,10 @@ def execute(self, command,
685683 :note:
686684 If you add additional keyword arguments to the signature of this method,
687685 you must update the execute_kwargs tuple housed in this module."""
686+ # Remove password for the command if present
687+ redacted_command = remove_password_if_present (command )
688688 if self .GIT_PYTHON_TRACE and (self .GIT_PYTHON_TRACE != 'full' or as_process ):
689- log .info (' ' .join (command ))
689+ log .info (' ' .join (redacted_command ))
690690
691691 # Allow the user to have the command executed in their working dir.
692692 cwd = self ._working_dir or os .getcwd ()
@@ -707,7 +707,7 @@ def execute(self, command,
707707 if is_win :
708708 cmd_not_found_exception = OSError
709709 if kill_after_timeout :
710- raise GitCommandError (command , '"kill_after_timeout" feature is not supported on Windows.' )
710+ raise GitCommandError (redacted_command , '"kill_after_timeout" feature is not supported on Windows.' )
711711 else :
712712 if sys .version_info [0 ] > 2 :
713713 cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable
@@ -722,7 +722,7 @@ def execute(self, command,
722722 if istream :
723723 istream_ok = "<valid stream>"
724724 log .debug ("Popen(%s, cwd=%s, universal_newlines=%s, shell=%s, istream=%s)" ,
725- command , cwd , universal_newlines , shell , istream_ok )
725+ redacted_command , cwd , universal_newlines , shell , istream_ok )
726726 try :
727727 proc = Popen (command ,
728728 env = env ,
@@ -738,7 +738,7 @@ def execute(self, command,
738738 ** subprocess_kwargs
739739 )
740740 except cmd_not_found_exception as err :
741- raise GitCommandNotFound (command , err ) from err
741+ raise GitCommandNotFound (redacted_command , err ) from err
742742
743743 if as_process :
744744 return self .AutoInterrupt (proc , command )
@@ -788,7 +788,7 @@ def _kill_process(pid):
788788 watchdog .cancel ()
789789 if kill_check .isSet ():
790790 stderr_value = ('Timeout: the command "%s" did not complete in %d '
791- 'secs.' % (" " .join (command ), kill_after_timeout ))
791+ 'secs.' % (" " .join (redacted_command ), kill_after_timeout ))
792792 if not universal_newlines :
793793 stderr_value = stderr_value .encode (defenc )
794794 # strip trailing "\n"
@@ -812,7 +812,7 @@ def _kill_process(pid):
812812 proc .stderr .close ()
813813
814814 if self .GIT_PYTHON_TRACE == 'full' :
815- cmdstr = " " .join (command )
815+ cmdstr = " " .join (redacted_command )
816816
817817 def as_text (stdout_value ):
818818 return not output_stream and safe_decode (stdout_value ) or '<OUTPUT_STREAM>'
@@ -828,7 +828,7 @@ def as_text(stdout_value):
828828 # END handle debug printing
829829
830830 if with_exceptions and status != 0 :
831- raise GitCommandError (command , status , stderr_value , stdout_value )
831+ raise GitCommandError (redacted_command , status , stderr_value , stdout_value )
832832
833833 if isinstance (stdout_value , bytes ) and stdout_as_string : # could also be output_stream
834834 stdout_value = safe_decode (stdout_value )
@@ -905,8 +905,14 @@ def transform_kwarg(self, name, value, split_single_char_options):
905905
906906 def transform_kwargs (self , split_single_char_options = True , ** kwargs ):
907907 """Transforms Python style kwargs into git command line options."""
908+ # Python 3.6 preserves the order of kwargs and thus has a stable
909+ # order. For older versions sort the kwargs by the key to get a stable
910+ # order.
911+ if sys .version_info [:2 ] < (3 , 6 ):
912+ kwargs = OrderedDict (sorted (kwargs .items (), key = lambda x : x [0 ]))
913+ warnings .warn ("Python 3.5 support is deprecated and will be removed 2021-09-05.\n " +
914+ "It does not preserve the order for key-word arguments and enforce lexical sorting instead." )
908915 args = []
909- kwargs = OrderedDict (sorted (kwargs .items (), key = lambda x : x [0 ]))
910916 for k , v in kwargs .items ():
911917 if isinstance (v , (list , tuple )):
912918 for value in v :
0 commit comments