| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 6 # Copyright (C) 2008 Evan Martin <martine@danga.com> |
| 7 | 7 |
| 8 """A git-command for integrating reviews on Rietveld.""" | 8 """A git-command for integrating reviews on Rietveld.""" |
| 9 | 9 |
| 10 import difflib |
| 10 import json | 11 import json |
| 11 import logging | 12 import logging |
| 12 import optparse | 13 import optparse |
| 13 import os | 14 import os |
| 14 import re | 15 import re |
| 15 import stat | 16 import stat |
| 16 import sys | 17 import sys |
| 17 import textwrap | 18 import textwrap |
| 19 import urllib2 |
| 18 import urlparse | 20 import urlparse |
| 19 import urllib2 | |
| 20 | 21 |
| 21 try: | 22 try: |
| 22 import readline # pylint: disable=F0401,W0611 | 23 import readline # pylint: disable=F0401,W0611 |
| 23 except ImportError: | 24 except ImportError: |
| 24 pass | 25 pass |
| 25 | 26 |
| 26 | 27 |
| 27 from third_party import colorama | 28 from third_party import colorama |
| 28 from third_party import upload | 29 from third_party import upload |
| 29 import breakpad # pylint: disable=W0611 | 30 import breakpad # pylint: disable=W0611 |
| (...skipping 1982 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2012 cfd_path = os.path.join('/usr', 'lib', 'clang-format', | 2013 cfd_path = os.path.join('/usr', 'lib', 'clang-format', |
| 2013 'clang-format-diff.py') | 2014 'clang-format-diff.py') |
| 2014 if not os.path.exists(cfd_path): | 2015 if not os.path.exists(cfd_path): |
| 2015 DieWithError('Could not find clang-format-diff at %s.' % cfd_path) | 2016 DieWithError('Could not find clang-format-diff at %s.' % cfd_path) |
| 2016 cmd = [sys.executable, cfd_path, '-style', 'Chromium'] | 2017 cmd = [sys.executable, cfd_path, '-style', 'Chromium'] |
| 2017 RunCommand(cmd, stdin=diff_output) | 2018 RunCommand(cmd, stdin=diff_output) |
| 2018 | 2019 |
| 2019 return 0 | 2020 return 0 |
| 2020 | 2021 |
| 2021 | 2022 |
| 2023 ### Glue code for subcommand handling. |
| 2024 |
| 2025 |
| 2026 def Commands(): |
| 2027 """Returns a dict of command and their handling function.""" |
| 2028 module = sys.modules[__name__] |
| 2029 cmds = (fn[3:] for fn in dir(module) if fn.startswith('CMD')) |
| 2030 return dict((cmd, getattr(module, 'CMD' + cmd)) for cmd in cmds) |
| 2031 |
| 2032 |
| 2022 def Command(name): | 2033 def Command(name): |
| 2023 return getattr(sys.modules[__name__], 'CMD' + name, None) | 2034 """Retrieves the function to handle a command.""" |
| 2035 commands = Commands() |
| 2036 if name in commands: |
| 2037 return commands[name] |
| 2038 |
| 2039 # Try to be smart and look if there's something similar. |
| 2040 commands_with_prefix = [c for c in commands if c.startswith(name)] |
| 2041 if len(commands_with_prefix) == 1: |
| 2042 return commands[commands_with_prefix[0]] |
| 2043 |
| 2044 # A #closeenough approximation of levenshtein distance. |
| 2045 def close_enough(a, b): |
| 2046 return difflib.SequenceMatcher(a=a, b=b).ratio() |
| 2047 |
| 2048 hamming_commands = sorted( |
| 2049 ((close_enough(c, name), c) for c in commands), |
| 2050 reverse=True) |
| 2051 if (hamming_commands[0][0] - hamming_commands[1][0]) < 0.3: |
| 2052 # Too ambiguous. |
| 2053 return |
| 2054 |
| 2055 if hamming_commands[0][0] < 0.8: |
| 2056 # Not similar enough. Don't be a fool and run a random command. |
| 2057 return |
| 2058 |
| 2059 return commands[hamming_commands[0][1]] |
| 2024 | 2060 |
| 2025 | 2061 |
| 2026 def CMDhelp(parser, args): | 2062 def CMDhelp(parser, args): |
| 2027 """print list of commands or help for a specific command""" | 2063 """print list of commands or help for a specific command""" |
| 2028 _, args = parser.parse_args(args) | 2064 _, args = parser.parse_args(args) |
| 2029 if len(args) == 1: | 2065 if len(args) == 1: |
| 2030 return main(args + ['--help']) | 2066 return main(args + ['--help']) |
| 2031 parser.print_help() | 2067 parser.print_help() |
| 2032 return 0 | 2068 return 0 |
| 2033 | 2069 |
| 2034 | 2070 |
| 2035 def GenUsage(parser, command): | 2071 def GenUsage(parser, command): |
| 2036 """Modify an OptParse object with the function's documentation.""" | 2072 """Modify an OptParse object with the function's documentation.""" |
| 2037 obj = Command(command) | 2073 obj = Command(command) |
| 2074 # Get back the real command name in case Command() guess the actual command |
| 2075 # name. |
| 2076 command = obj.__name__[3:] |
| 2038 more = getattr(obj, 'usage_more', '') | 2077 more = getattr(obj, 'usage_more', '') |
| 2039 if command == 'help': | 2078 if command == 'help': |
| 2040 command = '<command>' | 2079 command = '<command>' |
| 2041 else: | 2080 else: |
| 2042 # OptParser.description prefer nicely non-formatted strings. | 2081 # OptParser.description prefer nicely non-formatted strings. |
| 2043 parser.description = re.sub('[\r\n ]{2,}', ' ', obj.__doc__) | 2082 parser.description = re.sub('[\r\n ]{2,}', ' ', obj.__doc__) |
| 2044 parser.set_usage('usage: %%prog %s [options] %s' % (command, more)) | 2083 parser.set_usage('usage: %%prog %s [options] %s' % (command, more)) |
| 2045 | 2084 |
| 2046 | 2085 |
| 2047 def main(argv): | 2086 def main(argv): |
| 2048 """Doesn't parse the arguments here, just find the right subcommand to | 2087 """Doesn't parse the arguments here, just find the right subcommand to |
| 2049 execute.""" | 2088 execute.""" |
| 2050 if sys.hexversion < 0x02060000: | 2089 if sys.hexversion < 0x02060000: |
| 2051 print >> sys.stderr, ( | 2090 print >> sys.stderr, ( |
| 2052 '\nYour python version %s is unsupported, please upgrade.\n' % | 2091 '\nYour python version %s is unsupported, please upgrade.\n' % |
| 2053 sys.version.split(' ', 1)[0]) | 2092 sys.version.split(' ', 1)[0]) |
| 2054 return 2 | 2093 return 2 |
| 2055 | 2094 |
| 2056 # Reload settings. | 2095 # Reload settings. |
| 2057 global settings | 2096 global settings |
| 2058 settings = Settings() | 2097 settings = Settings() |
| 2059 | 2098 |
| 2060 # Do it late so all commands are listed. | 2099 # Do it late so all commands are listed. |
| 2061 CMDhelp.usage_more = ('\n\nCommands are:\n' + '\n'.join([ | 2100 commands = Commands() |
| 2062 ' %-10s %s' % (fn[3:], Command(fn[3:]).__doc__.split('\n')[0].strip()) | 2101 length = max(len(c) for c in commands) |
| 2063 for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')])) | 2102 docs = sorted( |
| 2103 (name, handler.__doc__.split('\n')[0].strip()) |
| 2104 for name, handler in commands.iteritems()) |
| 2105 CMDhelp.usage_more = ('\n\nCommands are:\n' + '\n'.join( |
| 2106 ' %-*s %s' % (length, name, doc) for name, doc in docs)) |
| 2064 | 2107 |
| 2065 # Create the option parse and add --verbose support. | 2108 # Create the option parse and add --verbose support. |
| 2066 parser = optparse.OptionParser() | 2109 parser = optparse.OptionParser() |
| 2067 parser.add_option( | 2110 parser.add_option( |
| 2068 '-v', '--verbose', action='count', default=0, | 2111 '-v', '--verbose', action='count', default=0, |
| 2069 help='Use 2 times for more debugging info') | 2112 help='Use 2 times for more debugging info') |
| 2070 old_parser_args = parser.parse_args | 2113 old_parser_args = parser.parse_args |
| 2071 def Parse(args): | 2114 def Parse(args): |
| 2072 options, args = old_parser_args(args) | 2115 options, args = old_parser_args(args) |
| 2073 if options.verbose >= 2: | 2116 if options.verbose >= 2: |
| (...skipping 23 matching lines...) Expand all Loading... |
| 2097 GenUsage(parser, 'help') | 2140 GenUsage(parser, 'help') |
| 2098 return CMDhelp(parser, argv) | 2141 return CMDhelp(parser, argv) |
| 2099 | 2142 |
| 2100 | 2143 |
| 2101 if __name__ == '__main__': | 2144 if __name__ == '__main__': |
| 2102 # These affect sys.stdout so do it outside of main() to simplify mocks in | 2145 # These affect sys.stdout so do it outside of main() to simplify mocks in |
| 2103 # unit testing. | 2146 # unit testing. |
| 2104 fix_encoding.fix_encoding() | 2147 fix_encoding.fix_encoding() |
| 2105 colorama.init() | 2148 colorama.init() |
| 2106 sys.exit(main(sys.argv[1:])) | 2149 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |