From 013731e832662e94b65052341e0054436243068e Mon Sep 17 00:00:00 2001 From: "sbc@chromium.org" Date: Thu, 26 Feb 2015 18:28:43 +0000 Subject: [PATCH] Handle KeyboardInterrupt consistently in python scripts Handle KeyboardInterrupt gracefully rather the printing a backtrace. Most users of these tools don't expect a backtrace when then hit Ctrl-C. Also, fix a few other inconsistencies found in the python startup code of these different scripts: - always call main function 'main' (rather than 'Main') - always return 0 from main function - if main takes args never include argv[0] Review URL: https://codereview.chromium.org/955993006 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@294250 0039d316-1c4b-4281-b951-d872f2087c98 --- apply_issue.py | 6 +++++- chrome-update.py | 8 ++++++-- clang_format.py | 8 ++++++-- commit_queue.py | 6 +++++- drover.py | 6 +++++- fetch.py | 6 +++++- gcl.py | 6 +++++- gclient.py | 9 +++++++-- git_auto_svn.py | 9 +++++++-- git_cache.py | 6 +++++- git_cherry_pick_upload.py | 8 +++++++- git_cl.py | 7 ++++++- git_footers.py | 7 ++++++- git_freezer.py | 11 ++++++++--- git_map.py | 11 +++++++---- git_map_branches.py | 9 +++++++-- git_mark_merge_base.py | 9 ++++++--- git_nav_downstream.py | 7 +++++-- git_new_branch.py | 7 ++++++- git_number.py | 33 +++++++++++++++++---------------- git_rebase_update.py | 8 ++++++-- git_rename_branch.py | 7 ++++++- git_reparent_branch.py | 6 +++++- git_retry.py | 6 +++++- git_squash_branch.py | 8 +++++++- git_try.py | 12 ++++++++++-- git_upstream_diff.py | 8 ++++++-- gn.py | 6 +++++- my_activity.py | 6 +++++- my_reviews.py | 6 +++++- presubmit_support.py | 8 ++++++-- pylint.py | 6 +++++- roll_dep.py | 6 +++++- tests/presubmit_unittest.py | 6 +++--- upload_to_google_storage.py | 8 ++++++-- 35 files changed, 216 insertions(+), 70 deletions(-) diff --git a/apply_issue.py b/apply_issue.py index 8bcb3b2e55..2b7b334f51 100755 --- a/apply_issue.py +++ b/apply_issue.py @@ -262,4 +262,8 @@ def main(): if __name__ == "__main__": fix_encoding.fix_encoding() - sys.exit(main()) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/chrome-update.py b/chrome-update.py index 4f5731cb4d..4fb11e5aba 100755 --- a/chrome-update.py +++ b/chrome-update.py @@ -60,7 +60,7 @@ def DoBuild(chrome_root, args): return subprocess.call(cmd, cwd=chrome_root, shell=IS_WIN) -def Main(args): +def main(args): if len(args) < 3: print('Usage: chrome-update.py [options]') print('See options from compile.py at') @@ -84,4 +84,8 @@ def Main(args): if __name__ == "__main__": - sys.exit(Main(sys.argv)) + try: + sys.exit(main(sys.argv)) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/clang_format.py b/clang_format.py index 8320e6d2a0..f69f94d6e7 100755 --- a/clang_format.py +++ b/clang_format.py @@ -62,8 +62,12 @@ def main(args): if any(match in args for match in help_syntax): print '\nDepot tools redirects you to the clang-format at:\n %s\n' % tool - return subprocess.call([tool] + sys.argv[1:]) + return subprocess.call([tool] + args) if __name__ == '__main__': - sys.exit(main(sys.argv)) + try: + sys.exit(main(sys.argv[1])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/commit_queue.py b/commit_queue.py index 7639065f87..1bbf8a5c9e 100755 --- a/commit_queue.py +++ b/commit_queue.py @@ -181,4 +181,8 @@ def main(args=None): if __name__ == "__main__": fix_encoding.fix_encoding() - sys.exit(main()) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/drover.py b/drover.py index ec8620c81f..87025300d6 100755 --- a/drover.py +++ b/drover.py @@ -642,4 +642,8 @@ def main(): if __name__ == "__main__": - sys.exit(main()) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/fetch.py b/fetch.py index 5335f75a44..04be95fa48 100755 --- a/fetch.py +++ b/fetch.py @@ -331,4 +331,8 @@ def main(): if __name__ == '__main__': - sys.exit(main()) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/gcl.py b/gcl.py index a32b950fe4..cf657893c7 100755 --- a/gcl.py +++ b/gcl.py @@ -1511,4 +1511,8 @@ def main(argv): if __name__ == "__main__": fix_encoding.fix_encoding() - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/gclient.py b/gclient.py index 5f9dac5c56..41f9d5b3d3 100755 --- a/gclient.py +++ b/gclient.py @@ -2266,7 +2266,7 @@ def disable_buffering(): sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout) -def Main(argv): +def main(argv): """Doesn't parse the arguments here, just find the right subcommand to execute.""" if sys.hexversion < 0x02060000: @@ -2292,9 +2292,14 @@ def Main(argv): return 1 finally: gclient_utils.PrintWarnings() + return 0 if '__main__' == __name__: - sys.exit(Main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) # vim: ts=2:sw=2:tw=80:et: diff --git a/git_auto_svn.py b/git_auto_svn.py index 8aed18f6d6..0e8f341c7c 100755 --- a/git_auto_svn.py +++ b/git_auto_svn.py @@ -55,7 +55,7 @@ def main(argv): # to pass flags that don't do anything, and to provide 'usage'. parser = argparse.ArgumentParser( description='Automatically set up git-svn for a repo mirrored from svn.') - parser.parse_args(argv[1:]) + parser.parse_args(argv) upstream = root() message = run_git('log', '-1', '--format=%B', upstream) @@ -95,7 +95,12 @@ def main(argv): print 'Configured metadata, running "git svn fetch". This may take some time.' for line in run_git_stream('svn', 'fetch').xreadlines(): print line.strip() + return 0 if __name__ == '__main__': - sys.exit(main(sys.argv)) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_cache.py b/git_cache.py index 7cc19bdb24..8b27842482 100755 --- a/git_cache.py +++ b/git_cache.py @@ -697,4 +697,8 @@ def main(argv): if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_cherry_pick_upload.py b/git_cherry_pick_upload.py index 2a048faae3..0bcb9fd926 100755 --- a/git_cherry_pick_upload.py +++ b/git_cherry_pick_upload.py @@ -139,6 +139,12 @@ def main(): ) args = parser.parse_args() cherry_pick(args.branch, args.commit) + return 0 + if __name__ == '__main__': - sys.exit(main()) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_cl.py b/git_cl.py index 9ac9470e22..bcd4a6252b 100755 --- a/git_cl.py +++ b/git_cl.py @@ -3088,6 +3088,7 @@ def main(argv): DieWithError( ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) + return 0 if __name__ == '__main__': @@ -3095,4 +3096,8 @@ if __name__ == '__main__': # unit testing. fix_encoding.fix_encoding() colorama.init() - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_footers.py b/git_footers.py index f3b2482a08..01a4cd5805 100755 --- a/git_footers.py +++ b/git_footers.py @@ -139,7 +139,12 @@ def main(args): for k in footers.keys(): for v in footers[k]: print '%s: %s' % (k, v) + return 0 if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_freezer.py b/git_freezer.py index 7a23be21fe..91a4ec0d94 100755 --- a/git_freezer.py +++ b/git_freezer.py @@ -22,12 +22,17 @@ def CMDthaw(parser, args): return thaw() -def main(): +def main(args): dispatcher = subcommand.CommandDispatcher(__name__) - ret = dispatcher.execute(optparse.OptionParser(), sys.argv[1:]) + ret = dispatcher.execute(optparse.OptionParser(), args) if ret: print ret + return 0 if __name__ == '__main__': - main() + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_map.py b/git_map.py index 65814b9f33..99c8b05c75 100755 --- a/git_map.py +++ b/git_map.py @@ -37,13 +37,13 @@ RESET = colorama.Fore.RESET + colorama.Back.RESET + colorama.Style.RESET_ALL # Git emits combined color BRIGHT_RED = '\x1b[1;31m' -def main(): +def main(argv): map_extra = config_list('depot_tools.map_extra') fmt = '%C(red bold)%h%x09%Creset%C(green)%d%Creset %C(yellow)%ad%Creset ~ %s' log_proc = subprocess2.Popen( [GIT_EXE, 'log', '--graph', '--branches', '--tags', root(), '--color=always', '--date=short', ('--pretty=format:' + fmt) - ] + map_extra + sys.argv[1:], + ] + map_extra + argv, stdout=subprocess2.PIPE, shell=False) @@ -110,5 +110,8 @@ def main(): if __name__ == '__main__': - sys.exit(main()) - + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_map_branches.py b/git_map_branches.py index 44f13c3bd5..0bf57499e7 100755 --- a/git_map_branches.py +++ b/git_map_branches.py @@ -266,13 +266,18 @@ def main(argv): parser.add_argument('--no-color', action='store_true', dest='nocolor', help='Turn off colors.') - opts = parser.parse_args(argv[1:]) + opts = parser.parse_args(argv) mapper = BranchMapper() mapper.verbosity = opts.v mapper.output.nocolor = opts.nocolor mapper.start() print mapper.output.as_formatted_string() + return 0 if __name__ == '__main__': - sys.exit(main(sys.argv)) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_mark_merge_base.py b/git_mark_merge_base.py index 673e2b4332..214b3dfe46 100755 --- a/git_mark_merge_base.py +++ b/git_mark_merge_base.py @@ -39,7 +39,7 @@ def main(argv): try: remove_merge_base(cur) except CalledProcessError: - print "No merge base currently exists for %s." % cur + print 'No merge base currently exists for %s.' % cur return 0 if opts.merge_base: @@ -60,9 +60,12 @@ def main(argv): print "Invalid merge_base %s" % opts.merge_base print "merge_base(%s): %s" % (cur, actual) - return ret if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_nav_downstream.py b/git_nav_downstream.py index ed3110bfb4..6ea085b431 100755 --- a/git_nav_downstream.py +++ b/git_nav_downstream.py @@ -34,7 +34,8 @@ def main(args): cur = hash_one(cur) downstreams = [b for b in branches() if upfn(b) == cur] if not downstreams: - return "No downstream branches" + print "No downstream branches" + return 1 elif len(downstreams) == 1: run('checkout', downstreams[0], stdout=sys.stdout, stderr=sys.stderr) else: @@ -55,10 +56,12 @@ def main(args): run('checkout', downstreams[int(r)], stdout=sys.stdout, stderr=sys.stderr) break + return 0 if __name__ == '__main__': try: sys.exit(main(sys.argv[1:])) except KeyboardInterrupt: - pass + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_new_branch.py b/git_new_branch.py index 18ce0180b7..03b0fcc484 100755 --- a/git_new_branch.py +++ b/git_new_branch.py @@ -48,7 +48,12 @@ def main(args): sys.stderr.write(cpe.stderr) return 1 sys.stderr.write('Switched to branch %s.\n' % opts.branch_name) + return 0 if __name__ == '__main__': # pragma: no cover - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_number.py b/git_number.py index 6997f51b3d..1867b97d7c 100755 --- a/git_number.py +++ b/git_number.py @@ -259,25 +259,26 @@ def main(): # pragma: no cover "use the 'Cr-Commit-Position' value in the commit's message.") return 1 + if opts.reset: + clear_caches(on_disk=True) + return + try: - if opts.reset: - clear_caches(on_disk=True) - return + targets = git.parse_commitrefs(*(args or ['HEAD'])) + except git.BadCommitRefException as e: + parser.error(e) - try: - targets = git.parse_commitrefs(*(args or ['HEAD'])) - except git.BadCommitRefException as e: - parser.error(e) + load_generation_numbers(targets) + if not opts.no_cache: + finalize(targets) - load_generation_numbers(targets) - if not opts.no_cache: - finalize(targets) - - print '\n'.join(map(str, map(get_num, targets))) - return 0 - except KeyboardInterrupt: - return 1 + print '\n'.join(map(str, map(get_num, targets))) + return 0 if __name__ == '__main__': # pragma: no cover - sys.exit(main()) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_rebase_update.py b/git_rebase_update.py index a61e53a3ab..10c10d151a 100755 --- a/git_rebase_update.py +++ b/git_rebase_update.py @@ -187,7 +187,7 @@ def rebase_branch(branch, parent, start_hash): return True -def main(args=()): +def main(args=None): parser = argparse.ArgumentParser() parser.add_argument('--verbose', '-v', action='store_true') parser.add_argument('--no_fetch', '--no-fetch', '-n', @@ -270,4 +270,8 @@ def main(args=()): if __name__ == '__main__': # pragma: no cover - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_rename_branch.py b/git_rename_branch.py index cefa012ff6..c0ac42ec11 100755 --- a/git_rename_branch.py +++ b/git_rename_branch.py @@ -44,7 +44,12 @@ def main(args): except subprocess2.CalledProcessError as cpe: sys.stderr.write(cpe.stderr) return 1 + return 0 if __name__ == '__main__': # pragma: no cover - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_reparent_branch.py b/git_reparent_branch.py index fe79d3cc0d..f24f52fea2 100755 --- a/git_reparent_branch.py +++ b/git_reparent_branch.py @@ -73,4 +73,8 @@ def main(args): if __name__ == '__main__': # pragma: no cover - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_retry.py b/git_retry.py index b40e6d2ef8..d6dee6536b 100755 --- a/git_retry.py +++ b/git_retry.py @@ -153,4 +153,8 @@ def main(args): if __name__ == '__main__': logging.basicConfig() logging.getLogger().setLevel(logging.WARNING) - sys.exit(main(sys.argv[2:])) + try: + sys.exit(main(sys.argv[2:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_squash_branch.py b/git_squash_branch.py index 0e02539356..83acfa7f03 100755 --- a/git_squash_branch.py +++ b/git_squash_branch.py @@ -15,6 +15,12 @@ def main(args): help='Use the given as the first line of the commit message.') opts = parser.parse_args(args) squash_current_branch(opts.message) + return 0 + if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_try.py b/git_try.py index 45a5217e6f..f8f6d306a8 100755 --- a/git_try.py +++ b/git_try.py @@ -41,8 +41,7 @@ def GetRietveldServerUrl(): return None -if __name__ == '__main__': - args = sys.argv[1:] +def main(args): patchset = GetRietveldPatchsetNumber() if patchset: args.extend([ @@ -68,3 +67,12 @@ if __name__ == '__main__': except third_party.upload.ClientLoginError, e: print('Got an exception while trying to log in to Rietveld.') print(str(e)) + return 0 + + +if __name__ == '__main__': + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/git_upstream_diff.py b/git_upstream_diff.py index 9d9db95fd5..3e38c78077 100755 --- a/git_upstream_diff.py +++ b/git_upstream_diff.py @@ -38,8 +38,12 @@ def main(args): cmd += extra_args - subprocess2.check_call(cmd) + return subprocess2.check_call(cmd) if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/gn.py b/gn.py index 7837dbddcc..32c2fa0b2c 100755 --- a/gn.py +++ b/gn.py @@ -34,4 +34,8 @@ def main(args): if __name__ == '__main__': - sys.exit(main(sys.argv)) + try: + sys.exit(main(sys.argv)) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/my_activity.py b/my_activity.py index 08e8dcc268..a5942963b2 100755 --- a/my_activity.py +++ b/my_activity.py @@ -847,4 +847,8 @@ def main(): if __name__ == '__main__': - sys.exit(main()) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/my_reviews.py b/my_reviews.py index a55485fe3a..6b36c5682c 100755 --- a/my_reviews.py +++ b/my_reviews.py @@ -374,4 +374,8 @@ def main(): if __name__ == '__main__': - sys.exit(main()) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/presubmit_support.py b/presubmit_support.py index db677cfd04..a95b9adf6e 100755 --- a/presubmit_support.py +++ b/presubmit_support.py @@ -1605,7 +1605,7 @@ def CallCommand(cmd_data): return cmd_data.info('%s (%4.2fs)' % (cmd_data.name, duration)) -def Main(argv): +def main(argv=None): parser = optparse.OptionParser(usage="%prog [options] ", version="%prog " + str(__version__)) parser.add_option("-c", "--commit", action="store_true", default=False, @@ -1749,4 +1749,8 @@ def Main(argv): if __name__ == '__main__': fix_encoding.fix_encoding() - sys.exit(Main(None)) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/pylint.py b/pylint.py index 9caa3a3711..f24ab17801 100755 --- a/pylint.py +++ b/pylint.py @@ -19,4 +19,8 @@ _RC_FILE = os.path.join(_HERE, 'pylintrc') # another rcfile is to be used, passing --rcfile a second time on the command- # line will work fine. command = [sys.executable, _PYLINT, '--rcfile=%s' % _RC_FILE] + sys.argv[1:] -sys.exit(subprocess.call(command)) +try: + sys.exit(subprocess.call(command)) +except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/roll_dep.py b/roll_dep.py index 5e9c47874d..2f78983b27 100755 --- a/roll_dep.py +++ b/roll_dep.py @@ -398,4 +398,8 @@ def main(argv): return update_deps(deps_file, dep_path, dep_name, git_rev, comment) if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + try: + sys.exit(main(sys.argv[1:])) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1) diff --git a/tests/presubmit_unittest.py b/tests/presubmit_unittest.py index a55b736f39..d6cbd09ae9 100755 --- a/tests/presubmit_unittest.py +++ b/tests/presubmit_unittest.py @@ -168,7 +168,7 @@ class PresubmitUnittest(PresubmitTestsBase): 'AffectedFile', 'Change', 'DoGetTrySlaves', 'DoPostUploadExecuter', 'DoPresubmitChecks', 'GetPostUploadExecuter', 'GetTrySlavesExecuter', 'GitAffectedFile', 'CallCommand', 'CommandData', - 'GitChange', 'InputApi', 'ListRelevantPresubmitFiles', 'Main', + 'GitChange', 'InputApi', 'ListRelevantPresubmitFiles', 'main', 'NonexistantCannedCheckFilter', 'OutputApi', 'ParseFiles', 'PresubmitFailure', 'PresubmitExecuter', 'PresubmitOutput', 'ScanSubDirs', 'SvnAffectedFile', 'SvnChange', 'cPickle', 'cpplint', 'cStringIO', @@ -1153,7 +1153,7 @@ def CheckChangeOnCommit(input_api, output_api): self.assertEquals( True, - presubmit.Main(['--root', self.fake_root_dir, 'random_file.txt'])) + presubmit.main(['--root', self.fake_root_dir, 'random_file.txt'])) def testMainUnversionedFail(self): # OptParser calls presubmit.os.path.exists and is a pain when mocked. @@ -1171,7 +1171,7 @@ def CheckChangeOnCommit(input_api, output_api): self.mox.ReplayAll() try: - presubmit.Main(['--root', self.fake_root_dir]) + presubmit.main(['--root', self.fake_root_dir]) self.fail() except SystemExit, e: self.assertEquals(2, e.code) diff --git a/upload_to_google_storage.py b/upload_to_google_storage.py index e81e0198f2..d88597cef0 100755 --- a/upload_to_google_storage.py +++ b/upload_to_google_storage.py @@ -203,7 +203,7 @@ def upload_to_google_storage( return max_ret_code -def main(args): +def main(): parser = optparse.OptionParser(USAGE_STRING) parser.add_option('-b', '--bucket', help='Google Storage bucket to upload to.') @@ -248,4 +248,8 @@ def main(args): if __name__ == '__main__': - sys.exit(main(sys.argv)) + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.stderr.write('interrupted\n') + sys.exit(1)