From f573b6840509bf41be822ab7ed79e0a776005133 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 9 Feb 2014 21:09:00 +0100 Subject: tabs to 4 spaces - this won't make integrating the patches easier, but it's probably a good idea to go a little more pep8 (and fix sins of my youth ;) ) --- git/__init__.py | 32 +- git/cmd.py | 1062 ++++++++-------- git/config.py | 790 ++++++------ git/db.py | 94 +- git/diff.py | 620 +++++----- git/exc.py | 72 +- git/ext/gitdb | 2 +- git/index/base.py | 2232 +++++++++++++++++----------------- git/index/fun.py | 558 ++++----- git/index/typ.py | 292 ++--- git/index/util.py | 120 +- git/objects/base.py | 310 ++--- git/objects/blob.py | 36 +- git/objects/commit.py | 864 ++++++------- git/objects/fun.py | 364 +++--- git/objects/submodule/base.py | 1784 +++++++++++++-------------- git/objects/submodule/root.py | 586 ++++----- git/objects/submodule/util.py | 150 +-- git/objects/tag.py | 124 +- git/objects/tree.py | 490 ++++---- git/objects/util.py | 546 ++++----- git/refs/__init__.py | 2 +- git/refs/head.py | 450 +++---- git/refs/log.py | 544 ++++----- git/refs/reference.py | 216 ++-- git/refs/remote.py | 70 +- git/refs/symbolic.py | 1214 +++++++++--------- git/refs/tag.py | 164 +-- git/remote.py | 1228 +++++++++---------- git/repo/base.py | 1470 +++++++++++----------- git/repo/fun.py | 516 ++++---- git/test/lib/helper.py | 412 +++---- git/test/performance/lib.py | 92 +- git/test/performance/test_commit.py | 164 +-- git/test/performance/test_odb.py | 120 +- git/test/performance/test_streams.py | 224 ++-- git/test/performance/test_utils.py | 330 ++--- git/test/test_base.py | 162 +-- git/test/test_blob.py | 20 +- git/test/test_commit.py | 490 ++++---- git/test/test_db.py | 26 +- git/test/test_diff.py | 188 +-- git/test/test_fun.py | 470 +++---- git/test/test_git.py | 180 +-- git/test/test_index.py | 1302 ++++++++++---------- git/test/test_reflog.py | 178 +-- git/test/test_refs.py | 1048 ++++++++-------- git/test/test_remote.py | 952 +++++++-------- git/test/test_repo.py | 1164 +++++++++--------- git/test/test_submodule.py | 1082 ++++++++-------- git/test/test_tree.py | 260 ++-- git/test/test_util.py | 286 ++--- git/util.py | 1220 +++++++++---------- setup.py | 130 +- 54 files changed, 13751 insertions(+), 13751 deletions(-) mode change 100755 => 100644 setup.py diff --git a/git/__init__.py b/git/__init__.py index 0658c330..9ea81112 100644 --- a/git/__init__.py +++ b/git/__init__.py @@ -13,15 +13,15 @@ __version__ = 'git' #{ Initialization def _init_externals(): - """Initialize external projects by putting them into the path""" - sys.path.append(os.path.join(os.path.dirname(__file__), 'ext', 'gitdb')) - - try: - import gitdb - except ImportError: - raise ImportError("'gitdb' could not be found in your PYTHONPATH") - #END verify import - + """Initialize external projects by putting them into the path""" + sys.path.append(os.path.join(os.path.dirname(__file__), 'ext', 'gitdb')) + + try: + import gitdb + except ImportError: + raise ImportError("'gitdb' could not be found in your PYTHONPATH") + #END verify import + #} END initialization ################# @@ -41,14 +41,14 @@ from git.repo import Repo from git.remote import * from git.index import * from git.util import ( - LockFile, - BlockingLockFile, - Stats, - Actor - ) + LockFile, + BlockingLockFile, + Stats, + Actor + ) #} END imports __all__ = [ name for name, obj in locals().items() - if not (name.startswith('_') or inspect.ismodule(obj)) ] - + if not (name.startswith('_') or inspect.ismodule(obj)) ] + diff --git a/git/cmd.py b/git/cmd.py index 576a5300..2d4aa727 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -6,569 +6,569 @@ import os, sys from util import ( - LazyMixin, - stream_copy - ) + LazyMixin, + stream_copy + ) from exc import GitCommandError from subprocess import ( - call, - Popen, - PIPE - ) + call, + Popen, + PIPE + ) execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', - 'with_exceptions', 'as_process', - 'output_stream' ) + 'with_exceptions', 'as_process', + 'output_stream' ) __all__ = ('Git', ) def dashify(string): - return string.replace('_', '-') + return string.replace('_', '-') class Git(LazyMixin): - """ - The Git class manages communication with the Git binary. - - It provides a convenient interface to calling the Git binary, such as in:: - - g = Git( git_dir ) - g.init() # calls 'git init' program - rval = g.ls_files() # calls 'git ls-files' program - - ``Debugging`` - Set the GIT_PYTHON_TRACE environment variable print each invocation - of the command to stdout. - Set its value to 'full' to see details about the returned values. - """ - __slots__ = ("_working_dir", "cat_file_all", "cat_file_header", "_version_info") - - # CONFIGURATION - # The size in bytes read from stdout when copying git's output to another stream - max_chunk_size = 1024*64 - - git_exec_name = "git" # default that should work on linux and windows - git_exec_name_win = "git.cmd" # alternate command name, windows only - - # Enables debugging of GitPython's git commands - GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False) - - # Provide the full path to the git executable. Otherwise it assumes git is in the path - _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE" - GIT_PYTHON_GIT_EXECUTABLE = os.environ.get(_git_exec_env_var, git_exec_name) - - - class AutoInterrupt(object): - """Kill/Interrupt the stored process instance once this instance goes out of scope. It is - used to prevent processes piling up in case iterators stop reading. - Besides all attributes are wired through to the contained process object. - - The wait method was overridden to perform automatic status code checking - and possibly raise.""" - __slots__= ("proc", "args") - - def __init__(self, proc, args ): - self.proc = proc - self.args = args - - def __del__(self): - # did the process finish already so we have a return code ? - if self.proc.poll() is not None: - return - - # can be that nothing really exists anymore ... - if os is None: - return - - # try to kill it - try: - os.kill(self.proc.pid, 2) # interrupt signal - except AttributeError: - # try windows - # for some reason, providing None for stdout/stderr still prints something. This is why - # we simply use the shell and redirect to nul. Its slower than CreateProcess, question - # is whether we really want to see all these messages. Its annoying no matter what. - call(("TASKKILL /F /T /PID %s 2>nul 1>nul" % str(self.proc.pid)), shell=True) - # END exception handling - - def __getattr__(self, attr): - return getattr(self.proc, attr) - - def wait(self): - """Wait for the process and return its status code. - - :raise GitCommandError: if the return status is not 0""" - status = self.proc.wait() - if status != 0: - raise GitCommandError(self.args, status, self.proc.stderr.read()) - # END status handling - return status - # END auto interrupt - - class CatFileContentStream(object): - """Object representing a sized read-only stream returning the contents of - an object. - It behaves like a stream, but counts the data read and simulates an empty - stream once our sized content region is empty. - If not all data is read to the end of the objects's lifetime, we read the - rest to assure the underlying stream continues to work""" - - __slots__ = ('_stream', '_nbr', '_size') - - def __init__(self, size, stream): - self._stream = stream - self._size = size - self._nbr = 0 # num bytes read - - # special case: if the object is empty, has null bytes, get the - # final newline right away. - if size == 0: - stream.read(1) - # END handle empty streams - - def read(self, size=-1): - bytes_left = self._size - self._nbr - if bytes_left == 0: - return '' - if size > -1: - # assure we don't try to read past our limit - size = min(bytes_left, size) - else: - # they try to read all, make sure its not more than what remains - size = bytes_left - # END check early depletion - data = self._stream.read(size) - self._nbr += len(data) - - # check for depletion, read our final byte to make the stream usable by others - if self._size - self._nbr == 0: - self._stream.read(1) # final newline - # END finish reading - return data - - def readline(self, size=-1): - if self._nbr == self._size: - return '' - - # clamp size to lowest allowed value - bytes_left = self._size - self._nbr - if size > -1: - size = min(bytes_left, size) - else: - size = bytes_left - # END handle size - - data = self._stream.readline(size) - self._nbr += len(data) - - # handle final byte - if self._size - self._nbr == 0: - self._stream.read(1) - # END finish reading - - return data - - def readlines(self, size=-1): - if self._nbr == self._size: - return list() - - # leave all additional logic to our readline method, we just check the size - out = list() - nbr = 0 - while True: - line = self.readline() - if not line: - break - out.append(line) - if size > -1: - nbr += len(line) - if nbr > size: - break - # END handle size constraint - # END readline loop - return out - - def __iter__(self): - return self - - def next(self): - line = self.readline() - if not line: - raise StopIteration - return line - - def __del__(self): - bytes_left = self._size - self._nbr - if bytes_left: - # read and discard - seeking is impossible within a stream - # includes terminating newline - self._stream.read(bytes_left + 1) - # END handle incomplete read - - - def __init__(self, working_dir=None): - """Initialize this instance with: - - :param working_dir: - Git directory we should work in. If None, we always work in the current - directory as returned by os.getcwd(). - It is meant to be the working tree directory if available, or the - .git directory in case of bare repositories.""" - super(Git, self).__init__() - self._working_dir = working_dir - - # cached command slots - self.cat_file_header = None - self.cat_file_all = None + """ + The Git class manages communication with the Git binary. + + It provides a convenient interface to calling the Git binary, such as in:: + + g = Git( git_dir ) + g.init() # calls 'git init' program + rval = g.ls_files() # calls 'git ls-files' program + + ``Debugging`` + Set the GIT_PYTHON_TRACE environment variable print each invocation + of the command to stdout. + Set its value to 'full' to see details about the returned values. + """ + __slots__ = ("_working_dir", "cat_file_all", "cat_file_header", "_version_info") + + # CONFIGURATION + # The size in bytes read from stdout when copying git's output to another stream + max_chunk_size = 1024*64 + + git_exec_name = "git" # default that should work on linux and windows + git_exec_name_win = "git.cmd" # alternate command name, windows only + + # Enables debugging of GitPython's git commands + GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False) + + # Provide the full path to the git executable. Otherwise it assumes git is in the path + _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE" + GIT_PYTHON_GIT_EXECUTABLE = os.environ.get(_git_exec_env_var, git_exec_name) + + + class AutoInterrupt(object): + """Kill/Interrupt the stored process instance once this instance goes out of scope. It is + used to prevent processes piling up in case iterators stop reading. + Besides all attributes are wired through to the contained process object. + + The wait method was overridden to perform automatic status code checking + and possibly raise.""" + __slots__= ("proc", "args") + + def __init__(self, proc, args ): + self.proc = proc + self.args = args + + def __del__(self): + # did the process finish already so we have a return code ? + if self.proc.poll() is not None: + return + + # can be that nothing really exists anymore ... + if os is None: + return + + # try to kill it + try: + os.kill(self.proc.pid, 2) # interrupt signal + except AttributeError: + # try windows + # for some reason, providing None for stdout/stderr still prints something. This is why + # we simply use the shell and redirect to nul. Its slower than CreateProcess, question + # is whether we really want to see all these messages. Its annoying no matter what. + call(("TASKKILL /F /T /PID %s 2>nul 1>nul" % str(self.proc.pid)), shell=True) + # END exception handling + + def __getattr__(self, attr): + return getattr(self.proc, attr) + + def wait(self): + """Wait for the process and return its status code. + + :raise GitCommandError: if the return status is not 0""" + status = self.proc.wait() + if status != 0: + raise GitCommandError(self.args, status, self.proc.stderr.read()) + # END status handling + return status + # END auto interrupt + + class CatFileContentStream(object): + """Object representing a sized read-only stream returning the contents of + an object. + It behaves like a stream, but counts the data read and simulates an empty + stream once our sized content region is empty. + If not all data is read to the end of the objects's lifetime, we read the + rest to assure the underlying stream continues to work""" + + __slots__ = ('_stream', '_nbr', '_size') + + def __init__(self, size, stream): + self._stream = stream + self._size = size + self._nbr = 0 # num bytes read + + # special case: if the object is empty, has null bytes, get the + # final newline right away. + if size == 0: + stream.read(1) + # END handle empty streams + + def read(self, size=-1): + bytes_left = self._size - self._nbr + if bytes_left == 0: + return '' + if size > -1: + # assure we don't try to read past our limit + size = min(bytes_left, size) + else: + # they try to read all, make sure its not more than what remains + size = bytes_left + # END check early depletion + data = self._stream.read(size) + self._nbr += len(data) + + # check for depletion, read our final byte to make the stream usable by others + if self._size - self._nbr == 0: + self._stream.read(1) # final newline + # END finish reading + return data + + def readline(self, size=-1): + if self._nbr == self._size: + return '' + + # clamp size to lowest allowed value + bytes_left = self._size - self._nbr + if size > -1: + size = min(bytes_left, size) + else: + size = bytes_left + # END handle size + + data = self._stream.readline(size) + self._nbr += len(data) + + # handle final byte + if self._size - self._nbr == 0: + self._stream.read(1) + # END finish reading + + return data + + def readlines(self, size=-1): + if self._nbr == self._size: + return list() + + # leave all additional logic to our readline method, we just check the size + out = list() + nbr = 0 + while True: + line = self.readline() + if not line: + break + out.append(line) + if size > -1: + nbr += len(line) + if nbr > size: + break + # END handle size constraint + # END readline loop + return out + + def __iter__(self): + return self + + def next(self): + line = self.readline() + if not line: + raise StopIteration + return line + + def __del__(self): + bytes_left = self._size - self._nbr + if bytes_left: + # read and discard - seeking is impossible within a stream + # includes terminating newline + self._stream.read(bytes_left + 1) + # END handle incomplete read + + + def __init__(self, working_dir=None): + """Initialize this instance with: + + :param working_dir: + Git directory we should work in. If None, we always work in the current + directory as returned by os.getcwd(). + It is meant to be the working tree directory if available, or the + .git directory in case of bare repositories.""" + super(Git, self).__init__() + self._working_dir = working_dir + + # cached command slots + self.cat_file_header = None + self.cat_file_all = None - def __getattr__(self, name): - """A convenience method as it allows to call the command as if it was - an object. - :return: Callable object that will execute call _call_process with your arguments.""" - if name[0] == '_': - return LazyMixin.__getattr__(self, name) - return lambda *args, **kwargs: self._call_process(name, *args, **kwargs) + def __getattr__(self, name): + """A convenience method as it allows to call the command as if it was + an object. + :return: Callable object that will execute call _call_process with your arguments.""" + if name[0] == '_': + return LazyMixin.__getattr__(self, name) + return lambda *args, **kwargs: self._call_process(name, *args, **kwargs) - def _set_cache_(self, attr): - if attr == '_version_info': - # We only use the first 4 numbers, as everthing else could be strings in fact (on windows) - version_numbers = self._call_process('version').split(' ')[2] - self._version_info = tuple(int(n) for n in version_numbers.split('.')[:4]) - else: - super(Git, self)._set_cache_(attr) - #END handle version info - + def _set_cache_(self, attr): + if attr == '_version_info': + # We only use the first 4 numbers, as everthing else could be strings in fact (on windows) + version_numbers = self._call_process('version').split(' ')[2] + self._version_info = tuple(int(n) for n in version_numbers.split('.')[:4]) + else: + super(Git, self)._set_cache_(attr) + #END handle version info + - @property - def working_dir(self): - """:return: Git directory we are working on""" - return self._working_dir - - @property - def version_info(self): - """ - :return: tuple(int, int, int, int) tuple with integers representing the major, minor - and additional version numbers as parsed from git version. - This value is generated on demand and is cached""" - return self._version_info + @property + def working_dir(self): + """:return: Git directory we are working on""" + return self._working_dir + + @property + def version_info(self): + """ + :return: tuple(int, int, int, int) tuple with integers representing the major, minor + and additional version numbers as parsed from git version. + This value is generated on demand and is cached""" + return self._version_info - def execute(self, command, - istream=None, - with_keep_cwd=False, - with_extended_output=False, - with_exceptions=True, - as_process=False, - output_stream=None, - **subprocess_kwargs - ): - """Handles executing the command on the shell and consumes and returns - the returned information (stdout) + def execute(self, command, + istream=None, + with_keep_cwd=False, + with_extended_output=False, + with_exceptions=True, + as_process=False, + output_stream=None, + **subprocess_kwargs + ): + """Handles executing the command on the shell and consumes and returns + the returned information (stdout) - :param command: - The command argument list to execute. - It should be a string, or a sequence of program arguments. The - program to execute is the first item in the args sequence or string. + :param command: + The command argument list to execute. + It should be a string, or a sequence of program arguments. The + program to execute is the first item in the args sequence or string. - :param istream: - Standard input filehandle passed to subprocess.Popen. + :param istream: + Standard input filehandle passed to subprocess.Popen. - :param with_keep_cwd: - Whether to use the current working directory from os.getcwd(). - The cmd otherwise uses its own working_dir that it has been initialized - with if possible. + :param with_keep_cwd: + Whether to use the current working directory from os.getcwd(). + The cmd otherwise uses its own working_dir that it has been initialized + with if possible. - :param with_extended_output: - Whether to return a (status, stdout, stderr) tuple. + :param with_extended_output: + Whether to return a (status, stdout, stderr) tuple. - :param with_exceptions: - Whether to raise an exception when git returns a non-zero status. + :param with_exceptions: + Whether to raise an exception when git returns a non-zero status. - :param as_process: - Whether to return the created process instance directly from which - streams can be read on demand. This will render with_extended_output and - with_exceptions ineffective - the caller will have - to deal with the details himself. - It is important to note that the process will be placed into an AutoInterrupt - wrapper that will interrupt the process once it goes out of scope. If you - use the command in iterators, you should pass the whole process instance - instead of a single stream. - - :param output_stream: - If set to a file-like object, data produced by the git command will be - output to the given stream directly. - This feature only has any effect if as_process is False. Processes will - always be created with a pipe due to issues with subprocess. - This merely is a workaround as data will be copied from the - output pipe to the given output stream directly. - - :param subprocess_kwargs: - Keyword arguments to be passed to subprocess.Popen. Please note that - some of the valid kwargs are already set by this method, the ones you - specify may not be the same ones. - - :return: - * str(output) if extended_output = False (Default) - * tuple(int(status), str(stdout), str(stderr)) if extended_output = True - - if ouput_stream is True, the stdout value will be your output stream: - * output_stream if extended_output = False - * tuple(int(status), output_stream, str(stderr)) if extended_output = True - - :raise GitCommandError: - - :note: - If you add additional keyword arguments to the signature of this method, - you must update the execute_kwargs tuple housed in this module.""" - if self.GIT_PYTHON_TRACE and not self.GIT_PYTHON_TRACE == 'full': - print ' '.join(command) + :param as_process: + Whether to return the created process instance directly from which + streams can be read on demand. This will render with_extended_output and + with_exceptions ineffective - the caller will have + to deal with the details himself. + It is important to note that the process will be placed into an AutoInterrupt + wrapper that will interrupt the process once it goes out of scope. If you + use the command in iterators, you should pass the whole process instance + instead of a single stream. + + :param output_stream: + If set to a file-like object, data produced by the git command will be + output to the given stream directly. + This feature only has any effect if as_process is False. Processes will + always be created with a pipe due to issues with subprocess. + This merely is a workaround as data will be copied from the + output pipe to the given output stream directly. + + :param subprocess_kwargs: + Keyword arguments to be passed to subprocess.Popen. Please note that + some of the valid kwargs are already set by this method, the ones you + specify may not be the same ones. + + :return: + * str(output) if extended_output = False (Default) + * tuple(int(status), str(stdout), str(stderr)) if extended_output = True + + if ouput_stream is True, the stdout value will be your output stream: + * output_stream if extended_output = False + * tuple(int(status), output_stream, str(stderr)) if extended_output = True + + :raise GitCommandError: + + :note: + If you add additional keyword arguments to the signature of this method, + you must update the execute_kwargs tuple housed in this module.""" + if self.GIT_PYTHON_TRACE and not self.GIT_PYTHON_TRACE == 'full': + print ' '.join(command) - # Allow the user to have the command executed in their working dir. - if with_keep_cwd or self._working_dir is None: - cwd = os.getcwd() - else: - cwd=self._working_dir - - # Start the process - proc = Popen(command, - cwd=cwd, - stdin=istream, - stderr=PIPE, - stdout=PIPE, - close_fds=(os.name=='posix'),# unsupported on linux - **subprocess_kwargs - ) - if as_process: - return self.AutoInterrupt(proc, command) - - # Wait for the process to return - status = 0 - stdout_value = '' - stderr_value = '' - try: - if output_stream is None: - stdout_value, stderr_value = proc.communicate() - # strip trailing "\n" - if stdout_value.endswith("\n"): - stdout_value = stdout_value[:-1] - if stderr_value.endswith("\n"): - stderr_value = stderr_value[:-1] - status = proc.returncode - else: - stream_copy(proc.stdout, output_stream, self.max_chunk_size) - stdout_value = output_stream - stderr_value = proc.stderr.read() - # strip trailing "\n" - if stderr_value.endswith("\n"): - stderr_value = stderr_value[:-1] - status = proc.wait() - # END stdout handling - finally: - proc.stdout.close() - proc.stderr.close() + # Allow the user to have the command executed in their working dir. + if with_keep_cwd or self._working_dir is None: + cwd = os.getcwd() + else: + cwd=self._working_dir + + # Start the process + proc = Popen(command, + cwd=cwd, + stdin=istream, + stderr=PIPE, + stdout=PIPE, + close_fds=(os.name=='posix'),# unsupported on linux + **subprocess_kwargs + ) + if as_process: + return self.AutoInterrupt(proc, command) + + # Wait for the process to return + status = 0 + stdout_value = '' + stderr_value = '' + try: + if output_stream is None: + stdout_value, stderr_value = proc.communicate() + # strip trailing "\n" + if stdout_value.endswith("\n"): + stdout_value = stdout_value[:-1] + if stderr_value.endswith("\n"): + stderr_value = stderr_value[:-1] + status = proc.returncode + else: + stream_copy(proc.stdout, output_stream, self.max_chunk_size) + stdout_value = output_stream + stderr_value = proc.stderr.read() + # strip trailing "\n" + if stderr_value.endswith("\n"): + stderr_value = stderr_value[:-1] + status = proc.wait() + # END stdout handling + finally: + proc.stdout.close() + proc.stderr.close() - if self.GIT_PYTHON_TRACE == 'full': - cmdstr = " ".join(command) - if stderr_value: - print "%s -> %d; stdout: '%s'; stderr: '%s'" % (cmdstr, status, stdout_value, stderr_value) - elif stdout_value: - print "%s -> %d; stdout: '%s'" % (cmdstr, status, stdout_value) - else: - print "%s -> %d" % (cmdstr, status) - # END handle debug printing + if self.GIT_PYTHON_TRACE == 'full': + cmdstr = " ".join(command) + if stderr_value: + print "%s -> %d; stdout: '%s'; stderr: '%s'" % (cmdstr, status, stdout_value, stderr_value) + elif stdout_value: + print "%s -> %d; stdout: '%s'" % (cmdstr, status, stdout_value) + else: + print "%s -> %d" % (cmdstr, status) + # END handle debug printing - if with_exceptions and status != 0: - raise GitCommandError(command, status, stderr_value) + if with_exceptions and status != 0: + raise GitCommandError(command, status, stderr_value) - # Allow access to the command's status code - if with_extended_output: - return (status, stdout_value, stderr_value) - else: - return stdout_value + # Allow access to the command's status code + if with_extended_output: + return (status, stdout_value, stderr_value) + else: + return stdout_value - def transform_kwargs(self, **kwargs): - """Transforms Python style kwargs into git command line options.""" - args = list() - for k, v in kwargs.items(): - if len(k) == 1: - if v is True: - args.append("-%s" % k) - elif type(v) is not bool: - args.append("-%s%s" % (k, v)) - else: - if v is True: - args.append("--%s" % dashify(k)) - elif type(v) is not bool: - args.append("--%s=%s" % (dashify(k), v)) - return args + def transform_kwargs(self, **kwargs): + """Transforms Python style kwargs into git command line options.""" + args = list() + for k, v in kwargs.items(): + if len(k) == 1: + if v is True: + args.append("-%s" % k) + elif type(v) is not bool: + args.append("-%s%s" % (k, v)) + else: + if v is True: + args.append("--%s" % dashify(k)) + elif type(v) is not bool: + args.append("--%s=%s" % (dashify(k), v)) + return args - @classmethod - def __unpack_args(cls, arg_list): - if not isinstance(arg_list, (list,tuple)): - return [ str(arg_list) ] - - outlist = list() - for arg in arg_list: - if isinstance(arg_list, (list, tuple)): - outlist.extend(cls.__unpack_args( arg )) - # END recursion - else: - outlist.append(str(arg)) - # END for each arg - return outlist + @classmethod + def __unpack_args(cls, arg_list): + if not isinstance(arg_list, (list,tuple)): + return [ str(arg_list) ] + + outlist = list() + for arg in arg_list: + if isinstance(arg_list, (list, tuple)): + outlist.extend(cls.__unpack_args( arg )) + # END recursion + else: + outlist.append(str(arg)) + # END for each arg + return outlist - def _call_process(self, method, *args, **kwargs): - """Run the given git command with the specified arguments and return - the result as a String + def _call_process(self, method, *args, **kwargs): + """Run the given git command with the specified arguments and return + the result as a String - :param method: - is the command. Contained "_" characters will be converted to dashes, - such as in 'ls_files' to call 'ls-files'. + :param method: + is the command. Contained "_" characters will be converted to dashes, + such as in 'ls_files' to call 'ls-files'. - :param args: - is the list of arguments. If None is included, it will be pruned. - This allows your commands to call git more conveniently as None - is realized as non-existent + :param args: + is the list of arguments. If None is included, it will be pruned. + This allows your commands to call git more conveniently as None + is realized as non-existent - :param kwargs: - is a dict of keyword arguments. - This function accepts the same optional keyword arguments - as execute(). + :param kwargs: + is a dict of keyword arguments. + This function accepts the same optional keyword arguments + as execute(). - ``Examples``:: - git.rev_list('master', max_count=10, header=True) + ``Examples``:: + git.rev_list('master', max_count=10, header=True) - :return: Same as ``execute``""" - # Handle optional arguments prior to calling transform_kwargs - # otherwise these'll end up in args, which is bad. - _kwargs = dict() - for kwarg in execute_kwargs: - try: - _kwargs[kwarg] = kwargs.pop(kwarg) - except KeyError: - pass + :return: Same as ``execute``""" + # Handle optional arguments prior to calling transform_kwargs + # otherwise these'll end up in args, which is bad. + _kwargs = dict() + for kwarg in execute_kwargs: + try: + _kwargs[kwarg] = kwargs.pop(kwarg) + except KeyError: + pass - # Prepare the argument list - opt_args = self.transform_kwargs(**kwargs) - - ext_args = self.__unpack_args([a for a in args if a is not None]) - args = opt_args + ext_args - - def make_call(): - call = [self.GIT_PYTHON_GIT_EXECUTABLE, dashify(method)] - call.extend(args) - return call - #END utility to recreate call after changes - - if sys.platform == 'win32': - try: - try: - return self.execute(make_call(), **_kwargs) - except WindowsError: - # did we switch to git.cmd already, or was it changed from default ? permanently fail - if self.GIT_PYTHON_GIT_EXECUTABLE != self.git_exec_name: - raise - #END handle overridden variable - type(self).GIT_PYTHON_GIT_EXECUTABLE = self.git_exec_name_win - call = [self.GIT_PYTHON_GIT_EXECUTABLE] + list(args) - - try: - return self.execute(make_call(), **_kwargs) - finally: - import warnings - msg = "WARNING: Automatically switched to use git.cmd as git executable, which reduces performance by ~70%." - msg += "Its recommended to put git.exe into the PATH or to set the %s environment variable to the executable's location" % self._git_exec_env_var - warnings.warn(msg) - #END print of warning - #END catch first failure - except WindowsError: - raise WindowsError("The system cannot find or execute the file at %r" % self.GIT_PYTHON_GIT_EXECUTABLE) - #END provide better error message - else: - return self.execute(make_call(), **_kwargs) - #END handle windows default installation - - def _parse_object_header(self, header_line): - """ - :param header_line: - type_string size_as_int - - :return: (hex_sha, type_string, size_as_int) - - :raise ValueError: if the header contains indication for an error due to - incorrect input sha""" - tokens = header_line.split() - if len(tokens) != 3: - if not tokens: - raise ValueError("SHA could not be resolved, git returned: %r" % (header_line.strip())) - else: - raise ValueError("SHA %s could not be resolved, git returned: %r" % (tokens[0], header_line.strip())) - # END handle actual return value - # END error handling - - if len(tokens[0]) != 40: - raise ValueError("Failed to parse header: %r" % header_line) - return (tokens[0], tokens[1], int(tokens[2])) - - def __prepare_ref(self, ref): - # required for command to separate refs on stdin - refstr = str(ref) # could be ref-object - if refstr.endswith("\n"): - return refstr - return refstr + "\n" - - def __get_persistent_cmd(self, attr_name, cmd_name, *args,**kwargs): - cur_val = getattr(self, attr_name) - if cur_val is not None: - return cur_val - - options = { "istream" : PIPE, "as_process" : True } - options.update( kwargs ) - - cmd = self._call_process( cmd_name, *args, **options ) - setattr(self, attr_name, cmd ) - return cmd - - def __get_object_header(self, cmd, ref): - cmd.stdin.write(self.__prepare_ref(ref)) - cmd.stdin.flush() - return self._parse_object_header(cmd.stdout.readline()) - - def get_object_header(self, ref): - """ Use this method to quickly examine the type and size of the object behind - the given ref. - - :note: The method will only suffer from the costs of command invocation - once and reuses the command in subsequent calls. - - :return: (hexsha, type_string, size_as_int)""" - cmd = self.__get_persistent_cmd("cat_file_header", "cat_file", batch_check=True) - return self.__get_object_header(cmd, ref) - - def get_object_data(self, ref): - """ As get_object_header, but returns object data as well - :return: (hexsha, type_string, size_as_int,data_string) - :note: not threadsafe""" - hexsha, typename, size, stream = self.stream_object_data(ref) - data = stream.read(size) - del(stream) - return (hexsha, typename, size, data) - - def stream_object_data(self, ref): - """As get_object_header, but returns the data as a stream - :return: (hexsha, type_string, size_as_int, stream) - :note: This method is not threadsafe, you need one independent Command instance - per thread to be safe !""" - cmd = self.__get_persistent_cmd("cat_file_all", "cat_file", batch=True) - hexsha, typename, size = self.__get_object_header(cmd, ref) - return (hexsha, typename, size, self.CatFileContentStream(size, cmd.stdout)) - - def clear_cache(self): - """Clear all kinds of internal caches to release resources. - - Currently persistent commands will be interrupted. - - :return: self""" - self.cat_file_all = None - self.cat_file_header = None - return self + # Prepare the argument list + opt_args = self.transform_kwargs(**kwargs) + + ext_args = self.__unpack_args([a for a in args if a is not None]) + args = opt_args + ext_args + + def make_call(): + call = [self.GIT_PYTHON_GIT_EXECUTABLE, dashify(method)] + call.extend(args) + return call + #END utility to recreate call after changes + + if sys.platform == 'win32': + try: + try: + return self.execute(make_call(), **_kwargs) + except WindowsError: + # did we switch to git.cmd already, or was it changed from default ? permanently fail + if self.GIT_PYTHON_GIT_EXECUTABLE != self.git_exec_name: + raise + #END handle overridden variable + type(self).GIT_PYTHON_GIT_EXECUTABLE = self.git_exec_name_win + call = [self.GIT_PYTHON_GIT_EXECUTABLE] + list(args) + + try: + return self.execute(make_call(), **_kwargs) + finally: + import warnings + msg = "WARNING: Automatically switched to use git.cmd as git executable, which reduces performance by ~70%." + msg += "Its recommended to put git.exe into the PATH or to set the %s environment variable to the executable's location" % self._git_exec_env_var + warnings.warn(msg) + #END print of warning + #END catch first failure + except WindowsError: + raise WindowsError("The system cannot find or execute the file at %r" % self.GIT_PYTHON_GIT_EXECUTABLE) + #END provide better error message + else: + return self.execute(make_call(), **_kwargs) + #END handle windows default installation + + def _parse_object_header(self, header_line): + """ + :param header_line: + type_string size_as_int + + :return: (hex_sha, type_string, size_as_int) + + :raise ValueError: if the header contains indication for an error due to + incorrect input sha""" + tokens = header_line.split() + if len(tokens) != 3: + if not tokens: + raise ValueError("SHA could not be resolved, git returned: %r" % (header_line.strip())) + else: + raise ValueError("SHA %s could not be resolved, git returned: %r" % (tokens[0], header_line.strip())) + # END handle actual return value + # END error handling + + if len(tokens[0]) != 40: + raise ValueError("Failed to parse header: %r" % header_line) + return (tokens[0], tokens[1], int(tokens[2])) + + def __prepare_ref(self, ref): + # required for command to separate refs on stdin + refstr = str(ref) # could be ref-object + if refstr.endswith("\n"): + return refstr + return refstr + "\n" + + def __get_persistent_cmd(self, attr_name, cmd_name, *args,**kwargs): + cur_val = getattr(self, attr_name) + if cur_val is not None: + return cur_val + + options = { "istream" : PIPE, "as_process" : True } + options.update( kwargs ) + + cmd = self._call_process( cmd_name, *args, **options ) + setattr(self, attr_name, cmd ) + return cmd + + def __get_object_header(self, cmd, ref): + cmd.stdin.write(self.__prepare_ref(ref)) + cmd.stdin.flush() + return self._parse_object_header(cmd.stdout.readline()) + + def get_object_header(self, ref): + """ Use this method to quickly examine the type and size of the object behind + the given ref. + + :note: The method will only suffer from the costs of command invocation + once and reuses the command in subsequent calls. + + :return: (hexsha, type_string, size_as_int)""" + cmd = self.__get_persistent_cmd("cat_file_header", "cat_file", batch_check=True) + return self.__get_object_header(cmd, ref) + + def get_object_data(self, ref): + """ As get_object_header, but returns object data as well + :return: (hexsha, type_string, size_as_int,data_string) + :note: not threadsafe""" + hexsha, typename, size, stream = self.stream_object_data(ref) + data = stream.read(size) + del(stream) + return (hexsha, typename, size, data) + + def stream_object_data(self, ref): + """As get_object_header, but returns the data as a stream + :return: (hexsha, type_string, size_as_int, stream) + :note: This method is not threadsafe, you need one independent Command instance + per thread to be safe !""" + cmd = self.__get_persistent_cmd("cat_file_all", "cat_file", batch=True) + hexsha, typename, size = self.__get_object_header(cmd, ref) + return (hexsha, typename, size, self.CatFileContentStream(size, cmd.stdout)) + + def clear_cache(self): + """Clear all kinds of internal caches to release resources. + + Currently persistent commands will be interrupted. + + :return: self""" + self.cat_file_all = None + self.cat_file_header = None + return self diff --git a/git/config.py b/git/config.py index c71bb8ca..285ade6b 100644 --- a/git/config.py +++ b/git/config.py @@ -18,408 +18,408 @@ from git.util import LockFile __all__ = ('GitConfigParser', 'SectionConstraint') class MetaParserBuilder(type): - """Utlity class wrapping base-class methods into decorators that assure read-only properties""" - def __new__(metacls, name, bases, clsdict): - """ - Equip all base-class methods with a needs_values decorator, and all non-const methods - with a set_dirty_and_flush_changes decorator in addition to that.""" - kmm = '_mutating_methods_' - if kmm in clsdict: - mutating_methods = clsdict[kmm] - for base in bases: - methods = ( t for t in inspect.getmembers(base, inspect.ismethod) if not t[0].startswith("_") ) - for name, method in methods: - if name in clsdict: - continue - method_with_values = needs_values(method) - if name in mutating_methods: - method_with_values = set_dirty_and_flush_changes(method_with_values) - # END mutating methods handling - - clsdict[name] = method_with_values - # END for each name/method pair - # END for each base - # END if mutating methods configuration is set - - new_type = super(MetaParserBuilder, metacls).__new__(metacls, name, bases, clsdict) - return new_type - - + """Utlity class wrapping base-class methods into decorators that assure read-only properties""" + def __new__(metacls, name, bases, clsdict): + """ + Equip all base-class methods with a needs_values decorator, and all non-const methods + with a set_dirty_and_flush_changes decorator in addition to that.""" + kmm = '_mutating_methods_' + if kmm in clsdict: + mutating_methods = clsdict[kmm] + for base in bases: + methods = ( t for t in inspect.getmembers(base, inspect.ismethod) if not t[0].startswith("_") ) + for name, method in methods: + if name in clsdict: + continue + method_with_values = needs_values(method) + if name in mutating_methods: + method_with_values = set_dirty_and_flush_changes(method_with_values) + # END mutating methods handling + + clsdict[name] = method_with_values + # END for each name/method pair + # END for each base + # END if mutating methods configuration is set + + new_type = super(MetaParserBuilder, metacls).__new__(metacls, name, bases, clsdict) + return new_type + + def needs_values(func): - """Returns method assuring we read values (on demand) before we try to access them""" - def assure_data_present(self, *args, **kwargs): - self.read() - return func(self, *args, **kwargs) - # END wrapper method - assure_data_present.__name__ = func.__name__ - return assure_data_present - + """Returns method assuring we read values (on demand) before we try to access them""" + def assure_data_present(self, *args, **kwargs): + self.read() + return func(self, *args, **kwargs) + # END wrapper method + assure_data_present.__name__ = func.__name__ + return assure_data_present + def set_dirty_and_flush_changes(non_const_func): - """Return method that checks whether given non constant function may be called. - If so, the instance will be set dirty. - Additionally, we flush the changes right to disk""" - def flush_changes(self, *args, **kwargs): - rval = non_const_func(self, *args, **kwargs) - self.write() - return rval - # END wrapper method - flush_changes.__name__ = non_const_func.__name__ - return flush_changes - + """Return method that checks whether given non constant function may be called. + If so, the instance will be set dirty. + Additionally, we flush the changes right to disk""" + def flush_changes(self, *args, **kwargs): + rval = non_const_func(self, *args, **kwargs) + self.write() + return rval + # END wrapper method + flush_changes.__name__ = non_const_func.__name__ + return flush_changes + class SectionConstraint(object): - """Constrains a ConfigParser to only option commands which are constrained to - always use the section we have been initialized with. - - It supports all ConfigParser methods that operate on an option""" - __slots__ = ("_config", "_section_name") - _valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option", - "remove_section", "remove_option", "options") - - def __init__(self, config, section): - self._config = config - self._section_name = section - - def __getattr__(self, attr): - if attr in self._valid_attrs_: - return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) - return super(SectionConstraint,self).__getattribute__(attr) - - def _call_config(self, method, *args, **kwargs): - """Call the configuration at the given method which must take a section name - as first argument""" - return getattr(self._config, method)(self._section_name, *args, **kwargs) - - @property - def config(self): - """return: Configparser instance we constrain""" - return self._config - + """Constrains a ConfigParser to only option commands which are constrained to + always use the section we have been initialized with. + + It supports all ConfigParser methods that operate on an option""" + __slots__ = ("_config", "_section_name") + _valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option", + "remove_section", "remove_option", "options") + + def __init__(self, config, section): + self._config = config + self._section_name = section + + def __getattr__(self, attr): + if attr in self._valid_attrs_: + return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) + return super(SectionConstraint,self).__getattribute__(attr) + + def _call_config(self, method, *args, **kwargs): + """Call the configuration at the given method which must take a section name + as first argument""" + return getattr(self._config, method)(self._section_name, *args, **kwargs) + + @property + def config(self): + """return: Configparser instance we constrain""" + return self._config + class GitConfigParser(cp.RawConfigParser, object): - """Implements specifics required to read git style configuration files. - - This variation behaves much like the git.config command such that the configuration - will be read on demand based on the filepath given during initialization. - - The changes will automatically be written once the instance goes out of scope, but - can be triggered manually as well. - - The configuration file will be locked if you intend to change values preventing other - instances to write concurrently. - - :note: - The config is case-sensitive even when queried, hence section and option names - must match perfectly.""" - __metaclass__ = MetaParserBuilder - - - #{ Configuration - # The lock type determines the type of lock to use in new configuration readers. - # They must be compatible to the LockFile interface. - # A suitable alternative would be the BlockingLockFile - t_lock = LockFile - re_comment = re.compile('^\s*[#;]') - - #} END configuration - - OPTCRE = re.compile( - r'\s*(?P