Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(170)

Side by Side Diff: third_party/google-endpoints/endpoints/resource_container.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2016 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 """Module for a class that contains a request body resource and parameters."""
16
17 from protorpc import message_types
18 from protorpc import messages
19
20
21 class ResourceContainer(object):
22 """Container for a request body resource combined with parameters.
23
24 Used for API methods which may also have path or query parameters in addition
25 to a request body.
26
27 Attributes:
28 body_message_class: A message class to represent a request body.
29 parameters_message_class: A placeholder message class for request
30 parameters.
31 """
32
33 __remote_info_cache = {} # pylint: disable=g-bad-name
34
35 __combined_message_class = None # pylint: disable=invalid-name
36
37 def __init__(self, _body_message_class=message_types.VoidMessage, **kwargs):
38 """Constructor for ResourceContainer.
39
40 Stores a request body message class and attempts to create one from the
41 keyword arguments passed in.
42
43 Args:
44 _body_message_class: A keyword argument to be treated like a positional
45 argument. This will not conflict with the potential names of fields
46 since they can't begin with underscore. We make this a keyword
47 argument since the default VoidMessage is a very common choice given
48 the prevalence of GET methods.
49 **kwargs: Keyword arguments specifying field names (the named arguments)
50 and instances of ProtoRPC fields as the values.
51 """
52 self.body_message_class = _body_message_class
53 self.parameters_message_class = type('ParameterContainer',
54 (messages.Message,), kwargs)
55
56 @property
57 def combined_message_class(self):
58 """A ProtoRPC message class with both request and parameters fields.
59
60 Caches the result in a local private variable. Uses _CopyField to create
61 copies of the fields from the existing request and parameters classes since
62 those fields are "owned" by the message classes.
63
64 Raises:
65 TypeError: If a field name is used in both the request message and the
66 parameters but the two fields do not represent the same type.
67
68 Returns:
69 Value of combined message class for this property.
70 """
71 if self.__combined_message_class is not None:
72 return self.__combined_message_class
73
74 fields = {}
75 # We don't need to preserve field.number since this combined class is only
76 # used for the protorpc remote.method and is not needed for the API config.
77 # The only place field.number matters is in parameterOrder, but this is set
78 # based on container.parameters_message_class which will use the field
79 # numbers originally passed in.
80
81 # Counter for fields.
82 field_number = 1
83 for field in self.body_message_class.all_fields():
84 fields[field.name] = _CopyField(field, number=field_number)
85 field_number += 1
86 for field in self.parameters_message_class.all_fields():
87 if field.name in fields:
88 if not _CompareFields(field, fields[field.name]):
89 raise TypeError('Field %r contained in both parameters and request '
90 'body, but the fields differ.' % (field.name,))
91 else:
92 # Skip a field that's already there.
93 continue
94 fields[field.name] = _CopyField(field, number=field_number)
95 field_number += 1
96
97 self.__combined_message_class = type('CombinedContainer',
98 (messages.Message,), fields)
99 return self.__combined_message_class
100
101 @classmethod
102 def add_to_cache(cls, remote_info, container): # pylint: disable=g-bad-name
103 """Adds a ResourceContainer to a cache tying it to a protorpc method.
104
105 Args:
106 remote_info: Instance of protorpc.remote._RemoteMethodInfo corresponding
107 to a method.
108 container: An instance of ResourceContainer.
109
110 Raises:
111 TypeError: if the container is not an instance of cls.
112 KeyError: if the remote method has been reference by a container before.
113 This created remote method should never occur because a remote method
114 is created once.
115 """
116 if not isinstance(container, cls):
117 raise TypeError('%r not an instance of %r, could not be added to cache.' %
118 (container, cls))
119 if remote_info in cls.__remote_info_cache:
120 raise KeyError('Cache has collision but should not.')
121 cls.__remote_info_cache[remote_info] = container
122
123 @classmethod
124 def get_request_message(cls, remote_info): # pylint: disable=g-bad-name
125 """Gets request message or container from remote info.
126
127 Args:
128 remote_info: Instance of protorpc.remote._RemoteMethodInfo corresponding
129 to a method.
130
131 Returns:
132 Either an instance of the request type from the remote or the
133 ResourceContainer that was cached with the remote method.
134 """
135 if remote_info in cls.__remote_info_cache:
136 return cls.__remote_info_cache[remote_info]
137 else:
138 return remote_info.request_type()
139
140
141 def _GetFieldAttributes(field):
142 """Decomposes field into the needed arguments to pass to the constructor.
143
144 This can be used to create copies of the field or to compare if two fields
145 are "equal" (since __eq__ is not implemented on messages.Field).
146
147 Args:
148 field: A ProtoRPC message field (potentially to be copied).
149
150 Raises:
151 TypeError: If the field is not an instance of messages.Field.
152
153 Returns:
154 A pair of relevant arguments to be passed to the constructor for the field
155 type. The first element is a list of positional arguments for the
156 constructor and the second is a dictionary of keyword arguments.
157 """
158 if not isinstance(field, messages.Field):
159 raise TypeError('Field %r to be copied not a ProtoRPC field.' % (field,))
160
161 positional_args = []
162 kwargs = {
163 'required': field.required,
164 'repeated': field.repeated,
165 'variant': field.variant,
166 'default': field._Field__default, # pylint: disable=protected-access
167 }
168
169 if isinstance(field, messages.MessageField):
170 # Message fields can't have a default
171 kwargs.pop('default')
172 if not isinstance(field, message_types.DateTimeField):
173 positional_args.insert(0, field.message_type)
174 elif isinstance(field, messages.EnumField):
175 positional_args.insert(0, field.type)
176
177 return positional_args, kwargs
178
179
180 def _CompareFields(field, other_field):
181 """Checks if two ProtoRPC fields are "equal".
182
183 Compares the arguments, rather than the id of the elements (which is
184 the default __eq__ behavior) as well as the class of the fields.
185
186 Args:
187 field: A ProtoRPC message field to be compared.
188 other_field: A ProtoRPC message field to be compared.
189
190 Returns:
191 Boolean indicating whether the fields are equal.
192 """
193 field_attrs = _GetFieldAttributes(field)
194 other_field_attrs = _GetFieldAttributes(other_field)
195 if field_attrs != other_field_attrs:
196 return False
197 return field.__class__ == other_field.__class__
198
199
200 def _CopyField(field, number=None):
201 """Copies a (potentially) owned ProtoRPC field instance into a new copy.
202
203 Args:
204 field: A ProtoRPC message field to be copied.
205 number: An integer for the field to override the number of the field.
206 Defaults to None.
207
208 Raises:
209 TypeError: If the field is not an instance of messages.Field.
210
211 Returns:
212 A copy of the ProtoRPC message field.
213 """
214 positional_args, kwargs = _GetFieldAttributes(field)
215 number = number or field.number
216 positional_args.append(number)
217 return field.__class__(*positional_args, **kwargs)
OLDNEW
« no previous file with comments | « third_party/google-endpoints/endpoints/protojson.py ('k') | third_party/google-endpoints/endpoints/test/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698