Starting to cleanup, organize files, and make it look like a real project.
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()