Making oauth2client/ files pass PEP8.
This commit is contained in:
@@ -58,8 +58,8 @@ class PyCryptoVerifier(object):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
key_pem: string, public key in PEM format.
|
key_pem: string, public key in PEM format.
|
||||||
is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it is
|
is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it
|
||||||
expected to be an RSA key in PEM format.
|
is expected to be an RSA key in PEM format.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Verifier instance.
|
Verifier instance.
|
||||||
@@ -123,6 +123,7 @@ class PyCryptoSigner(object):
|
|||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'PKCS12 format is not supported by the PyCrypto library. '
|
'PKCS12 format is not supported by the PyCrypto library. '
|
||||||
'Try converting to a "PEM" '
|
'Try converting to a "PEM" '
|
||||||
'(openssl pkcs12 -in xxxxx.p12 -nodes -nocerts > privatekey.pem) '
|
'(openssl pkcs12 -in xxxxx.p12 -nodes -nocerts > '
|
||||||
|
'privatekey.pem) '
|
||||||
'or using PyOpenSSL if native code is an option.')
|
'or using PyOpenSSL if native code is an option.')
|
||||||
return PyCryptoSigner(pkey)
|
return PyCryptoSigner(pkey)
|
||||||
|
|||||||
@@ -132,7 +132,8 @@ def xsrf_secret_key():
|
|||||||
model.secret = _generate_new_xsrf_secret_key()
|
model.secret = _generate_new_xsrf_secret_key()
|
||||||
model.put()
|
model.put()
|
||||||
secret = model.secret
|
secret = model.secret
|
||||||
memcache.add(XSRF_MEMCACHE_ID, secret, namespace=OAUTH2CLIENT_NAMESPACE)
|
memcache.add(XSRF_MEMCACHE_ID, secret,
|
||||||
|
namespace=OAUTH2CLIENT_NAMESPACE)
|
||||||
|
|
||||||
return str(secret)
|
return str(secret)
|
||||||
|
|
||||||
@@ -166,7 +167,8 @@ class AppAssertionCredentials(AssertionCredentials):
|
|||||||
self._kwargs = kwargs
|
self._kwargs = kwargs
|
||||||
self.service_account_id = kwargs.get('service_account_id', None)
|
self.service_account_id = kwargs.get('service_account_id', None)
|
||||||
|
|
||||||
# Assertion type is no longer used, but still in the parent class signature.
|
# Assertion type is no longer used, but still in the
|
||||||
|
# parent class signature.
|
||||||
super(AppAssertionCredentials, self).__init__(None)
|
super(AppAssertionCredentials, self).__init__(None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -199,7 +201,8 @@ class AppAssertionCredentials(AssertionCredentials):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def serialization_data(self):
|
def serialization_data(self):
|
||||||
raise NotImplementedError('Cannot serialize credentials for AppEngine.')
|
raise NotImplementedError('Cannot serialize credentials '
|
||||||
|
'for Google App Engine.')
|
||||||
|
|
||||||
def create_scoped_required(self):
|
def create_scoped_required(self):
|
||||||
return not self.scope
|
return not self.scope
|
||||||
@@ -220,8 +223,8 @@ class FlowProperty(db.Property):
|
|||||||
|
|
||||||
# For writing to datastore.
|
# For writing to datastore.
|
||||||
def get_value_for_datastore(self, model_instance):
|
def get_value_for_datastore(self, model_instance):
|
||||||
flow = super(FlowProperty,
|
flow = super(FlowProperty, self).get_value_for_datastore(
|
||||||
self).get_value_for_datastore(model_instance)
|
model_instance)
|
||||||
return db.Blob(pickle.dumps(flow))
|
return db.Blob(pickle.dumps(flow))
|
||||||
|
|
||||||
# For reading from datastore.
|
# For reading from datastore.
|
||||||
@@ -266,7 +269,8 @@ if ndb is not None:
|
|||||||
logger.info('validate: Got type %s', type(value))
|
logger.info('validate: Got type %s', type(value))
|
||||||
if value is not None and not isinstance(value, Flow):
|
if value is not None and not isinstance(value, Flow):
|
||||||
raise TypeError('Property %s must be convertible to a flow '
|
raise TypeError('Property %s must be convertible to a flow '
|
||||||
'instance; received: %s.' % (self._name, value))
|
'instance; received: %s.' % (self._name,
|
||||||
|
value))
|
||||||
|
|
||||||
|
|
||||||
class CredentialsProperty(db.Property):
|
class CredentialsProperty(db.Property):
|
||||||
@@ -282,8 +286,8 @@ class CredentialsProperty(db.Property):
|
|||||||
# For writing to datastore.
|
# For writing to datastore.
|
||||||
def get_value_for_datastore(self, model_instance):
|
def get_value_for_datastore(self, model_instance):
|
||||||
logger.info("get: Got type " + str(type(model_instance)))
|
logger.info("get: Got type " + str(type(model_instance)))
|
||||||
cred = super(CredentialsProperty,
|
cred = super(CredentialsProperty, self).get_value_for_datastore(
|
||||||
self).get_value_for_datastore(model_instance)
|
model_instance)
|
||||||
if cred is None:
|
if cred is None:
|
||||||
cred = ''
|
cred = ''
|
||||||
else:
|
else:
|
||||||
@@ -310,14 +314,10 @@ class CredentialsProperty(db.Property):
|
|||||||
raise db.BadValueError('Property %s must be convertible '
|
raise db.BadValueError('Property %s must be convertible '
|
||||||
'to a Credentials instance (%s)' %
|
'to a Credentials instance (%s)' %
|
||||||
(self.name, value))
|
(self.name, value))
|
||||||
#if value is not None and not isinstance(value, Credentials):
|
|
||||||
# return None
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
if ndb is not None:
|
if ndb is not None:
|
||||||
|
|
||||||
|
|
||||||
# TODO(dhermes): Turn this into a JsonProperty and overhaul the Credentials
|
# TODO(dhermes): Turn this into a JsonProperty and overhaul the Credentials
|
||||||
# and subclass mechanics to use new_from_dict, to_dict,
|
# and subclass mechanics to use new_from_dict, to_dict,
|
||||||
# from_dict, etc.
|
# from_dict, etc.
|
||||||
@@ -344,8 +344,9 @@ if ndb is not None:
|
|||||||
"""
|
"""
|
||||||
logger.info('validate: Got type %s', type(value))
|
logger.info('validate: Got type %s', type(value))
|
||||||
if value is not None and not isinstance(value, Credentials):
|
if value is not None and not isinstance(value, Credentials):
|
||||||
raise TypeError('Property %s must be convertible to a credentials '
|
raise TypeError('Property %s must be convertible to a '
|
||||||
'instance; received: %s.' % (self._name, value))
|
'credentials instance; received: %s.' %
|
||||||
|
(self._name, value))
|
||||||
|
|
||||||
def _to_base_type(self, value):
|
def _to_base_type(self, value):
|
||||||
"""Converts our validated value to a JSON serialized string.
|
"""Converts our validated value to a JSON serialized string.
|
||||||
@@ -409,7 +410,8 @@ class StorageByKeyName(Storage):
|
|||||||
"""
|
"""
|
||||||
if key_name is None:
|
if key_name is None:
|
||||||
if user is None:
|
if user is None:
|
||||||
raise ValueError('StorageByKeyName called with no key name or user.')
|
raise ValueError('StorageByKeyName called with no '
|
||||||
|
'key name or user.')
|
||||||
key_name = user.user_id()
|
key_name = user.user_id()
|
||||||
|
|
||||||
self._model = model
|
self._model = model
|
||||||
@@ -423,15 +425,17 @@ class StorageByKeyName(Storage):
|
|||||||
Returns:
|
Returns:
|
||||||
Boolean indicating whether or not the model is an NDB or DB model.
|
Boolean indicating whether or not the model is an NDB or DB model.
|
||||||
"""
|
"""
|
||||||
# issubclass will fail if one of the arguments is not a class, only need
|
# issubclass will fail if one of the arguments is not a class, only
|
||||||
# worry about new-style classes since ndb and db models are new-style
|
# need worry about new-style classes since ndb and db models are
|
||||||
|
# new-style
|
||||||
if isinstance(self._model, type):
|
if isinstance(self._model, type):
|
||||||
if ndb is not None and issubclass(self._model, ndb.Model):
|
if ndb is not None and issubclass(self._model, ndb.Model):
|
||||||
return True
|
return True
|
||||||
elif issubclass(self._model, db.Model):
|
elif issubclass(self._model, db.Model):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
raise TypeError('Model class not an NDB or DB model: %s.' % (self._model, ))
|
raise TypeError('Model class not an NDB or DB model: %s.' %
|
||||||
|
(self._model,))
|
||||||
|
|
||||||
def _get_entity(self):
|
def _get_entity(self):
|
||||||
"""Retrieve entity from datastore.
|
"""Retrieve entity from datastore.
|
||||||
@@ -640,7 +644,6 @@ class OAuth2Decorator(object):
|
|||||||
_credentials_class=CredentialsModel,
|
_credentials_class=CredentialsModel,
|
||||||
_credentials_property_name='credentials',
|
_credentials_property_name='credentials',
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
"""Constructor for OAuth2Decorator
|
"""Constructor for OAuth2Decorator
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -659,13 +662,14 @@ class OAuth2Decorator(object):
|
|||||||
provider can be used.
|
provider can be used.
|
||||||
user_agent: string, User agent of your application, default to
|
user_agent: string, User agent of your application, default to
|
||||||
None.
|
None.
|
||||||
message: Message to display if there are problems with the OAuth 2.0
|
message: Message to display if there are problems with the
|
||||||
configuration. The message may contain HTML and will be
|
OAuth 2.0 configuration. The message may contain HTML and
|
||||||
presented on the web interface for any method that uses
|
will be presented on the web interface for any method that
|
||||||
the decorator.
|
uses the decorator.
|
||||||
callback_path: string, The absolute path to use as the callback
|
callback_path: string, The absolute path to use as the callback
|
||||||
URI. Note that this must match up with the URI given
|
URI. Note that this must match up with the URI given
|
||||||
when registering the application in the APIs Console.
|
when registering the application in the APIs
|
||||||
|
Console.
|
||||||
token_response_param: string. If provided, the full JSON response
|
token_response_param: string. If provided, the full JSON response
|
||||||
to the access token request will be encoded
|
to the access token request will be encoded
|
||||||
and included in this query parameter in the
|
and included in this query parameter in the
|
||||||
@@ -730,7 +734,8 @@ class OAuth2Decorator(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
user = users.get_current_user()
|
user = users.get_current_user()
|
||||||
# Don't use @login_decorator as this could be used in a POST request.
|
# Don't use @login_decorator as this could be used in a
|
||||||
|
# POST request.
|
||||||
if not user:
|
if not user:
|
||||||
request_handler.redirect(users.create_login_url(
|
request_handler.redirect(users.create_login_url(
|
||||||
request_handler.request.uri))
|
request_handler.request.uri))
|
||||||
@@ -739,7 +744,8 @@ class OAuth2Decorator(object):
|
|||||||
self._create_flow(request_handler)
|
self._create_flow(request_handler)
|
||||||
|
|
||||||
# Store the request URI in 'state' so we can use it later
|
# Store the request URI in 'state' so we can use it later
|
||||||
self.flow.params['state'] = _build_state_value(request_handler, user)
|
self.flow.params['state'] = _build_state_value(
|
||||||
|
request_handler, user)
|
||||||
self.credentials = self._storage_class(
|
self.credentials = self._storage_class(
|
||||||
self._credentials_class, None,
|
self._credentials_class, None,
|
||||||
self._credentials_property_name, user=user).get()
|
self._credentials_property_name, user=user).get()
|
||||||
@@ -769,13 +775,11 @@ class OAuth2Decorator(object):
|
|||||||
if self.flow is None:
|
if self.flow is None:
|
||||||
redirect_uri = request_handler.request.relative_url(
|
redirect_uri = request_handler.request.relative_url(
|
||||||
self._callback_path) # Usually /oauth2callback
|
self._callback_path) # Usually /oauth2callback
|
||||||
self.flow = OAuth2WebServerFlow(self._client_id, self._client_secret,
|
self.flow = OAuth2WebServerFlow(
|
||||||
self._scope, redirect_uri=redirect_uri,
|
self._client_id, self._client_secret, self._scope,
|
||||||
user_agent=self._user_agent,
|
redirect_uri=redirect_uri, user_agent=self._user_agent,
|
||||||
auth_uri=self._auth_uri,
|
auth_uri=self._auth_uri, token_uri=self._token_uri,
|
||||||
token_uri=self._token_uri,
|
revoke_uri=self._revoke_uri, **self._kwargs)
|
||||||
revoke_uri=self._revoke_uri,
|
|
||||||
**self._kwargs)
|
|
||||||
|
|
||||||
def oauth_aware(self, method):
|
def oauth_aware(self, method):
|
||||||
"""Decorator that sets up for OAuth 2.0 dance, but doesn't do it.
|
"""Decorator that sets up for OAuth 2.0 dance, but doesn't do it.
|
||||||
@@ -797,7 +801,8 @@ class OAuth2Decorator(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
user = users.get_current_user()
|
user = users.get_current_user()
|
||||||
# Don't use @login_decorator as this could be used in a POST request.
|
# Don't use @login_decorator as this could be used in a
|
||||||
|
# POST request.
|
||||||
if not user:
|
if not user:
|
||||||
request_handler.redirect(users.create_login_url(
|
request_handler.redirect(users.create_login_url(
|
||||||
request_handler.request.uri))
|
request_handler.request.uri))
|
||||||
@@ -805,7 +810,8 @@ class OAuth2Decorator(object):
|
|||||||
|
|
||||||
self._create_flow(request_handler)
|
self._create_flow(request_handler)
|
||||||
|
|
||||||
self.flow.params['state'] = _build_state_value(request_handler, user)
|
self.flow.params['state'] = _build_state_value(request_handler,
|
||||||
|
user)
|
||||||
self.credentials = self._storage_class(
|
self.credentials = self._storage_class(
|
||||||
self._credentials_class, None,
|
self._credentials_class, None,
|
||||||
self._credentials_property_name, user=user).get()
|
self._credentials_property_name, user=user).get()
|
||||||
@@ -885,21 +891,26 @@ class OAuth2Decorator(object):
|
|||||||
if error:
|
if error:
|
||||||
errormsg = self.request.get('error_description', error)
|
errormsg = self.request.get('error_description', error)
|
||||||
self.response.out.write(
|
self.response.out.write(
|
||||||
'The authorization request failed: %s' % _safe_html(errormsg))
|
'The authorization request failed: %s' %
|
||||||
|
_safe_html(errormsg))
|
||||||
else:
|
else:
|
||||||
user = users.get_current_user()
|
user = users.get_current_user()
|
||||||
decorator._create_flow(self)
|
decorator._create_flow(self)
|
||||||
credentials = decorator.flow.step2_exchange(self.request.params)
|
credentials = decorator.flow.step2_exchange(
|
||||||
|
self.request.params)
|
||||||
decorator._storage_class(
|
decorator._storage_class(
|
||||||
decorator._credentials_class, None,
|
decorator._credentials_class, None,
|
||||||
decorator._credentials_property_name, user=user).put(credentials)
|
decorator._credentials_property_name,
|
||||||
redirect_uri = _parse_state_value(str(self.request.get('state')),
|
user=user).put(credentials)
|
||||||
user)
|
redirect_uri = _parse_state_value(
|
||||||
|
str(self.request.get('state')), user)
|
||||||
|
|
||||||
if decorator._token_response_param and credentials.token_response:
|
if (decorator._token_response_param and
|
||||||
|
credentials.token_response):
|
||||||
resp_json = json.dumps(credentials.token_response)
|
resp_json = json.dumps(credentials.token_response)
|
||||||
redirect_uri = util._add_query_parameter(
|
redirect_uri = util._add_query_parameter(
|
||||||
redirect_uri, decorator._token_response_param, resp_json)
|
redirect_uri, decorator._token_response_param,
|
||||||
|
resp_json)
|
||||||
|
|
||||||
self.redirect(redirect_uri)
|
self.redirect(redirect_uri)
|
||||||
|
|
||||||
@@ -959,11 +970,13 @@ class OAuth2DecoratorFromClientSecrets(OAuth2Decorator):
|
|||||||
**kwargs: dict, Keyword arguments are passed along as kwargs to
|
**kwargs: dict, Keyword arguments are passed along as kwargs to
|
||||||
the OAuth2WebServerFlow constructor.
|
the OAuth2WebServerFlow constructor.
|
||||||
"""
|
"""
|
||||||
client_type, client_info = clientsecrets.loadfile(filename, cache=cache)
|
client_type, client_info = clientsecrets.loadfile(filename,
|
||||||
if client_type not in [
|
cache=cache)
|
||||||
clientsecrets.TYPE_WEB, clientsecrets.TYPE_INSTALLED]:
|
if client_type not in (clientsecrets.TYPE_WEB,
|
||||||
|
clientsecrets.TYPE_INSTALLED):
|
||||||
raise InvalidClientSecretsError(
|
raise InvalidClientSecretsError(
|
||||||
"OAuth2Decorator doesn't support this OAuth 2.0 flow.")
|
"OAuth2Decorator doesn't support this OAuth 2.0 flow.")
|
||||||
|
|
||||||
constructor_kwargs = dict(kwargs)
|
constructor_kwargs = dict(kwargs)
|
||||||
constructor_kwargs.update({
|
constructor_kwargs.update({
|
||||||
'auth_uri': client_info['auth_uri'],
|
'auth_uri': client_info['auth_uri'],
|
||||||
|
|||||||
@@ -93,12 +93,13 @@ _CLOUDSDK_CONFIG_ENV_VAR = 'CLOUDSDK_CONFIG'
|
|||||||
# The error message we show users when we can't find the Application
|
# The error message we show users when we can't find the Application
|
||||||
# Default Credentials.
|
# Default Credentials.
|
||||||
ADC_HELP_MSG = (
|
ADC_HELP_MSG = (
|
||||||
'The Application Default Credentials are not available. They are available '
|
'The Application Default Credentials are not available. They are '
|
||||||
'if running in Google Compute Engine. Otherwise, the environment variable '
|
'available if running in Google Compute Engine. Otherwise, the '
|
||||||
+ GOOGLE_APPLICATION_CREDENTIALS +
|
'environment variable ' +
|
||||||
|
GOOGLE_APPLICATION_CREDENTIALS +
|
||||||
' must be defined pointing to a file defining the credentials. See '
|
' must be defined pointing to a file defining the credentials. See '
|
||||||
'https://developers.google.com/accounts/docs/application-default-credentials' # pylint:disable=line-too-long
|
'https://developers.google.com/accounts/docs/'
|
||||||
' for more information.')
|
'application-default-credentials for more information.')
|
||||||
|
|
||||||
# The access token along with the seconds in which it expires.
|
# The access token along with the seconds in which it expires.
|
||||||
AccessTokenInfo = collections.namedtuple(
|
AccessTokenInfo = collections.namedtuple(
|
||||||
@@ -283,16 +284,19 @@ class Credentials(object):
|
|||||||
"""
|
"""
|
||||||
json_string_as_unicode = _from_bytes(s)
|
json_string_as_unicode = _from_bytes(s)
|
||||||
data = json.loads(json_string_as_unicode)
|
data = json.loads(json_string_as_unicode)
|
||||||
# Find and call the right classmethod from_json() to restore the object.
|
# Find and call the right classmethod from_json() to restore
|
||||||
|
# the object.
|
||||||
module_name = data['_module']
|
module_name = data['_module']
|
||||||
try:
|
try:
|
||||||
module_obj = __import__(module_name)
|
module_obj = __import__(module_name)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# In case there's an object from the old package structure, update it
|
# In case there's an object from the old package structure,
|
||||||
|
# update it
|
||||||
module_name = module_name.replace('.googleapiclient', '')
|
module_name = module_name.replace('.googleapiclient', '')
|
||||||
module_obj = __import__(module_name)
|
module_obj = __import__(module_name)
|
||||||
|
|
||||||
module_obj = __import__(module_name, fromlist=module_name.split('.')[:-1])
|
module_obj = __import__(module_name,
|
||||||
|
fromlist=module_name.split('.')[:-1])
|
||||||
kls = getattr(module_obj, data['_class'])
|
kls = getattr(module_obj, data['_class'])
|
||||||
from_json = getattr(kls, 'from_json')
|
from_json = getattr(kls, 'from_json')
|
||||||
return from_json(json_string_as_unicode)
|
return from_json(json_string_as_unicode)
|
||||||
@@ -522,10 +526,9 @@ class OAuth2Credentials(Credentials):
|
|||||||
|
|
||||||
The modified http.request method will add authentication headers to
|
The modified http.request method will add authentication headers to
|
||||||
each request and will refresh access_tokens when a 401 is received on a
|
each request and will refresh access_tokens when a 401 is received on a
|
||||||
request. In addition the http.request method has a credentials property,
|
request. In addition the http.request method has a credentials
|
||||||
http.request.credentials, which is the Credentials object that
|
property, http.request.credentials, which is the Credentials object
|
||||||
authorized it.
|
that authorized it.
|
||||||
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
http: An instance of ``httplib2.Http`` or something that acts
|
http: An instance of ``httplib2.Http`` or something that acts
|
||||||
@@ -552,7 +555,8 @@ class OAuth2Credentials(Credentials):
|
|||||||
redirections=httplib2.DEFAULT_MAX_REDIRECTS,
|
redirections=httplib2.DEFAULT_MAX_REDIRECTS,
|
||||||
connection_type=None):
|
connection_type=None):
|
||||||
if not self.access_token:
|
if not self.access_token:
|
||||||
logger.info('Attempting refresh to obtain initial access_token')
|
logger.info('Attempting refresh to obtain '
|
||||||
|
'initial access_token')
|
||||||
self._refresh(request_orig)
|
self._refresh(request_orig)
|
||||||
|
|
||||||
# Clone and modify the request headers to add the appropriate
|
# Clone and modify the request headers to add the appropriate
|
||||||
@@ -565,7 +569,8 @@ class OAuth2Credentials(Credentials):
|
|||||||
|
|
||||||
if self.user_agent is not None:
|
if self.user_agent is not None:
|
||||||
if 'user-agent' in headers:
|
if 'user-agent' in headers:
|
||||||
headers['user-agent'] = self.user_agent + ' ' + headers['user-agent']
|
headers['user-agent'] = (self.user_agent + ' ' +
|
||||||
|
headers['user-agent'])
|
||||||
else:
|
else:
|
||||||
headers['user-agent'] = self.user_agent
|
headers['user-agent'] = self.user_agent
|
||||||
|
|
||||||
@@ -574,23 +579,26 @@ class OAuth2Credentials(Credentials):
|
|||||||
('read', 'seek', 'tell')):
|
('read', 'seek', 'tell')):
|
||||||
body_stream_position = body.tell()
|
body_stream_position = body.tell()
|
||||||
|
|
||||||
resp, content = request_orig(uri, method, body, clean_headers(headers),
|
resp, content = request_orig(uri, method, body,
|
||||||
|
clean_headers(headers),
|
||||||
redirections, connection_type)
|
redirections, connection_type)
|
||||||
|
|
||||||
# A stored token may expire between the time it is retrieved and the time
|
# A stored token may expire between the time it is retrieved and
|
||||||
# the request is made, so we may need to try twice.
|
# the time the request is made, so we may need to try twice.
|
||||||
max_refresh_attempts = 2
|
max_refresh_attempts = 2
|
||||||
for refresh_attempt in range(max_refresh_attempts):
|
for refresh_attempt in range(max_refresh_attempts):
|
||||||
if resp.status not in REFRESH_STATUS_CODES:
|
if resp.status not in REFRESH_STATUS_CODES:
|
||||||
break
|
break
|
||||||
logger.info('Refreshing due to a %s (attempt %s/%s)', resp.status,
|
logger.info('Refreshing due to a %s (attempt %s/%s)',
|
||||||
refresh_attempt + 1, max_refresh_attempts)
|
resp.status, refresh_attempt + 1,
|
||||||
|
max_refresh_attempts)
|
||||||
self._refresh(request_orig)
|
self._refresh(request_orig)
|
||||||
self.apply(headers)
|
self.apply(headers)
|
||||||
if body_stream_position is not None:
|
if body_stream_position is not None:
|
||||||
body.seek(body_stream_position)
|
body.seek(body_stream_position)
|
||||||
|
|
||||||
resp, content = request_orig(uri, method, body, clean_headers(headers),
|
resp, content = request_orig(uri, method, body,
|
||||||
|
clean_headers(headers),
|
||||||
redirections, connection_type)
|
redirections, connection_type)
|
||||||
|
|
||||||
return (resp, content)
|
return (resp, content)
|
||||||
@@ -869,8 +877,8 @@ class OAuth2Credentials(Credentials):
|
|||||||
if self.store:
|
if self.store:
|
||||||
self.store.locked_put(self)
|
self.store.locked_put(self)
|
||||||
else:
|
else:
|
||||||
# An {'error':...} response body means the token is expired or revoked,
|
# An {'error':...} response body means the token is expired or
|
||||||
# so we flag the credentials as such.
|
# revoked, so we flag the credentials as such.
|
||||||
logger.info('Failed to retrieve access token: %s', content)
|
logger.info('Failed to retrieve access token: %s', content)
|
||||||
error_msg = 'Invalid response %s.' % resp['status']
|
error_msg = 'Invalid response %s.' % resp['status']
|
||||||
try:
|
try:
|
||||||
@@ -955,7 +963,8 @@ class OAuth2Credentials(Credentials):
|
|||||||
"""
|
"""
|
||||||
logger.info('Refreshing scopes')
|
logger.info('Refreshing scopes')
|
||||||
query_params = {'access_token': token, 'fields': 'scope'}
|
query_params = {'access_token': token, 'fields': 'scope'}
|
||||||
token_info_uri = _update_query_params(self.token_info_uri, query_params)
|
token_info_uri = _update_query_params(self.token_info_uri,
|
||||||
|
query_params)
|
||||||
resp, content = http_request(token_info_uri)
|
resp, content = http_request(token_info_uri)
|
||||||
content = _from_bytes(content)
|
content = _from_bytes(content)
|
||||||
if resp.status == 200:
|
if resp.status == 200:
|
||||||
@@ -1166,8 +1175,8 @@ class GoogleCredentials(OAuth2Credentials):
|
|||||||
is None.
|
is None.
|
||||||
"""
|
"""
|
||||||
super(GoogleCredentials, self).__init__(
|
super(GoogleCredentials, self).__init__(
|
||||||
access_token, client_id, client_secret, refresh_token, token_expiry,
|
access_token, client_id, client_secret, refresh_token,
|
||||||
token_uri, user_agent, revoke_uri=revoke_uri)
|
token_expiry, token_uri, user_agent, revoke_uri=revoke_uri)
|
||||||
|
|
||||||
def create_scoped_required(self):
|
def create_scoped_required(self):
|
||||||
"""Whether this Credentials object is scopeless.
|
"""Whether this Credentials object is scopeless.
|
||||||
@@ -1257,14 +1266,16 @@ class GoogleCredentials(OAuth2Credentials):
|
|||||||
if not credentials_filename:
|
if not credentials_filename:
|
||||||
return
|
return
|
||||||
|
|
||||||
# If we can read the credentials from a file, we don't need to know what
|
# If we can read the credentials from a file, we don't need to know
|
||||||
# environment we are in.
|
# what environment we are in.
|
||||||
SETTINGS.env_name = DEFAULT_ENV_NAME
|
SETTINGS.env_name = DEFAULT_ENV_NAME
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return _get_application_default_credential_from_file(credentials_filename)
|
return _get_application_default_credential_from_file(
|
||||||
|
credentials_filename)
|
||||||
except (ApplicationDefaultCredentialsError, ValueError) as error:
|
except (ApplicationDefaultCredentialsError, ValueError) as error:
|
||||||
_raise_exception_for_reading_json(credentials_filename, extra_help, error)
|
_raise_exception_for_reading_json(credentials_filename,
|
||||||
|
extra_help, error)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_implicit_credentials(cls):
|
def _get_implicit_credentials(cls):
|
||||||
@@ -1325,7 +1336,8 @@ class GoogleCredentials(OAuth2Credentials):
|
|||||||
return _get_application_default_credential_from_file(
|
return _get_application_default_credential_from_file(
|
||||||
credential_filename)
|
credential_filename)
|
||||||
except (ApplicationDefaultCredentialsError, ValueError) as error:
|
except (ApplicationDefaultCredentialsError, ValueError) as error:
|
||||||
extra_help = ' (provided as parameter to the from_stream() method)'
|
extra_help = (' (provided as parameter to the '
|
||||||
|
'from_stream() method)')
|
||||||
_raise_exception_for_reading_json(credential_filename,
|
_raise_exception_for_reading_json(credential_filename,
|
||||||
extra_help,
|
extra_help,
|
||||||
error)
|
error)
|
||||||
@@ -1384,7 +1396,8 @@ def _get_environment_variable_file():
|
|||||||
return application_default_credential_filename
|
return application_default_credential_filename
|
||||||
else:
|
else:
|
||||||
raise ApplicationDefaultCredentialsError(
|
raise ApplicationDefaultCredentialsError(
|
||||||
'File ' + application_default_credential_filename + ' (pointed by ' +
|
'File ' + application_default_credential_filename +
|
||||||
|
' (pointed by ' +
|
||||||
GOOGLE_APPLICATION_CREDENTIALS +
|
GOOGLE_APPLICATION_CREDENTIALS +
|
||||||
' environment variable) does not exist!')
|
' environment variable) does not exist!')
|
||||||
|
|
||||||
@@ -1403,7 +1416,8 @@ def _get_well_known_file():
|
|||||||
default_config_dir = os.path.join(os.environ['APPDATA'],
|
default_config_dir = os.path.join(os.environ['APPDATA'],
|
||||||
_CLOUDSDK_CONFIG_DIRECTORY)
|
_CLOUDSDK_CONFIG_DIRECTORY)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# This should never happen unless someone is really messing with things.
|
# This should never happen unless someone is really
|
||||||
|
# messing with things.
|
||||||
drive = os.environ.get('SystemDrive', 'C:')
|
drive = os.environ.get('SystemDrive', 'C:')
|
||||||
default_config_dir = os.path.join(drive, '\\',
|
default_config_dir = os.path.join(drive, '\\',
|
||||||
_CLOUDSDK_CONFIG_DIRECTORY)
|
_CLOUDSDK_CONFIG_DIRECTORY)
|
||||||
@@ -1786,9 +1800,10 @@ def credentials_from_code(client_id, client_secret, scope, code,
|
|||||||
access token
|
access token
|
||||||
"""
|
"""
|
||||||
flow = OAuth2WebServerFlow(client_id, client_secret, scope,
|
flow = OAuth2WebServerFlow(client_id, client_secret, scope,
|
||||||
redirect_uri=redirect_uri, user_agent=user_agent,
|
redirect_uri=redirect_uri,
|
||||||
auth_uri=auth_uri, token_uri=token_uri,
|
user_agent=user_agent, auth_uri=auth_uri,
|
||||||
revoke_uri=revoke_uri, device_uri=device_uri,
|
token_uri=token_uri, revoke_uri=revoke_uri,
|
||||||
|
device_uri=device_uri,
|
||||||
token_info_uri=token_info_uri)
|
token_info_uri=token_info_uri)
|
||||||
|
|
||||||
credentials = flow.step2_exchange(code, http=http)
|
credentials = flow.step2_exchange(code, http=http)
|
||||||
@@ -1836,8 +1851,8 @@ def credentials_from_clientsecrets_and_code(filename, scope, code,
|
|||||||
clientsecrets.InvalidClientSecretsError: if the clientsecrets file is
|
clientsecrets.InvalidClientSecretsError: if the clientsecrets file is
|
||||||
invalid.
|
invalid.
|
||||||
"""
|
"""
|
||||||
flow = flow_from_clientsecrets(filename, scope, message=message, cache=cache,
|
flow = flow_from_clientsecrets(filename, scope, message=message,
|
||||||
redirect_uri=redirect_uri,
|
cache=cache, redirect_uri=redirect_uri,
|
||||||
device_uri=device_uri)
|
device_uri=device_uri)
|
||||||
credentials = flow.step2_exchange(code, http=http)
|
credentials = flow.step2_exchange(code, http=http)
|
||||||
return credentials
|
return credentials
|
||||||
@@ -1875,9 +1890,9 @@ class DeviceFlowInfo(collections.namedtuple('DeviceFlowInfo', (
|
|||||||
'user_code_expiry': None,
|
'user_code_expiry': None,
|
||||||
})
|
})
|
||||||
if 'expires_in' in response:
|
if 'expires_in' in response:
|
||||||
kwargs['user_code_expiry'] = datetime.datetime.now() + datetime.timedelta(
|
kwargs['user_code_expiry'] = (
|
||||||
seconds=int(response['expires_in']))
|
datetime.datetime.now() +
|
||||||
|
datetime.timedelta(seconds=int(response['expires_in'])))
|
||||||
return cls(**kwargs)
|
return cls(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
@@ -1915,7 +1930,8 @@ class OAuth2WebServerFlow(Flow):
|
|||||||
redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob'
|
redirect_uri: string, Either the string 'urn:ietf:wg:oauth:2.0:oob'
|
||||||
for a non-web-based application, or a URI that
|
for a non-web-based application, or a URI that
|
||||||
handles the callback from the authorization server.
|
handles the callback from the authorization server.
|
||||||
user_agent: string, HTTP User-Agent to provide for this application.
|
user_agent: string, HTTP User-Agent to provide for this
|
||||||
|
application.
|
||||||
auth_uri: string, URI for authorization endpoint. For convenience
|
auth_uri: string, URI for authorization endpoint. For convenience
|
||||||
defaults to Google's endpoints but any OAuth 2.0 provider
|
defaults to Google's endpoints but any OAuth 2.0 provider
|
||||||
can be used.
|
can be used.
|
||||||
@@ -1982,8 +1998,9 @@ class OAuth2WebServerFlow(Flow):
|
|||||||
if redirect_uri is not None:
|
if redirect_uri is not None:
|
||||||
logger.warning((
|
logger.warning((
|
||||||
'The redirect_uri parameter for '
|
'The redirect_uri parameter for '
|
||||||
'OAuth2WebServerFlow.step1_get_authorize_url is deprecated. Please '
|
'OAuth2WebServerFlow.step1_get_authorize_url is deprecated. '
|
||||||
'move to passing the redirect_uri in via the constructor.'))
|
'Please move to passing the redirect_uri in via the '
|
||||||
|
'constructor.'))
|
||||||
self.redirect_uri = redirect_uri
|
self.redirect_uri = redirect_uri
|
||||||
|
|
||||||
if self.redirect_uri is None:
|
if self.redirect_uri is None:
|
||||||
@@ -2034,8 +2051,8 @@ class OAuth2WebServerFlow(Flow):
|
|||||||
flow_info = json.loads(content)
|
flow_info = json.loads(content)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise OAuth2DeviceCodeError(
|
raise OAuth2DeviceCodeError(
|
||||||
'Could not parse server response as JSON: "%s", error: "%s"' % (
|
'Could not parse server response as JSON: "%s", '
|
||||||
content, e))
|
'error: "%s"' % (content, e))
|
||||||
return DeviceFlowInfo.FromResponse(flow_info)
|
return DeviceFlowInfo.FromResponse(flow_info)
|
||||||
else:
|
else:
|
||||||
error_msg = 'Invalid response %s.' % resp.status
|
error_msg = 'Invalid response %s.' % resp.status
|
||||||
@@ -2044,7 +2061,8 @@ class OAuth2WebServerFlow(Flow):
|
|||||||
if 'error' in d:
|
if 'error' in d:
|
||||||
error_msg += ' Error: %s' % d['error']
|
error_msg += ' Error: %s' % d['error']
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# Couldn't decode a JSON response, stick with the default message.
|
# Couldn't decode a JSON response, stick with the
|
||||||
|
# default message.
|
||||||
pass
|
pass
|
||||||
raise OAuth2DeviceCodeError(error_msg)
|
raise OAuth2DeviceCodeError(error_msg)
|
||||||
|
|
||||||
@@ -2120,27 +2138,27 @@ class OAuth2WebServerFlow(Flow):
|
|||||||
"reauthenticating with approval_prompt='force'.")
|
"reauthenticating with approval_prompt='force'.")
|
||||||
token_expiry = None
|
token_expiry = None
|
||||||
if 'expires_in' in d:
|
if 'expires_in' in d:
|
||||||
token_expiry = datetime.datetime.utcnow() + datetime.timedelta(
|
token_expiry = (
|
||||||
seconds=int(d['expires_in']))
|
datetime.datetime.utcnow() +
|
||||||
|
datetime.timedelta(seconds=int(d['expires_in'])))
|
||||||
|
|
||||||
extracted_id_token = None
|
extracted_id_token = None
|
||||||
if 'id_token' in d:
|
if 'id_token' in d:
|
||||||
extracted_id_token = _extract_id_token(d['id_token'])
|
extracted_id_token = _extract_id_token(d['id_token'])
|
||||||
|
|
||||||
logger.info('Successfully retrieved access token')
|
logger.info('Successfully retrieved access token')
|
||||||
return OAuth2Credentials(access_token, self.client_id,
|
return OAuth2Credentials(
|
||||||
self.client_secret, refresh_token, token_expiry,
|
access_token, self.client_id, self.client_secret,
|
||||||
self.token_uri, self.user_agent,
|
refresh_token, token_expiry, self.token_uri, self.user_agent,
|
||||||
revoke_uri=self.revoke_uri,
|
revoke_uri=self.revoke_uri, id_token=extracted_id_token,
|
||||||
id_token=extracted_id_token,
|
token_response=d, scopes=self.scope,
|
||||||
token_response=d,
|
|
||||||
scopes=self.scope,
|
|
||||||
token_info_uri=self.token_info_uri)
|
token_info_uri=self.token_info_uri)
|
||||||
else:
|
else:
|
||||||
logger.info('Failed to retrieve access token: %s', content)
|
logger.info('Failed to retrieve access token: %s', content)
|
||||||
if 'error' in d:
|
if 'error' in d:
|
||||||
# you never know what those providers got to say
|
# you never know what those providers got to say
|
||||||
error_msg = str(d['error']) + str(d.get('error_description', ''))
|
error_msg = (str(d['error']) +
|
||||||
|
str(d.get('error_description', '')))
|
||||||
else:
|
else:
|
||||||
error_msg = 'Invalid response: %s.' % str(resp.status)
|
error_msg = 'Invalid response: %s.' % str(resp.status)
|
||||||
raise FlowExchangeError(error_msg)
|
raise FlowExchangeError(error_msg)
|
||||||
@@ -2187,8 +2205,10 @@ def flow_from_clientsecrets(filename, scope, redirect_uri=None,
|
|||||||
invalid.
|
invalid.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
client_type, client_info = clientsecrets.loadfile(filename, cache=cache)
|
client_type, client_info = clientsecrets.loadfile(filename,
|
||||||
if client_type in (clientsecrets.TYPE_WEB, clientsecrets.TYPE_INSTALLED):
|
cache=cache)
|
||||||
|
if client_type in (clientsecrets.TYPE_WEB,
|
||||||
|
clientsecrets.TYPE_INSTALLED):
|
||||||
constructor_kwargs = {
|
constructor_kwargs = {
|
||||||
'redirect_uri': redirect_uri,
|
'redirect_uri': redirect_uri,
|
||||||
'auth_uri': client_info['auth_uri'],
|
'auth_uri': client_info['auth_uri'],
|
||||||
|
|||||||
@@ -83,13 +83,14 @@ def _validate_clientsecrets(obj):
|
|||||||
'"installed" application')
|
'"installed" application')
|
||||||
client_type = tuple(obj)[0]
|
client_type = tuple(obj)[0]
|
||||||
if client_type not in VALID_CLIENT:
|
if client_type not in VALID_CLIENT:
|
||||||
raise InvalidClientSecretsError('Unknown client type: %s.' % (client_type, ))
|
raise InvalidClientSecretsError(
|
||||||
|
'Unknown client type: %s.' % (client_type,))
|
||||||
client_info = obj[client_type]
|
client_info = obj[client_type]
|
||||||
for prop_name in VALID_CLIENT[client_type]['required']:
|
for prop_name in VALID_CLIENT[client_type]['required']:
|
||||||
if prop_name not in client_info:
|
if prop_name not in client_info:
|
||||||
raise InvalidClientSecretsError(
|
raise InvalidClientSecretsError(
|
||||||
'Missing property "%s" in a client type of "%s".' % (prop_name,
|
'Missing property "%s" in a client type of "%s".' %
|
||||||
client_type))
|
(prop_name, client_type))
|
||||||
for prop_name in VALID_CLIENT[client_type]['string']:
|
for prop_name in VALID_CLIENT[client_type]['string']:
|
||||||
if client_info[prop_name].startswith('[['):
|
if client_info[prop_name].startswith('[['):
|
||||||
raise InvalidClientSecretsError(
|
raise InvalidClientSecretsError(
|
||||||
|
|||||||
@@ -45,11 +45,9 @@ except ImportError:
|
|||||||
OpenSSLVerifier = None
|
OpenSSLVerifier = None
|
||||||
OpenSSLSigner = None
|
OpenSSLSigner = None
|
||||||
|
|
||||||
|
|
||||||
def pkcs12_key_as_pem(*args, **kwargs):
|
def pkcs12_key_as_pem(*args, **kwargs):
|
||||||
raise NotImplementedError('pkcs12_key_as_pem requires OpenSSL.')
|
raise NotImplementedError('pkcs12_key_as_pem requires OpenSSL.')
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from oauth2client._pycrypto_crypt import PyCryptoVerifier
|
from oauth2client._pycrypto_crypt import PyCryptoVerifier
|
||||||
from oauth2client._pycrypto_crypt import PyCryptoSigner
|
from oauth2client._pycrypto_crypt import PyCryptoSigner
|
||||||
|
|||||||
@@ -35,8 +35,9 @@ class CommunicationError(Error):
|
|||||||
class NoDevshellServer(Error):
|
class NoDevshellServer(Error):
|
||||||
"""Error when no Developer Shell server can be contacted."""
|
"""Error when no Developer Shell server can be contacted."""
|
||||||
|
|
||||||
# The request for credential information to the Developer Shell client socket is
|
# The request for credential information to the Developer Shell client socket
|
||||||
# always an empty PBLite-formatted JSON object, so just define it as a constant.
|
# is always an empty PBLite-formatted JSON object, so just define it as a
|
||||||
|
# constant.
|
||||||
CREDENTIAL_INFO_REQUEST_JSON = '[]'
|
CREDENTIAL_INFO_REQUEST_JSON = '[]'
|
||||||
|
|
||||||
|
|
||||||
@@ -44,7 +45,9 @@ class CredentialInfoResponse(object):
|
|||||||
"""Credential information response from Developer Shell server.
|
"""Credential information response from Developer Shell server.
|
||||||
|
|
||||||
The credential information response from Developer Shell socket is a
|
The credential information response from Developer Shell socket is a
|
||||||
PBLite-formatted JSON array with fields encoded by their index in the array:
|
PBLite-formatted JSON array with fields encoded by their index in the
|
||||||
|
array:
|
||||||
|
|
||||||
* Index 0 - user email
|
* Index 0 - user email
|
||||||
* Index 1 - default project ID. None if the project context is not known.
|
* Index 1 - default project ID. None if the project context is not known.
|
||||||
* Index 2 - OAuth2 access token. None if there is no valid auth context.
|
* Index 2 - OAuth2 access token. None if there is no valid auth context.
|
||||||
|
|||||||
@@ -93,7 +93,8 @@ class Storage(BaseStorage):
|
|||||||
Args:
|
Args:
|
||||||
model: db.Model, model class
|
model: db.Model, model class
|
||||||
key_name: string, key name for the entity that has the credentials
|
key_name: string, key name for the entity that has the credentials
|
||||||
key_value: string, key value for the entity that has the credentials
|
key_value: string, key value for the entity that has the
|
||||||
|
credentials
|
||||||
property_name: string, name of the property that is an
|
property_name: string, name of the property that is an
|
||||||
CredentialsProperty
|
CredentialsProperty
|
||||||
"""
|
"""
|
||||||
@@ -124,12 +125,14 @@ class Storage(BaseStorage):
|
|||||||
Args:
|
Args:
|
||||||
credentials: Credentials, the credentials to store.
|
credentials: Credentials, the credentials to store.
|
||||||
overwrite: Boolean, indicates whether you would like these
|
overwrite: Boolean, indicates whether you would like these
|
||||||
credentials to overwrite any existing stored credentials.
|
credentials to overwrite any existing stored
|
||||||
|
credentials.
|
||||||
"""
|
"""
|
||||||
args = {self.key_name: self.key_value}
|
args = {self.key_name: self.key_value}
|
||||||
|
|
||||||
if overwrite:
|
if overwrite:
|
||||||
entity, unused_is_new = self.model_class.objects.get_or_create(**args)
|
(entity,
|
||||||
|
unused_is_new) = self.model_class.objects.get_or_create(**args)
|
||||||
else:
|
else:
|
||||||
entity = self.model_class(**args)
|
entity = self.model_class(**args)
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ apiui/credential>`__.
|
|||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
|
|
||||||
Once configured, you can use the :meth:`UserOAuth2.required` decorator to ensure
|
Once configured, you can use the :meth:`UserOAuth2.required` decorator to
|
||||||
that credentials are available within a view.
|
ensure that credentials are available within a view.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:emphasize-lines: 3,7,10
|
:emphasize-lines: 3,7,10
|
||||||
@@ -291,8 +291,9 @@ class UserOAuth2(object):
|
|||||||
raise ValueError(
|
raise ValueError(
|
||||||
'OAuth2 configuration could not be found. Either specify the '
|
'OAuth2 configuration could not be found. Either specify the '
|
||||||
'client_secrets_file or client_id and client_secret or set the'
|
'client_secrets_file or client_id and client_secret or set the'
|
||||||
'app configuration variables GOOGLE_OAUTH2_CLIENT_SECRETS_FILE '
|
'app configuration variables '
|
||||||
'or GOOGLE_OAUTH2_CLIENT_ID and GOOGLE_OAUTH2_CLIENT_SECRET.')
|
'GOOGLE_OAUTH2_CLIENT_SECRETS_FILE or '
|
||||||
|
'GOOGLE_OAUTH2_CLIENT_ID and GOOGLE_OAUTH2_CLIENT_SECRET.')
|
||||||
|
|
||||||
def _load_client_secrets(self, filename):
|
def _load_client_secrets(self, filename):
|
||||||
"""Loads client secrets from the given filename."""
|
"""Loads client secrets from the given filename."""
|
||||||
@@ -392,7 +393,8 @@ class UserOAuth2(object):
|
|||||||
credentials = flow.step2_exchange(code)
|
credentials = flow.step2_exchange(code)
|
||||||
except FlowExchangeError as exchange_error:
|
except FlowExchangeError as exchange_error:
|
||||||
current_app.logger.exception(exchange_error)
|
current_app.logger.exception(exchange_error)
|
||||||
return 'An error occurred: %s' % exchange_error, httplib.BAD_REQUEST
|
content = 'An error occurred: %s' % (exchange_error,)
|
||||||
|
return content, httplib.BAD_REQUEST
|
||||||
|
|
||||||
# Save the credentials to the storage.
|
# Save the credentials to the storage.
|
||||||
self.storage.put(credentials)
|
self.storage.put(credentials)
|
||||||
@@ -420,9 +422,9 @@ class UserOAuth2(object):
|
|||||||
def email(self):
|
def email(self):
|
||||||
"""Returns the user's email address or None if there are no credentials.
|
"""Returns the user's email address or None if there are no credentials.
|
||||||
|
|
||||||
The email address is provided by the current credentials' id_token. This
|
The email address is provided by the current credentials' id_token.
|
||||||
should not be used as unique identifier as the user can change their
|
This should not be used as unique identifier as the user can change
|
||||||
email. If you need a unique identifier, use user_id.
|
their email. If you need a unique identifier, use user_id.
|
||||||
"""
|
"""
|
||||||
if not self.credentials:
|
if not self.credentials:
|
||||||
return None
|
return None
|
||||||
@@ -451,8 +453,9 @@ class UserOAuth2(object):
|
|||||||
def authorize_url(self, return_url, **kwargs):
|
def authorize_url(self, return_url, **kwargs):
|
||||||
"""Creates a URL that can be used to start the authorization flow.
|
"""Creates a URL that can be used to start the authorization flow.
|
||||||
|
|
||||||
When the user is directed to the URL, the authorization flow will begin.
|
When the user is directed to the URL, the authorization flow will
|
||||||
Once complete, the user will be redirected to the specified return URL.
|
begin. Once complete, the user will be redirected to the specified
|
||||||
|
return URL.
|
||||||
|
|
||||||
Any kwargs are passed into the flow constructor.
|
Any kwargs are passed into the flow constructor.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -44,9 +44,9 @@ class AppAssertionCredentials(AssertionCredentials):
|
|||||||
used for the purpose of accessing data stored under an account assigned to
|
used for the purpose of accessing data stored under an account assigned to
|
||||||
the Compute Engine instance itself.
|
the Compute Engine instance itself.
|
||||||
|
|
||||||
This credential does not require a flow to instantiate because it represents
|
This credential does not require a flow to instantiate because it
|
||||||
a two legged flow, and therefore has all of the required information to
|
represents a two legged flow, and therefore has all of the required
|
||||||
generate and refresh its own access tokens.
|
information to generate and refresh its own access tokens.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@util.positional(2)
|
@util.positional(2)
|
||||||
@@ -60,7 +60,8 @@ class AppAssertionCredentials(AssertionCredentials):
|
|||||||
self.scope = util.scopes_to_string(scope)
|
self.scope = util.scopes_to_string(scope)
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
|
||||||
# Assertion type is no longer used, but still in the parent class signature.
|
# Assertion type is no longer used, but still in the
|
||||||
|
# parent class signature.
|
||||||
super(AppAssertionCredentials, self).__init__(None)
|
super(AppAssertionCredentials, self).__init__(None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -74,9 +75,9 @@ class AppAssertionCredentials(AssertionCredentials):
|
|||||||
Skip all the storage hoops and just refresh using the API.
|
Skip all the storage hoops and just refresh using the API.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
http_request: callable, a callable that matches the method signature
|
http_request: callable, a callable that matches the method
|
||||||
of httplib2.Http.request, used to make the refresh
|
signature of httplib2.Http.request, used to make
|
||||||
request.
|
the refresh request.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AccessTokenRefreshError: When the refresh fails.
|
AccessTokenRefreshError: When the refresh fails.
|
||||||
|
|||||||
@@ -176,7 +176,6 @@ class _PosixOpener(_Opener):
|
|||||||
try:
|
try:
|
||||||
import fcntl
|
import fcntl
|
||||||
|
|
||||||
|
|
||||||
class _FcntlOpener(_Opener):
|
class _FcntlOpener(_Opener):
|
||||||
"""Open, lock, and unlock a file using fcntl.lockf."""
|
"""Open, lock, and unlock a file using fcntl.lockf."""
|
||||||
|
|
||||||
@@ -190,7 +189,8 @@ try:
|
|||||||
Raises:
|
Raises:
|
||||||
AlreadyLockedException: if the lock is already acquired.
|
AlreadyLockedException: if the lock is already acquired.
|
||||||
IOError: if the open fails.
|
IOError: if the open fails.
|
||||||
CredentialsFileSymbolicLinkError if the file is a symbolic link.
|
CredentialsFileSymbolicLinkError: if the file is a symbolic
|
||||||
|
link.
|
||||||
"""
|
"""
|
||||||
if self._locked:
|
if self._locked:
|
||||||
raise AlreadyLockedException('File %s is already locked' %
|
raise AlreadyLockedException('File %s is already locked' %
|
||||||
@@ -201,7 +201,8 @@ try:
|
|||||||
try:
|
try:
|
||||||
self._fh = open(self._filename, self._mode)
|
self._fh = open(self._filename, self._mode)
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
# If we can't access with _mode, try _fallback_mode and don't lock.
|
# If we can't access with _mode, try _fallback_mode and
|
||||||
|
# don't lock.
|
||||||
if e.errno in (errno.EPERM, errno.EACCES):
|
if e.errno in (errno.EPERM, errno.EACCES):
|
||||||
self._fh = open(self._filename, self._fallback_mode)
|
self._fh = open(self._filename, self._fallback_mode)
|
||||||
return
|
return
|
||||||
@@ -244,7 +245,6 @@ try:
|
|||||||
import win32con
|
import win32con
|
||||||
import win32file
|
import win32file
|
||||||
|
|
||||||
|
|
||||||
class _Win32Opener(_Opener):
|
class _Win32Opener(_Opener):
|
||||||
"""Open, lock, and unlock a file using windows primitives."""
|
"""Open, lock, and unlock a file using windows primitives."""
|
||||||
|
|
||||||
@@ -278,7 +278,8 @@ try:
|
|||||||
try:
|
try:
|
||||||
self._fh = open(self._filename, self._mode)
|
self._fh = open(self._filename, self._mode)
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
# If we can't access with _mode, try _fallback_mode and don't lock.
|
# If we can't access with _mode, try _fallback_mode
|
||||||
|
# and don't lock.
|
||||||
if e.errno == errno.EACCES:
|
if e.errno == errno.EACCES:
|
||||||
self._fh = open(self._filename, self._fallback_mode)
|
self._fh = open(self._filename, self._fallback_mode)
|
||||||
return
|
return
|
||||||
@@ -298,7 +299,8 @@ try:
|
|||||||
if timeout == 0:
|
if timeout == 0:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# If the error is not that the file is already in use, raise.
|
# If the error is not that the file is already
|
||||||
|
# in use, raise.
|
||||||
if e[0] != _Win32Opener.FILE_IN_USE_ERROR:
|
if e[0] != _Win32Opener.FILE_IN_USE_ERROR:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@@ -317,7 +319,8 @@ try:
|
|||||||
if self._locked:
|
if self._locked:
|
||||||
try:
|
try:
|
||||||
hfile = win32file._get_osfhandle(self._fh.fileno())
|
hfile = win32file._get_osfhandle(self._fh.fileno())
|
||||||
win32file.UnlockFileEx(hfile, 0, -0x10000, pywintypes.OVERLAPPED())
|
win32file.UnlockFileEx(hfile, 0, -0x10000,
|
||||||
|
pywintypes.OVERLAPPED())
|
||||||
except pywintypes.error as e:
|
except pywintypes.error as e:
|
||||||
if e[0] != _Win32Opener.FILE_ALREADY_UNLOCKED_ERROR:
|
if e[0] != _Win32Opener.FILE_ALREADY_UNLOCKED_ERROR:
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ def get_credential_storage(filename, client_id, user_agent, scope,
|
|||||||
|
|
||||||
|
|
||||||
@util.positional(2)
|
@util.positional(2)
|
||||||
def get_credential_storage_custom_string_key(
|
def get_credential_storage_custom_string_key(filename, key_string,
|
||||||
filename, key_string, warn_on_readonly=True):
|
warn_on_readonly=True):
|
||||||
"""Get a Storage instance for a credential using a single string as a key.
|
"""Get a Storage instance for a credential using a single string as a key.
|
||||||
|
|
||||||
Allows you to provide a string as a custom key that will be used for
|
Allows you to provide a string as a custom key that will be used for
|
||||||
@@ -120,8 +120,8 @@ def get_credential_storage_custom_string_key(
|
|||||||
|
|
||||||
|
|
||||||
@util.positional(2)
|
@util.positional(2)
|
||||||
def get_credential_storage_custom_key(
|
def get_credential_storage_custom_key(filename, key_dict,
|
||||||
filename, key_dict, warn_on_readonly=True):
|
warn_on_readonly=True):
|
||||||
"""Get a Storage instance for a credential using a dictionary as a key.
|
"""Get a Storage instance for a credential using a dictionary as a key.
|
||||||
|
|
||||||
Allows you to provide a dictionary as a custom key that will be used for
|
Allows you to provide a dictionary as a custom key that will be used for
|
||||||
@@ -211,7 +211,7 @@ class _MultiStore(object):
|
|||||||
self._data = None
|
self._data = None
|
||||||
|
|
||||||
class _Storage(BaseStorage):
|
class _Storage(BaseStorage):
|
||||||
"""A Storage object that knows how to read/write a single credential."""
|
"""A Storage object that can read/write a single credential."""
|
||||||
|
|
||||||
def __init__(self, multistore, key):
|
def __init__(self, multistore, key):
|
||||||
self._multistore = multistore
|
self._multistore = multistore
|
||||||
@@ -285,8 +285,8 @@ class _MultiStore(object):
|
|||||||
self._file.open_and_lock()
|
self._file.open_and_lock()
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
if e.errno == errno.ENOSYS:
|
if e.errno == errno.ENOSYS:
|
||||||
logger.warn('File system does not support locking the credentials '
|
logger.warn('File system does not support locking the '
|
||||||
'file.')
|
'credentials file.')
|
||||||
elif e.errno == errno.ENOLCK:
|
elif e.errno == errno.ENOLCK:
|
||||||
logger.warn('File system is out of resources for writing the '
|
logger.warn('File system is out of resources for writing the '
|
||||||
'credentials file (is your disk full?).')
|
'credentials file (is your disk full?).')
|
||||||
@@ -295,8 +295,9 @@ class _MultiStore(object):
|
|||||||
if not self._file.is_locked():
|
if not self._file.is_locked():
|
||||||
self._read_only = True
|
self._read_only = True
|
||||||
if self._warn_on_readonly:
|
if self._warn_on_readonly:
|
||||||
logger.warn('The credentials file (%s) is not writable. Opening in '
|
logger.warn('The credentials file (%s) is not writable. '
|
||||||
'read-only mode. Any refreshed credentials will only be '
|
'Opening in read-only mode. Any refreshed '
|
||||||
|
'credentials will only be '
|
||||||
'valid for this run.', self._file.filename())
|
'valid for this run.', self._file.filename())
|
||||||
if os.path.getsize(self._file.filename()) == 0:
|
if os.path.getsize(self._file.filename()) == 0:
|
||||||
logger.debug('Initializing empty multistore file')
|
logger.debug('Initializing empty multistore file')
|
||||||
@@ -340,7 +341,8 @@ class _MultiStore(object):
|
|||||||
if self._read_only:
|
if self._read_only:
|
||||||
return
|
return
|
||||||
self._file.file_handle().seek(0)
|
self._file.file_handle().seek(0)
|
||||||
json.dump(data, self._file.file_handle(), sort_keys=True, indent=2, separators=(',', ': '))
|
json.dump(data, self._file.file_handle(),
|
||||||
|
sort_keys=True, indent=2, separators=(',', ': '))
|
||||||
self._file.file_handle().truncate()
|
self._file.file_handle().truncate()
|
||||||
|
|
||||||
def _refresh_data_cache(self):
|
def _refresh_data_cache(self):
|
||||||
@@ -379,11 +381,12 @@ class _MultiStore(object):
|
|||||||
|
|
||||||
for cred_entry in credentials:
|
for cred_entry in credentials:
|
||||||
try:
|
try:
|
||||||
(key, credential) = self._decode_credential_from_json(cred_entry)
|
key, credential = self._decode_credential_from_json(cred_entry)
|
||||||
self._data[key] = credential
|
self._data[key] = credential
|
||||||
except:
|
except:
|
||||||
# If something goes wrong loading a credential, just ignore it
|
# If something goes wrong loading a credential, just ignore it
|
||||||
logger.info('Error decoding credential, skipping', exc_info=True)
|
logger.info('Error decoding credential, skipping',
|
||||||
|
exc_info=True)
|
||||||
|
|
||||||
def _decode_credential_from_json(self, cred_entry):
|
def _decode_credential_from_json(self, cred_entry):
|
||||||
"""Load a credential from our JSON serialization.
|
"""Load a credential from our JSON serialization.
|
||||||
@@ -398,7 +401,8 @@ class _MultiStore(object):
|
|||||||
raw_key = cred_entry['key']
|
raw_key = cred_entry['key']
|
||||||
key = util.dict_to_tuple_key(raw_key)
|
key = util.dict_to_tuple_key(raw_key)
|
||||||
credential = None
|
credential = None
|
||||||
credential = Credentials.new_from_json(json.dumps(cred_entry['credential']))
|
credential = Credentials.new_from_json(
|
||||||
|
json.dumps(cred_entry['credential']))
|
||||||
return (key, credential)
|
return (key, credential)
|
||||||
|
|
||||||
def _write(self):
|
def _write(self):
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ def run(flow, storage, http=None):
|
|||||||
of values.
|
of values.
|
||||||
|
|
||||||
``--[no]auth_local_webserver`` (boolean, default: ``True``)
|
``--[no]auth_local_webserver`` (boolean, default: ``True``)
|
||||||
Run a local web server to handle redirects during OAuth authorization.
|
Run a local web server to handle redirects during OAuth
|
||||||
|
authorization.
|
||||||
|
|
||||||
Since it uses flags make sure to initialize the ``gflags`` module before
|
Since it uses flags make sure to initialize the ``gflags`` module before
|
||||||
calling ``run()``.
|
calling ``run()``.
|
||||||
@@ -87,8 +88,8 @@ def run(flow, storage, http=None):
|
|||||||
Credentials, the obtained credential.
|
Credentials, the obtained credential.
|
||||||
"""
|
"""
|
||||||
logging.warning('This function, oauth2client.tools.run(), and the use of '
|
logging.warning('This function, oauth2client.tools.run(), and the use of '
|
||||||
'the gflags library are deprecated and will be removed in a future '
|
'the gflags library are deprecated and will be removed in '
|
||||||
'version of the library.')
|
'a future version of the library.')
|
||||||
if FLAGS.auth_local_webserver:
|
if FLAGS.auth_local_webserver:
|
||||||
success = False
|
success = False
|
||||||
port_number = 0
|
port_number = 0
|
||||||
@@ -104,7 +105,8 @@ def run(flow, storage, http=None):
|
|||||||
break
|
break
|
||||||
FLAGS.auth_local_webserver = success
|
FLAGS.auth_local_webserver = success
|
||||||
if not success:
|
if not success:
|
||||||
print('Failed to start a local webserver listening on either port 8080')
|
print('Failed to start a local webserver listening on '
|
||||||
|
'either port 8080')
|
||||||
print('or port 9090. Please check your firewall settings and locally')
|
print('or port 9090. Please check your firewall settings and locally')
|
||||||
print('running programs that may be blocking or using those ports.')
|
print('running programs that may be blocking or using those ports.')
|
||||||
print()
|
print()
|
||||||
@@ -144,7 +146,8 @@ def run(flow, storage, http=None):
|
|||||||
if 'code' in httpd.query_params:
|
if 'code' in httpd.query_params:
|
||||||
code = httpd.query_params['code']
|
code = httpd.query_params['code']
|
||||||
else:
|
else:
|
||||||
print('Failed to find "code" in the query parameters of the redirect.')
|
print('Failed to find "code" in the query '
|
||||||
|
'parameters of the redirect.')
|
||||||
sys.exit('Try running with --noauth_local_webserver.')
|
sys.exit('Try running with --noauth_local_webserver.')
|
||||||
else:
|
else:
|
||||||
code = input('Enter verification code: ').strip()
|
code = input('Enter verification code: ').strip()
|
||||||
|
|||||||
@@ -38,13 +38,14 @@ class _ServiceAccountCredentials(AssertionCredentials):
|
|||||||
|
|
||||||
MAX_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
|
MAX_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
|
||||||
|
|
||||||
def __init__(self, service_account_id, service_account_email, private_key_id,
|
def __init__(self, service_account_id, service_account_email,
|
||||||
private_key_pkcs8_text, scopes, user_agent=None,
|
private_key_id, private_key_pkcs8_text, scopes,
|
||||||
token_uri=GOOGLE_TOKEN_URI, revoke_uri=GOOGLE_REVOKE_URI,
|
user_agent=None, token_uri=GOOGLE_TOKEN_URI,
|
||||||
**kwargs):
|
revoke_uri=GOOGLE_REVOKE_URI, **kwargs):
|
||||||
|
|
||||||
super(_ServiceAccountCredentials, self).__init__(
|
super(_ServiceAccountCredentials, self).__init__(
|
||||||
None, user_agent=user_agent, token_uri=token_uri, revoke_uri=revoke_uri)
|
None, user_agent=user_agent, token_uri=token_uri,
|
||||||
|
revoke_uri=revoke_uri)
|
||||||
|
|
||||||
self._service_account_id = service_account_id
|
self._service_account_id = service_account_id
|
||||||
self._service_account_email = service_account_email
|
self._service_account_email = service_account_email
|
||||||
@@ -81,7 +82,8 @@ class _ServiceAccountCredentials(AssertionCredentials):
|
|||||||
assertion_input = first_segment + b'.' + second_segment
|
assertion_input = first_segment + b'.' + second_segment
|
||||||
|
|
||||||
# Sign the assertion.
|
# Sign the assertion.
|
||||||
rsa_bytes = rsa.pkcs1.sign(assertion_input, self._private_key, 'SHA-256')
|
rsa_bytes = rsa.pkcs1.sign(assertion_input, self._private_key,
|
||||||
|
'SHA-256')
|
||||||
signature = base64.urlsafe_b64encode(rsa_bytes).rstrip(b'=')
|
signature = base64.urlsafe_b64encode(rsa_bytes).rstrip(b'=')
|
||||||
|
|
||||||
return assertion_input + b'.' + signature
|
return assertion_input + b'.' + signature
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ def _CreateArgumentParser():
|
|||||||
default=False, help='Do not run a local web server.')
|
default=False, help='Do not run a local web server.')
|
||||||
parser.add_argument('--auth_host_port', default=[8080, 8090], type=int,
|
parser.add_argument('--auth_host_port', default=[8080, 8090], type=int,
|
||||||
nargs='*', help='Port web server should listen on.')
|
nargs='*', help='Port web server should listen on.')
|
||||||
parser.add_argument('--logging_level', default='ERROR',
|
parser.add_argument(
|
||||||
|
'--logging_level', default='ERROR',
|
||||||
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
|
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
|
||||||
help='Set the logging level of detail.')
|
help='Set the logging level of detail.')
|
||||||
return parser
|
return parser
|
||||||
@@ -100,12 +101,14 @@ class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||||||
query = self.path.split('?', 1)[-1]
|
query = self.path.split('?', 1)[-1]
|
||||||
query = dict(urllib.parse.parse_qsl(query))
|
query = dict(urllib.parse.parse_qsl(query))
|
||||||
self.server.query_params = query
|
self.server.query_params = query
|
||||||
self.wfile.write(b"<html><head><title>Authentication Status</title></head>")
|
self.wfile.write(
|
||||||
self.wfile.write(b"<body><p>The authentication flow has completed.</p>")
|
b"<html><head><title>Authentication Status</title></head>")
|
||||||
|
self.wfile.write(
|
||||||
|
b"<body><p>The authentication flow has completed.</p>")
|
||||||
self.wfile.write(b"</body></html>")
|
self.wfile.write(b"</body></html>")
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
def log_message(self, format, *args):
|
||||||
"""Do not log messages to stdout while running as command line program."""
|
"""Do not log messages to stdout while running as cmd. line program."""
|
||||||
|
|
||||||
|
|
||||||
@util.positional(3)
|
@util.positional(3)
|
||||||
@@ -137,9 +140,9 @@ def run_flow(flow, storage, flags, http=None):
|
|||||||
Run a local web server to handle redirects during OAuth
|
Run a local web server to handle redirects during OAuth
|
||||||
authorization.
|
authorization.
|
||||||
|
|
||||||
The tools module defines an ``ArgumentParser`` the already contains the flag
|
The tools module defines an ``ArgumentParser`` the already contains the
|
||||||
definitions that ``run()`` requires. You can pass that ``ArgumentParser`` to
|
flag definitions that ``run()`` requires. You can pass that
|
||||||
your ``ArgumentParser`` constructor::
|
``ArgumentParser`` to your ``ArgumentParser`` constructor::
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description=__doc__,
|
description=__doc__,
|
||||||
@@ -175,7 +178,8 @@ def run_flow(flow, storage, flags, http=None):
|
|||||||
break
|
break
|
||||||
flags.noauth_local_webserver = not success
|
flags.noauth_local_webserver = not success
|
||||||
if not success:
|
if not success:
|
||||||
print('Failed to start a local webserver listening on either port 8080')
|
print('Failed to start a local webserver listening '
|
||||||
|
'on either port 8080')
|
||||||
print('or port 9090. Please check your firewall settings and locally')
|
print('or port 9090. Please check your firewall settings and locally')
|
||||||
print('running programs that may be blocking or using those ports.')
|
print('running programs that may be blocking or using those ports.')
|
||||||
print()
|
print()
|
||||||
@@ -197,7 +201,8 @@ def run_flow(flow, storage, flags, http=None):
|
|||||||
print()
|
print()
|
||||||
print(' ' + authorize_url)
|
print(' ' + authorize_url)
|
||||||
print()
|
print()
|
||||||
print('If your browser is on a different machine then exit and re-run this')
|
print('If your browser is on a different machine then '
|
||||||
|
'exit and re-run this')
|
||||||
print('application with the command-line parameter ')
|
print('application with the command-line parameter ')
|
||||||
print()
|
print()
|
||||||
print(' --noauth_local_webserver')
|
print(' --noauth_local_webserver')
|
||||||
@@ -216,7 +221,8 @@ def run_flow(flow, storage, flags, http=None):
|
|||||||
if 'code' in httpd.query_params:
|
if 'code' in httpd.query_params:
|
||||||
code = httpd.query_params['code']
|
code = httpd.query_params['code']
|
||||||
else:
|
else:
|
||||||
print('Failed to find "code" in the query parameters of the redirect.')
|
print('Failed to find "code" in the query parameters '
|
||||||
|
'of the redirect.')
|
||||||
sys.exit('Try running with --noauth_local_webserver.')
|
sys.exit('Try running with --noauth_local_webserver.')
|
||||||
else:
|
else:
|
||||||
code = input('Enter verification code: ').strip()
|
code = input('Enter verification code: ').strip()
|
||||||
@@ -235,9 +241,9 @@ def run_flow(flow, storage, flags, http=None):
|
|||||||
|
|
||||||
def message_if_missing(filename):
|
def message_if_missing(filename):
|
||||||
"""Helpful message to display if the CLIENT_SECRETS file is missing."""
|
"""Helpful message to display if the CLIENT_SECRETS file is missing."""
|
||||||
|
|
||||||
return _CLIENT_SECRETS_MESSAGE % filename
|
return _CLIENT_SECRETS_MESSAGE % filename
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from oauth2client.old_run import run
|
from oauth2client.old_run import run
|
||||||
from oauth2client.old_run import FLAGS
|
from oauth2client.old_run import FLAGS
|
||||||
|
|||||||
@@ -129,8 +129,10 @@ def positional(max_positional_args):
|
|||||||
plural_s = ''
|
plural_s = ''
|
||||||
if max_positional_args != 1:
|
if max_positional_args != 1:
|
||||||
plural_s = 's'
|
plural_s = 's'
|
||||||
message = '%s() takes at most %d positional argument%s (%d given)' % (
|
message = ('%s() takes at most %d positional '
|
||||||
wrapped.__name__, max_positional_args, plural_s, len(args))
|
'argument%s (%d given)' % (
|
||||||
|
wrapped.__name__, max_positional_args,
|
||||||
|
plural_s, len(args)))
|
||||||
if positional_parameters_enforcement == POSITIONAL_EXCEPTION:
|
if positional_parameters_enforcement == POSITIONAL_EXCEPTION:
|
||||||
raise TypeError(message)
|
raise TypeError(message)
|
||||||
elif positional_parameters_enforcement == POSITIONAL_WARNING:
|
elif positional_parameters_enforcement == POSITIONAL_WARNING:
|
||||||
|
|||||||
Reference in New Issue
Block a user