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

Side by Side Diff: third_party/google_api_python_client/googleapiclient/model.py

Issue 963953003: OAuth2 support in depot_tools (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: restore git_cl Created 5 years, 8 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/python2.4
2 #
3 # Copyright 2014 Google Inc. All Rights Reserved.
4 #
5 # Licensed under the Apache License, Version 2.0 (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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 """Model objects for requests and responses.
18
19 Each API may support one or more serializations, such
20 as JSON, Atom, etc. The model classes are responsible
21 for converting between the wire format and the Python
22 object representation.
23 """
24
25 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
26
27 import json
28 import logging
29 import urllib
30
31 from googleapiclient import __version__
32 from errors import HttpError
33
34
35 dump_request_response = False
36
37
38 def _abstract():
39 raise NotImplementedError('You need to override this function')
40
41
42 class Model(object):
43 """Model base class.
44
45 All Model classes should implement this interface.
46 The Model serializes and de-serializes between a wire
47 format such as JSON and a Python object representation.
48 """
49
50 def request(self, headers, path_params, query_params, body_value):
51 """Updates outgoing requests with a serialized body.
52
53 Args:
54 headers: dict, request headers
55 path_params: dict, parameters that appear in the request path
56 query_params: dict, parameters that appear in the query
57 body_value: object, the request body as a Python object, which must be
58 serializable.
59 Returns:
60 A tuple of (headers, path_params, query, body)
61
62 headers: dict, request headers
63 path_params: dict, parameters that appear in the request path
64 query: string, query part of the request URI
65 body: string, the body serialized in the desired wire format.
66 """
67 _abstract()
68
69 def response(self, resp, content):
70 """Convert the response wire format into a Python object.
71
72 Args:
73 resp: httplib2.Response, the HTTP response headers and status
74 content: string, the body of the HTTP response
75
76 Returns:
77 The body de-serialized as a Python object.
78
79 Raises:
80 googleapiclient.errors.HttpError if a non 2xx response is received.
81 """
82 _abstract()
83
84
85 class BaseModel(Model):
86 """Base model class.
87
88 Subclasses should provide implementations for the "serialize" and
89 "deserialize" methods, as well as values for the following class attributes.
90
91 Attributes:
92 accept: The value to use for the HTTP Accept header.
93 content_type: The value to use for the HTTP Content-type header.
94 no_content_response: The value to return when deserializing a 204 "No
95 Content" response.
96 alt_param: The value to supply as the "alt" query parameter for requests.
97 """
98
99 accept = None
100 content_type = None
101 no_content_response = None
102 alt_param = None
103
104 def _log_request(self, headers, path_params, query, body):
105 """Logs debugging information about the request if requested."""
106 if dump_request_response:
107 logging.info('--request-start--')
108 logging.info('-headers-start-')
109 for h, v in headers.iteritems():
110 logging.info('%s: %s', h, v)
111 logging.info('-headers-end-')
112 logging.info('-path-parameters-start-')
113 for h, v in path_params.iteritems():
114 logging.info('%s: %s', h, v)
115 logging.info('-path-parameters-end-')
116 logging.info('body: %s', body)
117 logging.info('query: %s', query)
118 logging.info('--request-end--')
119
120 def request(self, headers, path_params, query_params, body_value):
121 """Updates outgoing requests with a serialized body.
122
123 Args:
124 headers: dict, request headers
125 path_params: dict, parameters that appear in the request path
126 query_params: dict, parameters that appear in the query
127 body_value: object, the request body as a Python object, which must be
128 serializable by json.
129 Returns:
130 A tuple of (headers, path_params, query, body)
131
132 headers: dict, request headers
133 path_params: dict, parameters that appear in the request path
134 query: string, query part of the request URI
135 body: string, the body serialized as JSON
136 """
137 query = self._build_query(query_params)
138 headers['accept'] = self.accept
139 headers['accept-encoding'] = 'gzip, deflate'
140 if 'user-agent' in headers:
141 headers['user-agent'] += ' '
142 else:
143 headers['user-agent'] = ''
144 headers['user-agent'] += 'google-api-python-client/%s (gzip)' % __version__
145
146 if body_value is not None:
147 headers['content-type'] = self.content_type
148 body_value = self.serialize(body_value)
149 self._log_request(headers, path_params, query, body_value)
150 return (headers, path_params, query, body_value)
151
152 def _build_query(self, params):
153 """Builds a query string.
154
155 Args:
156 params: dict, the query parameters
157
158 Returns:
159 The query parameters properly encoded into an HTTP URI query string.
160 """
161 if self.alt_param is not None:
162 params.update({'alt': self.alt_param})
163 astuples = []
164 for key, value in params.iteritems():
165 if type(value) == type([]):
166 for x in value:
167 x = x.encode('utf-8')
168 astuples.append((key, x))
169 else:
170 if getattr(value, 'encode', False) and callable(value.encode):
171 value = value.encode('utf-8')
172 astuples.append((key, value))
173 return '?' + urllib.urlencode(astuples)
174
175 def _log_response(self, resp, content):
176 """Logs debugging information about the response if requested."""
177 if dump_request_response:
178 logging.info('--response-start--')
179 for h, v in resp.iteritems():
180 logging.info('%s: %s', h, v)
181 if content:
182 logging.info(content)
183 logging.info('--response-end--')
184
185 def response(self, resp, content):
186 """Convert the response wire format into a Python object.
187
188 Args:
189 resp: httplib2.Response, the HTTP response headers and status
190 content: string, the body of the HTTP response
191
192 Returns:
193 The body de-serialized as a Python object.
194
195 Raises:
196 googleapiclient.errors.HttpError if a non 2xx response is received.
197 """
198 self._log_response(resp, content)
199 # Error handling is TBD, for example, do we retry
200 # for some operation/error combinations?
201 if resp.status < 300:
202 if resp.status == 204:
203 # A 204: No Content response should be treated differently
204 # to all the other success states
205 return self.no_content_response
206 return self.deserialize(content)
207 else:
208 logging.debug('Content from bad request was: %s' % content)
209 raise HttpError(resp, content)
210
211 def serialize(self, body_value):
212 """Perform the actual Python object serialization.
213
214 Args:
215 body_value: object, the request body as a Python object.
216
217 Returns:
218 string, the body in serialized form.
219 """
220 _abstract()
221
222 def deserialize(self, content):
223 """Perform the actual deserialization from response string to Python
224 object.
225
226 Args:
227 content: string, the body of the HTTP response
228
229 Returns:
230 The body de-serialized as a Python object.
231 """
232 _abstract()
233
234
235 class JsonModel(BaseModel):
236 """Model class for JSON.
237
238 Serializes and de-serializes between JSON and the Python
239 object representation of HTTP request and response bodies.
240 """
241 accept = 'application/json'
242 content_type = 'application/json'
243 alt_param = 'json'
244
245 def __init__(self, data_wrapper=False):
246 """Construct a JsonModel.
247
248 Args:
249 data_wrapper: boolean, wrap requests and responses in a data wrapper
250 """
251 self._data_wrapper = data_wrapper
252
253 def serialize(self, body_value):
254 if (isinstance(body_value, dict) and 'data' not in body_value and
255 self._data_wrapper):
256 body_value = {'data': body_value}
257 return json.dumps(body_value)
258
259 def deserialize(self, content):
260 content = content.decode('utf-8')
261 body = json.loads(content)
262 if self._data_wrapper and isinstance(body, dict) and 'data' in body:
263 body = body['data']
264 return body
265
266 @property
267 def no_content_response(self):
268 return {}
269
270
271 class RawModel(JsonModel):
272 """Model class for requests that don't return JSON.
273
274 Serializes and de-serializes between JSON and the Python
275 object representation of HTTP request, and returns the raw bytes
276 of the response body.
277 """
278 accept = '*/*'
279 content_type = 'application/json'
280 alt_param = None
281
282 def deserialize(self, content):
283 return content
284
285 @property
286 def no_content_response(self):
287 return ''
288
289
290 class MediaModel(JsonModel):
291 """Model class for requests that return Media.
292
293 Serializes and de-serializes between JSON and the Python
294 object representation of HTTP request, and returns the raw bytes
295 of the response body.
296 """
297 accept = '*/*'
298 content_type = 'application/json'
299 alt_param = 'media'
300
301 def deserialize(self, content):
302 return content
303
304 @property
305 def no_content_response(self):
306 return ''
307
308
309 class ProtocolBufferModel(BaseModel):
310 """Model class for protocol buffers.
311
312 Serializes and de-serializes the binary protocol buffer sent in the HTTP
313 request and response bodies.
314 """
315 accept = 'application/x-protobuf'
316 content_type = 'application/x-protobuf'
317 alt_param = 'proto'
318
319 def __init__(self, protocol_buffer):
320 """Constructs a ProtocolBufferModel.
321
322 The serialzed protocol buffer returned in an HTTP response will be
323 de-serialized using the given protocol buffer class.
324
325 Args:
326 protocol_buffer: The protocol buffer class used to de-serialize a
327 response from the API.
328 """
329 self._protocol_buffer = protocol_buffer
330
331 def serialize(self, body_value):
332 return body_value.SerializeToString()
333
334 def deserialize(self, content):
335 return self._protocol_buffer.FromString(content)
336
337 @property
338 def no_content_response(self):
339 return self._protocol_buffer()
340
341
342 def makepatch(original, modified):
343 """Create a patch object.
344
345 Some methods support PATCH, an efficient way to send updates to a resource.
346 This method allows the easy construction of patch bodies by looking at the
347 differences between a resource before and after it was modified.
348
349 Args:
350 original: object, the original deserialized resource
351 modified: object, the modified deserialized resource
352 Returns:
353 An object that contains only the changes from original to modified, in a
354 form suitable to pass to a PATCH method.
355
356 Example usage:
357 item = service.activities().get(postid=postid, userid=userid).execute()
358 original = copy.deepcopy(item)
359 item['object']['content'] = 'This is updated.'
360 service.activities.patch(postid=postid, userid=userid,
361 body=makepatch(original, item)).execute()
362 """
363 patch = {}
364 for key, original_value in original.iteritems():
365 modified_value = modified.get(key, None)
366 if modified_value is None:
367 # Use None to signal that the element is deleted
368 patch[key] = None
369 elif original_value != modified_value:
370 if type(original_value) == type({}):
371 # Recursively descend objects
372 patch[key] = makepatch(original_value, modified_value)
373 else:
374 # In the case of simple types or arrays we just replace
375 patch[key] = modified_value
376 else:
377 # Don't add anything to patch if there's no change
378 pass
379 for key in modified:
380 if key not in original:
381 patch[key] = modified[key]
382
383 return patch
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698