Index: third_party/google_api_python_client/googleapiclient/model.py |
diff --git a/third_party/google_api_python_client/googleapiclient/model.py b/third_party/google_api_python_client/googleapiclient/model.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0f0172cab5e4122ae5dae8183aa6a981c42293cc |
--- /dev/null |
+++ b/third_party/google_api_python_client/googleapiclient/model.py |
@@ -0,0 +1,383 @@ |
+#!/usr/bin/python2.4 |
+# |
+# Copyright 2014 Google Inc. All Rights Reserved. |
+# |
+# Licensed under the Apache License, Version 2.0 (the "License"); |
+# you may not use this file except in compliance with the License. |
+# You may obtain a copy of the License at |
+# |
+# http://www.apache.org/licenses/LICENSE-2.0 |
+# |
+# Unless required by applicable law or agreed to in writing, software |
+# distributed under the License is distributed on an "AS IS" BASIS, |
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+# See the License for the specific language governing permissions and |
+# limitations under the License. |
+ |
+"""Model objects for requests and responses. |
+ |
+Each API may support one or more serializations, such |
+as JSON, Atom, etc. The model classes are responsible |
+for converting between the wire format and the Python |
+object representation. |
+""" |
+ |
+__author__ = 'jcgregorio@google.com (Joe Gregorio)' |
+ |
+import json |
+import logging |
+import urllib |
+ |
+from googleapiclient import __version__ |
+from errors import HttpError |
+ |
+ |
+dump_request_response = False |
+ |
+ |
+def _abstract(): |
+ raise NotImplementedError('You need to override this function') |
+ |
+ |
+class Model(object): |
+ """Model base class. |
+ |
+ All Model classes should implement this interface. |
+ The Model serializes and de-serializes between a wire |
+ format such as JSON and a Python object representation. |
+ """ |
+ |
+ def request(self, headers, path_params, query_params, body_value): |
+ """Updates outgoing requests with a serialized body. |
+ |
+ Args: |
+ headers: dict, request headers |
+ path_params: dict, parameters that appear in the request path |
+ query_params: dict, parameters that appear in the query |
+ body_value: object, the request body as a Python object, which must be |
+ serializable. |
+ Returns: |
+ A tuple of (headers, path_params, query, body) |
+ |
+ headers: dict, request headers |
+ path_params: dict, parameters that appear in the request path |
+ query: string, query part of the request URI |
+ body: string, the body serialized in the desired wire format. |
+ """ |
+ _abstract() |
+ |
+ def response(self, resp, content): |
+ """Convert the response wire format into a Python object. |
+ |
+ Args: |
+ resp: httplib2.Response, the HTTP response headers and status |
+ content: string, the body of the HTTP response |
+ |
+ Returns: |
+ The body de-serialized as a Python object. |
+ |
+ Raises: |
+ googleapiclient.errors.HttpError if a non 2xx response is received. |
+ """ |
+ _abstract() |
+ |
+ |
+class BaseModel(Model): |
+ """Base model class. |
+ |
+ Subclasses should provide implementations for the "serialize" and |
+ "deserialize" methods, as well as values for the following class attributes. |
+ |
+ Attributes: |
+ accept: The value to use for the HTTP Accept header. |
+ content_type: The value to use for the HTTP Content-type header. |
+ no_content_response: The value to return when deserializing a 204 "No |
+ Content" response. |
+ alt_param: The value to supply as the "alt" query parameter for requests. |
+ """ |
+ |
+ accept = None |
+ content_type = None |
+ no_content_response = None |
+ alt_param = None |
+ |
+ def _log_request(self, headers, path_params, query, body): |
+ """Logs debugging information about the request if requested.""" |
+ if dump_request_response: |
+ logging.info('--request-start--') |
+ logging.info('-headers-start-') |
+ for h, v in headers.iteritems(): |
+ logging.info('%s: %s', h, v) |
+ logging.info('-headers-end-') |
+ logging.info('-path-parameters-start-') |
+ for h, v in path_params.iteritems(): |
+ logging.info('%s: %s', h, v) |
+ logging.info('-path-parameters-end-') |
+ logging.info('body: %s', body) |
+ logging.info('query: %s', query) |
+ logging.info('--request-end--') |
+ |
+ def request(self, headers, path_params, query_params, body_value): |
+ """Updates outgoing requests with a serialized body. |
+ |
+ Args: |
+ headers: dict, request headers |
+ path_params: dict, parameters that appear in the request path |
+ query_params: dict, parameters that appear in the query |
+ body_value: object, the request body as a Python object, which must be |
+ serializable by json. |
+ Returns: |
+ A tuple of (headers, path_params, query, body) |
+ |
+ headers: dict, request headers |
+ path_params: dict, parameters that appear in the request path |
+ query: string, query part of the request URI |
+ body: string, the body serialized as JSON |
+ """ |
+ query = self._build_query(query_params) |
+ headers['accept'] = self.accept |
+ headers['accept-encoding'] = 'gzip, deflate' |
+ if 'user-agent' in headers: |
+ headers['user-agent'] += ' ' |
+ else: |
+ headers['user-agent'] = '' |
+ headers['user-agent'] += 'google-api-python-client/%s (gzip)' % __version__ |
+ |
+ if body_value is not None: |
+ headers['content-type'] = self.content_type |
+ body_value = self.serialize(body_value) |
+ self._log_request(headers, path_params, query, body_value) |
+ return (headers, path_params, query, body_value) |
+ |
+ def _build_query(self, params): |
+ """Builds a query string. |
+ |
+ Args: |
+ params: dict, the query parameters |
+ |
+ Returns: |
+ The query parameters properly encoded into an HTTP URI query string. |
+ """ |
+ if self.alt_param is not None: |
+ params.update({'alt': self.alt_param}) |
+ astuples = [] |
+ for key, value in params.iteritems(): |
+ if type(value) == type([]): |
+ for x in value: |
+ x = x.encode('utf-8') |
+ astuples.append((key, x)) |
+ else: |
+ if getattr(value, 'encode', False) and callable(value.encode): |
+ value = value.encode('utf-8') |
+ astuples.append((key, value)) |
+ return '?' + urllib.urlencode(astuples) |
+ |
+ def _log_response(self, resp, content): |
+ """Logs debugging information about the response if requested.""" |
+ if dump_request_response: |
+ logging.info('--response-start--') |
+ for h, v in resp.iteritems(): |
+ logging.info('%s: %s', h, v) |
+ if content: |
+ logging.info(content) |
+ logging.info('--response-end--') |
+ |
+ def response(self, resp, content): |
+ """Convert the response wire format into a Python object. |
+ |
+ Args: |
+ resp: httplib2.Response, the HTTP response headers and status |
+ content: string, the body of the HTTP response |
+ |
+ Returns: |
+ The body de-serialized as a Python object. |
+ |
+ Raises: |
+ googleapiclient.errors.HttpError if a non 2xx response is received. |
+ """ |
+ self._log_response(resp, content) |
+ # Error handling is TBD, for example, do we retry |
+ # for some operation/error combinations? |
+ if resp.status < 300: |
+ if resp.status == 204: |
+ # A 204: No Content response should be treated differently |
+ # to all the other success states |
+ return self.no_content_response |
+ return self.deserialize(content) |
+ else: |
+ logging.debug('Content from bad request was: %s' % content) |
+ raise HttpError(resp, content) |
+ |
+ def serialize(self, body_value): |
+ """Perform the actual Python object serialization. |
+ |
+ Args: |
+ body_value: object, the request body as a Python object. |
+ |
+ Returns: |
+ string, the body in serialized form. |
+ """ |
+ _abstract() |
+ |
+ def deserialize(self, content): |
+ """Perform the actual deserialization from response string to Python |
+ object. |
+ |
+ Args: |
+ content: string, the body of the HTTP response |
+ |
+ Returns: |
+ The body de-serialized as a Python object. |
+ """ |
+ _abstract() |
+ |
+ |
+class JsonModel(BaseModel): |
+ """Model class for JSON. |
+ |
+ Serializes and de-serializes between JSON and the Python |
+ object representation of HTTP request and response bodies. |
+ """ |
+ accept = 'application/json' |
+ content_type = 'application/json' |
+ alt_param = 'json' |
+ |
+ def __init__(self, data_wrapper=False): |
+ """Construct a JsonModel. |
+ |
+ Args: |
+ data_wrapper: boolean, wrap requests and responses in a data wrapper |
+ """ |
+ self._data_wrapper = data_wrapper |
+ |
+ def serialize(self, body_value): |
+ if (isinstance(body_value, dict) and 'data' not in body_value and |
+ self._data_wrapper): |
+ body_value = {'data': body_value} |
+ return json.dumps(body_value) |
+ |
+ def deserialize(self, content): |
+ content = content.decode('utf-8') |
+ body = json.loads(content) |
+ if self._data_wrapper and isinstance(body, dict) and 'data' in body: |
+ body = body['data'] |
+ return body |
+ |
+ @property |
+ def no_content_response(self): |
+ return {} |
+ |
+ |
+class RawModel(JsonModel): |
+ """Model class for requests that don't return JSON. |
+ |
+ Serializes and de-serializes between JSON and the Python |
+ object representation of HTTP request, and returns the raw bytes |
+ of the response body. |
+ """ |
+ accept = '*/*' |
+ content_type = 'application/json' |
+ alt_param = None |
+ |
+ def deserialize(self, content): |
+ return content |
+ |
+ @property |
+ def no_content_response(self): |
+ return '' |
+ |
+ |
+class MediaModel(JsonModel): |
+ """Model class for requests that return Media. |
+ |
+ Serializes and de-serializes between JSON and the Python |
+ object representation of HTTP request, and returns the raw bytes |
+ of the response body. |
+ """ |
+ accept = '*/*' |
+ content_type = 'application/json' |
+ alt_param = 'media' |
+ |
+ def deserialize(self, content): |
+ return content |
+ |
+ @property |
+ def no_content_response(self): |
+ return '' |
+ |
+ |
+class ProtocolBufferModel(BaseModel): |
+ """Model class for protocol buffers. |
+ |
+ Serializes and de-serializes the binary protocol buffer sent in the HTTP |
+ request and response bodies. |
+ """ |
+ accept = 'application/x-protobuf' |
+ content_type = 'application/x-protobuf' |
+ alt_param = 'proto' |
+ |
+ def __init__(self, protocol_buffer): |
+ """Constructs a ProtocolBufferModel. |
+ |
+ The serialzed protocol buffer returned in an HTTP response will be |
+ de-serialized using the given protocol buffer class. |
+ |
+ Args: |
+ protocol_buffer: The protocol buffer class used to de-serialize a |
+ response from the API. |
+ """ |
+ self._protocol_buffer = protocol_buffer |
+ |
+ def serialize(self, body_value): |
+ return body_value.SerializeToString() |
+ |
+ def deserialize(self, content): |
+ return self._protocol_buffer.FromString(content) |
+ |
+ @property |
+ def no_content_response(self): |
+ return self._protocol_buffer() |
+ |
+ |
+def makepatch(original, modified): |
+ """Create a patch object. |
+ |
+ Some methods support PATCH, an efficient way to send updates to a resource. |
+ This method allows the easy construction of patch bodies by looking at the |
+ differences between a resource before and after it was modified. |
+ |
+ Args: |
+ original: object, the original deserialized resource |
+ modified: object, the modified deserialized resource |
+ Returns: |
+ An object that contains only the changes from original to modified, in a |
+ form suitable to pass to a PATCH method. |
+ |
+ Example usage: |
+ item = service.activities().get(postid=postid, userid=userid).execute() |
+ original = copy.deepcopy(item) |
+ item['object']['content'] = 'This is updated.' |
+ service.activities.patch(postid=postid, userid=userid, |
+ body=makepatch(original, item)).execute() |
+ """ |
+ patch = {} |
+ for key, original_value in original.iteritems(): |
+ modified_value = modified.get(key, None) |
+ if modified_value is None: |
+ # Use None to signal that the element is deleted |
+ patch[key] = None |
+ elif original_value != modified_value: |
+ if type(original_value) == type({}): |
+ # Recursively descend objects |
+ patch[key] = makepatch(original_value, modified_value) |
+ else: |
+ # In the case of simple types or arrays we just replace |
+ patch[key] = modified_value |
+ else: |
+ # Don't add anything to patch if there's no change |
+ pass |
+ for key in modified: |
+ if key not in original: |
+ patch[key] = modified[key] |
+ |
+ return patch |