Cleaned up OAuth 2.0 support fully using Storage() and updating samples.
diff --git a/apiclient/discovery.py b/apiclient/discovery.py
index fa796ed..22cb4e0 100644
--- a/apiclient/discovery.py
+++ b/apiclient/discovery.py
@@ -84,9 +84,11 @@
       parameters {api} and {apiVersion} that when filled in
       produce an absolute URI to the discovery document for
       that service.
-    developerKey: string, key obtained from https://code.google.com/apis/console
+    developerKey: string, key obtained
+      from https://code.google.com/apis/console
     model: apiclient.Model, converts to and from the wire format
-    requestBuilder: apiclient.http.HttpRequest, encapsulator for an HTTP request
+    requestBuilder: apiclient.http.HttpRequest, encapsulator for
+      an HTTP request
 
   Returns:
     A Resource object with methods for interacting with
diff --git a/apiclient/http.py b/apiclient/http.py
index e9fd7e0..8f64baf 100644
--- a/apiclient/http.py
+++ b/apiclient/http.py
@@ -22,7 +22,10 @@
   """Encapsulates a single HTTP request.
   """
 
-  def __init__(self, http, postproc, uri, method="GET", body=None, headers=None,
+  def __init__(self, http, postproc, uri,
+               method="GET",
+               body=None,
+               headers=None,
                methodId=None):
     """Constructor for an HttpRequest.
 
@@ -150,6 +153,7 @@
       model = JsonModel()
       return HttpRequestMock(None, '{}', model.response)
 
+
 class HttpMock(object):
   """Mock of httplib2.Http"""
 
@@ -164,5 +168,10 @@
     f.close()
     self.headers = headers
 
-  def request(self, uri, method="GET", body=None, headers=None, redirections=1, connection_type=None):
+  def request(self, uri,
+              method="GET",
+              body=None,
+              headers=None,
+              redirections=1,
+              connection_type=None):
     return httplib2.Response(self.headers), self.data
diff --git a/apiclient/model.py b/apiclient/model.py
index 1f2e1c1..68da13e 100644
--- a/apiclient/model.py
+++ b/apiclient/model.py
@@ -18,6 +18,7 @@
 from anyjson import simplejson
 from errors import HttpError
 
+
 def _abstract():
   raise NotImplementedError('You need to override this function')
 
diff --git a/oauth2client/appengine.py b/oauth2client/appengine.py
index 80c6fab..f35da8f 100644
--- a/oauth2client/appengine.py
+++ b/oauth2client/appengine.py
@@ -25,6 +25,7 @@
 from google.appengine.ext import db
 from client import Credentials
 from client import Flow
+from client import Storage
 
 
 class FlowProperty(db.Property):
@@ -90,7 +91,7 @@
     return not value
 
 
-class StorageByKeyName(object):
+class StorageByKeyName(Storage):
   """Store and retrieve a single credential to and from
   the App Engine datastore.
 
diff --git a/oauth2client/client.py b/oauth2client/client.py
index eeaa453..5ba7af8 100644
--- a/oauth2client/client.py
+++ b/oauth2client/client.py
@@ -70,6 +70,30 @@
   pass
 
 
+class Storage(object):
+  """Base class for all Storage objects.
+
+  Store and retrieve a single credential.
+  """
+
+
+  def get(self):
+    """Retrieve credential.
+
+    Returns:
+      apiclient.oauth.Credentials
+    """
+    _abstract()
+
+  def put(self, credentials):
+    """Write a credential.
+
+    Args:
+      credentials: Credentials, the credentials to store.
+    """
+    _abstract()
+
+
 class OAuth2Credentials(Credentials):
   """Credentials object for OAuth 2.0
 
diff --git a/oauth2client/django_orm.py b/oauth2client/django_orm.py
index 50b3dd6..b5e020e 100644
--- a/oauth2client/django_orm.py
+++ b/oauth2client/django_orm.py
@@ -1,5 +1,5 @@
 from django.db import models
-
+from oauth2client.client import Storage as BaseStorage
 
 class CredentialsField(models.Field):
 
@@ -36,3 +36,50 @@
 
   def get_db_prep_value(self, value):
     return base64.b64encode(pickle.dumps(value))
+
+
+class Storage(BaseStorage):
+  """Store and retrieve a single credential to and from
+  the datastore.
+
+  This Storage helper presumes the Credentials
+  have been stored as a CredenialsField
+  on a db model class.
+  """
+
+  def __init__(self, model_class, key_name, key_value, property_name):
+    """Constructor for Storage.
+
+    Args:
+      model: db.Model, model class
+      key_name: string, key name for the entity that has the credentials
+      key_value: string, key value for the entity that has the credentials
+      property_name: string, name of the property that is an CredentialsProperty
+    """
+    self.model_class = model_class
+    self.key_name = key_name
+    self.key_value = key_value
+    self.property_name = property_name
+
+  def get(self):
+    """Retrieve Credential from datastore.
+
+    Returns:
+      oauth2client.Credentials
+    """
+    query = {self.key_name: self.key_value}
+    entity = self.model_class.objects.filter(*query)[0]
+    credential = getattr(entity, self.property_name)
+    if credential and hasattr(credential, 'set_store'):
+      credential.set_store(self.put)
+    return credential
+
+  def put(self, credentials):
+    """Write a Credentials to the datastore.
+
+    Args:
+      credentials: Credentials, the credentials to store.
+    """
+    entity = self.model_class(self.key_name=self.key_value)
+    setattr(entity, self.property_name, credentials)
+    entity.save()
diff --git a/oauth2client/file.py b/oauth2client/file.py
index e0e3997..0bcab03 100644
--- a/oauth2client/file.py
+++ b/oauth2client/file.py
@@ -10,8 +10,10 @@
 
 import pickle
 
+from client import Storage as BaseStorage
 
-class Storage(object):
+
+class Storage(BaseStorage):
   """Store and retrieve a single credential to and from a file."""
 
   def __init__(self, filename):
@@ -23,10 +25,13 @@
     Returns:
       apiclient.oauth.Credentials
     """
-    f = open(self.filename, 'r')
-    credentials = pickle.loads(f.read())
-    f.close()
-    credentials.set_store(self.put)
+    try:
+      f = open(self.filename, 'r')
+      credentials = pickle.loads(f.read())
+      f.close()
+      credentials.set_store(self.put)
+    except:
+      credentials = None
     return credentials
 
   def put(self, credentials):
@@ -38,5 +43,3 @@
     f = open(self.filename, 'w')
     f.write(pickle.dumps(credentials))
     f.close()
-
-
diff --git a/oauth2client/tools.py b/oauth2client/tools.py
index d541eeb..644e72b 100644
--- a/oauth2client/tools.py
+++ b/oauth2client/tools.py
@@ -23,111 +23,16 @@
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 __all__ = ["run"]
 
-import socket
-import sys
-import BaseHTTPServer
-import logging
 
-from optparse import OptionParser
-from oauth2client.file import Storage
-
-try:
-    from urlparse import parse_qsl
-except ImportError:
-    from cgi import parse_qsl
-
-# TODO(jcgregorio)
-#  - docs
-#  - error handling
-
-HOST_NAME = 'localhost'
-PORT_NUMBERS = [8080, 8090]
-
-class ClientRedirectServer(BaseHTTPServer.HTTPServer):
-  """A server to handle OAuth 2.0 redirects back to localhost.
-
-  Waits for a single request and parses the query parameters
-  into query_params and then stops serving.
-  """
-  query_params = {}
-
-class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler):
-  """A handler for OAuth 2.0 redirects back to localhost.
-
-  Waits for a single request and parses the query parameters
-  into the servers query_params and then stops serving.
-  """
-
-  def do_GET(s):
-    """Handle a GET request
-
-    Checks the query parameters and if an error
-    occurred print a message of failure, otherwise
-    indicate success.
-    """
-    s.send_response(200)
-    s.send_header("Content-type", "text/html")
-    s.end_headers()
-    query = s.path.split('?', 1)[-1]
-    query = dict(parse_qsl(query))
-    s.server.query_params = query
-    s.wfile.write("<html><head><title>Authentication Status</title></head>")
-    if 'error' in query:
-      s.wfile.write("<body><p>The authentication request failed.</p>")
-    else:
-      s.wfile.write("<body><p>You have successfully authenticated</p>")
-    s.wfile.write("</body></html>")
-
-  def log_message(self, format, *args):
-    """Do not log messages to stdout while running as a command line program."""
-    pass
-
-def run(flow, filename):
+def run(flow, storage):
   """Core code for a command-line application.
   """
