Index: tools/clang-format/git-clang-format =================================================================== --- tools/clang-format/git-clang-format +++ tools/clang-format/git-clang-format @@ -23,6 +23,7 @@ Requires Python 2.7 """ +from __future__ import print_function import argparse import collections import contextlib @@ -138,15 +139,15 @@ if opts.verbose >= 1: ignored_files.difference_update(changed_lines) if ignored_files: - print 'Ignoring changes in the following files (wrong extension):' + print('Ignoring changes in the following files (wrong extension):') for filename in ignored_files: - print ' ', filename + print(' %s' % filename) if changed_lines: - print 'Running clang-format on the following files:' + print('Running clang-format on the following files:') for filename in changed_lines: - print ' ', filename + print(' %s' % filename) if not changed_lines: - print 'no modified files to format' + print('no modified files to format') return # The computed diff outputs absolute paths, so we must cd before accessing # those files. @@ -163,20 +164,20 @@ binary=opts.binary, style=opts.style) if opts.verbose >= 1: - print 'old tree:', old_tree - print 'new tree:', new_tree + print('old tree: %s' % old_tree) + print('new tree: %s' % new_tree) if old_tree == new_tree: if opts.verbose >= 0: - print 'clang-format did not modify any files' + print('clang-format did not modify any files') elif opts.diff: print_diff(old_tree, new_tree) else: changed_files = apply_changes(old_tree, new_tree, force=opts.force, patch_mode=opts.patch) if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1: - print 'changed files:' + print('changed files:') for filename in changed_files: - print ' ', filename + print(' %s' % filename) def load_git_config(non_string_options=None): @@ -257,7 +258,7 @@ stdout, stderr = p.communicate() if p.returncode != 0: return None - return stdout.strip() + return convert_string(stdout.strip()) def compute_diff_and_extract_lines(commits, files): @@ -300,6 +301,7 @@ list of line `Range`s.""" matches = {} for line in patch_file: + line = convert_string(line) match = re.search(r'^\+\+\+\ [^/]+/(.*)', line) if match: filename = match.group(1).rstrip('\r\n') @@ -320,7 +322,7 @@ `allowed_extensions` must be a collection of lowercase file extensions, excluding the period.""" allowed_extensions = frozenset(allowed_extensions) - for filename in dictionary.keys(): + for filename in list(dictionary.keys()): base_ext = filename.rsplit('.', 1) if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions: del dictionary[filename] @@ -345,7 +347,7 @@ Returns the object ID (SHA-1) of the created tree.""" def index_info_generator(): - for filename, line_ranges in changed_lines.iteritems(): + for filename, line_ranges in changed_lines.items(): if revision: git_metadata_cmd = ['git', 'ls-tree', '%s:%s' % (revision, os.path.dirname(filename)), @@ -356,6 +358,9 @@ mode = oct(int(stdout.split()[0], 8)) else: mode = oct(os.stat(filename).st_mode) + # Adjust python3 octal format so that it matches what git expects + if mode.startswith('0o'): + mode = '0' + mode[2:] blob_id = clang_format_to_blob(filename, line_ranges, revision=revision, binary=binary, @@ -376,7 +381,7 @@ with temporary_index_file(): p = subprocess.Popen(cmd, stdin=subprocess.PIPE) for line in input_lines: - p.stdin.write('%s\0' % line) + p.stdin.write(to_bytes('%s\0' % line)) p.stdin.close() if p.wait() != 0: die('`%s` failed' % ' '.join(cmd)) @@ -431,7 +436,7 @@ die('`%s` failed' % ' '.join(clang_format_cmd)) if git_show and git_show.wait() != 0: die('`%s` failed' % ' '.join(git_show_cmd)) - return stdout.rstrip('\r\n') + return convert_string(stdout).rstrip('\r\n') @contextlib.contextmanager @@ -488,10 +493,10 @@ if not force: unstaged_files = run('git', 'diff-files', '--name-status', *changed_files) if unstaged_files: - print >>sys.stderr, ('The following files would be modified but ' - 'have unstaged changes:') - print >>sys.stderr, unstaged_files - print >>sys.stderr, 'Please commit, stage, or stash them first.' + print('The following files would be modified but ' + 'have unstaged changes:', file=sys.stderr) + print(unstaged_files, file=sys.stderr) + print('Please commit, stage, or stash them first.', file=sys.stderr) sys.exit(2) if patch_mode: # In patch mode, we could just as well create an index from the new tree @@ -518,25 +523,51 @@ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) stdout, stderr = p.communicate(input=stdin) - if p.returncode == 0: + exitCode = p.wait() + + stdout = convert_string(stdout) + stderr = convert_string(stderr) + + if exitCode == 0: if stderr: if verbose: - print >>sys.stderr, '`%s` printed to stderr:' % ' '.join(args) - print >>sys.stderr, stderr.rstrip() + print('`%s` printed to stderr:' % ' '.join(args), file=sys.stderr) + print(stderr.rstrip(), file=sys.stderr) if strip: stdout = stdout.rstrip('\r\n') return stdout if verbose: - print >>sys.stderr, '`%s` returned %s' % (' '.join(args), p.returncode) + print('`%s` returned %s' % (' '.join(args), p.returncode), file=sys.stderr) if stderr: - print >>sys.stderr, stderr.rstrip() + print(stderr.rstrip(), file=sys.stderr) sys.exit(2) def die(message): - print >>sys.stderr, 'error:', message + print('error:', message, file=sys.stderr) sys.exit(2) +def to_bytes(str_input): + # Encode to UTF-8 to get binary data. + if isinstance(str_input, bytes): + return str_input + return str_input.encode('utf-8') + + +def to_string(bytes_input): + if isinstance(bytes_input, str): + return bytes_input + return to_bytes(bytes_input) + + +def convert_string(bytes_input): + try: + return to_string(bytes_input.decode('utf-8')) + except AttributeError: # 'str' object has no attribute 'decode'. + return str(bytes_input) + except UnicodeError: + return str(bytes_input) + if __name__ == '__main__': main()