Use a python script to define Pylint config rather than an ini file (#69)
diff --git a/httplib2_transport/pylintrc b/httplib2_transport/pylintrc
deleted file mode 100644
index 1f01cfa..0000000
--- a/httplib2_transport/pylintrc
+++ /dev/null
@@ -1,165 +0,0 @@
-[MASTER]
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=CVS,.git,.cache,.tox,.nox
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-# DEFAULT: load-plugins=
-# RATIONALE: We want to make sure our docstrings match the objects
-# they document.
-load-plugins=pylint.extensions.check_docs
-
-[MESSAGES CONTROL]
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once).You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use"--disable=all --enable=classes
-# --disable=W"
-#
-# RATIONALE:
-# - maybe-no-member: bi-modal functions confuse pylint type inference.
-# - no-member: indirections in protobuf-generated code
-# - protected-access: helpers use '_foo' of classes from generated code.
-# - similarities: 'Bucket' and 'Blob' define 'metageneration' and 'owner' with
-# identical implementation but different docstrings.
-# - star-args: standard Python idioms for varargs:
-# ancestor = Query().filter(*order_props)
-# - redefined-variable-type: This error is overzealous and complains at e.g.
-# if some_prop:
-# return int(value)
-# else:
-# return float(value)
-# - import-error: imports are checked via tests.
-# - wrong-import-position: This error is overzealous. It assumes imports are
-# completed whenever something non-trivial is
-# defined, e.g.
-# try:
-# from foo import Bar
-# except ImportError:
-# class Bar(object):
-# """Hi everyone"""
-# and thus causes subsequent imports to be
-# diagnosed as out-of-order.
-# - no-name-in-module: Error gives a lot of false positives for names which
-# are defined dynamically. Also, any truly missing names
-# will be detected by our 100% code coverage.
-# - locally-disabled: Allow us to make exceptions in edge cases, notably where
-# pylint doesn't recognize inherited properties and methods
-# and gives unused-argument errors.
-# TEMPORARILY DISABLE AND SHOULD BE REMOVED:
-# - fixme: disabled until 1.0
-#
-disable =
- import-star-module-level,
- old-octal-literal,
- oct-method,
- print-statement,
- unpacking-in-except,
- parameter-unpacking,
- backtick,
- old-raise-syntax,
- old-ne-operator,
- long-suffix,
- dict-view-method,
- dict-iter-method,
- metaclass-assignment,
- next-method-called,
- raising-string,
- indexing-exception,
- raw_input-builtin,
- long-builtin,
- file-builtin,
- execfile-builtin,
- coerce-builtin,
- cmp-builtin,
- buffer-builtin,
- basestring-builtin,
- apply-builtin,
- filter-builtin-not-iterating,
- using-cmp-argument,
- useless-suppression,
- range-builtin-not-iterating,
- suppressed-message,
- no-absolute-import,
- old-division,
- cmp-method,
- reload-builtin,
- zip-builtin-not-iterating,
- intern-builtin,
- unichr-builtin,
- reduce-builtin,
- standarderror-builtin,
- unicode-builtin,
- xrange-builtin,
- coerce-method,
- delslice-method,
- getslice-method,
- setslice-method,
- input-builtin,
- round-builtin,
- hex-method,
- nonzero-method,
- map-builtin-not-iterating,
- maybe-no-member,
- no-member,
- protected-access,
- similarities,
- star-args,
- redefined-variable-type,
- import-error,
- wrong-import-position,
- no-name-in-module,
- locally-disabled,
- locally-enabled,
- fixme
-
-
-[REPORTS]
-# Tells whether to display a full report or only the messages
-# RATIONALE: noisy
-reports=no
-
-[BASIC]
-# Regular expression matching correct method names
-# DEFAULT: method-rgx=[a-z_][a-z0-9_]{2,30}$
-# RATIONALE: Some methods have longer names to be more descriptive or precise,
-# especially those that implemented wordy RFCs.
-method-rgx=[a-z_][a-z0-9_]{2,40}$
-
-# Regular expression matching correct function names
-# DEFAULT function-rgx=[a-z_][a-z0-9_]{2,30}$
-# RATIONALE: Some methods have longer names to be more descriptive or precise,
-# especially those that implemented wordy RFCs.
-function-rgx=[a-z_][a-z0-9_]{2,40}$
-
-[TYPECHECK]
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis. It
-# supports qualified module names, as well as Unix pattern matching.
-# DEFAULT: ignored-modules=
-# RATIONALE: six aliases stuff for compatibility.
-# google.protobuf fixes up namespace package "late".
-ignored-modules = six, google.protobuf
-
-
-[DESIGN]
-# Minimum number of public methods for a class (see R0903).
-# DEFAULT: min-public-methods=2
-# RATIONALE: context mgrs may have *no* public methods
-min-public-methods=0
-
-# Maximum number of arguments for function / method
-# DEFAULT: max-args=5
-# RATIONALE: Many credentials classes take a lot of parameters.
-max-args = 10
-
-# Maximum number of attributes for a class (see R0902).
-# DEFAULT: max-attributes=7
-# RATIONALE: Many credentials need to track lots of properties.
-max-attributes=15
diff --git a/httplib2_transport/pylintrc.tests b/httplib2_transport/pylintrc.tests
deleted file mode 100644
index 09772c5..0000000
--- a/httplib2_transport/pylintrc.tests
+++ /dev/null
@@ -1,166 +0,0 @@
-[MASTER]
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=CVS,.git,.cache,.tox,.nox
-
-[MESSAGES CONTROL]
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once).You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use"--disable=all --enable=classes
-# --disable=W"
-#
-# RATIONALE:
-# - maybe-no-member: bi-modal functions confuse pylint type inference.
-# - no-member: indirections in protobuf-generated code
-# - protected-access: helpers use '_foo' of classes from generated code.
-# - similarities: 'Bucket' and 'Blob' define 'metageneration' and 'owner' with
-# identical implementation but different docstrings.
-# - star-args: standard Python idioms for varargs:
-# ancestor = Query().filter(*order_props)
-# - redefined-variable-type: This error is overzealous and complains at e.g.
-# if some_prop:
-# return int(value)
-# else:
-# return float(value)
-# - import-error: imports are checked via tests.
-# - wrong-import-position: This error is overzealous. It assumes imports are
-# completed whenever something non-trivial is
-# defined, e.g.
-# try:
-# from foo import Bar
-# except ImportError:
-# class Bar(object):
-# """Hi everyone"""
-# and thus causes subsequent imports to be
-# diagnosed as out-of-order.
-# - no-name-in-module: Error gives a lot of false positives for names which
-# are defined dynamically. Also, any truly missing names
-# will be detected by our 100% code coverage.
-# - locally-disabled: Allow us to make exceptions in edge cases, notably where
-# pylint doesn't recognize inherited properties and methods
-# and gives unused-argument errors.
-disable =
- import-star-module-level,
- old-octal-literal,
- oct-method,
- print-statement,
- unpacking-in-except,
- parameter-unpacking,
- backtick,
- old-raise-syntax,
- old-ne-operator,
- long-suffix,
- dict-view-method,
- dict-iter-method,
- metaclass-assignment,
- next-method-called,
- raising-string,
- indexing-exception,
- raw_input-builtin,
- long-builtin,
- file-builtin,
- execfile-builtin,
- coerce-builtin,
- cmp-builtin,
- buffer-builtin,
- basestring-builtin,
- apply-builtin,
- filter-builtin-not-iterating,
- using-cmp-argument,
- useless-suppression,
- range-builtin-not-iterating,
- suppressed-message,
- no-absolute-import,
- old-division,
- cmp-method,
- reload-builtin,
- zip-builtin-not-iterating,
- intern-builtin,
- unichr-builtin,
- reduce-builtin,
- standarderror-builtin,
- unicode-builtin,
- xrange-builtin,
- coerce-method,
- delslice-method,
- getslice-method,
- setslice-method,
- input-builtin,
- round-builtin,
- hex-method,
- nonzero-method,
- map-builtin-not-iterating,
- maybe-no-member,
- no-member,
- protected-access,
- similarities,
- star-args,
- redefined-variable-type,
- import-error,
- wrong-import-position,
- no-name-in-module,
- locally-disabled,
- locally-enabled,
- missing-docstring,
- redefined-outer-name,
- no-self-use,
- too-many-locals,
- too-many-public-methods,
- abstract-method,
- method-hidden,
- unused-argument
-
-
-[REPORTS]
-# Tells whether to display a full report or only the messages
-# RATIONALE: noisy
-reports=no
-
-[BASIC]
-# Good variable names which should always be accepted, separated by a comma
-# DEFAULT: good-names=i,j,k,ex,Run,_
-# RATIONALE: 'fh' is a well-known file handle variable name.
-good-names = i, j, k, ex, Run, _,
- fh
-
-# Regular expression matching correct method names
-# DEFAULT: method-rgx=[a-z_][a-z0-9_]{2,30}$
-# RATIONALE: Some methods have longer names to be precise against wordy RFCs.
-method-rgx=[a-z_][a-z0-9_]{2,80}$
-
-# Regular expression matching correct function names
-# DEFAULT function-rgx=[a-z_][a-z0-9_]{2,30}$
-# RATIONALE: Some test methods have long descriptive names.
-function-rgx=[a-z_][a-z0-9_]{2,80}$
-
-[TYPECHECK]
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis. It
-# supports qualified module names, as well as Unix pattern matching.
-# DEFAULT: ignored-modules=
-# RATIONALE: six aliases stuff for compatibility.
-# google.protobuf fixes up namespace package "late".
-ignored-modules = six, google.protobuf
-
-
-[DESIGN]
-# Minimum number of public methods for a class (see R0903).
-# DEFAULT: min-public-methods=2
-# RATIONALE: context mgrs may have *no* public methods
-min-public-methods=0
-
-# Maximum number of arguments for function / method
-# DEFAULT: max-args=5
-# RATIONALE: Many credentials classes take a lot of parameters.
-max-args = 10
-
-# Maximum number of attributes for a class (see R0902).
-# DEFAULT: max-attributes=7
-# RATIONALE: Many credentials need to track lots of properties.
-max-attributes=15
diff --git a/httplib2_transport/scripts/.gitignore b/httplib2_transport/scripts/.gitignore
new file mode 100644
index 0000000..3596d32
--- /dev/null
+++ b/httplib2_transport/scripts/.gitignore
@@ -0,0 +1,3 @@
+# Generated files
+pylintrc
+pylintrc.test
diff --git a/httplib2_transport/scripts/run_pylint.py b/httplib2_transport/scripts/run_pylint.py
new file mode 100644
index 0000000..643957a
--- /dev/null
+++ b/httplib2_transport/scripts/run_pylint.py
@@ -0,0 +1,256 @@
+# 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/httplib2_transport/tox.ini b/httplib2_transport/tox.ini
index 4d54203..5d7457e 100644
--- a/httplib2_transport/tox.ini
+++ b/httplib2_transport/tox.ini
@@ -31,8 +31,7 @@
--import-order-style=google \
--application-import-names="google_auth_httplib2,tests" \
google_auth_httplib2.py tests
- pylint --rcfile pylintrc google_auth_httplib2.py
- pylint --rcfile pylintrc.tests tests
+ python {toxinidir}/scripts/run_pylint.py
deps =
flake8
flake8-import-order
diff --git a/pylintrc b/pylintrc
deleted file mode 100644
index 1f01cfa..0000000
--- a/pylintrc
+++ /dev/null
@@ -1,165 +0,0 @@
-[MASTER]
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=CVS,.git,.cache,.tox,.nox
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-# DEFAULT: load-plugins=
-# RATIONALE: We want to make sure our docstrings match the objects
-# they document.
-load-plugins=pylint.extensions.check_docs
-
-[MESSAGES CONTROL]
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once).You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use"--disable=all --enable=classes
-# --disable=W"
-#
-# RATIONALE:
-# - maybe-no-member: bi-modal functions confuse pylint type inference.
-# - no-member: indirections in protobuf-generated code
-# - protected-access: helpers use '_foo' of classes from generated code.
-# - similarities: 'Bucket' and 'Blob' define 'metageneration' and 'owner' with
-# identical implementation but different docstrings.
-# - star-args: standard Python idioms for varargs:
-# ancestor = Query().filter(*order_props)
-# - redefined-variable-type: This error is overzealous and complains at e.g.
-# if some_prop:
-# return int(value)
-# else:
-# return float(value)
-# - import-error: imports are checked via tests.
-# - wrong-import-position: This error is overzealous. It assumes imports are
-# completed whenever something non-trivial is
-# defined, e.g.
-# try:
-# from foo import Bar
-# except ImportError:
-# class Bar(object):
-# """Hi everyone"""
-# and thus causes subsequent imports to be
-# diagnosed as out-of-order.
-# - no-name-in-module: Error gives a lot of false positives for names which
-# are defined dynamically. Also, any truly missing names
-# will be detected by our 100% code coverage.
-# - locally-disabled: Allow us to make exceptions in edge cases, notably where
-# pylint doesn't recognize inherited properties and methods
-# and gives unused-argument errors.
-# TEMPORARILY DISABLE AND SHOULD BE REMOVED:
-# - fixme: disabled until 1.0
-#
-disable =
- import-star-module-level,
- old-octal-literal,
- oct-method,
- print-statement,
- unpacking-in-except,
- parameter-unpacking,
- backtick,
- old-raise-syntax,
- old-ne-operator,
- long-suffix,
- dict-view-method,
- dict-iter-method,
- metaclass-assignment,
- next-method-called,
- raising-string,
- indexing-exception,
- raw_input-builtin,
- long-builtin,
- file-builtin,
- execfile-builtin,
- coerce-builtin,
- cmp-builtin,
- buffer-builtin,
- basestring-builtin,
- apply-builtin,
- filter-builtin-not-iterating,
- using-cmp-argument,
- useless-suppression,
- range-builtin-not-iterating,
- suppressed-message,
- no-absolute-import,
- old-division,
- cmp-method,
- reload-builtin,
- zip-builtin-not-iterating,
- intern-builtin,
- unichr-builtin,
- reduce-builtin,
- standarderror-builtin,
- unicode-builtin,
- xrange-builtin,
- coerce-method,
- delslice-method,
- getslice-method,
- setslice-method,
- input-builtin,
- round-builtin,
- hex-method,
- nonzero-method,
- map-builtin-not-iterating,
- maybe-no-member,
- no-member,
- protected-access,
- similarities,
- star-args,
- redefined-variable-type,
- import-error,
- wrong-import-position,
- no-name-in-module,
- locally-disabled,
- locally-enabled,
- fixme
-
-
-[REPORTS]
-# Tells whether to display a full report or only the messages
-# RATIONALE: noisy
-reports=no
-
-[BASIC]
-# Regular expression matching correct method names
-# DEFAULT: method-rgx=[a-z_][a-z0-9_]{2,30}$
-# RATIONALE: Some methods have longer names to be more descriptive or precise,
-# especially those that implemented wordy RFCs.
-method-rgx=[a-z_][a-z0-9_]{2,40}$
-
-# Regular expression matching correct function names
-# DEFAULT function-rgx=[a-z_][a-z0-9_]{2,30}$
-# RATIONALE: Some methods have longer names to be more descriptive or precise,
-# especially those that implemented wordy RFCs.
-function-rgx=[a-z_][a-z0-9_]{2,40}$
-
-[TYPECHECK]
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis. It
-# supports qualified module names, as well as Unix pattern matching.
-# DEFAULT: ignored-modules=
-# RATIONALE: six aliases stuff for compatibility.
-# google.protobuf fixes up namespace package "late".
-ignored-modules = six, google.protobuf
-
-
-[DESIGN]
-# Minimum number of public methods for a class (see R0903).
-# DEFAULT: min-public-methods=2
-# RATIONALE: context mgrs may have *no* public methods
-min-public-methods=0
-
-# Maximum number of arguments for function / method
-# DEFAULT: max-args=5
-# RATIONALE: Many credentials classes take a lot of parameters.
-max-args = 10
-
-# Maximum number of attributes for a class (see R0902).
-# DEFAULT: max-attributes=7
-# RATIONALE: Many credentials need to track lots of properties.
-max-attributes=15
diff --git a/pylintrc.tests b/pylintrc.tests
deleted file mode 100644
index 09772c5..0000000
--- a/pylintrc.tests
+++ /dev/null
@@ -1,166 +0,0 @@
-[MASTER]
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=CVS,.git,.cache,.tox,.nox
-
-[MESSAGES CONTROL]
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once).You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use"--disable=all --enable=classes
-# --disable=W"
-#
-# RATIONALE:
-# - maybe-no-member: bi-modal functions confuse pylint type inference.
-# - no-member: indirections in protobuf-generated code
-# - protected-access: helpers use '_foo' of classes from generated code.
-# - similarities: 'Bucket' and 'Blob' define 'metageneration' and 'owner' with
-# identical implementation but different docstrings.
-# - star-args: standard Python idioms for varargs:
-# ancestor = Query().filter(*order_props)
-# - redefined-variable-type: This error is overzealous and complains at e.g.
-# if some_prop:
-# return int(value)
-# else:
-# return float(value)
-# - import-error: imports are checked via tests.
-# - wrong-import-position: This error is overzealous. It assumes imports are
-# completed whenever something non-trivial is
-# defined, e.g.
-# try:
-# from foo import Bar
-# except ImportError:
-# class Bar(object):
-# """Hi everyone"""
-# and thus causes subsequent imports to be
-# diagnosed as out-of-order.
-# - no-name-in-module: Error gives a lot of false positives for names which
-# are defined dynamically. Also, any truly missing names
-# will be detected by our 100% code coverage.
-# - locally-disabled: Allow us to make exceptions in edge cases, notably where
-# pylint doesn't recognize inherited properties and methods
-# and gives unused-argument errors.
-disable =
- import-star-module-level,
- old-octal-literal,
- oct-method,
- print-statement,
- unpacking-in-except,
- parameter-unpacking,
- backtick,
- old-raise-syntax,
- old-ne-operator,
- long-suffix,
- dict-view-method,
- dict-iter-method,
- metaclass-assignment,
- next-method-called,
- raising-string,
- indexing-exception,
- raw_input-builtin,
- long-builtin,
- file-builtin,
- execfile-builtin,
- coerce-builtin,
- cmp-builtin,
- buffer-builtin,
- basestring-builtin,
- apply-builtin,
- filter-builtin-not-iterating,
- using-cmp-argument,
- useless-suppression,
- range-builtin-not-iterating,
- suppressed-message,
- no-absolute-import,
- old-division,
- cmp-method,
- reload-builtin,
- zip-builtin-not-iterating,
- intern-builtin,
- unichr-builtin,
- reduce-builtin,
- standarderror-builtin,
- unicode-builtin,
- xrange-builtin,
- coerce-method,
- delslice-method,
- getslice-method,
- setslice-method,
- input-builtin,
- round-builtin,
- hex-method,
- nonzero-method,
- map-builtin-not-iterating,
- maybe-no-member,
- no-member,
- protected-access,
- similarities,
- star-args,
- redefined-variable-type,
- import-error,
- wrong-import-position,
- no-name-in-module,
- locally-disabled,
- locally-enabled,
- missing-docstring,
- redefined-outer-name,
- no-self-use,
- too-many-locals,
- too-many-public-methods,
- abstract-method,
- method-hidden,
- unused-argument
-
-
-[REPORTS]
-# Tells whether to display a full report or only the messages
-# RATIONALE: noisy
-reports=no
-
-[BASIC]
-# Good variable names which should always be accepted, separated by a comma
-# DEFAULT: good-names=i,j,k,ex,Run,_
-# RATIONALE: 'fh' is a well-known file handle variable name.
-good-names = i, j, k, ex, Run, _,
- fh
-
-# Regular expression matching correct method names
-# DEFAULT: method-rgx=[a-z_][a-z0-9_]{2,30}$
-# RATIONALE: Some methods have longer names to be precise against wordy RFCs.
-method-rgx=[a-z_][a-z0-9_]{2,80}$
-
-# Regular expression matching correct function names
-# DEFAULT function-rgx=[a-z_][a-z0-9_]{2,30}$
-# RATIONALE: Some test methods have long descriptive names.
-function-rgx=[a-z_][a-z0-9_]{2,80}$
-
-[TYPECHECK]
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis. It
-# supports qualified module names, as well as Unix pattern matching.
-# DEFAULT: ignored-modules=
-# RATIONALE: six aliases stuff for compatibility.
-# google.protobuf fixes up namespace package "late".
-ignored-modules = six, google.protobuf
-
-
-[DESIGN]
-# Minimum number of public methods for a class (see R0903).
-# DEFAULT: min-public-methods=2
-# RATIONALE: context mgrs may have *no* public methods
-min-public-methods=0
-
-# Maximum number of arguments for function / method
-# DEFAULT: max-args=5
-# RATIONALE: Many credentials classes take a lot of parameters.
-max-args = 10
-
-# Maximum number of attributes for a class (see R0902).
-# DEFAULT: max-attributes=7
-# RATIONALE: Many credentials need to track lots of properties.
-max-attributes=15
diff --git a/scripts/.gitignore b/scripts/.gitignore
new file mode 100644
index 0000000..3596d32
--- /dev/null
+++ b/scripts/.gitignore
@@ -0,0 +1,3 @@
+# Generated files
+pylintrc
+pylintrc.test
diff --git a/scripts/run_pylint.py b/scripts/run_pylint.py
new file mode 100644
index 0000000..ed994ff
--- /dev/null
+++ b/scripts/run_pylint.py
@@ -0,0 +1,251 @@
+# 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.
+"""
+
+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',
+ 'no-member',
+ 'protected-access',
+ 'redefined-variable-type',
+ 'similarities',
+ ],
+ },
+}
+_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-self-use',
+ 'redefined-outer-name',
+ '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(*dirnames, **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:
+ dirnames (tuple): Directories to run Pylint in.
+ 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(dirnames)
+ 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', rc_filename=PRODUCTION_RC,
+ description='Library')
+ lint_fileset('tests', 'system_tests', rc_filename=TEST_RC,
+ description='Test')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/oauth2/test_credentials.py b/tests/oauth2/test_credentials.py
index 173bb00..5c76cb2 100644
--- a/tests/oauth2/test_credentials.py
+++ b/tests/oauth2/test_credentials.py
@@ -26,9 +26,10 @@
REFRESH_TOKEN = 'refresh_token'
CLIENT_ID = 'client_id'
CLIENT_SECRET = 'client_secret'
+ credentials = None
@pytest.fixture(autouse=True)
- def credentials(self):
+ def credentials_fixture(self):
self.credentials = credentials.Credentials(
token=None, refresh_token=self.REFRESH_TOKEN,
token_uri=self.TOKEN_URI, client_id=self.CLIENT_ID,
diff --git a/tox.ini b/tox.ini
index 1ef803c..f41ffd1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -73,8 +73,7 @@
--import-order-style=google \
--application-import-names="google,tests,system_tests" \
google tests
- pylint --rcfile pylintrc google
- pylint --rcfile pylintrc.tests tests system_tests
+ python {toxinidir}/scripts/run_pylint.py
deps =
flake8
flake8-import-order