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

Side by Side Diff: third_party/upload.py

Issue 1074673002: Add OAuth2 support for end users (i.e. 3-legged flow with the browser). (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: windows lineendings for depot-tools-auth.bat Created 5 years, 8 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 | « third_party/oauth2client/multistore_file.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 # coding: utf-8 2 # coding: utf-8
3 # 3 #
4 # Copyright 2007 Google Inc. 4 # Copyright 2007 Google Inc.
5 # 5 #
6 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License. 7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at 8 # You may obtain a copy of the License at
9 # 9 #
10 # http://www.apache.org/licenses/LICENSE-2.0 10 # http://www.apache.org/licenses/LICENSE-2.0
(...skipping 16 matching lines...) Expand all
27 Subversion 27 Subversion
28 Perforce 28 Perforce
29 CVS 29 CVS
30 30
31 It is important for Git/Mercurial users to specify a tree/node/branch to diff 31 It is important for Git/Mercurial users to specify a tree/node/branch to diff
32 against by using the '--rev' option. 32 against by using the '--rev' option.
33 """ 33 """
34 # This code is derived from appcfg.py in the App Engine SDK (open source), 34 # This code is derived from appcfg.py in the App Engine SDK (open source),
35 # and from ASPN recipe #146306. 35 # and from ASPN recipe #146306.
36 36
37 import BaseHTTPServer
38 import ConfigParser 37 import ConfigParser
39 import cookielib 38 import cookielib
40 import errno 39 import errno
41 import fnmatch 40 import fnmatch
42 import getpass 41 import getpass
43 import logging 42 import logging
44 import marshal 43 import marshal
45 import mimetypes 44 import mimetypes
46 import optparse 45 import optparse
47 import os 46 import os
48 import re 47 import re
49 import socket 48 import socket
50 import subprocess 49 import subprocess
51 import sys 50 import sys
52 import urllib 51 import urllib
53 import urllib2 52 import urllib2
54 import urlparse 53 import urlparse
55 import webbrowser
56 54
57 from multiprocessing.pool import ThreadPool 55 from multiprocessing.pool import ThreadPool
58 56
59 # The md5 module was deprecated in Python 2.5. 57 # The md5 module was deprecated in Python 2.5.
60 try: 58 try:
61 from hashlib import md5 59 from hashlib import md5
62 except ImportError: 60 except ImportError:
63 from md5 import md5 61 from md5 import md5
64 62
65 try: 63 try:
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 ] 117 ]
120 118
121 119
122 VCS_SHORT_NAMES = [] # hg, svn, ... 120 VCS_SHORT_NAMES = [] # hg, svn, ...
123 VCS_ABBREVIATIONS = {} # alias: name, ... 121 VCS_ABBREVIATIONS = {} # alias: name, ...
124 for vcs in VCS: 122 for vcs in VCS:
125 VCS_SHORT_NAMES.append(min(vcs['aliases'], key=len)) 123 VCS_SHORT_NAMES.append(min(vcs['aliases'], key=len))
126 VCS_ABBREVIATIONS.update((alias, vcs['name']) for alias in vcs['aliases']) 124 VCS_ABBREVIATIONS.update((alias, vcs['name']) for alias in vcs['aliases'])
127 125
128 126
129 # OAuth 2.0-Related Constants
130 LOCALHOST_IP = '127.0.0.1'
131 DEFAULT_OAUTH2_PORT = 8001
132 ACCESS_TOKEN_PARAM = 'access_token'
133 ERROR_PARAM = 'error'
134 OAUTH_DEFAULT_ERROR_MESSAGE = 'OAuth 2.0 error occurred.'
135 OAUTH_PATH = '/get-access-token'
136 OAUTH_PATH_PORT_TEMPLATE = OAUTH_PATH + '?port=%(port)d'
137 AUTH_HANDLER_RESPONSE = """\
138 <html>
139 <head>
140 <title>Authentication Status</title>
141 <script>
142 window.onload = function() {
143 window.close();
144 }
145 </script>
146 </head>
147 <body>
148 <p>The authentication flow has completed.</p>
149 </body>
150 </html>
151 """
152 # Borrowed from google-api-python-client
153 OPEN_LOCAL_MESSAGE_TEMPLATE = """\
154 Your browser has been opened to visit:
155
156 %s
157
158 If your browser is on a different machine then exit and re-run
159 upload.py with the command-line parameter
160
161 --no_oauth2_webbrowser
162 """
163 NO_OPEN_LOCAL_MESSAGE_TEMPLATE = """\
164 Go to the following link in your browser:
165
166 %s
167
168 and copy the access token.
169 """
170
171 # The result of parsing Subversion's [auto-props] setting. 127 # The result of parsing Subversion's [auto-props] setting.
172 svn_auto_props_map = None 128 svn_auto_props_map = None
173 129
174 def GetEmail(prompt): 130 def GetEmail(prompt):
175 """Prompts the user for their email address and returns it. 131 """Prompts the user for their email address and returns it.
176 132
177 The last used email address is saved to a file and offered up as a suggestion 133 The last used email address is saved to a file and offered up as a suggestion
178 to the user. If the user presses enter without typing in anything the last 134 to the user. If the user presses enter without typing in anything the last
179 used email address is used. If the user enters a new address, it is saved 135 used email address is used. If the user enters a new address, it is saved
180 for next time we prompt. 136 for next time we prompt.
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 try: 310 try:
355 response = self.opener.open(req) 311 response = self.opener.open(req)
356 except urllib2.HTTPError, e: 312 except urllib2.HTTPError, e:
357 response = e 313 response = e
358 if (response.code != 302 or 314 if (response.code != 302 or
359 response.info()["location"] != continue_location): 315 response.info()["location"] != continue_location):
360 raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg, 316 raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg,
361 response.headers, response.fp) 317 response.headers, response.fp)
362 self.authenticated = True 318 self.authenticated = True
363 319
364 def _Authenticate(self): 320 def _Authenticate(self, force_refresh):
365 """Authenticates the user. 321 """Authenticates the user.
366 322
367 The authentication process works as follows: 323 The authentication process works as follows:
368 1) We get a username and password from the user 324 1) We get a username and password from the user
369 2) We use ClientLogin to obtain an AUTH token for the user 325 2) We use ClientLogin to obtain an AUTH token for the user
370 (see http://code.google.com/apis/accounts/AuthForInstalledApps.html). 326 (see http://code.google.com/apis/accounts/AuthForInstalledApps.html).
371 3) We pass the auth token to /_ah/login on the server to obtain an 327 3) We pass the auth token to /_ah/login on the server to obtain an
372 authentication cookie. If login was successful, it tries to redirect 328 authentication cookie. If login was successful, it tries to redirect
373 us to the URL we provided. 329 us to the URL we provided.
374 330
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 included in the request (string header names mapped to their values), 415 included in the request (string header names mapped to their values),
460 or None to not include any additional headers. 416 or None to not include any additional headers.
461 kwargs: Any keyword arguments are converted into query string parameters. 417 kwargs: Any keyword arguments are converted into query string parameters.
462 418
463 Returns: 419 Returns:
464 The response body, as a string. 420 The response body, as a string.
465 """ 421 """
466 # TODO: Don't require authentication. Let the server say 422 # TODO: Don't require authentication. Let the server say
467 # whether it is necessary. 423 # whether it is necessary.
468 if not self.authenticated and self.auth_function: 424 if not self.authenticated and self.auth_function:
469 self._Authenticate() 425 self._Authenticate(force_refresh=False)
470 426
471 old_timeout = socket.getdefaulttimeout() 427 old_timeout = socket.getdefaulttimeout()
472 socket.setdefaulttimeout(timeout) 428 socket.setdefaulttimeout(timeout)
429 auth_attempted = False
473 try: 430 try:
474 tries = 0 431 tries = 0
475 while True: 432 while True:
476 tries += 1 433 tries += 1
477 args = dict(kwargs) 434 args = dict(kwargs)
478 url = "%s%s" % (self.host, request_path) 435 url = "%s%s" % (self.host, request_path)
479 if args: 436 if args:
480 url += "?" + urllib.urlencode(args) 437 url += "?" + urllib.urlencode(args)
481 req = self._CreateRequest(url=url, data=payload) 438 req = self._CreateRequest(url=url, data=payload)
482 req.add_header("Content-Type", content_type) 439 req.add_header("Content-Type", content_type)
483 if extra_headers: 440 if extra_headers:
484 for header, value in extra_headers.items(): 441 for header, value in extra_headers.items():
485 req.add_header(header, value) 442 req.add_header(header, value)
486 try: 443 try:
487 f = self.opener.open(req, timeout=70) 444 f = self.opener.open(req, timeout=70)
488 response = f.read() 445 response = f.read()
489 f.close() 446 f.close()
490 return response 447 return response
491 except urllib2.HTTPError, e: 448 except urllib2.HTTPError, e:
492 if tries > 3: 449 if tries > 3:
493 raise 450 raise
494 elif e.code == 401 or e.code == 302: 451 elif e.code in (302, 401, 403):
495 if not self.auth_function: 452 if not self.auth_function:
496 raise 453 raise
497 self._Authenticate() 454 # Already tried force refresh, didn't help -> give up with error.
455 if auth_attempted:
456 raise auth.AuthenticationError(
457 'Access to %s is denied (server returned HTTP %d).'
458 % (self.host, e.code))
459 self._Authenticate(force_refresh=True)
460 auth_attempted = True
498 elif e.code == 301: 461 elif e.code == 301:
499 # Handle permanent redirect manually. 462 # Handle permanent redirect manually.
500 url = e.info()["location"] 463 url = e.info()["location"]
501 url_loc = urlparse.urlparse(url) 464 url_loc = urlparse.urlparse(url)
502 self.host = '%s://%s' % (url_loc[0], url_loc[1]) 465 self.host = '%s://%s' % (url_loc[0], url_loc[1])
503 elif e.code >= 500: 466 elif e.code >= 500:
504 # TODO: We should error out on a 500, but the server is too flaky 467 # TODO: We should error out on a 500, but the server is too flaky
505 # for that at the moment. 468 # for that at the moment.
506 StatusUpdate('Upload got a 500 response: %d' % e.code) 469 StatusUpdate('Upload got a 500 response: %d' % e.code)
507 else: 470 else:
508 raise 471 raise
509 finally: 472 finally:
510 socket.setdefaulttimeout(old_timeout) 473 socket.setdefaulttimeout(old_timeout)
511 474
512 475
513 class HttpRpcServer(AbstractRpcServer): 476 class HttpRpcServer(AbstractRpcServer):
514 """Provides a simplified RPC-style interface for HTTP requests.""" 477 """Provides a simplified RPC-style interface for HTTP requests."""
515 478
516 def _Authenticate(self): 479 def _Authenticate(self, force_refresh):
517 """Save the cookie jar after authentication.""" 480 """Save the cookie jar after authentication."""
518 if isinstance(self.auth_function, OAuth2Creds): 481 if isinstance(self.auth_function, auth.Authenticator):
519 access_token = self.auth_function() 482 try:
520 if access_token is not None: 483 access_token = self.auth_function.get_access_token(force_refresh)
521 self.extra_headers['Authorization'] = 'OAuth %s' % (access_token,) 484 except auth.LoginRequiredError:
522 self.authenticated = True 485 # Attempt to make unauthenticated request first if there's no cached
486 # credentials. HttpRpcServer calls __Authenticate(force_refresh=True)
487 # again if unauthenticated request doesn't work.
488 if not force_refresh:
489 return
490 raise
491 self.extra_headers['Authorization'] = 'Bearer %s' % (
492 access_token.token,)
523 else: 493 else:
524 super(HttpRpcServer, self)._Authenticate() 494 super(HttpRpcServer, self)._Authenticate(force_refresh)
525 if self.save_cookies: 495 if self.save_cookies:
526 StatusUpdate("Saving authentication cookies to %s" % self.cookie_file) 496 StatusUpdate("Saving authentication cookies to %s" % self.cookie_file)
527 self.cookie_jar.save() 497 self.cookie_jar.save()
528 498
529 def _GetOpener(self): 499 def _GetOpener(self):
530 """Returns an OpenerDirector that supports cookies and ignores redirects. 500 """Returns an OpenerDirector that supports cookies and ignores redirects.
531 501
532 Returns: 502 Returns:
533 A urllib2.OpenerDirector object. 503 A urllib2.OpenerDirector object.
534 """ 504 """
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
707 metavar="P4_CHANGELIST", default=None, 677 metavar="P4_CHANGELIST", default=None,
708 help=("Perforce changelist id")) 678 help=("Perforce changelist id"))
709 group.add_option("--p4_client", action="store", dest="p4_client", 679 group.add_option("--p4_client", action="store", dest="p4_client",
710 metavar="P4_CLIENT", default=None, 680 metavar="P4_CLIENT", default=None,
711 help=("Perforce client/workspace")) 681 help=("Perforce client/workspace"))
712 group.add_option("--p4_user", action="store", dest="p4_user", 682 group.add_option("--p4_user", action="store", dest="p4_user",
713 metavar="P4_USER", default=None, 683 metavar="P4_USER", default=None,
714 help=("Perforce user")) 684 help=("Perforce user"))
715 685
716 686
717 # OAuth 2.0 Methods and Helpers
718 class ClientRedirectServer(BaseHTTPServer.HTTPServer):
719 """A server for redirects back to localhost from the associated server.
720
721 Waits for a single request and parses the query parameters for an access token
722 or an error and then stops serving.
723 """
724 access_token = None
725 error = None
726
727
728 class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler):
729 """A handler for redirects back to localhost from the associated server.
730
731 Waits for a single request and parses the query parameters into the server's
732 access_token or error and then stops serving.
733 """
734
735 def SetResponseValue(self):
736 """Stores the access token or error from the request on the server.
737
738 Will only do this if exactly one query parameter was passed in to the
739 request and that query parameter used 'access_token' or 'error' as the key.
740 """
741 query_string = urlparse.urlparse(self.path).query
742 query_params = urlparse.parse_qs(query_string)
743
744 if len(query_params) == 1:
745 if query_params.has_key(ACCESS_TOKEN_PARAM):
746 access_token_list = query_params[ACCESS_TOKEN_PARAM]
747 if len(access_token_list) == 1:
748 self.server.access_token = access_token_list[0]
749 else:
750 error_list = query_params.get(ERROR_PARAM, [])
751 if len(error_list) == 1:
752 self.server.error = error_list[0]
753
754 def do_GET(self):
755 """Handle a GET request.
756
757 Parses and saves the query parameters and prints a message that the server
758 has completed its lone task (handling a redirect).
759
760 Note that we can't detect if an error occurred.
761 """
762 self.send_response(200)
763 self.send_header('Content-type', 'text/html')
764 self.end_headers()
765 self.SetResponseValue()
766 self.wfile.write(AUTH_HANDLER_RESPONSE)
767
768 def log_message(self, format, *args):
769 """Do not log messages to stdout while running as command line program."""
770 pass
771
772
773 def OpenOAuth2ConsentPage(server=DEFAULT_REVIEW_SERVER,
774 port=DEFAULT_OAUTH2_PORT):
775 """Opens the OAuth 2.0 consent page or prints instructions how to.
776
777 Uses the webbrowser module to open the OAuth server side page in a browser.
778
779 Args:
780 server: String containing the review server URL. Defaults to
781 DEFAULT_REVIEW_SERVER.
782 port: Integer, the port where the localhost server receiving the redirect
783 is serving. Defaults to DEFAULT_OAUTH2_PORT.
784
785 Returns:
786 A boolean indicating whether the page opened successfully.
787 """
788 path = OAUTH_PATH_PORT_TEMPLATE % {'port': port}
789 parsed_url = urlparse.urlparse(server)
790 scheme = parsed_url[0] or 'https'
791 if scheme != 'https':
792 ErrorExit('Using OAuth requires a review server with SSL enabled.')
793 # If no scheme was given on command line the server address ends up in
794 # parsed_url.path otherwise in netloc.
795 host = parsed_url[1] or parsed_url[2]
796 page = '%s://%s%s' % (scheme, host, path)
797 page_opened = webbrowser.open(page, new=1, autoraise=True)
798 if page_opened:
799 print OPEN_LOCAL_MESSAGE_TEMPLATE % (page,)
800 return page_opened
801
802
803 def WaitForAccessToken(port=DEFAULT_OAUTH2_PORT):
804 """Spins up a simple HTTP Server to handle a single request.
805
806 Intended to handle a single redirect from the production server after the
807 user authenticated via OAuth 2.0 with the server.
808
809 Args:
810 port: Integer, the port where the localhost server receiving the redirect
811 is serving. Defaults to DEFAULT_OAUTH2_PORT.
812
813 Returns:
814 The access token passed to the localhost server, or None if no access token
815 was passed.
816 """
817 httpd = ClientRedirectServer((LOCALHOST_IP, port), ClientRedirectHandler)
818 # Wait to serve just one request before deferring control back
819 # to the caller of wait_for_refresh_token
820 httpd.handle_request()
821 if httpd.access_token is None:
822 ErrorExit(httpd.error or OAUTH_DEFAULT_ERROR_MESSAGE)
823 return httpd.access_token
824
825
826 def GetAccessToken(server=DEFAULT_REVIEW_SERVER, port=DEFAULT_OAUTH2_PORT,
827 open_local_webbrowser=True):
828 """Gets an Access Token for the current user.
829
830 Args:
831 server: String containing the review server URL. Defaults to
832 DEFAULT_REVIEW_SERVER.
833 port: Integer, the port where the localhost server receiving the redirect
834 is serving. Defaults to DEFAULT_OAUTH2_PORT.
835 open_local_webbrowser: Boolean, defaults to True. If set, opens a page in
836 the user's browser.
837
838 Returns:
839 A string access token that was sent to the local server. If the serving page
840 via WaitForAccessToken does not receive an access token, this method
841 returns None.
842 """
843 access_token = None
844 if open_local_webbrowser:
845 page_opened = OpenOAuth2ConsentPage(server=server, port=port)
846 if page_opened:
847 try:
848 access_token = WaitForAccessToken(port=port)
849 except socket.error, e:
850 print 'Can\'t start local webserver. Socket Error: %s\n' % (e.strerror,)
851
852 if access_token is None:
853 # TODO(dhermes): Offer to add to clipboard using xsel, xclip, pbcopy, etc.
854 page = 'https://%s%s' % (server, OAUTH_PATH)
855 print NO_OPEN_LOCAL_MESSAGE_TEMPLATE % (page,)
856 access_token = raw_input('Enter access token: ').strip()
857
858 return access_token
859
860
861 class KeyringCreds(object): 687 class KeyringCreds(object):
862 def __init__(self, server, host, email): 688 def __init__(self, server, host, email):
863 self.server = server 689 self.server = server
864 # Explicitly cast host to str to work around bug in old versions of Keyring 690 # Explicitly cast host to str to work around bug in old versions of Keyring
865 # (versions before 0.10). Even though newer versions of Keyring fix this, 691 # (versions before 0.10). Even though newer versions of Keyring fix this,
866 # some modern linuxes (such as Ubuntu 12.04) still bundle a version with 692 # some modern linuxes (such as Ubuntu 12.04) still bundle a version with
867 # the bug. 693 # the bug.
868 self.host = str(host) 694 self.host = str(host)
869 self.email = email 695 self.email = email
870 self.accounts_seen = set() 696 self.accounts_seen = set()
(...skipping 25 matching lines...) Expand all
896 else: 722 else:
897 password = getpass.getpass("Password for %s: " % email) 723 password = getpass.getpass("Password for %s: " % email)
898 if keyring: 724 if keyring:
899 answer = raw_input("Store password in system keyring?(y/N) ").strip() 725 answer = raw_input("Store password in system keyring?(y/N) ").strip()
900 if answer == "y": 726 if answer == "y":
901 keyring.set_password(self.host, email, password) 727 keyring.set_password(self.host, email, password)
902 self.accounts_seen.add(email) 728 self.accounts_seen.add(email)
903 return (email, password) 729 return (email, password)
904 730
905 731
906 class OAuth2Creds(object):
907 """Simple object to hold server and port to be passed to GetAccessToken."""
908
909 def __init__(self, server, port, open_local_webbrowser=True):
910 self.server = server
911 self.port = port
912 self.open_local_webbrowser = open_local_webbrowser
913
914 def __call__(self):
915 """Uses stored server and port to retrieve OAuth 2.0 access token."""
916 return GetAccessToken(server=self.server, port=self.port,
917 open_local_webbrowser=self.open_local_webbrowser)
918
919
920 def GetRpcServer(server, auth_config=None, email=None): 732 def GetRpcServer(server, auth_config=None, email=None):
921 """Returns an instance of an AbstractRpcServer. 733 """Returns an instance of an AbstractRpcServer.
922 734
923 Args: 735 Args:
924 server: String containing the review server URL. 736 server: String containing the review server URL.
925 auth_config: auth.AuthConfig tuple with OAuth2 configuration. 737 auth_config: auth.AuthConfig tuple with OAuth2 configuration.
926 email: String containing user's email address [deprecated]. 738 email: String containing user's email address [deprecated].
927 739
928 Returns: 740 Returns:
929 A new HttpRpcServer, on which RPC calls can be made. 741 A new HttpRpcServer, on which RPC calls can be made.
930 """ 742 """
931 # If email is given as an empty string or no auth config is passed, then 743 # If email is given as an empty string or no auth config is passed, then
932 # assume we want to make requests that do not need authentication. Bypass 744 # assume we want to make requests that do not need authentication. Bypass
933 # authentication by setting the auth_function to None. 745 # authentication by setting the auth_function to None.
934 if email == '' or not auth_config: 746 if email == '' or not auth_config:
935 return HttpRpcServer(server, None) 747 return HttpRpcServer(server, None)
936 748
937 if auth_config.use_oauth2:
938 raise NotImplementedError('See https://crbug.com/356813')
939
940 # If this is the dev_appserver, use fake authentication. 749 # If this is the dev_appserver, use fake authentication.
941 host = server.lower() 750 host = server.lower()
942 if re.match(r'(http://)?localhost([:/]|$)', host): 751 if re.match(r'(http://)?localhost([:/]|$)', host):
943 if email is None: 752 if email is None:
944 email = "test@example.com" 753 email = "test@example.com"
945 LOGGER.info("Using debug user %s. Override with --email" % email) 754 LOGGER.info("Using debug user %s. Override with --email" % email)
946 server = HttpRpcServer( 755 server = HttpRpcServer(
947 server, 756 server,
948 lambda: (email, "password"), 757 lambda: (email, "password"),
949 extra_headers={"Cookie": 758 extra_headers={"Cookie":
950 'dev_appserver_login="%s:False"' % email}, 759 'dev_appserver_login="%s:False"' % email},
951 save_cookies=auth_config.save_cookies, 760 save_cookies=auth_config.save_cookies,
952 account_type=AUTH_ACCOUNT_TYPE) 761 account_type=AUTH_ACCOUNT_TYPE)
953 # Don't try to talk to ClientLogin. 762 # Don't try to talk to ClientLogin.
954 server.authenticated = True 763 server.authenticated = True
955 return server 764 return server
956 765
766 if auth_config.use_oauth2:
767 auth_func = auth.get_authenticator_for_host(server, auth_config)
768 else:
769 auth_func = KeyringCreds(server, host, email).GetUserCredentials
770
957 return HttpRpcServer( 771 return HttpRpcServer(
958 server, 772 server,
959 KeyringCreds(server, host, email).GetUserCredentials, 773 auth_func,
960 save_cookies=auth_config.save_cookies, 774 save_cookies=auth_config.save_cookies,
961 account_type=AUTH_ACCOUNT_TYPE) 775 account_type=AUTH_ACCOUNT_TYPE)
962 776
963 777
964 def EncodeMultipartFormData(fields, files): 778 def EncodeMultipartFormData(fields, files):
965 """Encode form fields for multipart/form-data. 779 """Encode form fields for multipart/form-data.
966 780
967 Args: 781 Args:
968 fields: A sequence of (name, value) elements for regular form fields. 782 fields: A sequence of (name, value) elements for regular form fields.
969 files: A sequence of (name, filename, value) elements for data to be 783 files: A sequence of (name, filename, value) elements for data to be
(...skipping 1736 matching lines...) Expand 10 before | Expand all | Expand 10 after
2706 def main(): 2520 def main():
2707 try: 2521 try:
2708 logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" 2522 logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:"
2709 "%(lineno)s %(message)s ")) 2523 "%(lineno)s %(message)s "))
2710 os.environ['LC_ALL'] = 'C' 2524 os.environ['LC_ALL'] = 'C'
2711 RealMain(sys.argv) 2525 RealMain(sys.argv)
2712 except KeyboardInterrupt: 2526 except KeyboardInterrupt:
2713 print 2527 print
2714 StatusUpdate("Interrupted.") 2528 StatusUpdate("Interrupted.")
2715 sys.exit(1) 2529 sys.exit(1)
2530 except auth.AuthenticationError as e:
2531 print >> sys.stderr, e
2532 sys.exit(1)
2716 2533
2717 2534
2718 if __name__ == "__main__": 2535 if __name__ == "__main__":
2719 main() 2536 main()
OLDNEW
« no previous file with comments | « third_party/oauth2client/multistore_file.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698