This repository has been archived by the owner on Nov 5, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 430
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A new file, `_helpers.py`, was created without realizing that `utils.py` existed for the same purpose. Moving all to `_helpers.py`.
- Loading branch information
Showing
22 changed files
with
388 additions
and
412 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,12 +11,216 @@ | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
"""Helper functions for commonly used utilities.""" | ||
|
||
import base64 | ||
import functools | ||
import inspect | ||
import json | ||
import logging | ||
import os | ||
import warnings | ||
|
||
import six | ||
from six.moves import urllib | ||
|
||
|
||
__author__ = [ | ||
'[email protected] (Rafe Kaplan)', | ||
'[email protected] (Guido van Rossum)', | ||
] | ||
|
||
__all__ = [ | ||
'positional', | ||
'POSITIONAL_WARNING', | ||
'POSITIONAL_EXCEPTION', | ||
'POSITIONAL_IGNORE', | ||
] | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
POSITIONAL_WARNING = 'WARNING' | ||
POSITIONAL_EXCEPTION = 'EXCEPTION' | ||
POSITIONAL_IGNORE = 'IGNORE' | ||
POSITIONAL_SET = frozenset([POSITIONAL_WARNING, POSITIONAL_EXCEPTION, | ||
POSITIONAL_IGNORE]) | ||
|
||
positional_parameters_enforcement = POSITIONAL_WARNING | ||
|
||
_SYM_LINK_MESSAGE = 'File: {0}: Is a symbolic link.' | ||
_IS_DIR_MESSAGE = '{0}: Is a directory' | ||
_MISSING_FILE_MESSAGE = 'Cannot access {0}: No such file or directory' | ||
|
||
|
||
def positional(max_positional_args): | ||
"""A decorator to declare that only the first N arguments my be positional. | ||
This decorator makes it easy to support Python 3 style keyword-only | ||
parameters. For example, in Python 3 it is possible to write:: | ||
def fn(pos1, *, kwonly1=None, kwonly1=None): | ||
... | ||
All named parameters after ``*`` must be a keyword:: | ||
fn(10, 'kw1', 'kw2') # Raises exception. | ||
fn(10, kwonly1='kw1') # Ok. | ||
Example | ||
^^^^^^^ | ||
To define a function like above, do:: | ||
@positional(1) | ||
def fn(pos1, kwonly1=None, kwonly2=None): | ||
... | ||
If no default value is provided to a keyword argument, it becomes a | ||
required keyword argument:: | ||
@positional(0) | ||
def fn(required_kw): | ||
... | ||
This must be called with the keyword parameter:: | ||
fn() # Raises exception. | ||
fn(10) # Raises exception. | ||
fn(required_kw=10) # Ok. | ||
When defining instance or class methods always remember to account for | ||
``self`` and ``cls``:: | ||
class MyClass(object): | ||
@positional(2) | ||
def my_method(self, pos1, kwonly1=None): | ||
... | ||
@classmethod | ||
@positional(2) | ||
def my_method(cls, pos1, kwonly1=None): | ||
... | ||
The positional decorator behavior is controlled by | ||
``util.positional_parameters_enforcement``, which may be set to | ||
``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or | ||
``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do | ||
nothing, respectively, if a declaration is violated. | ||
Args: | ||
max_positional_arguments: Maximum number of positional arguments. All | ||
parameters after the this index must be | ||
keyword only. | ||
Returns: | ||
A decorator that prevents using arguments after max_positional_args | ||
from being used as positional parameters. | ||
Raises: | ||
TypeError: if a key-word only argument is provided as a positional | ||
parameter, but only if | ||
util.positional_parameters_enforcement is set to | ||
POSITIONAL_EXCEPTION. | ||
""" | ||
|
||
def positional_decorator(wrapped): | ||
@functools.wraps(wrapped) | ||
def positional_wrapper(*args, **kwargs): | ||
if len(args) > max_positional_args: | ||
plural_s = '' | ||
if max_positional_args != 1: | ||
plural_s = 's' | ||
message = ('{function}() takes at most {args_max} positional ' | ||
'argument{plural} ({args_given} given)'.format( | ||
function=wrapped.__name__, | ||
args_max=max_positional_args, | ||
args_given=len(args), | ||
plural=plural_s)) | ||
if positional_parameters_enforcement == POSITIONAL_EXCEPTION: | ||
raise TypeError(message) | ||
elif positional_parameters_enforcement == POSITIONAL_WARNING: | ||
logger.warning(message) | ||
return wrapped(*args, **kwargs) | ||
return positional_wrapper | ||
|
||
if isinstance(max_positional_args, six.integer_types): | ||
return positional_decorator | ||
else: | ||
args, _, _, defaults = inspect.getargspec(max_positional_args) | ||
return positional(len(args) - len(defaults))(max_positional_args) | ||
|
||
|
||
def scopes_to_string(scopes): | ||
"""Converts scope value to a string. | ||
If scopes is a string then it is simply passed through. If scopes is an | ||
iterable then a string is returned that is all the individual scopes | ||
concatenated with spaces. | ||
Args: | ||
scopes: string or iterable of strings, the scopes. | ||
Returns: | ||
The scopes formatted as a single string. | ||
""" | ||
if isinstance(scopes, six.string_types): | ||
return scopes | ||
else: | ||
return ' '.join(scopes) | ||
|
||
|
||
def string_to_scopes(scopes): | ||
"""Converts stringifed scope value to a list. | ||
If scopes is a list then it is simply passed through. If scopes is an | ||
string then a list of each individual scope is returned. | ||
Args: | ||
scopes: a string or iterable of strings, the scopes. | ||
Returns: | ||
The scopes in a list. | ||
""" | ||
if not scopes: | ||
return [] | ||
if isinstance(scopes, six.string_types): | ||
return scopes.split(' ') | ||
else: | ||
return scopes | ||
|
||
|
||
def _add_query_parameter(url, name, value): | ||
"""Adds a query parameter to a url. | ||
Replaces the current value if it already exists in the URL. | ||
Args: | ||
url: string, url to add the query parameter to. | ||
name: string, query parameter name. | ||
value: string, query parameter value. | ||
Returns: | ||
Updated query parameter. Does not update the url if value is None. | ||
""" | ||
if value is None: | ||
return url | ||
else: | ||
parsed = list(urllib.parse.urlparse(url)) | ||
q = dict(urllib.parse.parse_qsl(parsed[4])) | ||
q[name] = value | ||
parsed[4] = urllib.parse.urlencode(q) | ||
return urllib.parse.urlunparse(parsed) | ||
|
||
|
||
def validate_file(filename): | ||
if os.path.islink(filename): | ||
raise IOError(_SYM_LINK_MESSAGE.format(filename)) | ||
elif os.path.isdir(filename): | ||
raise IOError(_IS_DIR_MESSAGE.format(filename)) | ||
elif not os.path.isfile(filename): | ||
warnings.warn(_MISSING_FILE_MESSAGE.format(filename)) | ||
|
||
|
||
def _parse_pem_key(raw_key_input): | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,7 +36,6 @@ | |
from oauth2client import _helpers | ||
from oauth2client import clientsecrets | ||
from oauth2client import transport | ||
from oauth2client import util | ||
|
||
|
||
__author__ = '[email protected] (Joe Gregorio)' | ||
|
@@ -466,7 +465,7 @@ class OAuth2Credentials(Credentials): | |
OAuth2Credentials objects may be safely pickled and unpickled. | ||
""" | ||
|
||
@util.positional(8) | ||
@_helpers.positional(8) | ||
def __init__(self, access_token, client_id, client_secret, refresh_token, | ||
token_expiry, token_uri, user_agent, revoke_uri=None, | ||
id_token=None, token_response=None, scopes=None, | ||
|
@@ -513,7 +512,7 @@ def __init__(self, access_token, client_id, client_secret, refresh_token, | |
self.revoke_uri = revoke_uri | ||
self.id_token = id_token | ||
self.token_response = token_response | ||
self.scopes = set(util.string_to_scopes(scopes or [])) | ||
self.scopes = set(_helpers.string_to_scopes(scopes or [])) | ||
self.token_info_uri = token_info_uri | ||
|
||
# True if the credentials have been revoked or expired and can't be | ||
|
@@ -592,7 +591,7 @@ def has_scopes(self, scopes): | |
not have scopes. In both cases, you can use refresh_scopes() to | ||
obtain the canonical set of scopes. | ||
""" | ||
scopes = util.string_to_scopes(scopes) | ||
scopes = _helpers.string_to_scopes(scopes) | ||
return set(scopes).issubset(self.scopes) | ||
|
||
def retrieve_scopes(self, http): | ||
|
@@ -908,7 +907,7 @@ def _do_retrieve_scopes(self, http_request, token): | |
content = _helpers._from_bytes(content) | ||
if resp.status == http_client.OK: | ||
d = json.loads(content) | ||
self.scopes = set(util.string_to_scopes(d.get('scope', ''))) | ||
self.scopes = set(_helpers.string_to_scopes(d.get('scope', ''))) | ||
else: | ||
error_msg = 'Invalid response {0}.'.format(resp.status) | ||
try: | ||
|
@@ -1469,7 +1468,7 @@ class AssertionCredentials(GoogleCredentials): | |
AssertionCredentials objects may be safely pickled and unpickled. | ||
""" | ||
|
||
@util.positional(2) | ||
@_helpers.positional(2) | ||
def __init__(self, assertion_type, user_agent=None, | ||
token_uri=oauth2client.GOOGLE_TOKEN_URI, | ||
revoke_uri=oauth2client.GOOGLE_REVOKE_URI, | ||
|
@@ -1545,7 +1544,7 @@ def _require_crypto_or_die(): | |
raise CryptoUnavailableError('No crypto library available') | ||
|
||
|
||
@util.positional(2) | ||
@_helpers.positional(2) | ||
def verify_id_token(id_token, audience, http=None, | ||
cert_uri=ID_TOKEN_VERIFICATION_CERTS): | ||
"""Verifies a signed JWT id_token. | ||
|
@@ -1633,7 +1632,7 @@ def _parse_exchange_token_response(content): | |
return resp | ||
|
||
|
||
@util.positional(4) | ||
@_helpers.positional(4) | ||
def credentials_from_code(client_id, client_secret, scope, code, | ||
redirect_uri='postmessage', http=None, | ||
user_agent=None, | ||
|
@@ -1684,7 +1683,7 @@ def credentials_from_code(client_id, client_secret, scope, code, | |
return credentials | ||
|
||
|
||
@util.positional(3) | ||
@_helpers.positional(3) | ||
def credentials_from_clientsecrets_and_code(filename, scope, code, | ||
message=None, | ||
redirect_uri='postmessage', | ||
|
@@ -1803,7 +1802,7 @@ class OAuth2WebServerFlow(Flow): | |
OAuth2WebServerFlow objects may be safely pickled and unpickled. | ||
""" | ||
|
||
@util.positional(4) | ||
@_helpers.positional(4) | ||
def __init__(self, client_id, | ||
client_secret=None, | ||
scope=None, | ||
|
@@ -1862,7 +1861,7 @@ def __init__(self, client_id, | |
raise TypeError("The value of scope must not be None") | ||
self.client_id = client_id | ||
self.client_secret = client_secret | ||
self.scope = util.scopes_to_string(scope) | ||
self.scope = _helpers.scopes_to_string(scope) | ||
self.redirect_uri = redirect_uri | ||
self.login_hint = login_hint | ||
self.user_agent = user_agent | ||
|
@@ -1874,7 +1873,7 @@ def __init__(self, client_id, | |
self.authorization_header = authorization_header | ||
self.params = _oauth2_web_server_flow_params(kwargs) | ||
|
||
@util.positional(1) | ||
@_helpers.positional(1) | ||
def step1_get_authorize_url(self, redirect_uri=None, state=None): | ||
"""Returns a URI to redirect to the provider. | ||
|
@@ -1915,7 +1914,7 @@ def step1_get_authorize_url(self, redirect_uri=None, state=None): | |
query_params.update(self.params) | ||
return _update_query_params(self.auth_uri, query_params) | ||
|
||
@util.positional(1) | ||
@_helpers.positional(1) | ||
def step1_get_device_and_user_codes(self, http=None): | ||
"""Returns a user code and the verification URL where to enter it | ||
|
@@ -1963,7 +1962,7 @@ def step1_get_device_and_user_codes(self, http=None): | |
pass | ||
raise OAuth2DeviceCodeError(error_msg) | ||
|
||
@util.positional(2) | ||
@_helpers.positional(2) | ||
def step2_exchange(self, code=None, http=None, device_flow_info=None): | ||
"""Exchanges a code for OAuth2Credentials. | ||
|
@@ -2060,7 +2059,7 @@ def step2_exchange(self, code=None, http=None, device_flow_info=None): | |
raise FlowExchangeError(error_msg) | ||
|
||
|
||
@util.positional(2) | ||
@_helpers.positional(2) | ||
def flow_from_clientsecrets(filename, scope, redirect_uri=None, | ||
message=None, cache=None, login_hint=None, | ||
device_uri=None): | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.