| OLD | NEW | 
|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python | 
| 2 # | 2 # | 
| 3 # Copyright 2014 Google Inc. All rights reserved. | 3 # Copyright 2010 Google Inc. | 
| 4 # | 4 # | 
| 5 # Licensed under the Apache License, Version 2.0 (the "License"); | 5 # Licensed under the Apache License, Version 2.0 (the "License"); | 
| 6 # you may not use this file except in compliance with the License. | 6 # you may not use this file except in compliance with the License. | 
| 7 # You may obtain a copy of the License at | 7 # You may obtain a copy of the License at | 
| 8 # | 8 # | 
| 9 #     http://www.apache.org/licenses/LICENSE-2.0 | 9 #     http://www.apache.org/licenses/LICENSE-2.0 | 
| 10 # | 10 # | 
| 11 # Unless required by applicable law or agreed to in writing, software | 11 # Unless required by applicable law or agreed to in writing, software | 
| 12 # distributed under the License is distributed on an "AS IS" BASIS, | 12 # distributed under the License is distributed on an "AS IS" BASIS, | 
| 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| 14 # See the License for the specific language governing permissions and | 14 # See the License for the specific language governing permissions and | 
| 15 # limitations under the License. | 15 # limitations under the License. | 
| 16 # | 16 # | 
| 17 | 17 | 
| 18 """Common utility library.""" | 18 """Common utility library.""" | 
| 19 | 19 | 
| 20 __author__ = [ | 20 __author__ = ['rafek@google.com (Rafe Kaplan)', | 
| 21     'rafek@google.com (Rafe Kaplan)', | 21               'guido@google.com (Guido van Rossum)', | 
| 22     'guido@google.com (Guido van Rossum)', | 22 ] | 
|  | 23 __all__ = [ | 
|  | 24   'positional', | 
|  | 25   'POSITIONAL_WARNING', | 
|  | 26   'POSITIONAL_EXCEPTION', | 
|  | 27   'POSITIONAL_IGNORE', | 
| 23 ] | 28 ] | 
| 24 | 29 | 
| 25 __all__ = [ |  | 
| 26     'positional', |  | 
| 27     'POSITIONAL_WARNING', |  | 
| 28     'POSITIONAL_EXCEPTION', |  | 
| 29     'POSITIONAL_IGNORE', |  | 
| 30 ] |  | 
| 31 |  | 
| 32 import functools |  | 
| 33 import inspect | 30 import inspect | 
| 34 import logging | 31 import logging | 
| 35 import sys |  | 
| 36 import types | 32 import types | 
|  | 33 import urllib | 
|  | 34 import urlparse | 
| 37 | 35 | 
| 38 from third_party import six | 36 try: | 
| 39 from third_party.six.moves import urllib | 37   from urlparse import parse_qsl | 
| 40 | 38 except ImportError: | 
|  | 39   from cgi import parse_qsl | 
| 41 | 40 | 
| 42 logger = logging.getLogger(__name__) | 41 logger = logging.getLogger(__name__) | 
| 43 | 42 | 
| 44 POSITIONAL_WARNING = 'WARNING' | 43 POSITIONAL_WARNING = 'WARNING' | 
| 45 POSITIONAL_EXCEPTION = 'EXCEPTION' | 44 POSITIONAL_EXCEPTION = 'EXCEPTION' | 
| 46 POSITIONAL_IGNORE = 'IGNORE' | 45 POSITIONAL_IGNORE = 'IGNORE' | 
| 47 POSITIONAL_SET = frozenset([POSITIONAL_WARNING, POSITIONAL_EXCEPTION, | 46 POSITIONAL_SET = frozenset([POSITIONAL_WARNING, POSITIONAL_EXCEPTION, | 
| 48                             POSITIONAL_IGNORE]) | 47                             POSITIONAL_IGNORE]) | 
| 49 | 48 | 
| 50 positional_parameters_enforcement = POSITIONAL_WARNING | 49 positional_parameters_enforcement = POSITIONAL_WARNING | 
| 51 | 50 | 
| 52 def positional(max_positional_args): | 51 def positional(max_positional_args): | 
| 53   """A decorator to declare that only the first N arguments my be positional. | 52   """A decorator to declare that only the first N arguments my be positional. | 
| 54 | 53 | 
| 55   This decorator makes it easy to support Python 3 style keyword-only | 54   This decorator makes it easy to support Python 3 style key-word only | 
| 56   parameters. For example, in Python 3 it is possible to write:: | 55   parameters. For example, in Python 3 it is possible to write: | 
| 57 | 56 | 
| 58     def fn(pos1, *, kwonly1=None, kwonly1=None): | 57     def fn(pos1, *, kwonly1=None, kwonly1=None): | 
| 59       ... | 58       ... | 
| 60 | 59 | 
| 61   All named parameters after ``*`` must be a keyword:: | 60   All named parameters after * must be a keyword: | 
| 62 | 61 | 
| 63     fn(10, 'kw1', 'kw2')  # Raises exception. | 62     fn(10, 'kw1', 'kw2')  # Raises exception. | 
| 64     fn(10, kwonly1='kw1')  # Ok. | 63     fn(10, kwonly1='kw1')  # Ok. | 
| 65 | 64 | 
| 66   Example | 65   Example: | 
| 67   ^^^^^^^ | 66     To define a function like above, do: | 
| 68 | 67 | 
| 69   To define a function like above, do:: | 68       @positional(1) | 
| 70 | 69       def fn(pos1, kwonly1=None, kwonly2=None): | 
| 71     @positional(1) |  | 
| 72     def fn(pos1, kwonly1=None, kwonly2=None): |  | 
| 73       ... |  | 
| 74 |  | 
| 75   If no default value is provided to a keyword argument, it becomes a required |  | 
| 76   keyword argument:: |  | 
| 77 |  | 
| 78     @positional(0) |  | 
| 79     def fn(required_kw): |  | 
| 80       ... |  | 
| 81 |  | 
| 82   This must be called with the keyword parameter:: |  | 
| 83 |  | 
| 84     fn()  # Raises exception. |  | 
| 85     fn(10)  # Raises exception. |  | 
| 86     fn(required_kw=10)  # Ok. |  | 
| 87 |  | 
| 88   When defining instance or class methods always remember to account for |  | 
| 89   ``self`` and ``cls``:: |  | 
| 90 |  | 
| 91     class MyClass(object): |  | 
| 92 |  | 
| 93       @positional(2) |  | 
| 94       def my_method(self, pos1, kwonly1=None): |  | 
| 95         ... | 70         ... | 
| 96 | 71 | 
| 97       @classmethod | 72     If no default value is provided to a keyword argument, it becomes a required | 
| 98       @positional(2) | 73     keyword argument: | 
| 99       def my_method(cls, pos1, kwonly1=None): | 74 | 
|  | 75       @positional(0) | 
|  | 76       def fn(required_kw): | 
| 100         ... | 77         ... | 
| 101 | 78 | 
|  | 79     This must be called with the keyword parameter: | 
|  | 80 | 
|  | 81       fn()  # Raises exception. | 
|  | 82       fn(10)  # Raises exception. | 
|  | 83       fn(required_kw=10)  # Ok. | 
|  | 84 | 
|  | 85     When defining instance or class methods always remember to account for | 
|  | 86     'self' and 'cls': | 
|  | 87 | 
|  | 88       class MyClass(object): | 
|  | 89 | 
|  | 90         @positional(2) | 
|  | 91         def my_method(self, pos1, kwonly1=None): | 
|  | 92           ... | 
|  | 93 | 
|  | 94         @classmethod | 
|  | 95         @positional(2) | 
|  | 96         def my_method(cls, pos1, kwonly1=None): | 
|  | 97           ... | 
|  | 98 | 
| 102   The positional decorator behavior is controlled by | 99   The positional decorator behavior is controlled by | 
| 103   ``util.positional_parameters_enforcement``, which may be set to | 100   util.positional_parameters_enforcement, which may be set to | 
| 104   ``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or | 101   POSITIONAL_EXCEPTION, POSITIONAL_WARNING or POSITIONAL_IGNORE to raise an | 
| 105   ``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do | 102   exception, log a warning, or do nothing, respectively, if a declaration is | 
| 106   nothing, respectively, if a declaration is violated. | 103   violated. | 
| 107 | 104 | 
| 108   Args: | 105   Args: | 
| 109     max_positional_arguments: Maximum number of positional arguments. All | 106     max_positional_arguments: Maximum number of positional arguments. All | 
| 110       parameters after the this index must be keyword only. | 107       parameters after the this index must be keyword only. | 
| 111 | 108 | 
| 112   Returns: | 109   Returns: | 
| 113     A decorator that prevents using arguments after max_positional_args from | 110     A decorator that prevents using arguments after max_positional_args from | 
| 114     being used as positional parameters. | 111     being used as positional parameters. | 
| 115 | 112 | 
| 116   Raises: | 113   Raises: | 
| 117     TypeError if a key-word only argument is provided as a positional | 114     TypeError if a key-word only argument is provided as a positional | 
| 118     parameter, but only if util.positional_parameters_enforcement is set to | 115     parameter, but only if util.positional_parameters_enforcement is set to | 
| 119     POSITIONAL_EXCEPTION. | 116     POSITIONAL_EXCEPTION. | 
| 120 |  | 
| 121   """ | 117   """ | 
| 122   def positional_decorator(wrapped): | 118   def positional_decorator(wrapped): | 
| 123     @functools.wraps(wrapped) |  | 
| 124     def positional_wrapper(*args, **kwargs): | 119     def positional_wrapper(*args, **kwargs): | 
| 125       if len(args) > max_positional_args: | 120       if len(args) > max_positional_args: | 
| 126         plural_s = '' | 121         plural_s = '' | 
| 127         if max_positional_args != 1: | 122         if max_positional_args != 1: | 
| 128           plural_s = 's' | 123           plural_s = 's' | 
| 129         message = '%s() takes at most %d positional argument%s (%d given)' % ( | 124         message = '%s() takes at most %d positional argument%s (%d given)' % ( | 
| 130             wrapped.__name__, max_positional_args, plural_s, len(args)) | 125             wrapped.__name__, max_positional_args, plural_s, len(args)) | 
| 131         if positional_parameters_enforcement == POSITIONAL_EXCEPTION: | 126         if positional_parameters_enforcement == POSITIONAL_EXCEPTION: | 
| 132           raise TypeError(message) | 127           raise TypeError(message) | 
| 133         elif positional_parameters_enforcement == POSITIONAL_WARNING: | 128         elif positional_parameters_enforcement == POSITIONAL_WARNING: | 
| 134           logger.warning(message) | 129           logger.warning(message) | 
| 135         else: # IGNORE | 130         else: # IGNORE | 
| 136           pass | 131           pass | 
| 137       return wrapped(*args, **kwargs) | 132       return wrapped(*args, **kwargs) | 
| 138     return positional_wrapper | 133     return positional_wrapper | 
| 139 | 134 | 
| 140   if isinstance(max_positional_args, six.integer_types): | 135   if isinstance(max_positional_args, (int, long)): | 
| 141     return positional_decorator | 136     return positional_decorator | 
| 142   else: | 137   else: | 
| 143     args, _, _, defaults = inspect.getargspec(max_positional_args) | 138     args, _, _, defaults = inspect.getargspec(max_positional_args) | 
| 144     return positional(len(args) - len(defaults))(max_positional_args) | 139     return positional(len(args) - len(defaults))(max_positional_args) | 
| 145 | 140 | 
| 146 | 141 | 
| 147 def scopes_to_string(scopes): | 142 def scopes_to_string(scopes): | 
| 148   """Converts scope value to a string. | 143   """Converts scope value to a string. | 
| 149 | 144 | 
| 150   If scopes is a string then it is simply passed through. If scopes is an | 145   If scopes is a string then it is simply passed through. If scopes is an | 
| 151   iterable then a string is returned that is all the individual scopes | 146   iterable then a string is returned that is all the individual scopes | 
| 152   concatenated with spaces. | 147   concatenated with spaces. | 
| 153 | 148 | 
| 154   Args: | 149   Args: | 
| 155     scopes: string or iterable of strings, the scopes. | 150     scopes: string or iterable of strings, the scopes. | 
| 156 | 151 | 
| 157   Returns: | 152   Returns: | 
| 158     The scopes formatted as a single string. | 153     The scopes formatted as a single string. | 
| 159   """ | 154   """ | 
| 160   if isinstance(scopes, six.string_types): | 155   if isinstance(scopes, types.StringTypes): | 
| 161     return scopes | 156     return scopes | 
| 162   else: | 157   else: | 
| 163     return ' '.join(scopes) | 158     return ' '.join(scopes) | 
| 164 | 159 | 
| 165 | 160 | 
| 166 def dict_to_tuple_key(dictionary): | 161 def dict_to_tuple_key(dictionary): | 
| 167   """Converts a dictionary to a tuple that can be used as an immutable key. | 162   """Converts a dictionary to a tuple that can be used as an immutable key. | 
| 168 | 163 | 
| 169   The resulting key is always sorted so that logically equivalent dictionaries | 164   The resulting key is always sorted so that logically equivalent dictionaries | 
| 170   always produce an identical tuple for a key. | 165   always produce an identical tuple for a key. | 
| (...skipping 16 matching lines...) Expand all  Loading... | 
| 187     url: string, url to add the query parameter to. | 182     url: string, url to add the query parameter to. | 
| 188     name: string, query parameter name. | 183     name: string, query parameter name. | 
| 189     value: string, query parameter value. | 184     value: string, query parameter value. | 
| 190 | 185 | 
| 191   Returns: | 186   Returns: | 
| 192     Updated query parameter. Does not update the url if value is None. | 187     Updated query parameter. Does not update the url if value is None. | 
| 193   """ | 188   """ | 
| 194   if value is None: | 189   if value is None: | 
| 195     return url | 190     return url | 
| 196   else: | 191   else: | 
| 197     parsed = list(urllib.parse.urlparse(url)) | 192     parsed = list(urlparse.urlparse(url)) | 
| 198     q = dict(urllib.parse.parse_qsl(parsed[4])) | 193     q = dict(parse_qsl(parsed[4])) | 
| 199     q[name] = value | 194     q[name] = value | 
| 200     parsed[4] = urllib.parse.urlencode(q) | 195     parsed[4] = urllib.urlencode(q) | 
| 201     return urllib.parse.urlunparse(parsed) | 196     return urlparse.urlunparse(parsed) | 
| OLD | NEW | 
|---|