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

Side by Side Diff: git_cl/upload.py

Issue 6685023: Make git_cl pass pylint cleanly (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: whoops Created 9 years, 9 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 | Annotate | Revision Log
« git_cl/git_cl.py ('K') | « git_cl/git_cl.py ('k') | 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 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 import urllib2 47 import urllib2
48 import urlparse 48 import urlparse
49 49
50 # The md5 module was deprecated in Python 2.5. 50 # The md5 module was deprecated in Python 2.5.
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 # pylint: disable=W0611
M-A Ruel 2011/03/13 20:45:48 Don't modify this file here, you need to modify it
58 except ImportError: 58 except ImportError:
59 pass 59 pass
60 60
61 try: 61 try:
62 import keyring 62 import keyring # pylint: disable=F0401
63 except ImportError: 63 except ImportError:
64 keyring = None 64 keyring = None
65 65
66 # don't worry about methods that could be functions
67 # pylint: disable=R0201
68
66 # The logging verbosity: 69 # The logging verbosity:
67 # 0: Errors only. 70 # 0: Errors only.
68 # 1: Status messages. 71 # 1: Status messages.
69 # 2: Info logs. 72 # 2: Info logs.
70 # 3: Debug logs. 73 # 3: Debug logs.
71 verbosity = 1 74 verbosity = 1
72 75
73 # The account type used for authentication. 76 # The account type used for authentication.
74 # This line could be changed by the review server (see handler for 77 # This line could be changed by the review server (see handler for
75 # upload.py). 78 # upload.py).
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 118
116 """ 119 """
117 last_email_file_name = os.path.expanduser("~/.last_codereview_email_address") 120 last_email_file_name = os.path.expanduser("~/.last_codereview_email_address")
118 last_email = "" 121 last_email = ""
119 if os.path.exists(last_email_file_name): 122 if os.path.exists(last_email_file_name):
120 try: 123 try:
121 last_email_file = open(last_email_file_name, "r") 124 last_email_file = open(last_email_file_name, "r")
122 last_email = last_email_file.readline().strip("\n") 125 last_email = last_email_file.readline().strip("\n")
123 last_email_file.close() 126 last_email_file.close()
124 prompt += " [%s]" % last_email 127 prompt += " [%s]" % last_email
125 except IOError, e: 128 except IOError:
126 pass 129 pass
127 email = raw_input(prompt + ": ").strip() 130 email = raw_input(prompt + ": ").strip()
128 if email: 131 if email:
129 try: 132 try:
130 last_email_file = open(last_email_file_name, "w") 133 last_email_file = open(last_email_file_name, "w")
131 last_email_file.write(email) 134 last_email_file.write(email)
132 last_email_file.close() 135 last_email_file.close()
133 except IOError, e: 136 except IOError:
134 pass 137 pass
135 else: 138 else:
136 email = last_email 139 email = last_email
137 return email 140 return email
138 141
139 142
140 def StatusUpdate(msg): 143 def StatusUpdate(msg):
141 """Print a status message to stdout. 144 """Print a status message to stdout.
142 145
143 If 'verbosity' is greater than 0, print the message. 146 If 'verbosity' is greater than 0, print the message.
144 147
145 Args: 148 Args:
146 msg: The string to print. 149 msg: The string to print.
147 """ 150 """
148 if verbosity > 0: 151 if verbosity > 0:
149 print msg 152 print msg
150 153
151 154
152 def ErrorExit(msg): 155 def ErrorExit(msg):
153 """Print an error message to stderr and exit.""" 156 """Print an error message to stderr and exit."""
154 print >>sys.stderr, msg 157 print >> sys.stderr, msg
155 sys.exit(1) 158 sys.exit(1)
156 159
157 160
158 class ClientLoginError(urllib2.HTTPError): 161 class ClientLoginError(urllib2.HTTPError):
159 """Raised to indicate there was an error authenticating with ClientLogin.""" 162 """Raised to indicate there was an error authenticating with ClientLogin."""
160 163
161 def __init__(self, url, code, msg, headers, args): 164 def __init__(self, url, code, msg, headers, args):
162 urllib2.HTTPError.__init__(self, url, code, msg, headers, None) 165 urllib2.HTTPError.__init__(self, url, code, msg, headers, None)
163 self.args = args 166 self.args = args
164 self.reason = args["Error"] 167 self.reason = args["Error"]
165 168
166 169
167 class AbstractRpcServer(object): 170 class AbstractRpcServer(object):
168 """Provides a common interface for a simple RPC server.""" 171 """Provides a common interface for a simple RPC server."""
169 172
170 def __init__(self, host, auth_function, host_override=None, extra_headers={}, 173 def __init__(self, host, auth_function, host_override=None,
171 save_cookies=False, account_type=AUTH_ACCOUNT_TYPE): 174 extra_headers=None, save_cookies=False,
175 account_type=AUTH_ACCOUNT_TYPE):
172 """Creates a new HttpRpcServer. 176 """Creates a new HttpRpcServer.
173 177
174 Args: 178 Args:
175 host: The host to send requests to. 179 host: The host to send requests to.
176 auth_function: A function that takes no arguments and returns an 180 auth_function: A function that takes no arguments and returns an
177 (email, password) tuple when called. Will be called if authentication 181 (email, password) tuple when called. Will be called if authentication
178 is required. 182 is required.
179 host_override: The host header to send to the server (defaults to host). 183 host_override: The host header to send to the server (defaults to host).
180 extra_headers: A dict of extra headers to append to every request. 184 extra_headers: A dict of extra headers to append to every request.
181 save_cookies: If True, save the authentication cookies to local disk. 185 save_cookies: If True, save the authentication cookies to local disk.
182 If False, use an in-memory cookiejar instead. Subclasses must 186 If False, use an in-memory cookiejar instead. Subclasses must
183 implement this functionality. Defaults to False. 187 implement this functionality. Defaults to False.
184 account_type: Account type used for authentication. Defaults to 188 account_type: Account type used for authentication. Defaults to
185 AUTH_ACCOUNT_TYPE. 189 AUTH_ACCOUNT_TYPE.
186 """ 190 """
191 extra_headers = extra_headers or {}
187 self.host = host 192 self.host = host
188 if (not self.host.startswith("http://") and 193 if (not self.host.startswith("http://") and
189 not self.host.startswith("https://")): 194 not self.host.startswith("https://")):
190 self.host = "http://" + self.host 195 self.host = "http://" + self.host
191 assert re.match(r'^[a-z]+://[a-z0-9\.-_]+(|:[0-9]+)$', self.host), ( 196 assert re.match(r'^[a-z]+://[a-z0-9\.-_]+(|:[0-9]+)$', self.host), (
192 '%s is malformed' % host) 197 '%s is malformed' % host)
193 self.host_override = host_override 198 self.host_override = host_override
194 self.auth_function = auth_function 199 self.auth_function = auth_function
195 self.authenticated = False 200 self.authenticated = False
196 self.extra_headers = extra_headers 201 self.extra_headers = extra_headers
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 2) We use ClientLogin to obtain an AUTH token for the user 315 2) We use ClientLogin to obtain an AUTH token for the user
311 (see http://code.google.com/apis/accounts/AuthForInstalledApps.html). 316 (see http://code.google.com/apis/accounts/AuthForInstalledApps.html).
312 3) We pass the auth token to /_ah/login on the server to obtain an 317 3) We pass the auth token to /_ah/login on the server to obtain an
313 authentication cookie. If login was successful, it tries to redirect 318 authentication cookie. If login was successful, it tries to redirect
314 us to the URL we provided. 319 us to the URL we provided.
315 320
316 If we attempt to access the upload API without first obtaining an 321 If we attempt to access the upload API without first obtaining an
317 authentication cookie, it returns a 401 response (or a 302) and 322 authentication cookie, it returns a 401 response (or a 302) and
318 directs us to authenticate ourselves with ClientLogin. 323 directs us to authenticate ourselves with ClientLogin.
319 """ 324 """
320 for i in range(3): 325 for i in range(3): # pylint: disable=W0612
321 credentials = self.auth_function() 326 credentials = self.auth_function()
322 try: 327 try:
323 auth_token = self._GetAuthToken(host, credentials[0], credentials[1]) 328 auth_token = self._GetAuthToken(host, credentials[0], credentials[1])
324 except ClientLoginError, e: 329 except ClientLoginError, e:
325 if e.reason == "BadAuthentication": 330 if e.reason == "BadAuthentication":
326 print >>sys.stderr, "Invalid username or password." 331 print >> sys.stderr, "Invalid username or password."
327 continue 332 continue
328 if e.reason == "CaptchaRequired": 333 if e.reason == "CaptchaRequired":
329 print >>sys.stderr, ( 334 print >> sys.stderr, (
330 "Please go to\n" 335 "Please go to\n"
331 "https://www.google.com/accounts/DisplayUnlockCaptcha\n" 336 "https://www.google.com/accounts/DisplayUnlockCaptcha\n"
332 "and verify you are a human. Then try again.\n" 337 "and verify you are a human. Then try again.\n"
333 "If you are using a Google Apps account the URL is:\n" 338 "If you are using a Google Apps account the URL is:\n"
334 "https://www.google.com/a/yourdomain.com/UnlockCaptcha") 339 "https://www.google.com/a/yourdomain.com/UnlockCaptcha")
335 break 340 break
336 if e.reason == "NotVerified": 341 if e.reason == "NotVerified":
337 print >>sys.stderr, "Account not verified." 342 print >> sys.stderr, "Account not verified."
338 break 343 break
339 if e.reason == "TermsNotAgreed": 344 if e.reason == "TermsNotAgreed":
340 print >>sys.stderr, "User has not agreed to TOS." 345 print >> sys.stderr, "User has not agreed to TOS."
341 break 346 break
342 if e.reason == "AccountDeleted": 347 if e.reason == "AccountDeleted":
343 print >>sys.stderr, "The user account has been deleted." 348 print >> sys.stderr, "The user account has been deleted."
344 break 349 break
345 if e.reason == "AccountDisabled": 350 if e.reason == "AccountDisabled":
346 print >>sys.stderr, "The user account has been disabled." 351 print >> sys.stderr, "The user account has been disabled."
347 break 352 break
348 if e.reason == "ServiceDisabled": 353 if e.reason == "ServiceDisabled":
349 print >>sys.stderr, ("The user's access to the service has been " 354 print >> sys.stderr, ("The user's access to the service has been "
350 "disabled.") 355 "disabled.")
351 break 356 break
352 if e.reason == "ServiceUnavailable": 357 if e.reason == "ServiceUnavailable":
353 print >>sys.stderr, "The service is not available; try again later." 358 print >> sys.stderr, "The service is not available; try again later."
354 break 359 break
355 raise 360 raise
356 self._GetAuthCookie(host, auth_token) 361 self._GetAuthCookie(host, auth_token)
357 return 362 return
358 363
359 def Send(self, request_path, payload=None, 364 def Send(self, request_path, payload=None,
360 content_type="application/octet-stream", 365 content_type="application/octet-stream",
361 timeout=None, 366 timeout=None,
362 extra_headers=None, 367 extra_headers=None,
363 **kwargs): 368 **kwargs):
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
444 Returns: 449 Returns:
445 A urllib2.OpenerDirector object. 450 A urllib2.OpenerDirector object.
446 """ 451 """
447 opener = urllib2.OpenerDirector() 452 opener = urllib2.OpenerDirector()
448 opener.add_handler(urllib2.ProxyHandler()) 453 opener.add_handler(urllib2.ProxyHandler())
449 opener.add_handler(urllib2.UnknownHandler()) 454 opener.add_handler(urllib2.UnknownHandler())
450 opener.add_handler(urllib2.HTTPHandler()) 455 opener.add_handler(urllib2.HTTPHandler())
451 opener.add_handler(urllib2.HTTPDefaultErrorHandler()) 456 opener.add_handler(urllib2.HTTPDefaultErrorHandler())
452 opener.add_handler(urllib2.HTTPSHandler()) 457 opener.add_handler(urllib2.HTTPSHandler())
453 opener.add_handler(urllib2.HTTPErrorProcessor()) 458 opener.add_handler(urllib2.HTTPErrorProcessor())
459 # pylint: disable=W0201
454 if self.save_cookies: 460 if self.save_cookies:
455 self.cookie_file = os.path.expanduser("~/.codereview_upload_cookies") 461 self.cookie_file = os.path.expanduser("~/.codereview_upload_cookies")
456 self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file) 462 self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file)
457 if os.path.exists(self.cookie_file): 463 if os.path.exists(self.cookie_file):
458 try: 464 try:
459 self.cookie_jar.load() 465 self.cookie_jar.load()
460 self.authenticated = True 466 self.authenticated = True
461 StatusUpdate("Loaded authentication cookies from %s" % 467 StatusUpdate("Loaded authentication cookies from %s" %
462 self.cookie_file) 468 self.cookie_file)
463 except (cookielib.LoadError, IOError): 469 except (cookielib.LoadError, IOError):
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
668 674
669 def GetContentType(filename): 675 def GetContentType(filename):
670 """Helper to guess the content-type from the filename.""" 676 """Helper to guess the content-type from the filename."""
671 return mimetypes.guess_type(filename)[0] or 'application/octet-stream' 677 return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
672 678
673 679
674 # Use a shell for subcommands on Windows to get a PATH search. 680 # Use a shell for subcommands on Windows to get a PATH search.
675 use_shell = sys.platform.startswith("win") 681 use_shell = sys.platform.startswith("win")
676 682
677 def RunShellWithReturnCode(command, print_output=False, 683 def RunShellWithReturnCode(command, print_output=False,
678 universal_newlines=True, 684 universal_newlines=True, env=None):
679 env=os.environ):
680 """Executes a command and returns the output from stdout and the return code. 685 """Executes a command and returns the output from stdout and the return code.
681 686
682 Args: 687 Args:
683 command: Command to execute. 688 command: Command to execute.
684 print_output: If True, the output is printed to stdout. 689 print_output: If True, the output is printed to stdout.
685 If False, both stdout and stderr are ignored. 690 If False, both stdout and stderr are ignored.
686 universal_newlines: Use universal_newlines flag (default: True). 691 universal_newlines: Use universal_newlines flag (default: True).
687 692
688 Returns: 693 Returns:
689 Tuple (output, return code) 694 Tuple (output, return code)
690 """ 695 """
691 logging.info("Running %s", command) 696 logging.info("Running %s", command)
692 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 697 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
693 shell=use_shell, universal_newlines=universal_newlines, 698 shell=use_shell, universal_newlines=universal_newlines,
694 env=env) 699 env=env)
695 if print_output: 700 if print_output:
696 output_array = [] 701 output_array = []
697 while True: 702 while True:
698 line = p.stdout.readline() 703 line = p.stdout.readline()
699 if not line: 704 if not line:
700 break 705 break
701 print line.strip("\n") 706 print line.strip("\n")
702 output_array.append(line) 707 output_array.append(line)
703 output = "".join(output_array) 708 output = "".join(output_array)
704 else: 709 else:
705 output = p.stdout.read() 710 output = p.stdout.read()
706 p.wait() 711 p.wait()
707 errout = p.stderr.read() 712 errout = p.stderr.read()
708 if print_output and errout: 713 if print_output and errout:
709 print >>sys.stderr, errout 714 print >> sys.stderr, errout
710 p.stdout.close() 715 p.stdout.close()
711 p.stderr.close() 716 p.stderr.close()
712 return output, p.returncode 717 return output, p.returncode
713 718
714 719
715 def RunShell(command, silent_ok=False, universal_newlines=True, 720 def RunShell(command, silent_ok=False, universal_newlines=True,
716 print_output=False, env=os.environ): 721 print_output=False, env=None):
717 data, retcode = RunShellWithReturnCode(command, print_output, 722 data, retcode = RunShellWithReturnCode(command, print_output,
718 universal_newlines, env) 723 universal_newlines, env=env)
719 if retcode: 724 if retcode:
720 ErrorExit("Got error status from %s:\n%s" % (command, data)) 725 ErrorExit("Got error status from %s:\n%s" % (command, data))
721 if not silent_ok and not data: 726 if not silent_ok and not data:
722 ErrorExit("No output from %s" % command) 727 ErrorExit("No output from %s" % command)
723 return data 728 return data
724 729
725 730
726 class VersionControlSystem(object): 731 class VersionControlSystem(object):
727 """Abstract base class providing an interface to the VCS.""" 732 """Abstract base class providing an interface to the VCS."""
728 733
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
786 """Helper that calls GetBase file for each file in the patch. 791 """Helper that calls GetBase file for each file in the patch.
787 792
788 Returns: 793 Returns:
789 A dictionary that maps from filename to GetBaseFile's tuple. Filenames 794 A dictionary that maps from filename to GetBaseFile's tuple. Filenames
790 are retrieved based on lines that start with "Index:" or 795 are retrieved based on lines that start with "Index:" or
791 "Property changes on:". 796 "Property changes on:".
792 """ 797 """
793 files = {} 798 files = {}
794 for line in diff.splitlines(True): 799 for line in diff.splitlines(True):
795 if line.startswith('Index:') or line.startswith('Property changes on:'): 800 if line.startswith('Index:') or line.startswith('Property changes on:'):
796 unused, filename = line.split(':', 1) 801 _, filename = line.split(':', 1)
797 # On Windows if a file has property changes its filename uses '\' 802 # On Windows if a file has property changes its filename uses '\'
798 # instead of '/'. 803 # instead of '/'.
799 filename = filename.strip().replace('\\', '/') 804 filename = filename.strip().replace('\\', '/')
800 files[filename] = self.GetBaseFile(filename) 805 files[filename] = self.GetBaseFile(filename)
801 return files 806 return files
802 807
803 808
804 def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options, 809 def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options,
805 files): 810 files):
806 """Uploads the base files (and if necessary, the current ones as well).""" 811 """Uploads the base files (and if necessary, the current ones as well)."""
807 812
808 def UploadFile(filename, file_id, content, is_binary, status, is_base): 813 def UploadFile(filename, file_id, content, is_binary, status, is_base):
809 """Uploads a file to the server.""" 814 """Uploads a file to the server."""
810 file_too_large = False 815 file_too_large = False
811 if is_base: 816 if is_base:
812 type = "base" 817 file_type = "base"
813 else: 818 else:
814 type = "current" 819 file_type = "current"
815 if len(content) > MAX_UPLOAD_SIZE: 820 if len(content) > MAX_UPLOAD_SIZE:
816 print ("Not uploading the %s file for %s because it's too large." % 821 print ("Not uploading the %s file for %s because it's too large." %
817 (type, filename)) 822 (type, filename))
818 file_too_large = True 823 file_too_large = True
819 content = "" 824 content = ""
820 checksum = md5(content).hexdigest() 825 checksum = md5(content).hexdigest()
821 if options.verbose > 0 and not file_too_large: 826 if options.verbose > 0 and not file_too_large:
822 print "Uploading %s file for %s" % (type, filename) 827 print "Uploading %s file for %s" % (file_type, filename)
823 url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id) 828 url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id)
824 form_fields = [("filename", filename), 829 form_fields = [("filename", filename),
825 ("status", status), 830 ("status", status),
826 ("checksum", checksum), 831 ("checksum", checksum),
827 ("is_binary", str(is_binary)), 832 ("is_binary", str(is_binary)),
828 ("is_current", str(not is_base)), 833 ("is_current", str(not is_base)),
829 ] 834 ]
830 if file_too_large: 835 if file_too_large:
831 form_fields.append(("file_too_large", "1")) 836 form_fields.append(("file_too_large", "1"))
832 if options.email: 837 if options.email:
833 form_fields.append(("user", options.email)) 838 form_fields.append(("user", options.email))
834 ctype, body = EncodeMultipartFormData(form_fields, 839 ctype, body = EncodeMultipartFormData(form_fields,
835 [("data", filename, content)]) 840 [("data", filename, content)])
836 response_body = rpc_server.Send(url, body, 841 response_body = rpc_server.Send(url, body,
837 content_type=ctype) 842 content_type=ctype)
838 if not response_body.startswith("OK"): 843 if not response_body.startswith("OK"):
839 StatusUpdate(" --> %s" % response_body) 844 StatusUpdate(" --> %s" % response_body)
840 sys.exit(1) 845 sys.exit(1)
841 846
842 patches = dict() 847 patches = dict()
843 [patches.setdefault(v, k) for k, v in patch_list] 848 for k, v in patch_list:
849 patches.setdefault(v, k)
844 for filename in patches.keys(): 850 for filename in patches.keys():
845 base_content, new_content, is_binary, status = files[filename] 851 base_content, new_content, is_binary, status = files[filename]
846 file_id_str = patches.get(filename) 852 file_id_str = patches.get(filename)
847 if file_id_str.find("nobase") != -1: 853 if file_id_str.find("nobase") != -1:
848 base_content = None 854 base_content = None
849 file_id_str = file_id_str[file_id_str.rfind("_") + 1:] 855 file_id_str = file_id_str[file_id_str.rfind("_") + 1:]
850 file_id = int(file_id_str) 856 file_id = int(file_id_str)
851 if base_content != None: 857 if base_content != None:
852 UploadFile(filename, file_id, base_content, is_binary, status, True) 858 UploadFile(filename, file_id, base_content, is_binary, status, True)
853 if new_content != None: 859 if new_content != None:
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
959 'Id': ['Id'], 965 'Id': ['Id'],
960 966
961 # Aliases 967 # Aliases
962 'LastChangedDate': ['LastChangedDate', 'Date'], 968 'LastChangedDate': ['LastChangedDate', 'Date'],
963 'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'], 969 'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'],
964 'LastChangedBy': ['LastChangedBy', 'Author'], 970 'LastChangedBy': ['LastChangedBy', 'Author'],
965 'URL': ['URL', 'HeadURL'], 971 'URL': ['URL', 'HeadURL'],
966 } 972 }
967 973
968 def repl(m): 974 def repl(m):
969 if m.group(2): 975 if m.group(2):
970 return "$%s::%s$" % (m.group(1), " " * len(m.group(3))) 976 return "$%s::%s$" % (m.group(1), " " * len(m.group(3)))
971 return "$%s$" % m.group(1) 977 return "$%s$" % m.group(1)
972 keywords = [keyword 978 keywords = [keyword
973 for name in keyword_str.split(" ") 979 for name in keyword_str.split(" ")
974 for keyword in svn_keywords.get(name, [])] 980 for keyword in svn_keywords.get(name, [])]
975 return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content) 981 return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content)
976 982
977 def GetUnknownFiles(self): 983 def GetUnknownFiles(self):
978 status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True) 984 status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True)
979 unknown_files = [] 985 unknown_files = []
980 for line in status.split("\n"): 986 for line in status.split("\n"):
981 if line and line[0] == "?": 987 if line and line[0] == "?":
982 unknown_files.append(line) 988 unknown_files.append(line)
983 return unknown_files 989 return unknown_files
984 990
985 def ReadFile(self, filename): 991 def ReadFile(self, filename):
986 """Returns the contents of a file.""" 992 """Returns the contents of a file."""
987 file = open(filename, 'rb') 993 f = open(filename, 'rb')
988 result = "" 994 result = ""
989 try: 995 try:
990 result = file.read() 996 result = f.read()
991 finally: 997 finally:
992 file.close() 998 f.close()
993 return result 999 return result
994 1000
995 def GetStatus(self, filename): 1001 def GetStatus(self, filename):
996 """Returns the status of a file.""" 1002 """Returns the status of a file."""
997 if not self.options.revision: 1003 if not self.options.revision:
998 status = RunShell(["svn", "status", "--ignore-externals", filename]) 1004 status = RunShell(["svn", "status", "--ignore-externals", filename])
999 if not status: 1005 if not status:
1000 ErrorExit("svn status returned no output for %s" % filename) 1006 ErrorExit("svn status returned no output for %s" % filename)
1001 status_lines = status.splitlines() 1007 status_lines = status.splitlines()
1002 # If file is in a cl, the output will begin with 1008 # If file is in a cl, the output will begin with
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
1205 if self.options.revision: 1211 if self.options.revision:
1206 if ":" in self.options.revision: 1212 if ":" in self.options.revision:
1207 extra_args = self.options.revision.split(":", 1) + extra_args 1213 extra_args = self.options.revision.split(":", 1) + extra_args
1208 else: 1214 else:
1209 extra_args = [self.options.revision] + extra_args 1215 extra_args = [self.options.revision] + extra_args
1210 1216
1211 # --no-ext-diff is broken in some versions of Git, so try to work around 1217 # --no-ext-diff is broken in some versions of Git, so try to work around
1212 # this by overriding the environment (but there is still a problem if the 1218 # this by overriding the environment (but there is still a problem if the
1213 # git config key "diff.external" is used). 1219 # git config key "diff.external" is used).
1214 env = os.environ.copy() 1220 env = os.environ.copy()
1215 if 'GIT_EXTERNAL_DIFF' in env: del env['GIT_EXTERNAL_DIFF'] 1221 if 'GIT_EXTERNAL_DIFF' in env:
1222 del env['GIT_EXTERNAL_DIFF']
1216 return RunShell(["git", "diff", "--no-ext-diff", "--full-index", "-M"] 1223 return RunShell(["git", "diff", "--no-ext-diff", "--full-index", "-M"]
1217 + extra_args, env=env) 1224 + extra_args, env=env)
1218 1225
1219 def GetUnknownFiles(self): 1226 def GetUnknownFiles(self):
1220 status = RunShell(["git", "ls-files", "--exclude-standard", "--others"], 1227 status = RunShell(["git", "ls-files", "--exclude-standard", "--others"],
1221 silent_ok=True) 1228 silent_ok=True)
1222 return status.splitlines() 1229 return status.splitlines()
1223 1230
1224 def GetFileContent(self, file_hash, is_binary): 1231 def GetFileContent(self, file_hash, is_binary):
1225 """Returns the content of a file identified by its git hash.""" 1232 """Returns the content of a file identified by its git hash."""
1226 data, retcode = RunShellWithReturnCode(["git", "show", file_hash], 1233 data, retcode = RunShellWithReturnCode(["git", "show", file_hash],
1227 universal_newlines=not is_binary) 1234 universal_newlines=not is_binary)
1228 if retcode: 1235 if retcode:
1229 ErrorExit("Got error status from 'git show %s'" % file_hash) 1236 ErrorExit("Got error status from 'git show %s'" % file_hash)
1230 return data 1237 return data
1231 1238
1232 def GetBaseFile(self, filename): 1239 def GetBaseFile(self, filename):
1233 hash_before, hash_after = self.hashes.get(filename, (None,None)) 1240 hash_before, hash_after = self.hashes.get(filename, (None, None))
1234 base_content = None 1241 base_content = None
1235 new_content = None 1242 new_content = None
1236 is_binary = self.IsBinary(filename) 1243 is_binary = self.IsBinary(filename)
1237 status = None 1244 status = None
1238 1245
1239 if filename in self.renames: 1246 if filename in self.renames:
1240 status = "A +" # Match svn attribute name for renames. 1247 status = "A +" # Match svn attribute name for renames.
1241 if filename not in self.hashes: 1248 if filename not in self.hashes:
1242 # If a rename doesn't change the content, we never get a hash. 1249 # If a rename doesn't change the content, we never get a hash.
1243 base_content = RunShell(["git", "show", "HEAD:" + filename]) 1250 base_content = RunShell(["git", "show", "HEAD:" + filename])
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
1306 filecount += 1 1313 filecount += 1
1307 logging.info(line) 1314 logging.info(line)
1308 else: 1315 else:
1309 svndiff.append(line) 1316 svndiff.append(line)
1310 if not filecount: 1317 if not filecount:
1311 ErrorExit("No valid patches found in output from hg diff") 1318 ErrorExit("No valid patches found in output from hg diff")
1312 return "\n".join(svndiff) + "\n" 1319 return "\n".join(svndiff) + "\n"
1313 1320
1314 def GetUnknownFiles(self): 1321 def GetUnknownFiles(self):
1315 """Return a list of files unknown to the VCS.""" 1322 """Return a list of files unknown to the VCS."""
1316 args = []
1317 status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."], 1323 status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."],
1318 silent_ok=True) 1324 silent_ok=True)
1319 unknown_files = [] 1325 unknown_files = []
1320 for line in status.splitlines(): 1326 for line in status.splitlines():
1321 st, fn = line.split(" ", 1) 1327 st, fn = line.split(" ", 1)
1322 if st == "?": 1328 if st == "?":
1323 unknown_files.append(fn) 1329 unknown_files.append(fn)
1324 return unknown_files 1330 return unknown_files
1325 1331
1326 def GetBaseFile(self, filename): 1332 def GetBaseFile(self, filename):
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
1374 Returns: 1380 Returns:
1375 A list of 2-tuple (filename, text) where text is the svn diff output 1381 A list of 2-tuple (filename, text) where text is the svn diff output
1376 pertaining to filename. 1382 pertaining to filename.
1377 """ 1383 """
1378 patches = [] 1384 patches = []
1379 filename = None 1385 filename = None
1380 diff = [] 1386 diff = []
1381 for line in data.splitlines(True): 1387 for line in data.splitlines(True):
1382 new_filename = None 1388 new_filename = None
1383 if line.startswith('Index:'): 1389 if line.startswith('Index:'):
1384 unused, new_filename = line.split(':', 1) 1390 _, new_filename = line.split(':', 1)
1385 new_filename = new_filename.strip() 1391 new_filename = new_filename.strip()
1386 elif line.startswith('Property changes on:'): 1392 elif line.startswith('Property changes on:'):
1387 unused, temp_filename = line.split(':', 1) 1393 _, temp_filename = line.split(':', 1)
1388 # When a file is modified, paths use '/' between directories, however 1394 # When a file is modified, paths use '/' between directories, however
1389 # when a property is modified '\' is used on Windows. Make them the same 1395 # when a property is modified '\' is used on Windows. Make them the same
1390 # otherwise the file shows up twice. 1396 # otherwise the file shows up twice.
1391 temp_filename = temp_filename.strip().replace('\\', '/') 1397 temp_filename = temp_filename.strip().replace('\\', '/')
1392 if temp_filename != filename: 1398 if temp_filename != filename:
1393 # File has property changes but no modifications, create a new diff. 1399 # File has property changes but no modifications, create a new diff.
1394 new_filename = temp_filename 1400 new_filename = temp_filename
1395 if new_filename: 1401 if new_filename:
1396 if filename and diff: 1402 if filename and diff:
1397 patches.append((filename, ''.join(diff))) 1403 patches.append((filename, ''.join(diff)))
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
1445 output is a string containing any interesting output from the vcs 1451 output is a string containing any interesting output from the vcs
1446 detection routine, or None if there is nothing interesting. 1452 detection routine, or None if there is nothing interesting.
1447 """ 1453 """
1448 # Mercurial has a command to get the base directory of a repository 1454 # Mercurial has a command to get the base directory of a repository
1449 # Try running it, but don't die if we don't have hg installed. 1455 # Try running it, but don't die if we don't have hg installed.
1450 # NOTE: we try Mercurial first as it can sit on top of an SVN working copy. 1456 # NOTE: we try Mercurial first as it can sit on top of an SVN working copy.
1451 try: 1457 try:
1452 out, returncode = RunShellWithReturnCode(["hg", "root"]) 1458 out, returncode = RunShellWithReturnCode(["hg", "root"])
1453 if returncode == 0: 1459 if returncode == 0:
1454 return (VCS_MERCURIAL, out.strip()) 1460 return (VCS_MERCURIAL, out.strip())
1455 except OSError, (errno, message): 1461 except OSError, e:
1456 if errno != 2: # ENOENT -- they don't have hg installed. 1462 if e.errno != 2: # ENOENT -- they don't have hg installed.
1457 raise 1463 raise
1458 1464
1459 # Subversion has a .svn in all working directories. 1465 # Subversion has a .svn in all working directories.
1460 if os.path.isdir('.svn'): 1466 if os.path.isdir('.svn'):
1461 logging.info("Guessed VCS = Subversion") 1467 logging.info("Guessed VCS = Subversion")
1462 return (VCS_SUBVERSION, None) 1468 return (VCS_SUBVERSION, None)
1463 1469
1464 # Git has a command to test if you're in a git tree. 1470 # Git has a command to test if you're in a git tree.
1465 # Try running it, but don't die if we don't have git installed. 1471 # Try running it, but don't die if we don't have git installed.
1466 try: 1472 try:
1467 out, returncode = RunShellWithReturnCode(["git", "rev-parse", 1473 out, returncode = RunShellWithReturnCode(["git", "rev-parse",
1468 "--is-inside-work-tree"]) 1474 "--is-inside-work-tree"])
1469 if returncode == 0: 1475 if returncode == 0:
1470 return (VCS_GIT, None) 1476 return (VCS_GIT, None)
1471 except OSError, (errno, message): 1477 except OSError, e:
1472 if errno != 2: # ENOENT -- they don't have git installed. 1478 if e.errno != 2: # ENOENT -- they don't have git installed.
1473 raise 1479 raise
1474 1480
1475 return (VCS_UNKNOWN, None) 1481 return (VCS_UNKNOWN, None)
1476 1482
1477 1483
1478 def GuessVCS(options): 1484 def GuessVCS(options):
1479 """Helper to guess the version control system. 1485 """Helper to guess the version control system.
1480 1486
1481 This verifies any user-specified VersionControlSystem (by command line 1487 This verifies any user-specified VersionControlSystem (by command line
1482 or environment variable). If the user didn't specify one, this examines 1488 or environment variable). If the user didn't specify one, this examines
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after
1711 CheckReviewer(reviewer) 1717 CheckReviewer(reviewer)
1712 form_fields.append(("reviewers", options.reviewers)) 1718 form_fields.append(("reviewers", options.reviewers))
1713 if options.cc: 1719 if options.cc:
1714 for cc in options.cc.split(','): 1720 for cc in options.cc.split(','):
1715 CheckReviewer(cc) 1721 CheckReviewer(cc)
1716 form_fields.append(("cc", options.cc)) 1722 form_fields.append(("cc", options.cc))
1717 description = options.description 1723 description = options.description
1718 if options.description_file: 1724 if options.description_file:
1719 if options.description: 1725 if options.description:
1720 ErrorExit("Can't specify description and description_file") 1726 ErrorExit("Can't specify description and description_file")
1721 file = open(options.description_file, 'r') 1727 f = open(options.description_file, 'r')
1722 description = file.read() 1728 description = f.read()
1723 file.close() 1729 f.close()
1724 if description: 1730 if description:
1725 form_fields.append(("description", description)) 1731 form_fields.append(("description", description))
1726 # Send a hash of all the base file so the server can determine if a copy 1732 # Send a hash of all the base file so the server can determine if a copy
1727 # already exists in an earlier patchset. 1733 # already exists in an earlier patchset.
1728 base_hashes = "" 1734 base_hashes = ""
1729 for file, info in files.iteritems(): 1735 for filename, info in files.iteritems():
1730 if not info[0] is None: 1736 if not info[0] is None:
1731 checksum = md5(info[0]).hexdigest() 1737 checksum = md5(info[0]).hexdigest()
1732 if base_hashes: 1738 if base_hashes:
1733 base_hashes += "|" 1739 base_hashes += "|"
1734 base_hashes += checksum + ":" + file 1740 base_hashes += checksum + ":" + filename
1735 form_fields.append(("base_hashes", base_hashes)) 1741 form_fields.append(("base_hashes", base_hashes))
1736 if options.private: 1742 if options.private:
1737 if options.issue: 1743 if options.issue:
1738 print "Warning: Private flag ignored when updating an existing issue." 1744 print "Warning: Private flag ignored when updating an existing issue."
1739 else: 1745 else:
1740 form_fields.append(("private", "1")) 1746 form_fields.append(("private", "1"))
1741 # If we're uploading base files, don't send the email before the uploads, so 1747 # If we're uploading base files, don't send the email before the uploads, so
1742 # that it contains the file status. 1748 # that it contains the file status.
1743 if options.send_mail and options.download_base: 1749 if options.send_mail and options.download_base:
1744 form_fields.append(("send_mail", "1")) 1750 form_fields.append(("send_mail", "1"))
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1785 try: 1791 try:
1786 RealMain(sys.argv) 1792 RealMain(sys.argv)
1787 except KeyboardInterrupt: 1793 except KeyboardInterrupt:
1788 print 1794 print
1789 StatusUpdate("Interrupted.") 1795 StatusUpdate("Interrupted.")
1790 sys.exit(1) 1796 sys.exit(1)
1791 1797
1792 1798
1793 if __name__ == "__main__": 1799 if __name__ == "__main__":
1794 main() 1800 main()
OLDNEW
« git_cl/git_cl.py ('K') | « git_cl/git_cl.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698