From 7a6df3a31aee98dbaf71311a18068ed7e95a2391 Mon Sep 17 00:00:00 2001 From: Joe Gregorio Date: Mon, 31 Jan 2011 21:55:21 -0500 Subject: [PATCH] Fix lack of top level methods from a service. --- apiclient/discovery.py | 52 +++++++-------------- apiclient/oauth.py | 3 +- samples/searchforshopping/crowding.py | 10 ++-- samples/searchforshopping/fulltextsearch.py | 7 +-- samples/searchforshopping/histograms.py | 15 +++--- samples/searchforshopping/ranking.py | 13 +++--- samples/searchforshopping/restricting.py | 3 +- tests/data/zoo.json | 14 ++++++ tests/test_discovery.py | 8 ++++ 9 files changed, 67 insertions(+), 58 deletions(-) diff --git a/apiclient/discovery.py b/apiclient/discovery.py index b39e1b2..b557b8b 100644 --- a/apiclient/discovery.py +++ b/apiclient/discovery.py @@ -91,6 +91,7 @@ def build(serviceName, version, return build_from_document(content, discoveryServiceUrl, future, http, developerKey, model, requestBuilder) + def build_from_document( service, base, @@ -105,8 +106,8 @@ def build_from_document( base: string, base URI for all HTTP requests, usually the discovery URI future: string, discovery document with future capabilities auth_discovery: dict, information about the authentication the API supports - http: httplib2.Http, An instance of httplib2.Http or something that acts like - it that HTTP requests will be made through. + http: httplib2.Http, An instance of httplib2.Http or something that acts + like it that HTTP requests will be made through. developerKey: string, Key for controlling API usage, generated from the API Console. model: Model class instance that serializes and @@ -116,45 +117,26 @@ def build_from_document( service = simplejson.loads(service) base = urlparse.urljoin(base, service['restBasePath']) - resources = service['resources'] if future: - doc = simplejson.loads(future) - future = doc['resources'] - auth_discovery = doc.get('auth', {}) + future = simplejson.loads(future) + auth_discovery = future.get('auth', {}) else: future = {} auth_discovery = {} - class Service(object): - """Top level interface for a service""" + resource = createResource(http, base, model, requestBuilder, developerKey, + service, future) - def __init__(self, http=http): - self._http = http - self._baseUrl = base - self._model = model - self._developerKey = developerKey - self._requestBuilder = requestBuilder + def auth_method(): + """Discovery information about the authentication the API uses.""" + return auth_discovery - def auth_discovery(self): - return auth_discovery + setattr(resource, 'auth_discovery', auth_method) - def createMethod(theclass, methodName, methodDesc, futureDesc): - - def method(self): - return createResource(self._http, self._baseUrl, self._model, - self._requestBuilder, methodName, - self._developerKey, methodDesc, futureDesc) - - setattr(method, '__doc__', 'A description of how to use this function') - setattr(method, '__is_resource__', True) - setattr(theclass, methodName, method) - - for methodName, methodDesc in resources.iteritems(): - createMethod(Service, methodName, methodDesc, future.get(methodName, {})) - return Service() + return resource -def createResource(http, baseUrl, model, requestBuilder, resourceName, +def createResource(http, baseUrl, model, requestBuilder, developerKey, resourceDesc, futureDesc): class Resource(object): @@ -322,8 +304,8 @@ def createResource(http, baseUrl, model, requestBuilder, resourceName, def method(self): return createResource(self._http, self._baseUrl, self._model, - self._requestBuilder, methodName, - self._developerKey, methodDesc, futureDesc) + self._requestBuilder, self._developerKey, + methodDesc, futureDesc) setattr(method, '__doc__', 'A description of how to use this function') setattr(method, '__is_resource__', True) @@ -335,10 +317,10 @@ def createResource(http, baseUrl, model, requestBuilder, resourceName, else: future = {} createMethod(Resource, methodName, methodDesc, - future.get(methodName, {})) + future) # Add _next() methods to Resource - if futureDesc: + if futureDesc and 'methods' in futureDesc: for methodName, methodDesc in futureDesc['methods'].iteritems(): if 'next' in methodDesc and methodName in resourceDesc['methods']: createNextMethod(Resource, methodName + "_next", diff --git a/apiclient/oauth.py b/apiclient/oauth.py index 469b6e0..68e4300 100644 --- a/apiclient/oauth.py +++ b/apiclient/oauth.py @@ -79,10 +79,12 @@ class Credentials(object): """ _abstract() + class Flow(object): """Base class for all Flow objects.""" pass + class OAuthCredentials(Credentials): """Credentials object for OAuth 1.0a """ @@ -254,4 +256,3 @@ class FlowThreeLegged(Flow): oauth_params['oauth_token_secret']) return OAuthCredentials(consumer, token, self.user_agent) - diff --git a/samples/searchforshopping/crowding.py b/samples/searchforshopping/crowding.py index bf1114e..2eea38f 100644 --- a/samples/searchforshopping/crowding.py +++ b/samples/searchforshopping/crowding.py @@ -15,12 +15,12 @@ DEVELOPER_KEY = 'AIzaSyACZJW4JwcWwz5taR2gjIMNQrtgDLfILPc' def main(): - """Get and print a feed of public products in the United States mathing a text - search query for 'digital camera' and grouped by the 8 top brands. + """Get and print a feed of public products in the United States mathing a + text search query for 'digital camera' and grouped by the 8 top brands. - The list method of the resource should be called with the "crowdBy" parameter. - Each parameter should be designed as :, where - is the number of that that will be used. For + The list method of the resource should be called with the "crowdBy" + parameter. Each parameter should be designed as :, + where is the number of that that will be used. For example, to crowd by the 5 top brands, the parameter would be "brand:5". The possible rules for crowding are currently: diff --git a/samples/searchforshopping/fulltextsearch.py b/samples/searchforshopping/fulltextsearch.py index 9a5fa5e..f838223 100644 --- a/samples/searchforshopping/fulltextsearch.py +++ b/samples/searchforshopping/fulltextsearch.py @@ -23,9 +23,10 @@ def main(): The "|" operator can be used to search for alternative search terms, for example: q = 'banana|apple' will search for bananas or apples. - Search phrases such as those containing spaces can be specified by surrounding - them with double quotes, for example q='"mp3 player"'. This can be useful when - combining with the "|" operator such as q = '"mp3 player"|ipod'. + Search phrases such as those containing spaces can be specified by + surrounding them with double quotes, for example q='"mp3 player"'. This can + be useful when combining with the "|" operator such as q = '"mp3 + player"|ipod'. """ client = build('shopping', SHOPPING_API_VERSION, developerKey=DEVELOPER_KEY) resource = client.products() diff --git a/samples/searchforshopping/histograms.py b/samples/searchforshopping/histograms.py index 6d198ff..6bd3a2f 100644 --- a/samples/searchforshopping/histograms.py +++ b/samples/searchforshopping/histograms.py @@ -16,16 +16,17 @@ def main(): """Get and print a histogram of the top 15 brand distribution for a search query. - Histograms are created by using the "Facets" functionality of the API. A Facet - is a view of a certain property of products, containing a number of buckets, - one for each value of that property. Or concretely, for a parameter such as - "brand" of a product, the facets would include a facet for brand, which would - contain a number of buckets, one for each brand returned in the result. + Histograms are created by using the "Facets" functionality of the API. A + Facet is a view of a certain property of products, containing a number of + buckets, one for each value of that property. Or concretely, for a parameter + such as "brand" of a product, the facets would include a facet for brand, + which would contain a number of buckets, one for each brand returned in the + result. A bucket contains either a value and a count, or a value and a range. In the simple case of a value and a count for our example of the "brand" property, - the value would be the brand name, eg "sony" and the count would be the number - of results in the search. + the value would be the brand name, eg "sony" and the count would be the + number of results in the search. """ client = build('shopping', SHOPPING_API_VERSION, developerKey=DEVELOPER_KEY) resource = client.products() diff --git a/samples/searchforshopping/ranking.py b/samples/searchforshopping/ranking.py index bf6dc0f..86ad2ee 100644 --- a/samples/searchforshopping/ranking.py +++ b/samples/searchforshopping/ranking.py @@ -15,11 +15,12 @@ DEVELOPER_KEY = 'AIzaSyACZJW4JwcWwz5taR2gjIMNQrtgDLfILPc' def main(): - """Get and print a feed of public products in the United States mathing a text - search query for 'digital camera' ranked by ascending price. + """Get and print a feed of public products in the United States mathing a + text search query for 'digital camera' ranked by ascending price. - The list method for the resource should be called with the "rankBy" parameter. - 5 parameters to rankBy are currently supported by the API. They are: + The list method for the resource should be called with the "rankBy" + parameter. 5 parameters to rankBy are currently supported by the API. They + are: "relevancy" "modificationTime:ascending" @@ -33,8 +34,8 @@ def main(): """ client = build('shopping', SHOPPING_API_VERSION, developerKey=DEVELOPER_KEY) resource = client.products() - # The rankBy parameter to the list method causes results to be ranked, in this - # case by ascending price. + # The rankBy parameter to the list method causes results to be ranked, in + # this case by ascending price. request = resource.list(source='public', country='US', q=u'digital camera', rankBy='price:ascending') response = request.execute() diff --git a/samples/searchforshopping/restricting.py b/samples/searchforshopping/restricting.py index 07a4474..1d21c69 100644 --- a/samples/searchforshopping/restricting.py +++ b/samples/searchforshopping/restricting.py @@ -3,7 +3,8 @@ # # Copyright 2010 Google Inc. All Rights Reserved. -"""Query that is restricted by a parameter against the public shopping search API""" +"""Query that is restricted by a parameter against the public shopping search +API""" import pprint diff --git a/tests/data/zoo.json b/tests/data/zoo.json index f83e9dd..4f90c3d 100644 --- a/tests/data/zoo.json +++ b/tests/data/zoo.json @@ -4,6 +4,20 @@ "description": "Zoo API used for testing", "restBasePath": "/zoo", "rpcPath": "/rpc", + "methods": { + "query": { + "restPath": "query", + "rpcMethod": "bigquery.query", + "httpMethod": "GET", + "parameters": { + "q": { + "restParameterType": "query", + "required": false, + "repeated": false + } + } + } + }, "resources": { "my": { "resources": { diff --git a/tests/test_discovery.py b/tests/test_discovery.py index 0c64a09..541210c 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py @@ -113,6 +113,14 @@ class Discovery(unittest.TestCase): q = parse_qs(parsed[4]) self.assertEqual(q['max-results'], ['5']) + def test_top_level_functions(self): + self.http = HttpMock('zoo.json', {'status': '200'}) + zoo = build('zoo', 'v1', self.http) + self.assertTrue(getattr(zoo, 'query')) + request = zoo.query(q="foo") + parsed = urlparse.urlparse(request.uri) + q = parse_qs(parsed[4]) + self.assertEqual(q['q'], ['foo']) class Next(unittest.TestCase):