-  parser = OptionParser()
-  parser.add_option("-f", "--file", dest="filename",
-      default=filename, help="write credentials to FILE", metavar="FILE")
-
-  # The support for localhost is a work in progress and does not
-  # work at this point.
-  #parser.add_option("-p", "--no_local_web_server", dest="localhost",
-  #    action="store_false",
-  #    default=True,
-  #    help="Do not run a web server on localhost to handle redirect URIs")
-
-  (options, args) = parser.parse_args()
-
-#if options.localhost:
-#  server_class = BaseHTTPServer.HTTPServer
-#  try:
-#    port_number = PORT_NUMBERS[0]
-#    httpd = server_class((HOST_NAME, port_number), ClientRedirectHandler)
-#  except socket.error:
-#    port_number = PORT_NUMBERS[1]
-#    try:
-#      httpd = server_class((HOST_NAME, port_number), ClientRedirectHandler)
-#    except socket.error:
-#      options.localhost = False
-#  redirect_uri = 'http://%s:%s/' % (HOST_NAME, port_number)
-#else:
-#  redirect_uri = 'oob'
-
-  redirect_uri = 'oob'
-
-  authorize_url = flow.step1_get_authorize_url(redirect_uri)
+  authorize_url = flow.step1_get_authorize_url('oob')
 
   print 'Go to the following link in your browser:'
   print authorize_url
   print
 
