Start removing dependence on gflags.

Reviewed in https://codereview.appspot.com/7628044/.

This only removes the dependency from the core library, a second CL will
update all the samples.
diff --git a/LICENSE b/LICENSE
index 364d764..d223c5e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -20,4 +20,3 @@
 
 uritemplates - Apache License 2.0
 httplib2 - MIT License
-python-gflags - New BSD License
diff --git a/Makefile b/Makefile
index b0808fe..c4edbbe 100644
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,7 @@
 	-rm -rf snapshot/
 	-sudo rm -rf snapshot/
 	./tools/gae-zip-creator.sh
-	python expand-symlinks.py
+	python expandsymlinks.py
 	cd snapshot; python setup.py clean
 	cd snapshot; python setup.py sdist --formats=gztar,zip
 	cd snapshot; tar czf google-api-python-client-samples-$(shell python setup.py --version).tar.gz samples
@@ -58,7 +58,7 @@
 	-rm -rf snapshot/
 	-sudo rm -rf snapshot/
 	mkdir snapshot
-	python expand-symlinks.py --source=oauth2client --dest=snapshot/oauth2client
+	python expandsymlinks.py --source=oauth2client --dest=snapshot/oauth2client
 	cp setup_oauth2client.py snapshot/setup.py
 	cp MANIFEST_oauth2client.in snapshot/MANIFEST.in
 	cp README_oauth2client snapshot/README
diff --git a/README b/README
index 308e333..1c9ffcd 100644
--- a/README
+++ b/README
@@ -22,7 +22,6 @@
 
 http://code.google.com/p/httplib2
 http://code.google.com/p/uri-templates
-http://code.google.com/p/python-gflags
 
 Depending on your version of Python, these libraries may also be installed:
 
diff --git a/README_oauth2client b/README_oauth2client
index 7c51b9e..4b32ac0 100644
--- a/README_oauth2client
+++ b/README_oauth2client
@@ -15,7 +15,6 @@
 These libraries will be installed when you install the client library:
 
 http://code.google.com/p/httplib2
-http://code.google.com/p/python-gflags
 
 Depending on your version of Python, these libraries may also be installed:
 
diff --git a/apiclient/model.py b/apiclient/model.py
index 12fcab6..ef91bbd 100644
--- a/apiclient/model.py
+++ b/apiclient/model.py
@@ -24,18 +24,14 @@
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
-import gflags
 import logging
 import urllib
 
 from errors import HttpError
 from oauth2client.anyjson import simplejson
 
-FLAGS = gflags.FLAGS
 
-gflags.DEFINE_boolean('dump_request_response', False,
-                      'Dump all http server requests and responses. '
-                     )
+dump_request_response = False
 
 
 def _abstract():
@@ -106,7 +102,7 @@
 
   def _log_request(self, headers, path_params, query, body):
     """Logs debugging information about the request if requested."""
-    if FLAGS.dump_request_response:
+    if dump_request_response:
       logging.info('--request-start--')
       logging.info('-headers-start-')
       for h, v in headers.iteritems():
@@ -177,7 +173,7 @@
 
   def _log_response(self, resp, content):
     """Logs debugging information about the response if requested."""
-    if FLAGS.dump_request_response:
+    if dump_request_response:
       logging.info('--response-start--')
       for h, v in resp.iteritems():
         logging.info('%s: %s', h, v)
