From 4266c7e9853a30b32e8b1de9c23a1412eaeeee6f Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Thu, 23 Oct 2008 22:16:39 -0700 Subject: [PATCH] git.py: workaround osx interrupted system calls On osx select gets interrupted and exec'ing out commands fails. We now retry the system call to recover. Signed-off-by: David Aguilar --- cola/git.py | 93 +++++++++++++++++++++++++++++++++++++++++++++-------------- cola/utils.py | 68 ++----------------------------------------- 2 files changed, 75 insertions(+), 86 deletions(-) diff --git a/cola/git.py b/cola/git.py index 52782de9..d3b95674 100644 --- a/cola/git.py +++ b/cola/git.py @@ -3,11 +3,10 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php - +import re import os import sys import subprocess -from cola import utils from cola.exception import GitCommandError def dashify(string): @@ -34,7 +33,9 @@ class Git(object): raise AttributeError(name) return lambda *args, **kwargs: self._call_process(name, *args, **kwargs) - def execute(self, command, + @staticmethod + def execute(command, + cwd=None, istream=None, with_keep_cwd=False, with_extended_output=False, @@ -72,36 +73,41 @@ class Git(object): print ' '.join(command) # Allow the user to have the command executed in their working dir. - if with_keep_cwd or not self._git_cwd: + if with_keep_cwd or not cwd: cwd = os.getcwd() - else: - cwd=self._git_cwd # Start the process - if sys.platform == 'win32': - command = utils.shell_quote(*command) + wanky = sys.platform in ('win32',) + if wanky: + command = shell_quote(*command) proc = subprocess.Popen(command, cwd=cwd, - shell=sys.platform == 'win32', + shell=wanky, stdin=istream, stderr=subprocess.PIPE, stdout=subprocess.PIPE) - # Wait for the process to return - stdout_value, stderr_value = proc.communicate() + count = 0 + stdout_value = None + stderr_value = None + while count < 4: # osx interrupts system calls + count += 1 + try: + stdout_value, stderr_value = proc.communicate() + break + except: + pass + status = proc.returncode + if with_exceptions and status: + raise GitCommandError(command, status, stderr_value) + if not stdout_value: stdout_value = '' if not stderr_value: stderr_value = '' - status = proc.returncode - - # Strip off trailing whitespace by default if not with_raw_output: - stdout_value = stdout_value.rstrip() - stderr_value = stderr_value.rstrip() - - if with_exceptions and status: - raise GitCommandError(command, status, stderr_value) + stdout_value = stdout_value.strip() + stderr_value = stderr_value.strip() if GIT_PYTHON_TRACE == 'full': if stderr_value: @@ -165,7 +171,7 @@ class Git(object): # Handle optional arguments prior to calling transform_kwargs # otherwise these'll end up in args, which is bad. - _kwargs = {} + _kwargs = dict(cwd=self._git_cwd) for kwarg in execute_kwargs: try: _kwargs[kwarg] = kwargs.pop(kwarg) @@ -180,4 +186,49 @@ class Git(object): call = ['git', dashify(method)] call.extend(args) - return self.execute(call, **_kwargs) + return Git.execute(call, **_kwargs) + + +def shell_quote(*inputs): + """ + Quote strings so that they can be suitably martialled + off to the shell. This method supports POSIX sh syntax. + This is crucial to properly handle command line arguments + with spaces, quotes, double-quotes, etc. on darwin/win32... + """ + + regex = re.compile('[^\w!%+,\-./:@^]') + quote_regex = re.compile("((?:'\\''){2,})") + + ret = [] + for input in inputs: + if not input: + continue + + if '\x00' in input: + raise AssertionError,('No way to quote strings ' + 'containing null(\\000) bytes') + + # = does need quoting else in command position it's a + # program-local environment setting + match = regex.search(input) + if match and '=' not in input: + # ' -> '\'' + input = input.replace("'", "'\\''") + + # make multiple ' in a row look simpler + # '\'''\'''\'' -> '"'''"' + quote_match = quote_regex.match(input) + if quote_match: + quotes = match.group(1) + input.replace(quotes, ("'" *(len(quotes)/4)) + "\"'") + + input = "'%s'" % input + if input.startswith("''"): + input = input[2:] + + if input.endswith("''"): + input = input[:-2] + ret.append(input) + return ' '.join(ret) + diff --git a/cola/utils.py b/cola/utils.py index 81d81469..6749aac9 100644 --- a/cola/utils.py +++ b/cola/utils.py @@ -9,6 +9,8 @@ from glob import glob from cStringIO import StringIO from cola import defaults +from cola import git +from cola.git import shell_quote from cola.exception import ColaException PREFIX = os.path.realpath(os.path.dirname(os.path.dirname(sys.argv[0]))) @@ -30,33 +32,12 @@ KNOWN_FILE_TYPES = { 'image': 'image.png', } -class RunCommandException(ColaException): - """Thrown when something bad happened when we tried to run the - subprocess.""" - pass - def run_cmd(*command): """ Runs a *command argument list and returns the output. e.g. run_cmd("echo", "hello", "world") """ - # Start the process - try: - proc = subprocess.Popen(command, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - - # Wait for the process to return - stdout_value = proc.stdout.read() - proc.stdout.close() - proc.wait() - status = proc.poll() - - # Strip off trailing whitespace by default - return stdout_value.rstrip() - except: - raise RunCommandException('ERROR Running: [%s]' % ' '.join(command)) - + return git.Git.execute(command) def get_qm_for_locale(locale): regex = re.compile(r'([^\.])+\..*$') @@ -171,49 +152,6 @@ def basename(path): else: return pathstr -def shell_quote(*inputs): - """ - Quote strings so that they can be suitably martialled - off to the shell. This method supports POSIX sh syntax. - This is crucial to properly handle command line arguments - with spaces, quotes, double-quotes, etc. - """ - - regex = re.compile('[^\w!%+,\-./:@^]') - quote_regex = re.compile("((?:'\\''){2,})") - - ret = [] - for input in inputs: - if not input: - continue - - if '\x00' in input: - raise AssertionError,('No way to quote strings ' - 'containing null(\\000) bytes') - - # = does need quoting else in command position it's a - # program-local environment setting - match = regex.search(input) - if match and '=' not in input: - # ' -> '\'' - input = input.replace("'", "'\\''") - - # make multiple ' in a row look simpler - # '\'''\'''\'' -> '"'''"' - quote_match = quote_regex.match(input) - if quote_match: - quotes = match.group(1) - input.replace(quotes, ("'" *(len(quotes)/4)) + "\"'") - - input = "'%s'" % input - if input.startswith("''"): - input = input[2:] - - if input.endswith("''"): - input = input[:-2] - ret.append(input) - return ' '.join(ret) - HEADER_LENGTH = 80 def header(msg): pad = HEADER_LENGTH - len(msg) - 4 # len(':+') + len('+:') -- 2.11.4.GIT