diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-07-15 02:05:47 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-07-15 02:05:47 +0000 |
commit | 7f0026066bb58059589171b4e9abfa671d0cc9dd (patch) | |
tree | 70bb2d604d556baff0aaa19fb234feef5625efcc | |
parent | b47c0468630caa81e4b1b72bed29391331e084b6 (diff) | |
parent | 5197623930b8c7b7cad418f2bee37e233e50da90 (diff) | |
download | repohooks-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.py | 48 | ||||
-rw-r--r-- | rh/utils.py | 52 | ||||
-rwxr-xr-x | rh/utils_unittest.py | 54 | ||||
-rwxr-xr-x | tools/android_test_mapping_format.py | 11 |
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 ' |