@@ -198,6 +194,7 @@
     Raises:
       apiclient.errors.HttpError if a non 2xx response is received.
     """
+    content = content.decode('utf-8')
     self._log_response(resp, content)
     # Error handling is TBD, for example, do we retry
     # for some operation/error combinations?
diff --git a/bin/enable-app-engine-project b/bin/enable-app-engine-project
deleted file mode 100755
index 2cd22cd..0000000
--- a/bin/enable-app-engine-project
+++ /dev/null
@@ -1,137 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2007 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.
-
-"""Copy the sources for google-api-python-client into an App Engine project.
-
-Copies the sources of the google-api-python-client
-library into a Google App Engine project. This is necessary so that the
-source can be uploaded when the application is deployed.
-
-  $ enable-app-engine-project [flags] directory
-
-"""
-
-__author__ = 'jcgregorio@google.com (Joe Gregorio)'
-
-import gflags
-import logging
-import sys
-import os
-import pkg_resources
-
-from distutils.dir_util import copy_tree
-from distutils.file_util import copy_file
-from distutils.errors import DistutilsFileError
-
-FLAGS = gflags.FLAGS
-SOURCES = [
-    'gflags',
-    'gflags_validators',
-    'httplib2',
-    'oauth2client',
-    'apiclient',
-    'uritemplate',
-    ]
-
-gflags.DEFINE_enum('logging_level', 'ERROR',
-    ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
-    'Set the level of logging detail.')
-
-gflags.DEFINE_boolean('force', 'False',
-    'Forcibly copy over client library files.')
-
-gflags.DEFINE_boolean('dry_run', 'False', 'Don\'t actually do anything.')
-
-def find_source(module):
-  """Find the absolute path for the source of a module.
-
-  Args:
-    module: str, Name of the module.
-  Returns:
-    A tuple of (isdir, location), a boolean that's True if
-    the source is a directory, False is it's a file,
-    and the absolute path of the source.
-  """
-  isdir = False
-  location = ''
-  m = __import__(module)
-  logging.debug('Absolute path for module %s: %s' % (module, m.__file__))
-  basename = os.path.basename(m.__file__)
-  if basename.startswith('__init__.'):
-    isdir = True
-    location = os.path.dirname(
-        pkg_resources.resource_filename(module, '__init__.py'))
-  else:
-    if os.path.isfile(m.__file__):
-      location = m.__file__.rsplit('.', 1)[0] + '.py'
-    else:
-      # The file is an egg, extract to a temporary location
-      location = pkg_resources.resource_filename(module, module + '.py')
-
-  return (isdir, location)
-
-def main(argv):
-  # Let the gflags module process the command-line arguments
-  try:
-    argv = FLAGS(argv)
-  except gflags.FlagsError, e:
-    print '%s\nUsage: %s ARGS\n%s' % (e, argv[0], FLAGS)
-    sys.exit(1)
-
-  if len(argv) == 1:
-    print 'Usage: %s ARGS\n%s' % (argv[0], FLAGS)
-    sys.exit(1)
-
-  # Set the logging according to the command-line flag
-  logging.getLogger().setLevel(getattr(logging, FLAGS.logging_level))
-
-  logging.info('Setting up the directories: %s' % argv[1:])
-  for dir in argv[1:]:
-    # Check if the supplied directory is an App Engine project by looking
-    # for an app.yaml
-    if not FLAGS.force and not os.path.isfile(os.path.join(dir, 'app.yaml')):
-      sys.exit('The given directory is not a Google App Engine project: %s' %
-               dir)
-
-    # Build up the set of file or directory copying actions we need to do
-    action = [] # (src, dst, isdir)
-    for source in SOURCES:
-      isdir, source_location = find_source(source)
-      if isdir:
-        target = source
-      else:
-        target = source + ".py"
-      full_target = os.path.join(dir, target)
-      if not FLAGS.force and os.path.exists(full_target):
-        noun = isdir and 'Directory' or 'File'
-        sys.exit("%s already exists in project: %s" % (noun, target))
-      action.append((source_location, full_target, isdir))
-
-    # Now perform all the copying actions we collected
-    try:
-      for src, dst, isdir in action:
-        if isdir:
-          results = copy_tree(src, dst, FLAGS.dry_run)
-          for filename in results:
-            print 'Copied: %s' % filename
-        else:
-          filename, copied = copy_file(src, dst, FLAGS.dry_run)
-          print 'Copied: %s Successfully: %s' % (filename, copied)
-    except DistutilsFileError, e:
-      sys.exit(str(e))
-
-if __name__ == '__main__':
-  main(sys.argv)
diff --git a/describe.py b/describe.py
old mode 100644
new mode 100755
index 174941e..5cecf64
--- a/describe.py
+++ b/describe.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python
 #
 # Copyright 2012 Google Inc.
 #
@@ -23,21 +23,19 @@
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
+import argparse
 import os
 import re
+import string
 import sys
-import httplib2
 
-from string import Template
-
+from apiclient.discovery import DISCOVERY_URI
 from apiclient.discovery import build
 from apiclient.discovery import build_from_document
-from apiclient.discovery import DISCOVERY_URI
 from oauth2client.anyjson import simplejson
-import gflags
+import httplib2
 import uritemplate
 
-
 CSS = """<style>
 
 body, h1, h2, h3, div, span, p, pre, a {
@@ -132,19 +130,22 @@
 
 DIRECTORY_URI = 'https://www.googleapis.com/discovery/v1/apis?preferred=true'
 
-FLAGS = gflags.FLAGS
+parser = argparse.ArgumentParser(description=__doc__)
 
-gflags.DEFINE_string('discovery_uri_template', DISCOVERY_URI,
-                     'URI Template for discovery.')
+parser.add_argument('--discovery_uri_template', default=DISCOVERY_URI,
+                    help='URI Template for discovery.')
 
-gflags.DEFINE_string('discovery_uri', '', 'URI of discovery document. '
-                     'If supplied then only this API will be documented.')
+parser.add_argument('--discovery_uri', default='',
+                    help=('URI of discovery document. If supplied then only '
+                          'this API will be documented.'))
 
-gflags.DEFINE_string('directory_uri', DIRECTORY_URI,
-                     'URI of directory document. '
-                     'Unused if --discovery_uri is supplied.')
+parser.add_argument('--directory_uri', default=DIRECTORY_URI,
+                    help=('URI of directory document. Unused if --discovery_uri'
+                          ' is supplied.'))
 
-gflags.DEFINE_string('dest', BASE, 'Directory name to write documents into.')
+parser.add_argument('--dest', default=BASE,
+                    help='Directory name to write documents into.')
+
 
 
 def safe_version(version):
@@ -222,7 +223,8 @@
   """
 
   params = method_params(doc)
-  return Template(METHOD_TEMPLATE).substitute(name=name, params=params, doc=doc)
+  return string.Template(METHOD_TEMPLATE).substitute(
+      name=name, params=params, doc=doc)
 
 
 def breadcrumbs(path, root_discovery):
@@ -290,7 +292,8 @@
     for name in collections:
       if not name.startswith('_') and callable(getattr(resource, name)):
         href = path + name + '.html'
-        html.append(Template(COLLECTION_LINK).substitute(href=href, name=name))
+        html.append(string.Template(COLLECTION_LINK).substitute(
+            href=href, name=name))
 
   if methods:
     for name in methods:
@@ -298,7 +301,7 @@
         doc = getattr(resource, name).__doc__
         params = method_params(doc)
         firstline = doc.splitlines()[0]
-        html.append(Template(METHOD_LINK).substitute(
+        html.append(string.Template(METHOD_LINK).substitute(
             name=name, params=params, firstline=firstline))
 
   if methods:
@@ -371,13 +374,7 @@
 
 
 if __name__ == '__main__':
-  # Let the gflags module process the command-line arguments
-  try:
-    argv = FLAGS(sys.argv)
-  except gflags.FlagsError, e:
-    print '%s\\nUsage: %s ARGS\\n%s' % (e, argv[0], FLAGS)
-    sys.exit(1)
-
+  FLAGS = parser.parse_args(sys.argv[1:])
   if FLAGS.discovery_uri:
     document_api_from_discovery_document(FLAGS.discovery_uri)
   else:
diff --git a/expand-symlinks.py b/expandsymlinks.py
similarity index 74%
rename from expand-symlinks.py
rename to expandsymlinks.py
index 39b2c21..50967ae 100644
--- a/expand-symlinks.py
+++ b/expandsymlinks.py
@@ -20,23 +20,24 @@
 
 from shutil import copytree
 
-import gflags
+import argparse
 import sys
 
 
-FLAGS = gflags.FLAGS
-
 # Ignore these files and directories when copying over files into the snapshot.
-IGNORE = set(['.hg', 'httplib2', 'oauth2', 'simplejson', 'static', 'gflags.py',
-                      'gflags_validators.py'])
+IGNORE = set(['.hg', 'httplib2', 'oauth2', 'simplejson', 'static'])
 
 # In addition to the above files also ignore these files and directories when
 # copying over samples into the snapshot.
 IGNORE_IN_SAMPLES = set(['apiclient', 'oauth2client', 'uritemplate'])
 
+parser = argparse.ArgumentParser(description=__doc__)
 
-gflags.DEFINE_string('source', '.', 'Directory name to copy from.')
-gflags.DEFINE_string('dest', 'snapshot', 'Directory name to copy to.')
+parser.add_argument('--source', default='.',
+                    help='Directory name to copy from.')
+
+parser.add_argument('--dest', default='snapshot',
+                    help='Directory name to copy to.')
 
 
 def _ignore(path, names):
@@ -47,17 +48,11 @@
   return retval
 
 
-def main(argv):
-  # Let the gflags module process the command-line arguments
-  try:
-    argv = FLAGS(argv)
-  except gflags.FlagsError, e:
-    print '%s\\nUsage: %s ARGS\\n%s' % (e, argv[0], FLAGS)
-    sys.exit(1)
-
+def main():
   copytree(FLAGS.source, FLAGS.dest, symlinks=True,
             ignore=_ignore)
 
 
 if __name__ == '__main__':
-  main(sys.argv)
+  FLAGS = parser.parse_args(sys.argv[1:])
+  main()
diff --git a/oauth2client/tools.py b/oauth2client/tools.py
index 93b0171..839f296 100644
--- a/oauth2client/tools.py
+++ b/oauth2client/tools.py
@@ -24,7 +24,9 @@
 
 
 import BaseHTTPServer
-import gflags
+import argparse
+import logging
+import os
 import socket
 import sys
 import webbrowser
@@ -38,20 +40,31 @@
 except ImportError:
   from cgi import parse_qsl
 
+_CLIENT_SECRETS_MESSAGE = """WARNING: Please configure OAuth 2.0
 
-FLAGS = gflags.FLAGS
+To make this sample run you will need to populate the client_secrets.json file
+found at:
 
-gflags.DEFINE_boolean('auth_local_webserver', True,
-                      ('Run a local web server to handle redirects during '
-                       'OAuth authorization.'))
+   %s
 
-gflags.DEFINE_string('auth_host_name', 'localhost',
-                     ('Host name to use when running a local web server to '
-                      'handle redirects during OAuth authorization.'))
+with information from the APIs Console <https://code.google.com/apis/console>.
 
-gflags.DEFINE_multi_int('auth_host_port', [8080, 8090],
-                        ('Port to use when running a local web server to '
-                         'handle redirects during OAuth authorization.'))
+"""
+
+# run_parser is an ArgumentParser that contains command-line options expected
+# by tools.run(). Pass it in as part of the 'parents' argument to your own
+# ArgumentParser.
+argparser = argparse.ArgumentParser(add_help=False)
+argparser.add_argument('--auth_host_name', default='localhost',
+                        help='Hostname when running a local web server.')
+argparser.add_argument('--noauth_local_webserver', action='store_true',
+                        default=False, help='Do not run a local web server.')
+argparser.add_argument('--auth_host_port', default=[8080, 8090], type=int,
+                        nargs='*', help='Port web server should listen on.')
+argparser.add_argument('--logging_level', default='ERROR',
+                        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR',
+                                 'CRITICAL'],
+                        help='Set the logging level of detail.')
 
 
 class ClientRedirectServer(BaseHTTPServer.HTTPServer):
@@ -92,8 +105,8 @@
     pass
 
 
-@util.positional(2)
-def run(flow, storage, http=None):
+@util.positional(3)
+def run(flow, storage, flags, http=None):
   """Core code for a command-line application.
 
   The run() function is called from your application and runs through all the
@@ -121,32 +134,40 @@
       during OAuth authorization.
       (default: 'true')
 
-  Since it uses flags make sure to initialize the gflags module before calling
-  run().
+  The tools module defines an ArgumentParser the already contains the flag
+  definitions that run() requires. You can pass that ArgumentParser to your
+  ArgumentParser constructor:
+
+    parser = argparse.ArgumentParser(description=__doc__,
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        parents=[tools.run_parser])
+    flags = parser.parse_args(argv)
 
   Args:
     flow: Flow, an OAuth 2.0 Flow to step through.
     storage: Storage, a Storage to store the credential in.
+    flags: argparse.ArgumentParser, the command-line flags.
     http: An instance of httplib2.Http.request
          or something that acts like it.
 
   Returns:
     Credentials, the obtained credential.
   """
-  if FLAGS.auth_local_webserver:
+  logging.getLogger().setLevel(getattr(logging, flags.logging_level))
+  if not flags.noauth_local_webserver:
     success = False
     port_number = 0
-    for port in FLAGS.auth_host_port:
+    for port in flags.auth_host_port:
       port_number = port
       try:
-        httpd = ClientRedirectServer((FLAGS.auth_host_name, port),
+        httpd = ClientRedirectServer((flags.auth_host_name, port),
                                      ClientRedirectHandler)
       except socket.error, e:
         pass
       else:
         success = True
         break
-    FLAGS.auth_local_webserver = success
+    flags.noauth_local_webserver = not success
     if not success:
       print 'Failed to start a local webserver listening on either port 8080'
       print 'or port 9090. Please check your firewall settings and locally'
@@ -156,14 +177,14 @@
       print 'authorization.'
       print
 
-  if FLAGS.auth_local_webserver:
-    oauth_callback = 'http://%s:%s/' % (FLAGS.auth_host_name, port_number)
+  if not flags.noauth_local_webserver:
+    oauth_callback = 'http://%s:%s/' % (flags.auth_host_name, port_number)
   else:
     oauth_callback = OOB_CALLBACK_URN
   flow.redirect_uri = oauth_callback
   authorize_url = flow.step1_get_authorize_url()
 
-  if FLAGS.auth_local_webserver:
+  if not flags.noauth_local_webserver:
     webbrowser.open(authorize_url, new=1, autoraise=True)
     print 'Your browser has been opened to visit:'
     print
@@ -181,7 +202,7 @@
     print
 
   code = None
-  if FLAGS.auth_local_webserver:
+  if not flags.noauth_local_webserver:
     httpd.handle_request()
     if 'error' in httpd.query_params:
       sys.exit('Authentication request was rejected.')
@@ -203,3 +224,9 @@
   print 'Authentication successful.'
 
   return credential
+
+
+def message_if_missing(filename):
+  """Helpful message to display if the CLIENT_SECRETS file is missing."""
+
+  return _CLIENT_SECRETS_MESSAGE % filename
diff --git a/oauth2client/util.py b/oauth2client/util.py
index ee6a100..90dff15 100644
--- a/oauth2client/util.py
+++ b/oauth2client/util.py
@@ -22,9 +22,11 @@
 ]
 __all__ = [
   'positional',
+  'POSITIONAL_WARNING',
+  'POSITIONAL_EXCEPTION',
+  'POSITIONAL_IGNORE',
 ]
 
-import gflags
 import inspect
 import logging
 import types
@@ -38,12 +40,13 @@
 
 logger = logging.getLogger(__name__)
 
-FLAGS = gflags.FLAGS
+POSITIONAL_WARNING = 'WARNING'
+POSITIONAL_EXCEPTION = 'EXCEPTION'
+POSITIONAL_IGNORE = 'IGNORE'
+POSITIONAL_SET = frozenset([POSITIONAL_WARNING, POSITIONAL_EXCEPTION,
+                            POSITIONAL_IGNORE])
 
-gflags.DEFINE_enum('positional_parameters_enforcement', 'WARNING',
-    ['EXCEPTION', 'WARNING', 'IGNORE'],
-    'The action when an oauth2client.util.positional declaration is violated.')
-
+positional_parameters_enforcement = POSITIONAL_WARNING
 
 def positional(max_positional_args):
   """A decorator to declare that only the first N arguments my be positional.
@@ -93,10 +96,11 @@
         def my_method(cls, pos1, kwonly1=None):
           ...
 
-  The positional decorator behavior is controlled by the
-  --positional_parameters_enforcement flag. The flag may be set to 'EXCEPTION',
-  'WARNING' or 'IGNORE' to raise an exception, log a warning, or do nothing,
-  respectively, if a declaration is violated.
+  The positional decorator behavior is controlled by
+  util.positional_parameters_enforcement, which may be set to
+  POSITIONAL_EXCEPTION, POSITIONAL_WARNING or POSITIONAL_IGNORE to raise an
+  exception, log a warning, or do nothing, respectively, if a declaration is
+  violated.
 
   Args:
     max_positional_arguments: Maximum number of positional arguments. All
@@ -107,9 +111,9 @@
     being used as positional parameters.
 
   Raises:
-    TypeError if a key-word only argument is provided as a positional parameter,
-    but only if the --positional_parameters_enforcement flag is set to
-    'EXCEPTION'.
+    TypeError if a key-word only argument is provided as a positional
+    parameter, but only if util.positional_parameters_enforcement is set to
+    POSITIONAL_EXCEPTION.
   """
   def positional_decorator(wrapped):
     def positional_wrapper(*args, **kwargs):
@@ -119,9 +123,9 @@
           plural_s = 's'
         message = '%s() takes at most %d positional argument%s (%d given)' % (
             wrapped.__name__, max_positional_args, plural_s, len(args))
-        if FLAGS.positional_parameters_enforcement == 'EXCEPTION':
+        if positional_parameters_enforcement == POSITIONAL_EXCEPTION:
           raise TypeError(message)
-        elif FLAGS.positional_parameters_enforcement == 'WARNING':
+        elif positional_parameters_enforcement == POSITIONAL_WARNING:
           logger.warning(message)
         else: # IGNORE
           pass
diff --git a/runsamples.py b/runsamples.py
deleted file mode 100644
index a526f71..0000000
--- a/runsamples.py
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/usr/bin/env python
-"""Execute all sample applications.
-
-Runs over all the sample applications, determines their type (App Engine,
-Django, or a command-line application), and then runs them checking for a good
-return status in the case of command-line applications and a 200 OK response in
-the case of the App Engine and Django samples.
-"""
-import gflags
-import httplib2
-import logging
-import os
-import signal
-import subprocess
-import sys
-import time
-
-FLAGS = gflags.FLAGS
-
-gflags.DEFINE_list('samples_to_skip', ['latitude'],
-    'A comma separated list of project directory names to be skipped.')
-
-gflags.DEFINE_enum('logging_level', 'INFO',
-    ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
-    'Set the level of logging detail.')
-
-gflags.DEFINE_string('app_engine_dir', '../google_appengine/',
-    'Directory where Google App Engine is installed.')
-
-gflags.DEFINE_string('sample_root', 'samples/oauth2',
-    'The root directory for all the samples.')
-
-
-def main(argv):
-  try:
-    argv = FLAGS(argv)
-  except gflags.FlagsError, e:
-    print '%s\\nUsage: %s ARGS\\n%s' % (e, argv[0], FLAGS)
-    sys.exit(1)
-
-  logging.getLogger().setLevel(getattr(logging, FLAGS.logging_level))
-
-  for dirname in os.listdir(FLAGS.sample_root):
-    fulldirname = os.path.join(FLAGS.sample_root, dirname)
-    if dirname in FLAGS.samples_to_skip:
-      logging.debug('Skipping ' + fulldirname + ' (blacklist)')
-      continue
-    filelist = os.listdir(fulldirname)
-    if 'settings.py' in filelist and 'manage.py' in filelist:
-      logging.info(fulldirname + ' [Django]')
-      proc = subprocess.Popen(
-          [os.path.join(fulldirname, 'manage.py'),
-          'runserver'])
-      # Now just wait, because Django actually spawns a sub-process that does
-      # the I/O and does something funky with stdout so we can't read it and
-      # figure out when it is started.
-      time.sleep(3)
-      h = httplib2.Http()
-      resp, content = h.request('http://localhost:8000/')
-      assert(200 == resp.status)
-      time.sleep(1)
-      logging.debug('Django ppid: %d', proc.pid)
-      # Find and kill the sub-process manage.py forked.
-      findpids = subprocess.Popen(['ps', '--ppid', str(proc.pid), 'o', 'pid',],
-          stdout=subprocess.PIPE)
-      for p in findpids.stdout.readlines():
-        if 'PID' not in p:
-          os.kill(int(p), signal.SIGINT)
-      os.kill(proc.pid, signal.SIGINT)
-      proc.wait()
-    elif 'app.yaml' in filelist:
-      logging.info(fulldirname + ' [App Engine]')
-      proc = subprocess.Popen(
-          [os.path.join(FLAGS.app_engine_dir, 'dev_appserver.py'),
-          fulldirname],
-          stdout=subprocess.PIPE,
-          stderr=subprocess.STDOUT)
-      line = proc.stdout.readline()
-      logging.debug('READ: ' + line)
-      while '] Running application' not in line:
-        line = proc.stdout.readline()
-        logging.debug('READ: ' + line)
-      h = httplib2.Http()
-      resp, content = h.request('http://localhost:8080/')
-      assert(200 == resp.status)
-      time.sleep(1)
-      os.kill(proc.pid, signal.SIGINT)
-      proc.wait()
-    else:
-      logging.info(fulldirname + ' [Command-line]')
-      for filename in os.listdir(fulldirname):
-        if filename.endswith('.py'):
-          logging.info('Running: ' + filename)
-          proc = subprocess.Popen(['python',
-            os.path.join(fulldirname, filename)])
-          returncode = proc.wait()
-          assert(returncode == 0)
-
-
-if __name__ == '__main__':
-  main(sys.argv)
diff --git a/samples/plus/plus.py b/samples/plus/plus.py
old mode 100644
new mode 100755
index c6089f9..b88395b
--- a/samples/plus/plus.py
+++ b/samples/plus/plus.py
@@ -1,7 +1,7 @@
-#!/usr/bin/python2.4
+#!/usr/bin/env python
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2010 Google Inc.
+# Copyright (C) 2013 Google Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -17,122 +17,80 @@
 
 """Simple command-line sample for the Google+ API.
 
-Command-line application that retrieves the users latest content and
-then adds a new entry.
-
-Usage:
-  $ python plus.py
-
-You can also get help on all the command-line flags the program understands
-by running:
-
-  $ python plus.py --help
-
-To get detailed log output run:
-
-  $ python plus.py --logging_level=DEBUG
-"""
+Command-line application that retrieves the list of the user's posts."""
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
-import gflags
-import httplib2
+import argparse
 import logging
 import os
-import pprint
 import sys
 
-from apiclient.discovery import build
-from oauth2client.file import Storage
-from oauth2client.client import AccessTokenRefreshError
-from oauth2client.client import flow_from_clientsecrets
-from oauth2client.tools import run
+import httplib2
 
+from apiclient import discovery
+from oauth2client import file
+from oauth2client import client
+from oauth2client import tools
 
-FLAGS = gflags.FLAGS
 
 # CLIENT_SECRETS, name of a file containing the OAuth 2.0 information for this
 # application, including client_id and client_secret, which are found
 # on the API Access tab on the Google APIs
-# Console <http://code.google.com/apis/console>
-CLIENT_SECRETS = 'client_secrets.json'
-
-# Helpful message to display in the browser if the CLIENT_SECRETS file
-# is missing.
-MISSING_CLIENT_SECRETS_MESSAGE = """
-WARNING: Please configure OAuth 2.0
-
-To make this sample run you will need to populate the client_secrets.json file
-found at:
-
-   %s
-
-with information from the APIs Console <https://code.google.com/apis/console>.
-
-""" % os.path.join(os.path.dirname(__file__), CLIENT_SECRETS)
+# Console <http://code.google.com/apis/console>.
+CLIENT_SECRETS = os.path.join(os.path.dirname(__file__), 'client_secrets.json')
 
 # Set up a Flow object to be used if we need to authenticate.
-FLOW = flow_from_clientsecrets(CLIENT_SECRETS,
+FLOW = client.flow_from_clientsecrets(CLIENT_SECRETS,
     scope='https://www.googleapis.com/auth/plus.me',
-    message=MISSING_CLIENT_SECRETS_MESSAGE)
-
-
-# The gflags module makes defining command-line options easy for
-# applications. Run this program with the '--help' argument to see
-# all the flags that it understands.
-gflags.DEFINE_enum('logging_level', 'ERROR',
-    ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
-    'Set the level of logging detail.')
+    message=tools.message_if_missing(CLIENT_SECRETS))
 
 
 def main(argv):
-  # Let the gflags module process the command-line arguments
-  try:
-    argv = FLAGS(argv)
-  except gflags.FlagsError, e:
-    print '%s\\nUsage: %s ARGS\\n%s' % (e, argv[0], FLAGS)
-    sys.exit(1)
-
-  # Set the logging according to the command-line flag
-  logging.getLogger().setLevel(getattr(logging, FLAGS.logging_level))
+  # Parse command-line options.
+  parser = argparse.ArgumentParser(
+      description=__doc__,
+      formatter_class=argparse.RawDescriptionHelpFormatter,
+      parents=[tools.argparser])
+  flags = parser.parse_args(argv[1:])
 
   # If the Credentials don't exist or are invalid run through the native client
   # flow. The Storage object will ensure that if successful the good
   # Credentials will get written back to a file.
-  storage = Storage('plus.dat')
+  storage = file.Storage('plus.dat')
   credentials = storage.get()
 
   if credentials is None or credentials.invalid:
-    credentials = run(FLOW, storage)
+    credentials = tools.run(FLOW, storage, flags)
 
   # Create an httplib2.Http object to handle our HTTP requests and authorize it
   # with our good Credentials.
-  http = httplib2.Http()
-  http = credentials.authorize(http)
+  http = credentials.authorize(httplib2.Http())
 
-  service = build("plus", "v1", http=http)
+  service = discovery.build('plus', 'v1', http=http)
 
   try:
-    person = service.people().get(userId='me').execute(http=http)
+    person = service.people().get(userId='me').execute()
 
-    print "Got your ID: %s" % person['displayName']
+    print 'Got your ID: %s' % person['displayName']
     print
-    print "%-040s -> %s" % ("[Activitity ID]", "[Content]")
+    print '%-040s -> %s' % ('[Activitity ID]', '[Content]')
 
-    # Don't execute the request until we reach the paging loop below
+    # Don't execute the request until we reach the paging loop below.
     request = service.activities().list(
         userId=person['id'], collection='public')
+
     # Loop over every activity and print the ID and a short snippet of content.
-    while ( request != None ):
+    while request is not None:
       activities_doc = request.execute()
       for item in activities_doc.get('items', []):
         print '%-040s -> %s' % (item['id'], item['object']['content'][:30])
 
       request = service.activities().list_next(request, activities_doc)
 
-  except AccessTokenRefreshError:
-    print ("The credentials have been revoked or expired, please re-run"
-      "the application to re-authorize")
+  except client.AccessTokenRefreshError:
+    print ('The credentials have been revoked or expired, please re-run'
+      'the application to re-authorize.')
 
 if __name__ == '__main__':
   main(sys.argv)
diff --git a/setup.py b/setup.py
index 45bd0ae..64b484b 100644
--- a/setup.py
+++ b/setup.py
@@ -26,7 +26,6 @@
 
 install_requires = [
     'httplib2>=0.8',
-    'python-gflags',
     ]
 
 needs_json = False
@@ -56,9 +55,7 @@
       url="http://code.google.com/p/google-api-python-client/",
       install_requires=install_requires,
       packages=packages,
-      package_data={
-        },
-      scripts=['bin/enable-app-engine-project'],
+      package_data={},
       license="Apache 2.0",
       keywords="google api client",
       classifiers=['Development Status :: 5 - Production/Stable',
diff --git a/setup_oauth2client.py b/setup_oauth2client.py
index e67377d..581ed61 100644
--- a/setup_oauth2client.py
+++ b/setup_oauth2client.py
@@ -26,7 +26,6 @@
 
 install_requires = [
     'httplib2>=0.8',
-    'python-gflags',
     ]
 
 needs_json = False
diff --git a/tests/__init__.py b/tests/__init__.py
index 2451995..7913e6f 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -14,9 +14,8 @@
 
 __author__ = 'afshar@google.com (Ali Afshar)'
 
-import gflags
+import oauth2client.util
 
 def setup_package():
   """Run on testing package."""
-  FLAGS = gflags.FLAGS
-  FLAGS.positional_parameters_enforcement = 'EXCEPTION'
+  oauth2client.util.positional_parameters_enforcement = 'EXCEPTION'
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index b8b5ac0..499edda 100644
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -25,7 +25,6 @@
 
 import copy
 import datetime
-import gflags
 import httplib2
 import os
 import pickle
@@ -65,16 +64,16 @@
 from apiclient.http import MediaUploadProgress
 from apiclient.http import tunnel_patch
 from oauth2client import GOOGLE_TOKEN_URI
+from oauth2client import util
 from oauth2client.anyjson import simplejson
 from oauth2client.client import OAuth2Credentials
-from oauth2client.util import _add_query_parameter
+
 import uritemplate
 
 
 DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
 
-FLAGS = gflags.FLAGS
-FLAGS.positional_parameters_enforcement = 'EXCEPTION'
+util.positional_parameters_enforcement = util.POSITIONAL_EXCEPTION
 
 
 def assertUrisEqual(testcase, expected, actual):
@@ -331,20 +330,20 @@
 class DiscoveryFromDocument(unittest.TestCase):
 
   def test_can_build_from_local_document(self):
-    discovery = file(datafile('plus.json')).read()
+    discovery = open(datafile('plus.json')).read()
     plus = build_from_document(discovery, base="https://www.googleapis.com/")
     self.assertTrue(plus is not None)
     self.assertTrue(hasattr(plus, 'activities'))
 
   def test_can_build_from_local_deserialized_document(self):
-    discovery = file(datafile('plus.json')).read()
+    discovery = open(datafile('plus.json')).read()
     discovery = simplejson.loads(discovery)
     plus = build_from_document(discovery, base="https://www.googleapis.com/")
     self.assertTrue(plus is not None)
     self.assertTrue(hasattr(plus, 'activities'))
 
   def test_building_with_base_remembers_base(self):
-    discovery = file(datafile('plus.json')).read()
+    discovery = open(datafile('plus.json')).read()
 
     base = "https://www.example.com/"
     plus = build_from_document(discovery, base=base)
@@ -364,7 +363,7 @@
     os.environ['REMOTE_ADDR'] = '10.0.0.1'
     try:
       http = HttpMockSequence([
-        ({'status': '400'}, file(datafile('zoo.json'), 'r').read()),
+        ({'status': '400'}, open(datafile('zoo.json'), 'rb').read()),
         ])
       zoo = build('zoo', 'v1', http=http, developerKey='foo',
                   discoveryServiceUrl='http://example.com')
@@ -377,7 +376,7 @@
     # out of the raised exception.
     try:
       http = HttpMockSequence([
-        ({'status': '400'}, file(datafile('zoo.json'), 'r').read()),
+        ({'status': '400'}, open(datafile('zoo.json'), 'rb').read()),
         ])
       zoo = build('zoo', 'v1', http=http, developerKey=None,
                   discoveryServiceUrl='http://example.com')
@@ -501,7 +500,7 @@
 
   def test_tunnel_patch(self):
     http = HttpMockSequence([
-      ({'status': '200'}, file(datafile('zoo.json'), 'r').read()),
+      ({'status': '200'}, open(datafile('zoo.json'), 'rb').read()),
       ({'status': '200'}, 'echo_request_headers_as_json'),
       ])
     http = tunnel_patch(http)
@@ -1091,8 +1090,8 @@
     zoo_uri = uritemplate.expand(DISCOVERY_URI,
                                  {'api': 'zoo', 'apiVersion': 'v1'})
     if 'REMOTE_ADDR' in os.environ:
-        zoo_uri = _add_query_parameter(zoo_uri, 'userIp',
-                                       os.environ['REMOTE_ADDR'])
+        zoo_uri = util._add_query_parameter(zoo_uri, 'userIp',
+                                            os.environ['REMOTE_ADDR'])
 
     http = httplib2.Http()
     original_request = http.request
diff --git a/tests/test_json_model.py b/tests/test_json_model.py
index d6cf0c0..281affb 100644
--- a/tests/test_json_model.py
+++ b/tests/test_json_model.py
@@ -22,7 +22,6 @@
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
 import copy
-import gflags
 import os
 import unittest
 import httplib2
@@ -32,8 +31,6 @@
 from apiclient.model import JsonModel
 from oauth2client.anyjson import simplejson
 
-FLAGS = gflags.FLAGS
-
 # Python 2.5 requires different modules
 try:
   from urlparse import parse_qs
@@ -212,8 +209,7 @@
           self[key] = value
     old_logging = apiclient.model.logging
     apiclient.model.logging = MockLogging()
-    apiclient.model.FLAGS = copy.deepcopy(FLAGS)
-    apiclient.model.FLAGS.dump_request_response = True
+    apiclient.model.dump_request_response = True
     model = JsonModel()
     request_body = {
         'field1': 'value1',
diff --git a/tests/test_protobuf_model.py b/tests/test_protobuf_model.py
index 02e8846..b9c92b0 100644
--- a/tests/test_protobuf_model.py
+++ b/tests/test_protobuf_model.py
@@ -21,7 +21,6 @@
 
 __author__ = 'mmcdonald@google.com (Matt McDonald)'
 
-import gflags
 import unittest
 import httplib2
 import apiclient.model
@@ -29,8 +28,6 @@
 from apiclient.errors import HttpError
 from apiclient.model import ProtocolBufferModel
 
-FLAGS = gflags.FLAGS
-
 # Python 2.5 requires different modules
 try:
   from urlparse import parse_qs
diff --git a/tools/gae-zip-creator.sh b/tools/gae-zip-creator.sh
index 2e63da5..1f225b1 100755
--- a/tools/gae-zip-creator.sh
+++ b/tools/gae-zip-creator.sh
@@ -57,7 +57,7 @@
 # Sanity test the zip.
 # TODO (afshar): Run the complete test suite.
 echo "Sanity testing the zip:"
-export SANITY_MODS="gflags httplib2 apiclient uritemplate oauth2client"
+export SANITY_MODS="httplib2 apiclient uritemplate oauth2client"
 export SANITY_ZIP=${ZIP_PATH}
 export PYTHONPATH=${ZIP_PATH}
 ${ENV_PATH}/bin/python -c "import sys, os