Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Side by Side Diff: third_party/upload.py

Issue 3048047: Update upload.py from http://rietveld.googlecode.com/svn/trunk/upload.py at r546 (Closed)
Patch Set: Created 10 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2007 Google Inc. 3 # Copyright 2007 Google Inc.
4 # 4 #
5 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License. 6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at 7 # You may obtain a copy of the License at
8 # 8 #
9 # http://www.apache.org/licenses/LICENSE-2.0 9 # http://www.apache.org/licenses/LICENSE-2.0
10 # 10 #
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
51 try: 51 try:
52 from hashlib import md5 52 from hashlib import md5
53 except ImportError: 53 except ImportError:
54 from md5 import md5 54 from md5 import md5
55 55
56 try: 56 try:
57 import readline 57 import readline
58 except ImportError: 58 except ImportError:
59 pass 59 pass
60 60
61 try:
62 import keyring
63 except ImportError:
64 keyring = None
65
61 # The logging verbosity: 66 # The logging verbosity:
62 # 0: Errors only. 67 # 0: Errors only.
63 # 1: Status messages. 68 # 1: Status messages.
64 # 2: Info logs. 69 # 2: Info logs.
65 # 3: Debug logs. 70 # 3: Debug logs.
66 verbosity = 1 71 verbosity = 1
67 72
73 # The account type used for authentication.
74 # This line could be changed by the review server (see handler for
75 # upload.py).
76 AUTH_ACCOUNT_TYPE = "GOOGLE"
77
78 # URL of the default review server. As for AUTH_ACCOUNT_TYPE, this line could be
79 # changed by the review server (see handler for upload.py).
80 DEFAULT_REVIEW_SERVER = "codereview.appspot.com"
81
68 # Max size of patch or base file. 82 # Max size of patch or base file.
69 MAX_UPLOAD_SIZE = 900 * 1024 83 MAX_UPLOAD_SIZE = 900 * 1024
70 84
71 # Constants for version control names. Used by GuessVCSName. 85 # Constants for version control names. Used by GuessVCSName.
72 VCS_GIT = "Git" 86 VCS_GIT = "Git"
73 VCS_MERCURIAL = "Mercurial" 87 VCS_MERCURIAL = "Mercurial"
74 VCS_SUBVERSION = "Subversion" 88 VCS_SUBVERSION = "Subversion"
75 VCS_UNKNOWN = "Unknown" 89 VCS_UNKNOWN = "Unknown"
76 90
77 # whitelist for non-binary filetypes which do not start with "text/" 91 # whitelist for non-binary filetypes which do not start with "text/"
78 # .mm (Objective-C) shows up as application/x-freemind on my Linux box. 92 # .mm (Objective-C) shows up as application/x-freemind on my Linux box.
79 TEXT_MIMETYPES = ['application/javascript', 'application/x-javascript', 93 TEXT_MIMETYPES = ['application/javascript', 'application/x-javascript',
80 'application/xml', 'application/x-freemind'] 94 'application/xml', 'application/x-freemind',
95 'application/x-sh']
81 96
82 VCS_ABBREVIATIONS = { 97 VCS_ABBREVIATIONS = {
83 VCS_MERCURIAL.lower(): VCS_MERCURIAL, 98 VCS_MERCURIAL.lower(): VCS_MERCURIAL,
84 "hg": VCS_MERCURIAL, 99 "hg": VCS_MERCURIAL,
85 VCS_SUBVERSION.lower(): VCS_SUBVERSION, 100 VCS_SUBVERSION.lower(): VCS_SUBVERSION,
86 "svn": VCS_SUBVERSION, 101 "svn": VCS_SUBVERSION,
87 VCS_GIT.lower(): VCS_GIT, 102 VCS_GIT.lower(): VCS_GIT,
88 } 103 }
89 104
90 # The result of parsing Subversion's [auto-props] setting. 105 # The result of parsing Subversion's [auto-props] setting.
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
146 def __init__(self, url, code, msg, headers, args): 161 def __init__(self, url, code, msg, headers, args):
147 urllib2.HTTPError.__init__(self, url, code, msg, headers, None) 162 urllib2.HTTPError.__init__(self, url, code, msg, headers, None)
148 self.args = args 163 self.args = args
149 self.reason = args["Error"] 164 self.reason = args["Error"]
150 165
151 166
152 class AbstractRpcServer(object): 167 class AbstractRpcServer(object):
153 """Provides a common interface for a simple RPC server.""" 168 """Provides a common interface for a simple RPC server."""
154 169
155 def __init__(self, host, auth_function, host_override=None, extra_headers={}, 170 def __init__(self, host, auth_function, host_override=None, extra_headers={},
156 save_cookies=False): 171 save_cookies=False, account_type=AUTH_ACCOUNT_TYPE):
157 """Creates a new HttpRpcServer. 172 """Creates a new HttpRpcServer.
158 173
159 Args: 174 Args:
160 host: The host to send requests to. 175 host: The host to send requests to.
161 auth_function: A function that takes no arguments and returns an 176 auth_function: A function that takes no arguments and returns an
162 (email, password) tuple when called. Will be called if authentication 177 (email, password) tuple when called. Will be called if authentication
163 is required. 178 is required.
164 host_override: The host header to send to the server (defaults to host). 179 host_override: The host header to send to the server (defaults to host).
165 extra_headers: A dict of extra headers to append to every request. 180 extra_headers: A dict of extra headers to append to every request.
166 save_cookies: If True, save the authentication cookies to local disk. 181 save_cookies: If True, save the authentication cookies to local disk.
167 If False, use an in-memory cookiejar instead. Subclasses must 182 If False, use an in-memory cookiejar instead. Subclasses must
168 implement this functionality. Defaults to False. 183 implement this functionality. Defaults to False.
184 account_type: Account type used for authentication. Defaults to
185 AUTH_ACCOUNT_TYPE.
169 """ 186 """
170 self.host = host 187 self.host = host
171 if (not self.host.startswith("http://") and 188 if (not self.host.startswith("http://") and
172 not self.host.startswith("https://")): 189 not self.host.startswith("https://")):
173 self.host = "http://" + self.host 190 self.host = "http://" + self.host
174 self.host_override = host_override 191 self.host_override = host_override
175 self.auth_function = auth_function 192 self.auth_function = auth_function
176 self.authenticated = False 193 self.authenticated = False
177 self.extra_headers = extra_headers 194 self.extra_headers = extra_headers
178 self.save_cookies = save_cookies 195 self.save_cookies = save_cookies
196 self.account_type = account_type
179 self.opener = self._GetOpener() 197 self.opener = self._GetOpener()
180 if self.host_override: 198 if self.host_override:
181 logging.info("Server: %s; Host: %s", self.host, self.host_override) 199 logging.info("Server: %s; Host: %s", self.host, self.host_override)
182 else: 200 else:
183 logging.info("Server: %s", self.host) 201 logging.info("Server: %s", self.host)
184 202
185 def _GetOpener(self): 203 def _GetOpener(self):
186 """Returns an OpenerDirector for making HTTP requests. 204 """Returns an OpenerDirector for making HTTP requests.
187 205
188 Returns: 206 Returns:
(...skipping 18 matching lines...) Expand all
207 email: The user's email address 225 email: The user's email address
208 password: The user's password 226 password: The user's password
209 227
210 Raises: 228 Raises:
211 ClientLoginError: If there was an error authenticating with ClientLogin. 229 ClientLoginError: If there was an error authenticating with ClientLogin.
212 HTTPError: If there was some other form of HTTP error. 230 HTTPError: If there was some other form of HTTP error.
213 231
214 Returns: 232 Returns:
215 The authentication token returned by ClientLogin. 233 The authentication token returned by ClientLogin.
216 """ 234 """
217 account_type = "GOOGLE" 235 account_type = self.account_type
218 if self.host.endswith(".google.com"): 236 if self.host.endswith(".google.com"):
219 # Needed for use inside Google. 237 # Needed for use inside Google.
220 account_type = "HOSTED" 238 account_type = "HOSTED"
221 req = self._CreateRequest( 239 req = self._CreateRequest(
222 url="https://www.google.com/accounts/ClientLogin", 240 url="https://www.google.com/accounts/ClientLogin",
223 data=urllib.urlencode({ 241 data=urllib.urlencode({
224 "Email": email, 242 "Email": email,
225 "Passwd": password, 243 "Passwd": password,
226 "service": "ah", 244 "service": "ah",
227 "source": "rietveld-codereview-upload", 245 "source": "rietveld-codereview-upload",
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
287 try: 305 try:
288 auth_token = self._GetAuthToken(credentials[0], credentials[1]) 306 auth_token = self._GetAuthToken(credentials[0], credentials[1])
289 except ClientLoginError, e: 307 except ClientLoginError, e:
290 if e.reason == "BadAuthentication": 308 if e.reason == "BadAuthentication":
291 print >>sys.stderr, "Invalid username or password." 309 print >>sys.stderr, "Invalid username or password."
292 continue 310 continue
293 if e.reason == "CaptchaRequired": 311 if e.reason == "CaptchaRequired":
294 print >>sys.stderr, ( 312 print >>sys.stderr, (
295 "Please go to\n" 313 "Please go to\n"
296 "https://www.google.com/accounts/DisplayUnlockCaptcha\n" 314 "https://www.google.com/accounts/DisplayUnlockCaptcha\n"
297 "and verify you are a human. Then try again.") 315 "and verify you are a human. Then try again.\n"
316 "If you are using a Google Apps account the URL is:\n"
317 "https://www.google.com/a/yourdomain.com/UnlockCaptcha")
298 break 318 break
299 if e.reason == "NotVerified": 319 if e.reason == "NotVerified":
300 print >>sys.stderr, "Account not verified." 320 print >>sys.stderr, "Account not verified."
301 break 321 break
302 if e.reason == "TermsNotAgreed": 322 if e.reason == "TermsNotAgreed":
303 print >>sys.stderr, "User has not agreed to TOS." 323 print >>sys.stderr, "User has not agreed to TOS."
304 break 324 break
305 if e.reason == "AccountDeleted": 325 if e.reason == "AccountDeleted":
306 print >>sys.stderr, "The user account has been deleted." 326 print >>sys.stderr, "The user account has been deleted."
307 break 327 break
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
436 group.add_option("-q", "--quiet", action="store_const", const=0, 456 group.add_option("-q", "--quiet", action="store_const", const=0,
437 dest="verbose", help="Print errors only.") 457 dest="verbose", help="Print errors only.")
438 group.add_option("-v", "--verbose", action="store_const", const=2, 458 group.add_option("-v", "--verbose", action="store_const", const=2,
439 dest="verbose", default=1, 459 dest="verbose", default=1,
440 help="Print info level logs (default).") 460 help="Print info level logs (default).")
441 group.add_option("--noisy", action="store_const", const=3, 461 group.add_option("--noisy", action="store_const", const=3,
442 dest="verbose", help="Print all logs.") 462 dest="verbose", help="Print all logs.")
443 # Review server 463 # Review server
444 group = parser.add_option_group("Review server options") 464 group = parser.add_option_group("Review server options")
445 group.add_option("-s", "--server", action="store", dest="server", 465 group.add_option("-s", "--server", action="store", dest="server",
446 default="codereview.appspot.com", 466 default=DEFAULT_REVIEW_SERVER,
447 metavar="SERVER", 467 metavar="SERVER",
448 help=("The server to upload to. The format is host[:port]. " 468 help=("The server to upload to. The format is host[:port]. "
449 "Defaults to '%default'.")) 469 "Defaults to '%default'."))
450 group.add_option("-e", "--email", action="store", dest="email", 470 group.add_option("-e", "--email", action="store", dest="email",
451 metavar="EMAIL", default=None, 471 metavar="EMAIL", default=None,
452 help="The username to use. Will prompt if omitted.") 472 help="The username to use. Will prompt if omitted.")
453 group.add_option("-H", "--host", action="store", dest="host", 473 group.add_option("-H", "--host", action="store", dest="host",
454 metavar="HOST", default=None, 474 metavar="HOST", default=None,
455 help="Overrides the Host header sent with all RPCs.") 475 help="Overrides the Host header sent with all RPCs.")
456 group.add_option("--no_cookies", action="store_false", 476 group.add_option("--no_cookies", action="store_false",
457 dest="save_cookies", default=True, 477 dest="save_cookies", default=True,
458 help="Do not save authentication cookies to local disk.") 478 help="Do not save authentication cookies to local disk.")
479 group.add_option("--account_type", action="store", dest="account_type",
480 metavar="TYPE", default=AUTH_ACCOUNT_TYPE,
481 choices=["GOOGLE", "HOSTED"],
482 help=("Override the default account type "
483 "(defaults to '%default', "
484 "valid choices are 'GOOGLE' and 'HOSTED')."))
459 # Issue 485 # Issue
460 group = parser.add_option_group("Issue options") 486 group = parser.add_option_group("Issue options")
461 group.add_option("-d", "--description", action="store", dest="description", 487 group.add_option("-d", "--description", action="store", dest="description",
462 metavar="DESCRIPTION", default=None, 488 metavar="DESCRIPTION", default=None,
463 help="Optional description when creating an issue.") 489 help="Optional description when creating an issue.")
464 group.add_option("-f", "--description_file", action="store", 490 group.add_option("-f", "--description_file", action="store",
465 dest="description_file", metavar="DESCRIPTION_FILE", 491 dest="description_file", metavar="DESCRIPTION_FILE",
466 default=None, 492 default=None,
467 help="Optional path of a file that contains " 493 help="Optional path of a file that contains "
468 "the description when creating an issue.") 494 "the description when creating an issue.")
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
501 help="Send notification email to reviewers.") 527 help="Send notification email to reviewers.")
502 group.add_option("--vcs", action="store", dest="vcs", 528 group.add_option("--vcs", action="store", dest="vcs",
503 metavar="VCS", default=None, 529 metavar="VCS", default=None,
504 help=("Version control system (optional, usually upload.py " 530 help=("Version control system (optional, usually upload.py "
505 "already guesses the right VCS).")) 531 "already guesses the right VCS)."))
506 group.add_option("--emulate_svn_auto_props", action="store_true", 532 group.add_option("--emulate_svn_auto_props", action="store_true",
507 dest="emulate_svn_auto_props", default=False, 533 dest="emulate_svn_auto_props", default=False,
508 help=("Emulate Subversion's auto properties feature.")) 534 help=("Emulate Subversion's auto properties feature."))
509 535
510 536
511 def GetRpcServer(server, email=None, host_override=None, save_cookies=True): 537 def GetRpcServer(server, email=None, host_override=None, save_cookies=True,
538 account_type=AUTH_ACCOUNT_TYPE):
512 """Returns an instance of an AbstractRpcServer. 539 """Returns an instance of an AbstractRpcServer.
513 540
514 Args: 541 Args:
515 server: String containing the review server URL. 542 server: String containing the review server URL.
516 email: String containing user's email address. 543 email: String containing user's email address.
517 host_override: If not None, string containing an alternate hostname to use 544 host_override: If not None, string containing an alternate hostname to use
518 in the host header. 545 in the host header.
519 save_cookies: Whether authentication cookies should be saved to disk. 546 save_cookies: Whether authentication cookies should be saved to disk.
547 account_type: Account type for authentication, either 'GOOGLE'
548 or 'HOSTED'. Defaults to AUTH_ACCOUNT_TYPE.
520 549
521 Returns: 550 Returns:
522 A new AbstractRpcServer, on which RPC calls can be made. 551 A new AbstractRpcServer, on which RPC calls can be made.
523 """ 552 """
524 553
525 rpc_server_class = HttpRpcServer 554 rpc_server_class = HttpRpcServer
526 555
527 # If this is the dev_appserver, use fake authentication. 556 # If this is the dev_appserver, use fake authentication.
528 host = (host_override or server).lower() 557 host = (host_override or server).lower()
529 if host == "localhost" or host.startswith("localhost:"): 558 if host == "localhost" or host.startswith("localhost:"):
530 if email is None: 559 if email is None:
531 email = "test@example.com" 560 email = "test@example.com"
532 logging.info("Using debug user %s. Override with --email" % email) 561 logging.info("Using debug user %s. Override with --email" % email)
533 server = rpc_server_class( 562 server = rpc_server_class(
534 server, 563 server,
535 lambda: (email, "password"), 564 lambda: (email, "password"),
536 host_override=host_override, 565 host_override=host_override,
537 extra_headers={"Cookie": 566 extra_headers={"Cookie":
538 'dev_appserver_login="%s:False"' % email}, 567 'dev_appserver_login="%s:False"' % email},
539 save_cookies=save_cookies) 568 save_cookies=save_cookies,
569 account_type=account_type)
540 # Don't try to talk to ClientLogin. 570 # Don't try to talk to ClientLogin.
541 server.authenticated = True 571 server.authenticated = True
542 return server 572 return server
543 573
544 def GetUserCredentials(): 574 def GetUserCredentials():
545 """Prompts the user for a username and password.""" 575 """Prompts the user for a username and password."""
546 # Create a local alias to the email variable to avoid Python's crazy 576 # Create a local alias to the email variable to avoid Python's crazy
547 # scoping rules. 577 # scoping rules.
548 local_email = email 578 local_email = email
549 if local_email is None: 579 if local_email is None:
550 local_email = GetEmail("Email (login for uploading to %s)" % server) 580 local_email = GetEmail("Email (login for uploading to %s)" % server)
551 password = getpass.getpass("Password for %s: " % local_email) 581 password = None
582 if keyring:
583 password = keyring.get_password(host, local_email)
584 if password is not None:
585 print "Using password from system keyring."
586 else:
587 password = getpass.getpass("Password for %s: " % local_email)
588 if keyring:
589 answer = raw_input("Store password in system keyring?(y/N) ").strip()
590 if answer == "y":
591 keyring.set_password(host, local_email, password)
552 return (local_email, password) 592 return (local_email, password)
553 593
554 return rpc_server_class(server, 594 return rpc_server_class(server,
555 GetUserCredentials, 595 GetUserCredentials,
556 host_override=host_override, 596 host_override=host_override,
557 save_cookies=save_cookies) 597 save_cookies=save_cookies)
558 598
559 599
560 def EncodeMultipartFormData(fields, files): 600 def EncodeMultipartFormData(fields, files):
561 """Encode form fields for multipart/form-data. 601 """Encode form fields for multipart/form-data.
562 602
563 Args: 603 Args:
564 fields: A sequence of (name, value) elements for regular form fields. 604 fields: A sequence of (name, value) elements for regular form fields.
565 files: A sequence of (name, filename, value) elements for data to be 605 files: A sequence of (name, filename, value) elements for data to be
566 uploaded as files. 606 uploaded as files.
567 Returns: 607 Returns:
568 (content_type, body) ready for httplib.HTTP instance. 608 (content_type, body) ready for httplib.HTTP instance.
569 609
570 Source: 610 Source:
571 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306 611 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
572 """ 612 """
573 BOUNDARY = '-M-A-G-I-C---B-O-U-N-D-A-R-Y-' 613 BOUNDARY = '-M-A-G-I-C---B-O-U-N-D-A-R-Y-'
574 CRLF = '\r\n' 614 CRLF = '\r\n'
575 lines = [] 615 lines = []
576 for (key, value) in fields: 616 for (key, value) in fields:
577 lines.append('--' + BOUNDARY) 617 lines.append('--' + BOUNDARY)
578 lines.append('Content-Disposition: form-data; name="%s"' % key) 618 lines.append('Content-Disposition: form-data; name="%s"' % key)
579 lines.append('') 619 lines.append('')
620 if isinstance(value, unicode):
621 value = value.encode('utf-8')
580 lines.append(value) 622 lines.append(value)
581 for (key, filename, value) in files: 623 for (key, filename, value) in files:
582 lines.append('--' + BOUNDARY) 624 lines.append('--' + BOUNDARY)
583 lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % 625 lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' %
584 (key, filename)) 626 (key, filename))
585 lines.append('Content-Type: %s' % GetContentType(filename)) 627 lines.append('Content-Type: %s' % GetContentType(filename))
586 lines.append('') 628 lines.append('')
629 if isinstance(value, unicode):
630 value = value.encode('utf-8')
587 lines.append(value) 631 lines.append(value)
588 lines.append('--' + BOUNDARY + '--') 632 lines.append('--' + BOUNDARY + '--')
589 lines.append('') 633 lines.append('')
590 body = CRLF.join(lines) 634 body = CRLF.join(lines)
591 content_type = 'multipart/form-data; boundary=%s' % BOUNDARY 635 content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
592 return content_type, body 636 return content_type, body
593 637
594 638
595 def GetContentType(filename): 639 def GetContentType(filename):
596 """Helper to guess the content-type from the filename.""" 640 """Helper to guess the content-type from the filename."""
(...skipping 437 matching lines...) Expand 10 before | Expand all | Expand 10 after
1034 else: 1078 else:
1035 universal_newlines = True 1079 universal_newlines = True
1036 if self.rev_start: 1080 if self.rev_start:
1037 # "svn cat -r REV delete_file.txt" doesn't work. cat requires 1081 # "svn cat -r REV delete_file.txt" doesn't work. cat requires
1038 # the full URL with "@REV" appended instead of using "-r" option. 1082 # the full URL with "@REV" appended instead of using "-r" option.
1039 url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) 1083 url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start)
1040 base_content = RunShell(["svn", "cat", url], 1084 base_content = RunShell(["svn", "cat", url],
1041 universal_newlines=universal_newlines, 1085 universal_newlines=universal_newlines,
1042 silent_ok=True) 1086 silent_ok=True)
1043 else: 1087 else:
1044 base_content = RunShell(["svn", "cat", filename], 1088 base_content, ret_code = RunShellWithReturnCode(
1045 universal_newlines=universal_newlines, 1089 ["svn", "cat", filename], universal_newlines=universal_newlines)
1046 silent_ok=True) 1090 if ret_code and status[0] == "R":
1091 # It's a replaced file without local history (see issue208).
1092 # The base file needs to be fetched from the server.
1093 url = "%s/%s" % (self.svn_base, filename)
1094 base_content = RunShell(["svn", "cat", url],
1095 universal_newlines=universal_newlines,
1096 silent_ok=True)
1097 elif ret_code:
1098 ErrorExit("Got error status from 'svn cat %s'", filename)
1047 if not is_binary: 1099 if not is_binary:
1048 args = [] 1100 args = []
1049 if self.rev_start: 1101 if self.rev_start:
1050 url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) 1102 url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start)
1051 else: 1103 else:
1052 url = filename 1104 url = filename
1053 args += ["-r", "BASE"] 1105 args += ["-r", "BASE"]
1054 cmd = ["svn"] + args + ["propget", "svn:keywords", url] 1106 cmd = ["svn"] + args + ["propget", "svn:keywords", url]
1055 keywords, returncode = RunShellWithReturnCode(cmd) 1107 keywords, returncode = RunShellWithReturnCode(cmd)
1056 if keywords and not returncode: 1108 if keywords and not returncode:
(...skipping 561 matching lines...) Expand 10 before | Expand all | Expand 10 after
1618 if options.issue: 1670 if options.issue:
1619 prompt = "Message describing this patch set: " 1671 prompt = "Message describing this patch set: "
1620 else: 1672 else:
1621 prompt = "New issue subject: " 1673 prompt = "New issue subject: "
1622 message = options.message or raw_input(prompt).strip() 1674 message = options.message or raw_input(prompt).strip()
1623 if not message: 1675 if not message:
1624 ErrorExit("A non-empty message is required") 1676 ErrorExit("A non-empty message is required")
1625 rpc_server = GetRpcServer(options.server, 1677 rpc_server = GetRpcServer(options.server,
1626 options.email, 1678 options.email,
1627 options.host, 1679 options.host,
1628 options.save_cookies) 1680 options.save_cookies,
1681 options.account_type)
1629 form_fields = [("subject", message)] 1682 form_fields = [("subject", message)]
1630 if base: 1683 if base:
1631 form_fields.append(("base", base)) 1684 form_fields.append(("base", base))
1632 if options.issue: 1685 if options.issue:
1633 form_fields.append(("issue", str(options.issue))) 1686 form_fields.append(("issue", str(options.issue)))
1634 if options.email: 1687 if options.email:
1635 form_fields.append(("user", options.email)) 1688 form_fields.append(("user", options.email))
1636 if options.reviewers: 1689 if options.reviewers:
1637 for reviewer in options.reviewers.split(','): 1690 for reviewer in options.reviewers.split(','):
1638 CheckReviewer(reviewer) 1691 CheckReviewer(reviewer)
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
1712 try: 1765 try:
1713 RealMain(sys.argv) 1766 RealMain(sys.argv)
1714 except KeyboardInterrupt: 1767 except KeyboardInterrupt:
1715 print 1768 print
1716 StatusUpdate("Interrupted.") 1769 StatusUpdate("Interrupted.")
1717 sys.exit(1) 1770 sys.exit(1)
1718 1771
1719 1772
1720 if __name__ == "__main__": 1773 if __name__ == "__main__":
1721 main() 1774 main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698