Package googleapiclient :: Module _helpers
[hide private]
[frames] | no frames]

Source Code for Module googleapiclient._helpers

  1  # Copyright 2015 Google Inc. All rights reserved. 
  2  # 
  3  # Licensed under the Apache License, Version 2.0 (the "License"); 
  4  # you may not use this file except in compliance with the License. 
  5  # You may obtain a copy of the License at 
  6  # 
  7  #      http://www.apache.org/licenses/LICENSE-2.0 
  8  # 
  9  # Unless required by applicable law or agreed to in writing, software 
 10  # distributed under the License is distributed on an "AS IS" BASIS, 
 11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 12  # See the License for the specific language governing permissions and 
 13  # limitations under the License. 
 14   
 15  """Helper functions for commonly used utilities.""" 
 16   
 17  import functools 
 18  import inspect 
 19  import logging 
 20  import warnings 
 21   
 22  import six 
 23  from six.moves import urllib 
 24   
 25   
 26  logger = logging.getLogger(__name__) 
 27   
 28  POSITIONAL_WARNING = 'WARNING' 
 29  POSITIONAL_EXCEPTION = 'EXCEPTION' 
 30  POSITIONAL_IGNORE = 'IGNORE' 
 31  POSITIONAL_SET = frozenset([POSITIONAL_WARNING, POSITIONAL_EXCEPTION, 
 32                              POSITIONAL_IGNORE]) 
 33   
 34  positional_parameters_enforcement = POSITIONAL_WARNING 
 35   
 36  _SYM_LINK_MESSAGE = 'File: {0}: Is a symbolic link.' 
 37  _IS_DIR_MESSAGE = '{0}: Is a directory' 
 38  _MISSING_FILE_MESSAGE = 'Cannot access {0}: No such file or directory' 
39 40 41 -def positional(max_positional_args):
42 """A decorator to declare that only the first N arguments my be positional. 43 44 This decorator makes it easy to support Python 3 style keyword-only 45 parameters. For example, in Python 3 it is possible to write:: 46 47 def fn(pos1, *, kwonly1=None, kwonly1=None): 48 ... 49 50 All named parameters after ``*`` must be a keyword:: 51 52 fn(10, 'kw1', 'kw2') # Raises exception. 53 fn(10, kwonly1='kw1') # Ok. 54 55 Example 56 ^^^^^^^ 57 58 To define a function like above, do:: 59 60 @positional(1) 61 def fn(pos1, kwonly1=None, kwonly2=None): 62 ... 63 64 If no default value is provided to a keyword argument, it becomes a 65 required keyword argument:: 66 67 @positional(0) 68 def fn(required_kw): 69 ... 70 71 This must be called with the keyword parameter:: 72 73 fn() # Raises exception. 74 fn(10) # Raises exception. 75 fn(required_kw=10) # Ok. 76 77 When defining instance or class methods always remember to account for 78 ``self`` and ``cls``:: 79 80 class MyClass(object): 81 82 @positional(2) 83 def my_method(self, pos1, kwonly1=None): 84 ... 85 86 @classmethod 87 @positional(2) 88 def my_method(cls, pos1, kwonly1=None): 89 ... 90 91 The positional decorator behavior is controlled by 92 ``_helpers.positional_parameters_enforcement``, which may be set to 93 ``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or 94 ``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do 95 nothing, respectively, if a declaration is violated. 96 97 Args: 98 max_positional_arguments: Maximum number of positional arguments. All 99 parameters after the this index must be 100 keyword only. 101 102 Returns: 103 A decorator that prevents using arguments after max_positional_args 104 from being used as positional parameters. 105 106 Raises: 107 TypeError: if a key-word only argument is provided as a positional 108 parameter, but only if 109 _helpers.positional_parameters_enforcement is set to 110 POSITIONAL_EXCEPTION. 111 """ 112 113 def positional_decorator(wrapped): 114 @functools.wraps(wrapped) 115 def positional_wrapper(*args, **kwargs): 116 if len(args) > max_positional_args: 117 plural_s = '' 118 if max_positional_args != 1: 119 plural_s = 's' 120 message = ('{function}() takes at most {args_max} positional ' 121 'argument{plural} ({args_given} given)'.format( 122 function=wrapped.__name__, 123 args_max=max_positional_args, 124 args_given=len(args), 125 plural=plural_s)) 126 if positional_parameters_enforcement == POSITIONAL_EXCEPTION: 127 raise TypeError(message) 128 elif positional_parameters_enforcement == POSITIONAL_WARNING: 129 logger.warning(message) 130 return wrapped(*args, **kwargs)
131 return positional_wrapper 132 133 if isinstance(max_positional_args, six.integer_types): 134 return positional_decorator 135 else: 136 args, _, _, defaults = inspect.getargspec(max_positional_args) 137 return positional(len(args) - len(defaults))(max_positional_args) 138
139 140 -def parse_unique_urlencoded(content):
141 """Parses unique key-value parameters from urlencoded content. 142 143 Args: 144 content: string, URL-encoded key-value pairs. 145 146 Returns: 147 dict, The key-value pairs from ``content``. 148 149 Raises: 150 ValueError: if one of the keys is repeated. 151 """ 152 urlencoded_params = urllib.parse.parse_qs(content) 153 params = {} 154 for key, value in six.iteritems(urlencoded_params): 155 if len(value) != 1: 156 msg = ('URL-encoded content contains a repeated value:' 157 '%s -> %s' % (key, ', '.join(value))) 158 raise ValueError(msg) 159 params[key] = value[0] 160 return params
161
162 163 -def update_query_params(uri, params):
164 """Updates a URI with new query parameters. 165 166 If a given key from ``params`` is repeated in the ``uri``, then 167 the URI will be considered invalid and an error will occur. 168 169 If the URI is valid, then each value from ``params`` will 170 replace the corresponding value in the query parameters (if 171 it exists). 172 173 Args: 174 uri: string, A valid URI, with potential existing query parameters. 175 params: dict, A dictionary of query parameters. 176 177 Returns: 178 The same URI but with the new query parameters added. 179 """ 180 parts = urllib.parse.urlparse(uri) 181 query_params = parse_unique_urlencoded(parts.query) 182 query_params.update(params) 183 new_query = urllib.parse.urlencode(query_params) 184 new_parts = parts._replace(query=new_query) 185 return urllib.parse.urlunparse(new_parts)
186
187 188 -def _add_query_parameter(url, name, value):
189 """Adds a query parameter to a url. 190 191 Replaces the current value if it already exists in the URL. 192 193 Args: 194 url: string, url to add the query parameter to. 195 name: string, query parameter name. 196 value: string, query parameter value. 197 198 Returns: 199 Updated query parameter. Does not update the url if value is None. 200 """ 201 if value is None: 202 return url 203 else: 204 return update_query_params(url, {name: value})
205