Separate oauthlib integration into its own package (#137)

* Centralize the run_pylint script.
* Use io.open instead of open in setup.py for httplib2_transport
* Move httplib2_transport -> additional_packages/google_auth_httplib2
* Remove unneeded dependencies in google_auth_httplib2
* Update classifiers to note 3.6 support.
* Don't install the HEAD version of google-auth when testing google-auth-httplib2.
* Add google-auth-oauthlib package.
* Remove google.oauth2.oauthlib and google.oauth2.flow and associated tests.
* Make travis run google-auth-oauthlib's tox.
* Specify tox workdir.
diff --git a/additional_packages/README.md b/additional_packages/README.md
new file mode 100644
index 0000000..827c548
--- /dev/null
+++ b/additional_packages/README.md
@@ -0,0 +1,4 @@
+# Additional packages for Google Auth Library Python
+
+This folder contains seperately distributed auxilliary packages for use
+with google-auth.
diff --git a/httplib2_transport/.coveragerc b/additional_packages/google_auth_httplib2/.coveragerc
similarity index 100%
rename from httplib2_transport/.coveragerc
rename to additional_packages/google_auth_httplib2/.coveragerc
diff --git a/httplib2_transport/LICENSE b/additional_packages/google_auth_httplib2/LICENSE
similarity index 100%
rename from httplib2_transport/LICENSE
rename to additional_packages/google_auth_httplib2/LICENSE
diff --git a/httplib2_transport/MANIFEST.in b/additional_packages/google_auth_httplib2/MANIFEST.in
similarity index 100%
rename from httplib2_transport/MANIFEST.in
rename to additional_packages/google_auth_httplib2/MANIFEST.in
diff --git a/httplib2_transport/README.rst b/additional_packages/google_auth_httplib2/README.rst
similarity index 100%
rename from httplib2_transport/README.rst
rename to additional_packages/google_auth_httplib2/README.rst
diff --git a/httplib2_transport/google_auth_httplib2.py b/additional_packages/google_auth_httplib2/google_auth_httplib2.py
similarity index 100%
rename from httplib2_transport/google_auth_httplib2.py
rename to additional_packages/google_auth_httplib2/google_auth_httplib2.py
diff --git a/httplib2_transport/setup.cfg b/additional_packages/google_auth_httplib2/setup.cfg
similarity index 100%
rename from httplib2_transport/setup.cfg
rename to additional_packages/google_auth_httplib2/setup.cfg
diff --git a/httplib2_transport/setup.py b/additional_packages/google_auth_httplib2/setup.py
similarity index 93%
rename from httplib2_transport/setup.py
rename to additional_packages/google_auth_httplib2/setup.py
index e85c309..ec04881 100644
--- a/httplib2_transport/setup.py
+++ b/additional_packages/google_auth_httplib2/setup.py
@@ -12,19 +12,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import io
+
 from setuptools import setup
 
 
 DEPENDENCIES = (
-    'pyasn1>=0.1.7',
-    'pyasn1-modules>=0.0.5',
-    'rsa>=3.1.4',
-    'six>=1.9.0',
     'google-auth'
 )
 
 
-with open('README.rst', 'r') as fh:
+with io.open('README.rst', 'r') as fh:
     long_description = fh.read()
 
 
@@ -46,6 +44,7 @@
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
         'Development Status :: 3 - Alpha',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: Apache Software License',
diff --git a/httplib2_transport/tests/__init__.py b/additional_packages/google_auth_httplib2/tests/__init__.py
similarity index 100%
rename from httplib2_transport/tests/__init__.py
rename to additional_packages/google_auth_httplib2/tests/__init__.py
diff --git a/httplib2_transport/tests/compliance.py b/additional_packages/google_auth_httplib2/tests/compliance.py
similarity index 100%
rename from httplib2_transport/tests/compliance.py
rename to additional_packages/google_auth_httplib2/tests/compliance.py
diff --git a/httplib2_transport/tests/test_google_auth_httplib2.py b/additional_packages/google_auth_httplib2/tests/test_google_auth_httplib2.py
similarity index 100%
rename from httplib2_transport/tests/test_google_auth_httplib2.py
rename to additional_packages/google_auth_httplib2/tests/test_google_auth_httplib2.py
diff --git a/httplib2_transport/tox.ini b/additional_packages/google_auth_httplib2/tox.ini
similarity index 83%
rename from httplib2_transport/tox.ini
rename to additional_packages/google_auth_httplib2/tox.ini
index 84635e8..95f3454 100644
--- a/httplib2_transport/tox.ini
+++ b/additional_packages/google_auth_httplib2/tox.ini
@@ -9,9 +9,6 @@
   pytest-cov
   pytest-localserver
   httplib2
-  {toxinidir}/..
-# Always recreate because of the relative path in deps above.
-recreate = True
 commands =
   py.test --cov=google_auth_httplib2 --cov=tests {posargs:tests}
 
@@ -31,7 +28,9 @@
     --import-order-style=google \
     --application-import-names="google_auth_httplib2,tests" \
     google_auth_httplib2.py tests
-  python {toxinidir}/scripts/run_pylint.py
+  python {toxinidir}/../../scripts/run_pylint.py \
+    --library-filesets google_auth_httplib2.py \
+    --test-filesets tests
 deps =
   flake8
   flake8-import-order
diff --git a/httplib2_transport/.coveragerc b/additional_packages/google_auth_oauthlib/.coveragerc
similarity index 100%
copy from httplib2_transport/.coveragerc
copy to additional_packages/google_auth_oauthlib/.coveragerc
diff --git a/additional_packages/google_auth_oauthlib/CHANGELOG.rst b/additional_packages/google_auth_oauthlib/CHANGELOG.rst
new file mode 100644
index 0000000..dbb95df
--- /dev/null
+++ b/additional_packages/google_auth_oauthlib/CHANGELOG.rst
@@ -0,0 +1,4 @@
+v0.0.1
+------
+
+Initial release. This package contains the functionality previously located in `google.oauth2.oauthlib` and `google.oauth2.flows`.
diff --git a/httplib2_transport/LICENSE b/additional_packages/google_auth_oauthlib/LICENSE
similarity index 100%
copy from httplib2_transport/LICENSE
copy to additional_packages/google_auth_oauthlib/LICENSE
diff --git a/httplib2_transport/MANIFEST.in b/additional_packages/google_auth_oauthlib/MANIFEST.in
similarity index 100%
copy from httplib2_transport/MANIFEST.in
copy to additional_packages/google_auth_oauthlib/MANIFEST.in
diff --git a/additional_packages/google_auth_oauthlib/README.rst b/additional_packages/google_auth_oauthlib/README.rst
new file mode 100644
index 0000000..44c478b
--- /dev/null
+++ b/additional_packages/google_auth_oauthlib/README.rst
@@ -0,0 +1,28 @@
+oauthlib integration for Google Auth
+====================================
+
+|pypi|
+
+This library provides `oauthlib`_ integration with `google-auth`_.
+
+.. |pypi| image:: https://img.shields.io/pypi/v/google-auth-oauthlib.svg
+   :target: https://pypi.python.org/pypi/google-auth-oauthlib
+
+.. _oauthlib: https://github.com/idan/oauthlib
+.. _google-auth: https://github.com/GoogleCloudPlatform/google-auth
+
+Installing
+----------
+
+You can install using `pip`_::
+
+    $ pip install google-auth-oauthlib
+
+.. _pip: https://pip.pypa.io/en/stable/
+
+License
+-------
+
+Apache 2.0 - See `the LICENSE`_ for more information.
+
+.. _the LICENSE: https://github.com/GoogleCloudPlatform/google-auth-library-python/blob/master/LICENSE
diff --git a/httplib2_transport/tests/__init__.py b/additional_packages/google_auth_oauthlib/google_auth_oauthlib/__init__.py
similarity index 100%
copy from httplib2_transport/tests/__init__.py
copy to additional_packages/google_auth_oauthlib/google_auth_oauthlib/__init__.py
diff --git a/google/oauth2/flow.py b/additional_packages/google_auth_oauthlib/google_auth_oauthlib/flow.py
similarity index 95%
rename from google/oauth2/flow.py
rename to additional_packages/google_auth_oauthlib/google_auth_oauthlib/flow.py
index e70ee3d..a040659 100644
--- a/google/oauth2/flow.py
+++ b/additional_packages/google_auth_oauthlib/google_auth_oauthlib/flow.py
@@ -14,21 +14,17 @@
 
 """OAuth 2.0 Authorization Flow
 
-.. warning::
-    This module is experimental and is subject to change signficantly
-    within major version releases.
-
 This module provides integration with `requests-oauthlib`_ for running the
 `OAuth 2.0 Authorization Flow`_ and acquiring user credentials.
 
 Here's an example of using the flow with the installed application
 authorization flow::
 
-    import google.oauth2.flow
+    from google_auth_oauthlib.flow import Flow
 
     # Create the flow using the client secrets file from the Google API
     # Console.
-    flow = google.oauth2.flow.Flow.from_client_secrets_file(
+    flow = Flow.from_client_secrets_file(
         'path/to/client_secrets.json',
         scopes=['profile', 'email'],
         redirect_uri='urn:ietf:wg:oauth:2.0:oob')
@@ -57,7 +53,8 @@
 
 import google.auth.transport.requests
 import google.oauth2.credentials
-import google.oauth2.oauthlib
+
+import google_auth_oauthlib.helpers
 
 
 class Flow(object):
@@ -133,7 +130,7 @@
                 'Client secrets must be for a web or installed app.')
 
         session, client_config = (
-            google.oauth2.oauthlib.session_from_client_config(
+            google_auth_oauthlib.helpers.session_from_client_config(
                 client_config, scopes, **kwargs))
 
         return cls(session, client_type, client_config)
@@ -240,7 +237,7 @@
         Raises:
             ValueError: If there is no access token in the session.
         """
-        return google.oauth2.oauthlib.credentials_from_session(
+        return google_auth_oauthlib.helpers.credentials_from_session(
             self.oauth2session, self.client_config)
 
     def authorized_session(self):
diff --git a/google/oauth2/oauthlib.py b/additional_packages/google_auth_oauthlib/google_auth_oauthlib/helpers.py
similarity index 95%
rename from google/oauth2/oauthlib.py
rename to additional_packages/google_auth_oauthlib/google_auth_oauthlib/helpers.py
index e1c6089..901423b 100644
--- a/google/oauth2/oauthlib.py
+++ b/additional_packages/google_auth_oauthlib/google_auth_oauthlib/helpers.py
@@ -12,15 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""Integration with oauthlib
-
-.. warning::
-    This module is experimental and is subject to change signficantly
-    within major version releases.
+"""Integration helpers.
 
 This module provides helpers for integrating with `requests-oauthlib`_.
 Typically, you'll want to use the higher-level helpers in
-:mod:`google.oauth2.flow`.
+:mod:`google_auth_oauthlib.flow`.
 
 .. _requests-oauthlib: http://requests-oauthlib.readthedocs.io/en/stable/
 """
diff --git a/httplib2_transport/setup.cfg b/additional_packages/google_auth_oauthlib/setup.cfg
similarity index 100%
copy from httplib2_transport/setup.cfg
copy to additional_packages/google_auth_oauthlib/setup.cfg
diff --git a/httplib2_transport/setup.py b/additional_packages/google_auth_oauthlib/setup.py
similarity index 82%
copy from httplib2_transport/setup.py
copy to additional_packages/google_auth_oauthlib/setup.py
index e85c309..3652470 100644
--- a/httplib2_transport/setup.py
+++ b/additional_packages/google_auth_oauthlib/setup.py
@@ -12,40 +12,41 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import io
+
+from setuptools import find_packages
 from setuptools import setup
 
 
 DEPENDENCIES = (
-    'pyasn1>=0.1.7',
-    'pyasn1-modules>=0.0.5',
-    'rsa>=3.1.4',
-    'six>=1.9.0',
-    'google-auth'
+    'google-auth',
+    'requests-oauthlib>=0.7.0',
 )
 
 
-with open('README.rst', 'r') as fh:
+with io.open('README.rst', 'r') as fh:
     long_description = fh.read()
 
 
 setup(
-    name='google-auth-httplib2',
-    version='0.0.2',
+    name='google-auth-oauthlib',
+    version='0.0.1',
     author='Google Cloud Platform',
     author_email='jonwayne+google-auth@google.com',
     description='Google Authentication Library',
     long_description=long_description,
     url='https://github.com/GoogleCloudPlatform/google-auth-library-python',
-    py_modules=['google_auth_httplib2'],
+    packages=find_packages(exclude=('tests*',)),
     install_requires=DEPENDENCIES,
     license='Apache 2.0',
-    keywords='google auth oauth client',
+    keywords='google auth oauth client oauthlib',
     classifiers=(
         'Programming Language :: Python :: 2',
         'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
         'Development Status :: 3 - Alpha',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: Apache Software License',
diff --git a/httplib2_transport/tests/__init__.py b/additional_packages/google_auth_oauthlib/tests/__init__.py
similarity index 100%
copy from httplib2_transport/tests/__init__.py
copy to additional_packages/google_auth_oauthlib/tests/__init__.py
diff --git a/additional_packages/google_auth_oauthlib/tests/data/client_secrets.json b/additional_packages/google_auth_oauthlib/tests/data/client_secrets.json
new file mode 100644
index 0000000..1baa499
--- /dev/null
+++ b/additional_packages/google_auth_oauthlib/tests/data/client_secrets.json
@@ -0,0 +1,14 @@
+{
+  "web": {
+    "client_id": "example.apps.googleusercontent.com",
+    "project_id": "example",
+    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
+    "token_uri": "https://accounts.google.com/o/oauth2/token",
+    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
+    "client_secret": "itsasecrettoeveryone",
+    "redirect_uris": [
+      "urn:ietf:wg:oauth:2.0:oob",
+      "http://localhost"
+    ]
+  }
+}
diff --git a/tests/oauth2/test_flow.py b/additional_packages/google_auth_oauthlib/tests/test_flow.py
similarity index 97%
rename from tests/oauth2/test_flow.py
rename to additional_packages/google_auth_oauthlib/tests/test_flow.py
index e5d108f..663a02f 100644
--- a/tests/oauth2/test_flow.py
+++ b/additional_packages/google_auth_oauthlib/tests/test_flow.py
@@ -18,9 +18,9 @@
 import mock
 import pytest
 
-from google.oauth2 import flow
+from google_auth_oauthlib import flow
 
-DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data')
+DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
 CLIENT_SECRETS_FILE = os.path.join(DATA_DIR, 'client_secrets.json')
 
 with open(CLIENT_SECRETS_FILE, 'r') as fh:
diff --git a/tests/oauth2/test_oauthlib.py b/additional_packages/google_auth_oauthlib/tests/test_helpers.py
similarity index 81%
rename from tests/oauth2/test_oauthlib.py
rename to additional_packages/google_auth_oauthlib/tests/test_helpers.py
index a16c904..faae76b 100644
--- a/tests/oauth2/test_oauthlib.py
+++ b/additional_packages/google_auth_oauthlib/tests/test_helpers.py
@@ -18,9 +18,9 @@
 import mock
 import pytest
 
-from google.oauth2 import oauthlib
+from google_auth_oauthlib import helpers
 
-DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data')
+DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
 CLIENT_SECRETS_FILE = os.path.join(DATA_DIR, 'client_secrets.json')
 
 with open(CLIENT_SECRETS_FILE, 'r') as fh:
@@ -28,7 +28,7 @@
 
 
 def test_session_from_client_config_web():
-    session, config = oauthlib.session_from_client_config(
+    session, config = helpers.session_from_client_config(
         CLIENT_SECRETS_INFO, scopes=mock.sentinel.scopes)
 
     assert config == CLIENT_SECRETS_INFO
@@ -38,7 +38,7 @@
 
 def test_session_from_client_config_installed():
     info = {'installed': CLIENT_SECRETS_INFO['web']}
-    session, config = oauthlib.session_from_client_config(
+    session, config = helpers.session_from_client_config(
         info, scopes=mock.sentinel.scopes)
     assert config == info
     assert session.client_id == info['installed']['client_id']
@@ -47,16 +47,16 @@
 
 def test_session_from_client_config_bad_format():
     with pytest.raises(ValueError):
-        oauthlib.session_from_client_config({}, scopes=[])
+        helpers.session_from_client_config({}, scopes=[])
 
 
 def test_session_from_client_config_missing_keys():
     with pytest.raises(ValueError):
-        oauthlib.session_from_client_config({'web': {}}, scopes=[])
+        helpers.session_from_client_config({'web': {}}, scopes=[])
 
 
 def test_session_from_client_secrets_file():
-    session, config = oauthlib.session_from_client_secrets_file(
+    session, config = helpers.session_from_client_secrets_file(
         CLIENT_SECRETS_FILE, scopes=mock.sentinel.scopes)
     assert config == CLIENT_SECRETS_INFO
     assert session.client_id == CLIENT_SECRETS_INFO['web']['client_id']
@@ -65,7 +65,7 @@
 
 @pytest.fixture
 def session():
-    session, _ = oauthlib.session_from_client_config(
+    session, _ = helpers.session_from_client_config(
         CLIENT_SECRETS_INFO, scopes=mock.sentinel.scopes)
     yield session
 
@@ -76,7 +76,7 @@
         'refresh_token': mock.sentinel.refresh_token
     }
 
-    credentials = oauthlib.credentials_from_session(
+    credentials = helpers.credentials_from_session(
         session, CLIENT_SECRETS_INFO['web'])
 
     assert credentials.token == mock.sentinel.access_token
@@ -89,4 +89,4 @@
 
 def test_bad_credentials(session):
     with pytest.raises(ValueError):
-        oauthlib.credentials_from_session(session)
+        helpers.credentials_from_session(session)
diff --git a/additional_packages/google_auth_oauthlib/tox.ini b/additional_packages/google_auth_oauthlib/tox.ini
new file mode 100644
index 0000000..3cac089
--- /dev/null
+++ b/additional_packages/google_auth_oauthlib/tox.ini
@@ -0,0 +1,35 @@
+[tox]
+envlist = lint,py27,py34,py35,py36,pypy,cover
+
+[testenv]
+deps =
+  mock
+  pytest
+  pytest-cov
+commands =
+  py.test --cov=google_auth_oauthlib --cov=tests {posargs:tests}
+
+[testenv:cover]
+basepython = python3.6
+commands =
+  py.test --cov=google_auth_oauthlib --cov=tests --cov-report= tests
+  coverage report --show-missing --fail-under=100
+deps =
+  {[testenv]deps}
+
+[testenv:lint]
+basepython = python3.5
+commands =
+  python setup.py check --metadata --restructuredtext --strict
+  flake8 \
+    --import-order-style=google \
+    --application-import-names="google_auth_oauthlib,tests" \
+    google_auth_oauthlib tests
+  python {toxinidir}/../../scripts/run_pylint.py \
+    --library-filesets google_auth_oauthlib \
+    --test-filesets tests
+deps =
+  flake8
+  flake8-import-order
+  pylint
+  docutils
diff --git a/httplib2_transport/scripts/.gitignore b/httplib2_transport/scripts/.gitignore
deleted file mode 100644
index 3596d32..0000000
--- a/httplib2_transport/scripts/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Generated files
-pylintrc
-pylintrc.test
diff --git a/httplib2_transport/scripts/run_pylint.py b/httplib2_transport/scripts/run_pylint.py
deleted file mode 100644
index 643957a..0000000
--- a/httplib2_transport/scripts/run_pylint.py
+++ /dev/null
@@ -1,256 +0,0 @@
-# Copyright 2016 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.
-
-"""This script runs Pylint on the specified source.
-
-Before running Pylint, it generates a Pylint configuration on
-the fly based on programmatic defaults.
-
-.. note::
-
-    This file **really** breaks DRY, but is provided here as
-    **mostly** a copy of the true source file:
-    ``httplib2_transport/../scripts/run_pylint.py``.
-    This is done so that the ``httplib2_transport`` sub-package
-    can be self-contained.
-"""
-
-from __future__ import print_function
-
-import collections
-import copy
-import io
-import os
-import subprocess
-import sys
-
-import six
-
-
-_SCRIPTS_DIR = os.path.abspath(os.path.dirname(__file__))
-PRODUCTION_RC = os.path.join(_SCRIPTS_DIR, 'pylintrc')
-TEST_RC = os.path.join(_SCRIPTS_DIR, 'pylintrc.test')
-
-_PRODUCTION_RC_ADDITIONS = {
-    'MESSAGES CONTROL': {
-        'disable': [
-            'I',
-            'import-error',
-        ],
-    },
-}
-_PRODUCTION_RC_REPLACEMENTS = {
-    'MASTER': {
-        'ignore': ['CVS', '.git', '.cache', '.tox', '.nox'],
-        'load-plugins': 'pylint.extensions.check_docs',
-    },
-    'REPORTS': {
-        'reports': 'no',
-    },
-    'BASIC': {
-        'method-rgx': '[a-z_][a-z0-9_]{2,40}$',
-        'function-rgx': '[a-z_][a-z0-9_]{2,40}$',
-    },
-    'TYPECHECK': {
-        'ignored-modules': ['six', 'google.protobuf'],
-    },
-    'DESIGN': {
-        'min-public-methods': '0',
-        'max-args': '10',
-        'max-attributes': '15',
-    },
-}
-_TEST_RC_ADDITIONS = copy.deepcopy(_PRODUCTION_RC_ADDITIONS)
-_TEST_RC_ADDITIONS['MESSAGES CONTROL']['disable'].extend([
-    'missing-docstring',
-    'no-member',
-    'no-self-use',
-    'protected-access',
-    'unused-argument',
-])
-_TEST_RC_REPLACEMENTS = copy.deepcopy(_PRODUCTION_RC_REPLACEMENTS)
-_TEST_RC_REPLACEMENTS.setdefault('BASIC', {})
-_TEST_RC_REPLACEMENTS['BASIC'].update({
-    'good-names': ['i', 'j', 'k', 'ex', 'Run', '_', 'fh'],
-    'method-rgx': '[a-z_][a-z0-9_]{2,80}$',
-    'function-rgx': '[a-z_][a-z0-9_]{2,80}$',
-})
-IGNORED_FILES = ()
-
-_ERROR_TEMPLATE = 'Pylint failed on {} with status {:d}.'
-_LINT_FILESET_MSG = (
-    'Keyword arguments rc_filename and description are both '
-    'required. No other keyword arguments are allowed.')
-
-
-def get_default_config():
-    """Get the default Pylint configuration.
-
-    .. note::
-
-        The output of this function varies based on the current version of
-        Pylint installed.
-
-    Returns:
-        str: The default Pylint configuration.
-    """
-    # Swallow STDERR if it says
-    # "No config file found, using default configuration"
-    result = subprocess.check_output(['pylint', '--generate-rcfile'],
-                                     stderr=subprocess.PIPE)
-    # On Python 3, this returns bytes (from STDOUT), so we
-    # convert to a string.
-    return result.decode('utf-8')
-
-
-def read_config(contents):
-    """Reads pylintrc config into native ConfigParser object.
-
-    Args:
-        contents (str): The contents of the file containing the INI config.
-
-    Returns
-        ConfigParser.ConfigParser: The parsed configuration.
-    """
-    file_obj = io.StringIO(contents)
-    config = six.moves.configparser.ConfigParser()
-    config.readfp(file_obj)
-    return config
-
-
-def _transform_opt(opt_val):
-    """Transform a config option value to a string.
-
-    If already a string, do nothing. If an iterable, then
-    combine into a string by joining on ",".
-
-    Args:
-        opt_val (Union[str, list]): A config option's value.
-
-    Returns:
-        str: The option value converted to a string.
-    """
-    if isinstance(opt_val, (list, tuple)):
-        return ','.join(opt_val)
-    else:
-        return opt_val
-
-
-def lint_fileset(*paths, **kwargs):
-    """Lints a group of files using a given rcfile.
-
-    Keyword arguments are
-
-    * ``rc_filename`` (``str``): The name of the Pylint config RC file.
-    * ``description`` (``str``): A description of the files and configuration
-                                 currently being run.
-
-    Args:
-        paths (tuple): Directories or filenames to run Pylint in/on.
-        kwargs: The keyword arguments. The only keyword arguments
-            are ``rc_filename`` and ``description`` and both
-            are required.
-
-    Raises:
-        KeyError: If the wrong keyword arguments are used.
-    """
-    try:
-        rc_filename = kwargs['rc_filename']
-        description = kwargs['description']
-        if len(kwargs) != 2:
-            raise KeyError
-    except KeyError:
-        raise KeyError(_LINT_FILESET_MSG)
-
-    pylint_shell_command = ['pylint', '--rcfile', rc_filename]
-    pylint_shell_command.extend(paths)
-    status_code = subprocess.call(pylint_shell_command)
-    if status_code != 0:
-        error_message = _ERROR_TEMPLATE.format(description, status_code)
-        print(error_message, file=sys.stderr)
-        sys.exit(status_code)
-
-
-def make_rc(base_cfg, target_filename,
-            additions=None, replacements=None):
-    """Combines a base rc and additions into single file.
-
-    Args:
-        base_cfg (ConfigParser.ConfigParser): The configuration we are
-            merging into.
-        target_filename (str): The filename where the new configuration
-            will be saved.
-        additions (dict): (Optional) The values added to the configuration.
-        replacements (dict): (Optional) The wholesale replacements for
-            the new configuration.
-
-    Raises:
-        KeyError: if one of the additions or replacements does not
-            already exist in the current config.
-    """
-    # Set-up the mutable default values.
-    if additions is None:
-        additions = {}
-    if replacements is None:
-        replacements = {}
-
-    # Create fresh config, which must extend the base one.
-    new_cfg = six.moves.configparser.ConfigParser()
-    # pylint: disable=protected-access
-    new_cfg._sections = copy.deepcopy(base_cfg._sections)
-    new_sections = new_cfg._sections
-    # pylint: enable=protected-access
-
-    for section, opts in additions.items():
-        curr_section = new_sections.setdefault(
-            section, collections.OrderedDict())
-        for opt, opt_val in opts.items():
-            curr_val = curr_section.get(opt)
-            if curr_val is None:
-                raise KeyError('Expected to be adding to existing option.')
-            curr_val = curr_val.rstrip(',')
-            opt_val = _transform_opt(opt_val)
-            curr_section[opt] = '%s, %s' % (curr_val, opt_val)
-
-    for section, opts in replacements.items():
-        curr_section = new_sections.setdefault(
-            section, collections.OrderedDict())
-        for opt, opt_val in opts.items():
-            curr_val = curr_section.get(opt)
-            if curr_val is None:
-                raise KeyError('Expected to be replacing existing option.')
-            opt_val = _transform_opt(opt_val)
-            curr_section[opt] = '%s' % (opt_val,)
-
-    with open(target_filename, 'w') as file_obj:
-        new_cfg.write(file_obj)
-
-
-def main():
-    """Script entry point. Lints both sets of files."""
-    default_config = read_config(get_default_config())
-    make_rc(default_config, PRODUCTION_RC,
-            additions=_PRODUCTION_RC_ADDITIONS,
-            replacements=_PRODUCTION_RC_REPLACEMENTS)
-    make_rc(default_config, TEST_RC,
-            additions=_TEST_RC_ADDITIONS,
-            replacements=_TEST_RC_REPLACEMENTS)
-    lint_fileset('google_auth_httplib2.py', rc_filename=PRODUCTION_RC,
-                 description='Library')
-    lint_fileset('tests', rc_filename=TEST_RC,
-                 description='Test')
-
-
-if __name__ == '__main__':
-    main()
diff --git a/scripts/run_pylint.py b/scripts/run_pylint.py
index e9ac7ec..c33a53e 100644
--- a/scripts/run_pylint.py
+++ b/scripts/run_pylint.py
@@ -20,6 +20,7 @@
 
 from __future__ import print_function
 
+import argparse
 import collections
 import copy
 import io
@@ -236,6 +237,12 @@
 
 def main():
     """Script entry point. Lints both sets of files."""
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--library-filesets', nargs='+', default=[])
+    parser.add_argument('--test-filesets', nargs='+', default=[])
+
+    args = parser.parse_args()
+
     default_config = read_config(get_default_config())
     make_rc(default_config, PRODUCTION_RC,
             additions=_PRODUCTION_RC_ADDITIONS,
@@ -243,11 +250,11 @@
     make_rc(default_config, TEST_RC,
             additions=_TEST_RC_ADDITIONS,
             replacements=_TEST_RC_REPLACEMENTS)
-    lint_fileset('google', rc_filename=PRODUCTION_RC,
-                 description='Library')
-    lint_fileset('tests', 'system_tests', rc_filename=TEST_RC,
-                 description='Test')
 
+    lint_fileset(*args.library_filesets, rc_filename=PRODUCTION_RC,
+                 description='Library')
+    lint_fileset(*args.test_filesets, rc_filename=TEST_RC,
+                 description='Test')
 
 if __name__ == '__main__':
     main()
diff --git a/scripts/travis.sh b/scripts/travis.sh
index 9e11de8..5acb4c2 100755
--- a/scripts/travis.sh
+++ b/scripts/travis.sh
@@ -43,7 +43,14 @@
 
 # Run tox for sub-packages.
 if [[ $TOXENV != "docs"  && -z $SYSTEM_TEST ]]; then
-    echo "Running tox for httplib2_transport..."
-    cd httplib2_transport
-    tox
+    echo "Running tox for google_auth_httplib2..."
+    cd additional_packages/google_auth_httplib2
+    # --workdir is specified to avoid path names being too long, which
+    # causes subprocess calls to hit the execve character limit.
+    # See https://github.com/pypa/virtualenv/issues/596
+    tox --workdir ~/.tox-httplib2
+    cd $ROOT
+    echo "Running tox for google_auth_oauthlib..."
+    cd additional_packages/google_auth_oauthlib
+    tox --workdir  ~/.tox-oauthlib
 fi
diff --git a/setup.py b/setup.py
index 8c51932..76bceb8 100644
--- a/setup.py
+++ b/setup.py
@@ -25,10 +25,6 @@
     'six>=1.9.0',
 )
 
-EXTRA_OAUTHLIB_DEPENDENCIES = (
-    'requests-oauthlib>=0.7.0',
-)
-
 
 with io.open('README.rst', 'r') as fh:
     long_description = fh.read()
@@ -44,9 +40,6 @@
     packages=find_packages(exclude=('tests*', 'system_tests*')),
     namespace_packages=('google',),
     install_requires=DEPENDENCIES,
-    extras_require={
-        'oauthlib': EXTRA_OAUTHLIB_DEPENDENCIES,
-    },
     license='Apache 2.0',
     keywords='google auth oauth client',
     classifiers=(
@@ -55,6 +48,7 @@
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
         'Development Status :: 4 - Beta',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: Apache Software License',
diff --git a/tox.ini b/tox.ini
index 528806c..27f7d87 100644
--- a/tox.ini
+++ b/tox.ini
@@ -77,7 +77,9 @@
     --import-order-style=google \
     --application-import-names="google,tests,system_tests" \
     google tests
-  python {toxinidir}/scripts/run_pylint.py
+  python {toxinidir}/scripts/run_pylint.py \
+    --library-filesets google \
+    --test-filesets tests system_tests
 deps =
   flake8
   flake8-import-order