aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 02:05:47 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 02:05:47 +0000
commit7f0026066bb58059589171b4e9abfa671d0cc9dd (patch)
tree70bb2d604d556baff0aaa19fb234feef5625efcc
parentb47c0468630caa81e4b1b72bed29391331e084b6 (diff)
parent5197623930b8c7b7cad418f2bee37e233e50da90 (diff)
downloadrepohooks-android-mainline-12.0.0_r32.tar.gz
Snap for 7550930 from 5197623930b8c7b7cad418f2bee37e233e50da90 to mainline-networkstack-releaseandroid-mainline-12.0.0_r32android-mainline-12.0.0_r14
Change-Id: I2d3e887d3d97fd5b4dc238412a40a615afed7185
-rw-r--r--rh/config.py48
-rw-r--r--rh/utils.py52
-rwxr-xr-xrh/utils_unittest.py54
-rwxr-xr-xtools/android_test_mapping_format.py11
4 files changed, 68 insertions, 97 deletions
diff --git a/rh/config.py b/rh/config.py
index 1eb93a7..ed75007 100644
--- a/rh/config.py
+++ b/rh/config.py
@@ -63,10 +63,22 @@ class RawConfigParser(configparser.RawConfigParser):
return default
raise
+ def get(self, section, option, default=_UNSET):
+ """Return the value for |option| in |section| (with |default|)."""
+ try:
+ return configparser.RawConfigParser.get(self, section, option)
+ except (configparser.NoSectionError, configparser.NoOptionError):
+ if default is not _UNSET:
+ return default
+ raise
+
def items(self, section=_UNSET, default=_UNSET):
"""Return a list of (key, value) tuples for the options in |section|."""
if section is _UNSET:
- return super().items()
+ # Python 3 compat logic. Return a dict of section-to-options.
+ if sys.version_info.major < 3:
+ return [(x, self.items(x)) for x in self.sections()]
+ return super(RawConfigParser, self).items()
try:
return configparser.RawConfigParser.items(self, section)
@@ -75,6 +87,15 @@ class RawConfigParser(configparser.RawConfigParser):
return default
raise
+ if sys.version_info.major < 3:
+ def read_dict(self, dictionary):
+ """Store |dictionary| into ourselves."""
+ for section, settings in dictionary.items():
+ for option, value in settings:
+ if not self.has_section(section):
+ self.add_section(section)
+ self.set(section, option, value)
+
class PreUploadConfig(object):
"""A single (abstract) config used for `repo upload` hooks."""
@@ -117,8 +138,7 @@ class PreUploadConfig(object):
def custom_hook(self, hook):
"""The command to execute for |hook|."""
- return shlex.split(self.config.get(
- self.CUSTOM_HOOKS_SECTION, hook, fallback=''))
+ return shlex.split(self.config.get(self.CUSTOM_HOOKS_SECTION, hook, ''))
@property
def builtin_hooks(self):
@@ -128,13 +148,13 @@ class PreUploadConfig(object):
def builtin_hook_option(self, hook):
"""The options to pass to |hook|."""
- return shlex.split(self.config.get(
- self.BUILTIN_HOOKS_OPTIONS_SECTION, hook, fallback=''))
+ return shlex.split(self.config.get(self.BUILTIN_HOOKS_OPTIONS_SECTION,
+ hook, ''))
def builtin_hook_exclude_paths(self, hook):
"""List of paths for which |hook| should not be executed."""
- return shlex.split(self.config.get(
- self.BUILTIN_HOOKS_EXCLUDE_SECTION, hook, fallback=''))
+ return shlex.split(self.config.get(self.BUILTIN_HOOKS_EXCLUDE_SECTION,
+ hook, ''))
@property
def tool_paths(self):
@@ -166,7 +186,7 @@ class PreUploadConfig(object):
"""Whether to skip hooks for merged commits."""
return rh.shell.boolean_shell_value(
self.config.get(self.OPTIONS_SECTION,
- self.OPTION_IGNORE_MERGED_COMMITS, fallback=None),
+ self.OPTION_IGNORE_MERGED_COMMITS, None),
False)
def update(self, preupload_config):
@@ -214,7 +234,7 @@ class PreUploadConfig(object):
self.custom_hook(hook)
except ValueError as e:
raise ValidationError('%s: hook "%s" command line is invalid: '
- '%s' % (self.source, hook, e)) from e
+ '%s' % (self.source, hook, e))
# Verify hook options are valid shell strings.
for hook in self.builtin_hooks:
@@ -222,7 +242,7 @@ class PreUploadConfig(object):
self.builtin_hook_option(hook)
except ValueError as e:
raise ValidationError('%s: hook options "%s" are invalid: %s' %
- (self.source, hook, e)) from e
+ (self.source, hook, e))
# Reject unknown tools.
valid_tools = set(rh.hooks.TOOL_PATHS.keys())
@@ -259,13 +279,13 @@ class PreUploadFile(PreUploadConfig):
Args:
path: The config file to load.
"""
- super().__init__(source=path)
+ super(PreUploadFile, self).__init__(source=path)
self.path = path
try:
self.config.read(path)
except configparser.ParsingError as e:
- raise ValidationError('%s: %s' % (path, e)) from e
+ raise ValidationError('%s: %s' % (path, e))
self._validate()
@@ -290,7 +310,7 @@ class LocalPreUploadFile(PreUploadFile):
FILENAME = 'PREUPLOAD.cfg'
def _validate(self):
- super()._validate()
+ super(LocalPreUploadFile, self)._validate()
# Reject Exclude Paths section for local config.
if self.config.has_section(self.BUILTIN_HOOKS_EXCLUDE_SECTION):
@@ -320,7 +340,7 @@ class PreUploadSettings(PreUploadConfig):
paths: The directories to look for config files.
global_paths: The directories to look for global config files.
"""
- super().__init__()
+ super(PreUploadSettings, self).__init__()
self.paths = []
for config in itertools.chain(
diff --git a/rh/utils.py b/rh/utils.py
index aeab52f..1b36c7a 100644
--- a/rh/utils.py
+++ b/rh/utils.py
@@ -66,7 +66,7 @@ class CompletedProcess(getattr(subprocess, 'CompletedProcess', object)):
self.stderr = stderr
self.returncode = returncode
else:
- super().__init__(
+ super(CompletedProcess, self).__init__(
args=args, returncode=returncode, stdout=stdout, stderr=stderr)
@property
@@ -99,7 +99,7 @@ class CalledProcessError(subprocess.CalledProcessError):
raise TypeError('exception must be an exception instance; got %r'
% (exception,))
- super().__init__(returncode, cmd, stdout)
+ super(CalledProcessError, self).__init__(returncode, cmd, stdout)
# The parent class will set |output|, so delete it.
del self.output
# TODO(vapier): When we're Python 3-only, delete this assignment as the
@@ -183,8 +183,12 @@ def _kill_child_process(proc, int_timeout, kill_timeout, cmd, original_handler,
print('Ignoring unhandled exception in _kill_child_process: %s' % e,
file=sys.stderr)
- # Ensure our child process has been reaped, but don't wait forever.
- proc.wait_lock_breaker(timeout=60)
+ # Ensure our child process has been reaped.
+ kwargs = {}
+ if sys.version_info.major >= 3:
+ # ... but don't wait forever.
+ kwargs['timeout'] = 60
+ proc.wait_lock_breaker(**kwargs)
if not rh.signals.relay_signal(original_handler, signum, frame):
# Mock up our own, matching exit code for signaling.
@@ -306,8 +310,13 @@ def run(cmd, redirect_stdout=False, redirect_stderr=False, cwd=None, input=None,
kill_timeout = float(kill_timeout)
def _get_tempfile():
+ kwargs = {}
+ if sys.version_info.major < 3:
+ kwargs['bufsize'] = 0
+ else:
+ kwargs['buffering'] = 0
try:
- return tempfile.TemporaryFile(buffering=0)
+ return tempfile.TemporaryFile(**kwargs)
except EnvironmentError as e:
if e.errno != errno.ENOENT:
raise
@@ -316,7 +325,7 @@ def run(cmd, redirect_stdout=False, redirect_stderr=False, cwd=None, input=None,
# issue in this particular case since our usage gurantees deletion,
# and since this is primarily triggered during hard cgroups
# shutdown.
- return tempfile.TemporaryFile(dir='/tmp', buffering=0)
+ return tempfile.TemporaryFile(dir='/tmp', **kwargs)
# Modify defaults based on parameters.
# Note that tempfiles must be unbuffered else attempts to read
@@ -361,12 +370,6 @@ def run(cmd, redirect_stdout=False, redirect_stderr=False, cwd=None, input=None,
env = env.copy() if env is not None else os.environ.copy()
env.update(extra_env if extra_env else {})
- def ensure_text(s):
- """Make sure |s| is a string if it's bytes."""
- if isinstance(s, bytes):
- s = s.decode('utf-8', 'replace')
- return s
-
result.args = cmd
proc = None
@@ -412,26 +415,19 @@ def run(cmd, redirect_stdout=False, redirect_stderr=False, cwd=None, input=None,
if extra_env:
msg += ', extra env=%s' % extra_env
raise CalledProcessError(
- result.returncode, result.cmd, msg=msg,
- stdout=ensure_text(result.stdout),
- stderr=ensure_text(result.stderr))
+ result.returncode, result.cmd, stdout=result.stdout,
+ stderr=result.stderr, msg=msg)
except OSError as e:
- # Avoid leaking tempfiles.
- if popen_stdout is not None and not isinstance(popen_stdout, int):
- popen_stdout.close()
- if popen_stderr is not None and not isinstance(popen_stderr, int):
- popen_stderr.close()
-
estr = str(e)
if e.errno == errno.EACCES:
estr += '; does the program need `chmod a+x`?'
if not check:
- result = CompletedProcess(args=cmd, stderr=estr, returncode=255)
+ result = CompletedProcess(
+ args=cmd, stderr=estr.encode('utf-8'), returncode=255)
else:
raise CalledProcessError(
- result.returncode, result.cmd, msg=estr, exception=e,
- stdout=ensure_text(result.stdout),
- stderr=ensure_text(result.stderr)) from e
+ result.returncode, result.cmd, stdout=result.stdout,
+ stderr=result.stderr, msg=estr, exception=e)
finally:
if proc is not None:
# Ensure the process is dead.
@@ -441,8 +437,10 @@ def run(cmd, redirect_stdout=False, redirect_stderr=False, cwd=None, input=None,
None, None)
# Make sure output is returned as a string rather than bytes.
- result.stdout = ensure_text(result.stdout)
- result.stderr = ensure_text(result.stderr)
+ if result.stdout is not None:
+ result.stdout = result.stdout.decode('utf-8', 'replace')
+ if result.stderr is not None:
+ result.stderr = result.stderr.decode('utf-8', 'replace')
return result
# pylint: enable=redefined-builtin,input-builtin
diff --git a/rh/utils_unittest.py b/rh/utils_unittest.py
index ea2ddaa..f3098a9 100755
--- a/rh/utils_unittest.py
+++ b/rh/utils_unittest.py
@@ -161,60 +161,6 @@ class RunCommandTests(unittest.TestCase):
self.assertEqual(u'ß', ret.stdout)
self.assertIsNone(ret.stderr)
- def test_check_false(self):
- """Verify handling of check=False."""
- ret = rh.utils.run(['false'], check=False)
- self.assertNotEqual(0, ret.returncode)
- self.assertIn('false', str(ret))
-
- ret = rh.utils.run(['true'], check=False)
- self.assertEqual(0, ret.returncode)
- self.assertIn('true', str(ret))
-
- def test_check_true(self):
- """Verify handling of check=True."""
- with self.assertRaises(rh.utils.CalledProcessError) as e:
- rh.utils.run(['false'], check=True)
- err = e.exception
- self.assertNotEqual(0, err.returncode)
- self.assertIn('false', str(err))
-
- ret = rh.utils.run(['true'], check=True)
- self.assertEqual(0, ret.returncode)
- self.assertIn('true', str(ret))
-
- def test_check_false_output(self):
- """Verify handling of output capturing w/check=False."""
- with self.assertRaises(rh.utils.CalledProcessError) as e:
- rh.utils.run(['sh', '-c', 'echo out; echo err >&2; false'],
- check=True, capture_output=True)
- err = e.exception
- self.assertNotEqual(0, err.returncode)
- self.assertIn('false', str(err))
-
- def test_check_true_missing_prog_output(self):
- """Verify handling of output capturing w/missing progs."""
- with self.assertRaises(rh.utils.CalledProcessError) as e:
- rh.utils.run(['./!~a/b/c/d/'], check=True, capture_output=True)
- err = e.exception
- self.assertNotEqual(0, err.returncode)
- self.assertIn('a/b/c/d', str(err))
-
- def test_check_false_missing_prog_output(self):
- """Verify handling of output capturing w/missing progs."""
- ret = rh.utils.run(['./!~a/b/c/d/'], check=False, capture_output=True)
- self.assertNotEqual(0, ret.returncode)
- self.assertIn('a/b/c/d', str(ret))
-
- def test_check_false_missing_prog_combined_output(self):
- """Verify handling of combined output capturing w/missing progs."""
- with self.assertRaises(rh.utils.CalledProcessError) as e:
- rh.utils.run(['./!~a/b/c/d/'], check=True,
- combine_stdout_stderr=True)
- err = e.exception
- self.assertNotEqual(0, err.returncode)
- self.assertIn('a/b/c/d', str(err))
-
if __name__ == '__main__':
unittest.main()
diff --git a/tools/android_test_mapping_format.py b/tools/android_test_mapping_format.py
index 1e654e6..b87b886 100755
--- a/tools/android_test_mapping_format.py
+++ b/tools/android_test_mapping_format.py
@@ -53,6 +53,13 @@ TEST_MAPPING_URL = (
_COMMENTS_RE = re.compile(r'^\s*//')
+if sys.version_info.major < 3:
+ # pylint: disable=basestring-builtin,undefined-variable
+ string_types = basestring
+else:
+ string_types = str
+
+
class Error(Exception):
"""Base exception for all custom exceptions in this module."""
@@ -118,14 +125,14 @@ def _validate_test(test, test_mapping_file):
'Failed test config: %s' % (test_mapping_file, test))
preferred_targets = test.get(PREFERRED_TARGETS, [])
if (not isinstance(preferred_targets, list) or
- any(not isinstance(t, str) for t in preferred_targets)):
+ any(not isinstance(t, string_types) for t in preferred_targets)):
raise InvalidTestMappingError(
'Invalid test config in test mapping file %s. `preferred_targets` '
'setting in test config can only be a list of strings. Failed test '
'config: %s' % (test_mapping_file, test))
file_patterns = test.get(FILE_PATTERNS, [])
if (not isinstance(file_patterns, list) or
- any(not isinstance(p, str) for p in file_patterns)):
+ any(not isinstance(p, string_types) for p in file_patterns)):
raise InvalidTestMappingError(
'Invalid test config in test mapping file %s. `file_patterns` '
'setting in test config can only be a list of strings. Failed test '