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