-#if options.localhost:
-#  httpd.handle_request()
-#  if 'error' in httpd.query_params:
-#    sys.exit('Authentication request was rejected.')
-#  if 'code' in httpd.query_params:
-#    code = httpd.query_params['code']
-#else:
   accepted = 'n'
   while accepted.lower() == 'n':
     accepted = raw_input('Have you authorized me? (y/n) ')
@@ -135,5 +40,9 @@
 
   credentials = flow.step2_exchange(code)
 
-  Storage(options.filename).put(credentials)
-  print "You have successfully authenticated."
+  storage.put(credentials)
+  credentials.set_store(storage.put)
+
+  print 'You have successfully authenticated.'
+
+  return credentials
diff --git a/samples/django_sample/manage.py b/samples/django_sample/manage.py
index bcdd55e..0b932da 100755
--- a/samples/django_sample/manage.py
+++ b/samples/django_sample/manage.py
@@ -4,7 +4,11 @@
     import settings # Assumed to be in the same directory.
 except ImportError:
     import sys
-    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+    sys.stderr.write("""Error: Can't find the file 'settings.py' in the
+directory containing %r. It appears you've customized things. You'll
+have to run django-admin.py, passing it your settings module.
+(If the file settings.py does indeed exist, it's causing an ImportError
+somehow.)\n""" % __file__)
     sys.exit(1)
 
 if __name__ == "__main__":
diff --git a/samples/oauth2/appengine/main.py b/samples/oauth2/appengine/main.py
index 981e392..78634e7 100644
--- a/samples/oauth2/appengine/main.py
+++ b/samples/oauth2/appengine/main.py
@@ -45,7 +45,8 @@
   @login_required
   def get(self):
     user = users.get_current_user()
-    credentials = StorageByKeyName(Credentials, user.user_id(), 'credentials').get()
+    credentials = StorageByKeyName(
+        Credentials, user.user_id(), 'credentials').get()
 
     if credentials:
       http = httplib2.Http()
@@ -87,7 +88,8 @@
     flow = pickle.loads(memcache.get(user.user_id()))
     if flow:
       credentials = flow.step2_exchange(self.request.params)
-      StorageByKeyName(Credentials, user.user_id(), 'credentials').put(credentials)
+      StorageByKeyName(
+          Credentials, user.user_id(), 'credentials').put(credentials)
       self.redirect("/")
     else:
       pass
diff --git a/samples/oauth2/buzz/buzz.py b/samples/oauth2/buzz/buzz.py
index 7df6c72..fe30a32 100644
--- a/samples/oauth2/buzz/buzz.py
+++ b/samples/oauth2/buzz/buzz.py
@@ -11,28 +11,43 @@
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
-from apiclient.discovery import build
-from oauth2client.file import Storage
-
 import httplib2
 import pickle
 import pprint
 
+from apiclient.discovery import build
+from oauth2client.file import Storage
+from oauth2client.client import OAuth2WebServerFlow
+from oauth2client.tools import run
+
 # Uncomment the next line to get very detailed logging
 #httplib2.debuglevel = 4
 
 
 def main():
-  credentials = Storage('buzz.dat').get()
+  storage = Storage('buzz.dat')
+  credentials = storage.get()
+  if not credentials:
+    flow = OAuth2WebServerFlow(
+        client_id='433807057907.apps.googleusercontent.com',
+        client_secret='jigtZpMApkRxncxikFpR+SFg',
+        scope='https://www.googleapis.com/auth/buzz',
+        user_agent='buzz-cmdline-sample/1.0',
+        domain='anonymous',
+        xoauth_displayname='Buzz Client Example App'
+        )
+    credentials = run(flow, storage)
 
   http = httplib2.Http()
   http = credentials.authorize(http)
 
