Starting to cleanup, organize files, and make it look like a real project.
diff --git a/apiclient/__init__.py b/apiclient/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/apiclient/__init__.py
diff --git a/apiclient/discovery.py b/apiclient/discovery.py
new file mode 100644
index 0000000..85d2049
--- /dev/null
+++ b/apiclient/discovery.py
@@ -0,0 +1,176 @@
+# Copyright (C) 2010 Google Inc.
+#
+# 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.
+
+"""Client for discovery based APIs
+
+A client library for Google's discovery
+based APIs.
+"""
+
+__author__ = 'jcgregorio@google.com (Joe Gregorio)'
+
+
+import httplib2
+import re
+import simplejson
+import urlparse
+import uritemplate
+
+class HttpError(Exception): pass
+
+DISCOVERY_URI = 'http://www.googleapis.com/discovery/0.1/describe{?api,apiVersion}'
+
+
+def key2method(key):
+ """
+ max-results -> MaxResults
+ """
+ result = []
+ key = list(key)
+ newWord = True
+ if not key[0].isalpha():
+ result.append('X')
+ newWord = False
+ for c in key:
+ if c.isalnum():
+ if newWord:
+ result.append(c.upper())
+ newWord = False
+ else:
+ result.append(c.lower())
+ else:
+ newWord = True
+
+ return ''.join(result)
+
+
+def key2param(key):
+ """
+ max-results -> max_results
+ """
+ result = []
+ key = list(key)
+ if not key[0].isalpha():
+ result.append('x')
+ for c in key:
+ if c.isalnum():
+ result.append(c)
+ else:
+ result.append('_')
+
+ return ''.join(result)
+
+
+class JsonModel(object):
+ def request(self, headers, params):
+ model = params.get('body', None)
+ query = '?alt=json&prettyprint=true'
+ headers['Accept'] = 'application/json'
+ if model == None:
+ return (headers, params, query, None)
+ else:
+ model = {'data': model }
+ headers['Content-Type'] = 'application/json'
+ del params['body']
+ return (headers, params, query, simplejson.dumps(model))
+
+ def response(self, resp, content):
+ # Error handling is TBD
+ if resp.status < 300:
+ return simplejson.loads(content)['data']
+ else:
+ if resp['content-type'] != 'application/json':
+ raise HttpError("%d %s" % (resp.status, resp.reason))
+ else:
+ raise HttpError(simplejson.loads(content)['error'])
+
+
+def build(service, version, http=httplib2.Http(),
+ discoveryServiceUrl = DISCOVERY_URI, auth = None, model = JsonModel()):
+ params = {
+ 'api': service,
+ 'apiVersion': version
+ }
+ resp, content = http.request(uritemplate.expand(discoveryServiceUrl, params))
+ d = simplejson.loads(content)
+ service = d['data'][service][version]
+ base = service['baseUrl']
+ resources = service['resources']
+
+ class Service(object):
+ """Top level interface for a service"""
+
+ def __init__(self, http=http):
+ self._http = http
+ self._baseUrl = base
+ self._model = model
+
+ def createMethod(theclass, methodName, methodDesc):
+ def method(self, **kwargs):
+ return createResource(self._http, self._baseUrl, self._model,
+ methodName, methodDesc)
+
+ setattr(method, '__doc__', 'A description of how to use this function')
+ setattr(theclass, methodName, method)
+
+ for methodName, methodDesc in resources.iteritems():
+ createMethod(Service, methodName, methodDesc)
+ return Service()
+
+
+def createResource(http, baseUrl, model, resourceName, resourceDesc):
+
+ class Resource(object):
+ """A class for interacting with a resource."""
+
+ def __init__(self):
+ self._http = http
+ self._baseUrl = baseUrl
+ self._model = model
+
+ def createMethod(theclass, methodName, methodDesc):
+ pathUrl = methodDesc['pathUrl']
+ pathUrl = re.sub(r'\{', r'{+', pathUrl)
+ httpMethod = methodDesc['httpMethod']
+ args = methodDesc['parameters'].keys()
+ if httpMethod in ['PUT', 'POST']:
+ args.append('body')
+ argmap = dict([(key2param(key), key) for key in args])
+
+ def method(self, **kwargs):
+ for name in kwargs.iterkeys():
+ if name not in argmap:
+ raise TypeError('Got an unexpected keyword argument "%s"' % name)
+ params = dict(
+ [(argmap[key], value) for key, value in kwargs.iteritems()]
+ )
+ headers = {}
+ headers, params, query, body = self._model.request(headers, params)
+
+ url = urlparse.urljoin(self._baseUrl,
+ uritemplate.expand(pathUrl, params) + query)
+ return self._model.response(*self._http.request(
+ url, method=httpMethod, headers=headers, body=body))
+
+ docs = ['A description of how to use this function\n\n']
+ for arg in argmap.iterkeys():
+ docs.append('%s - A parameter\n' % arg)
+
+ setattr(method, '__doc__', ''.join(docs))
+ setattr(theclass, methodName, method)
+
+ for methodName, methodDesc in resourceDesc['methods'].iteritems():
+ createMethod(Resource, methodName, methodDesc)
+
+ return Resource()