-  p = build("buzz", "v1", http=http, developerKey="AIzaSyDRRpR3GS1F1_jKNNM9HCNd2wJQyPG3oN0")
+  p = build("buzz", "v1", http=http,
+            developerKey="AIzaSyDRRpR3GS1F1_jKNNM9HCNd2wJQyPG3oN0")
   activities = p.activities()
 
   # Retrieve the first two activities
-  activitylist = activities.list(max_results='2', scope='@self', userId='@me').execute()
+  activitylist = activities.list(
+      max_results='2', scope='@self', userId='@me').execute()
   print "Retrieved the first two activities"
 
   # Retrieve the next two activities
@@ -45,14 +60,16 @@
       "data": {
           'title': 'Testing insert',
           'object': {
-              'content': u'Just a short note to show that insert is working. ☄',
+              'content':
+                u'Just a short note to show that insert is working. ☄',
               'type': 'note'}
           }
       }
   activity = activities.insert(userId='@me', body=new_activity_body).execute()
   print "Added a new activity"
 
-  activitylist = activities.list(max_results='2', scope='@self', userId='@me').execute()
+  activitylist = activities.list(
+      max_results='2', scope='@self', userId='@me').execute()
 
   # Add a comment to that activity
   comment_body = {
diff --git a/samples/oauth2/buzz/web_server_dance.py b/samples/oauth2/buzz/web_server_dance.py
deleted file mode 100644
index d0e1f6f..0000000
--- a/samples/oauth2/buzz/web_server_dance.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# 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.
-
-"""Do the OAuth 2.0 Web Server dance.
-
-Do the OAuth 2.0 Web Server dance for
-a command line application. Store the generated
-credentials in a common file that is used by
-other example apps in the same directory.
-"""
-
-__author__ = 'jcgregorio@google.com (Joe Gregorio)'
-
-from oauth2client.client import OAuth2WebServerFlow
-from oauth2client.tools import run
-
-flow = OAuth2WebServerFlow(
-    client_id='433807057907.apps.googleusercontent.com',
-    client_secret='jigtZpMApkRxncxikFpR+SFg',
-    scope='https://www.googleapis.com/auth/buzz',
-    user_agent='buzz-cmdline-sample/1.0',
-    domain='anonymous',
-    xoauth_displayname='Buzz Client Example App'
-    )
-
-run(flow, 'buzz.dat')
diff --git a/samples/oauth2/latitude/latitude.py b/samples/oauth2/latitude/latitude.py
new file mode 100644
index 0000000..fc312dd
--- /dev/null
+++ b/samples/oauth2/latitude/latitude.py
@@ -0,0 +1,56 @@
+#!/usr/bin/python2.4
+# -*- coding: utf-8 -*-
+#
+# Copyright 2010 Google Inc. All Rights Reserved.
+
+"""Simple command-line example for Latitude.
+
+Command-line application that sets the users
+current location.
+"""
+
+__author__ = 'jcgregorio@google.com (Joe Gregorio)'
+
+import httplib2
+
+from apiclient.discovery import build
+from oauth2client.file import Storage
+from oauth2client.client import OAuth2WebServerFlow
+from oauth2client.tools import run
+
+# Uncomment to get detailed logging
+#httplib2.debuglevel = 4
+
+
+def main():
+  storage = Storage('latitude.dat')
+  credentials = storage.get()
+
+  if not credentials:
+    flow = OAuth2WebServerFlow(
+        client_id='433807057907.apps.googleusercontent.com',
+        client_secret='jigtZpMApkRxncxikFpR+SFg',
+        scope='https://www.googleapis.com/auth/latitude',
+        user_agent='latitude-cmdline-sample/1.0',
+        xoauth_displayname='Latitude Client Example App')
+
+    credentials = run(flow, storage)
+
+  http = httplib2.Http()
+  http = credentials.authorize(http)
+
+  p = build("latitude", "v1", http=http)
+
+  body = {
+      "data": {
+          "kind": "latitude#location",
+          "latitude": 37.420352,
+          "longitude": -122.083389,
+          "accuracy": 130,
+          "altitude": 35
+          }
+      }
+  print p.currentLocation().insert(body=body).execute()
+
+if __name__ == '__main__':
+  main()
diff --git a/samples/oauth2/moderator/moderator.py b/samples/oauth2/moderator/moderator.py
index 9a3dbb2..5a6e2b8 100644
--- a/samples/oauth2/moderator/moderator.py
+++ b/samples/oauth2/moderator/moderator.py
@@ -11,22 +11,30 @@
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
+import httplib2
 
 from apiclient.discovery import build
 from oauth2client.file import Storage
-
-import httplib2
+from oauth2client.client import OAuth2WebServerFlow
+from oauth2client.tools import run
 
 # Uncomment to get low level HTTP logging
 #httplib2.debuglevel = 4
 
-# Uncomment to get logging
-#import logging
-#logging.basicConfig(level=logging.DEBUG)
-
 
 def main():
-  credentials = Storage('moderator.dat').get()
+  storage = Storage('moderator.dat')
+  credentials = storage.get()
+
+  if not credentials:
+    flow = OAuth2WebServerFlow(
+        client_id='433807057907.apps.googleusercontent.com',
+        client_secret='jigtZpMApkRxncxikFpR+SFg',
+        scope='https://www.googleapis.com/auth/moderator',
+        user_agent='moderator-cmdline-sample/1.0',
+        xoauth_displayname='Moderator Client Example App')
+
+    credentials = run(flow, storage)
 
   http = httplib2.Http()
   http = credentials.authorize(http)
diff --git a/samples/oauth2/moderator/web_server_dance.py b/samples/oauth2/moderator/web_server_dance.py
deleted file mode 100644
index b3b9d35..0000000
--- a/samples/oauth2/moderator/web_server_dance.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# 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.
-
-"""Do the OAuth 2.0 Web Server dance.
-
-Do the OAuth 2.0 Web Server dance for
-a command line application. Store the generated
-credentials in a common file that is used by
-other example apps in the same directory.
-"""
-
-__author__ = 'jcgregorio@google.com (Joe Gregorio)'
-
-from oauth2client.client import OAuth2WebServerFlow
-from oauth2client.tools import run
-
-flow = OAuth2WebServerFlow(
-    client_id='433807057907.apps.googleusercontent.com',
-    client_secret='jigtZpMApkRxncxikFpR+SFg',
-    scope='https://www.googleapis.com/auth/moderator',
-    user_agent='moderator-cmdline-sample/1.0',
-    xoauth_displayname='Moderator Client Example App')
-
-run(flow, 'moderator.dat')
diff --git a/samples/oauth2/urlshortener/main.py b/samples/oauth2/urlshortener/main.py
new file mode 100644
index 0000000..f995f38
--- /dev/null
+++ b/samples/oauth2/urlshortener/main.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python2.4
+# -*- coding: utf-8 -*-
+#
+# Copyright 2010 Google Inc. All Rights Reserved.
+
+"""Simple command-line example for Google URL Shortener API.
+
+Command-line application that shortens a URL.
+"""
+
+__author__ = 'jcgregorio@google.com (Joe Gregorio)'
+
+import httplib2
+import pprint
+
+from apiclient.discovery import build
+from oauth2client.file import Storage
+from oauth2client.client import OAuth2WebServerFlow
+from oauth2client.tools import run
+
+# Uncomment to get detailed logging
+#httplib2.debuglevel = 4
+
+
+def main():
+  storage = Storage('urlshortener.dat')
+  credentials = storage.get()
+
+  if not credentials:
+    flow = OAuth2WebServerFlow(
+        client_id='433807057907.apps.googleusercontent.com',
+        client_secret='jigtZpMApkRxncxikFpR+SFg',
+        scope='https://www.googleapis.com/auth/urlshortener',
+        user_agent='urlshortener-cmdline-sample/1.0',
+        xoauth_displayname='URL Shortener Client Example App')
+
+    credentials = run(flow, storage)
+
+  http = httplib2.Http()
+  http = credentials.authorize(http)
+
+  # Build the url shortener service
+  service = build("urlshortener", "v1", http=http,
+            developerKey="AIzaSyDRRpR3GS1F1_jKNNM9HCNd2wJQyPG3oN0")
+  url = service.url()
+
+  # Create a shortened URL by inserting the URL into the url collection.
+  body = {"longUrl": "http://code.google.com/apis/urlshortener/" }
+  resp = url.insert(body=body).execute()
+  pprint.pprint(resp)
+
+  shortUrl = resp['id']
+
+  # Convert the shortened URL back into a long URL
+  resp = url.get(shortUrl=shortUrl).execute()
+  pprint.pprint(resp)
+
+if __name__ == '__main__':
+  main()