summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Matthews <glmatthe@cisco.com>2016-07-10 06:18:24 -0400
committerClaudiu Popa <pcmanticore@gmail.com>2016-07-10 11:18:24 +0100
commit07cd560c1358cfb784a8b78d85402126e08c291f (patch)
tree382a29585051368804d3f1da95cca90f17a767e2
parent7a9cb0cdcc8be45039cb46721baaae8f735b2771 (diff)
downloadpylint-git-07cd560c1358cfb784a8b78d85402126e08c291f.tar.gz
Automatic generation of extensions.rst (#988)
-rw-r--r--.gitignore1
-rw-r--r--CONTRIBUTORS.txt3
-rw-r--r--doc/Makefile20
-rw-r--r--doc/conf.py2
-rw-r--r--doc/extensions.rst215
-rwxr-xr-xdoc/exts/pylint_extensions.py113
-rwxr-xr-x[-rw-r--r--]doc/exts/pylint_features.py29
-rw-r--r--doc/generated_features.rst5
-rw-r--r--doc/index.rst2
-rw-r--r--doc/whatsnew/1.6.rst2
-rw-r--r--pylint/extensions/bad_builtin.rst9
-rw-r--r--pylint/extensions/check_elif.py2
-rw-r--r--pylint/extensions/docparams.py14
-rw-r--r--pylint/extensions/docparams.rst152
-rw-r--r--pylint/extensions/mccabe.rst37
-rw-r--r--pylint/extensions/redefined_variable_type.py1
-rw-r--r--pylint/test/unittest_lint.py17
-rw-r--r--pylint/utils.py155
18 files changed, 466 insertions, 313 deletions
diff --git a/.gitignore b/.gitignore
index cb8281678..9d9ec161c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
/pylint.egg-info/
.tox
*.sw[a-z]
+doc/extensions.rst
doc/features.rst
pyve
build-stamp
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 89cebdd17..918140ab0 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -99,4 +99,5 @@ Order doesn't matter (not that much, at least ;)
* Yannick Brehon: contributor.
-* Glenn Matthews: bug reports, occasional bug fixes
+* Glenn Matthews: bug reports, autogenerated documentation for optional
+ extensions, occasional bug fixes
diff --git a/doc/Makefile b/doc/Makefile
index 2cd7f7e5e..c33d15447 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -37,8 +37,9 @@ help:
clean:
-rm -rf $(BUILDDIR)/*
-rm -f features.rst
+ -rm -f extensions.rst
-html: features
+html: features.rst extensions.rst
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
@@ -131,15 +132,18 @@ doctest:
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
+extensions.rst: exts/pylint_extensions.py
+extensions.rst: ../pylint/utils.py
+extensions.rst: $(shell find ../pylint/extensions -type f -regex '.*\.py')
+extensions.rst: $(shell find ../pylint/extensions -type f -regex '.*\.rst')
+ rm -f extensions.rst
+ PYTHONPATH=$(PYTHONPATH) ./exts/pylint_extensions.py
-features:
+features.rst: exts/pylint_features.py
+features.rst: ../pylint/utils.py
+features.rst: $(shell find ../pylint/checkers -type f -regex '.*\.py')
rm -f features.rst
- echo "Pylint features" > features.rst
- echo "===============" >> features.rst
- echo "" >> features.rst
- echo ".. generated by pylint --full-documentation" >> features.rst
- echo "" >> features.rst
- PYTHONPATH=$(PYTHONPATH) pylint --full-documentation >> features.rst
+ PYTHONPATH=$(PYTHONPATH) ./exts/pylint_features.py
gen-examples:
chmod u+w ../examples/pylintrc
diff --git a/doc/conf.py b/doc/conf.py
index 38bb95ce2..c0b374a8d 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -25,7 +25,7 @@ sys.path.append(os.path.abspath('exts'))
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['pylint_features']
+extensions = ['pylint_features', 'pylint_extensions']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
diff --git a/doc/extensions.rst b/doc/extensions.rst
deleted file mode 100644
index 8fe4241e9..000000000
--- a/doc/extensions.rst
+++ /dev/null
@@ -1,215 +0,0 @@
-
-Optional Pylint checkers in the extensions module
-=================================================
-
-Parameter documentation checker
--------------------------------
-
-If you document the parameters of your functions, methods and constructors and
-their types systematically in your code this optional component might
-be useful for you. Sphinx style, Google style, and Numpy style are supported.
-(For some examples, see https://pypi.python.org/pypi/sphinxcontrib-napoleon .)
-
-You can activate this checker by adding the line::
-
- load-plugins=pylint.extensions.docparams
-
-to the ``MASTER`` section of your ``.pylintrc``.
-
-This checker verifies that all function, method, and constructor parameters are
-mentioned in the
-
-* Sphinx ``param`` and ``type`` parts of the docstring::
-
- def function_foo(x, y, z):
- '''function foo ...
-
- :param x: bla x
- :type x: int
-
- :param y: bla y
- :type y: float
-
- :param int z: bla z
-
- :return: sum
- :rtype: float
- '''
- return x + y + z
-
-* or the Google style ``Args:`` part of the docstring::
-
- def function_foo(x, y, z):
- '''function foo ...
-
- Args:
- x (int): bla x
- y (float): bla y
-
- z (int): bla z
-
- Returns:
- float: sum
- '''
- return x + y + z
-
-* or the Numpy style ``Parameters`` part of the docstring::
-
- def function_foo(x, y, z):
- '''function foo ...
-
- Parameters
- ----------
- x: int
- bla x
- y: float
- bla y
-
- z: int
- bla z
-
- Returns
- -------
- float
- sum
- '''
- return x + y + z
-
-
-You'll be notified of **missing parameter documentation** but also of
-**naming inconsistencies** between the signature and the documentation which
-often arise when parameters are renamed automatically in the code, but not in
-the documentation.
-
-By convention, constructor parameters are documented in the class docstring.
-(``__init__`` and ``__new__`` methods are considered constructors.)::
-
- class ClassFoo(object):
- '''Sphinx style docstring foo
-
- :param float x: bla x
-
- :param y: bla y
- :type y: int
- '''
- def __init__(self, x, y):
- pass
-
- class ClassFoo(object):
- '''Google style docstring foo
-
- Args:
- x (float): bla x
- y (int): bla y
- '''
- def __init__(self, x, y):
- pass
-
-In some cases, having to document all parameters is a nuisance, for instance if
-many of your functions or methods just follow a **common interface**. To remove
-this burden, the checker accepts missing parameter documentation if one of the
-following phrases is found in the docstring:
-
-* For the other parameters, see
-* For the parameters, see
-
-(with arbitrary whitespace between the words). Please add a link to the
-docstring defining the interface, e.g. a superclass method, after "see"::
-
- def callback(x, y, z):
- '''Sphinx style docstring for callback ...
-
- :param x: bla x
- :type x: int
-
- For the other parameters, see
- :class:`MyFrameworkUsingAndDefiningCallback`
- '''
- return x + y + z
-
- def callback(x, y, z):
- '''Google style docstring for callback ...
-
- Args:
- x (int): bla x
-
- For the other parameters, see
- :class:`MyFrameworkUsingAndDefiningCallback`
- '''
- return x + y + z
-
-Naming inconsistencies in existing parameter and their type documentations are
-still detected.
-
-By default, omitting the parameter documentation of a function altogether is
-tolerated without any warnings. If you want to switch off this behavior
-(forcing functions to document their parameters), set the option
-``accept-no-param-doc`` to ``no`` in your ``.pylintrc``.
-
-By default, omitting the exception raising documentation of a function
-altogether is tolerated without any warnings. If you want to switch off this
-behavior (forcing functions that raise exceptions to document them), set the
-option ``accept-no-raise-doc`` to ``no`` in your ``.pylintrc``.
-
-By default, omitting the return documentation of a function altogether is
-tolerated without any warnings. If you want to switch off this behavior
-(forcing functions to document their returns), set the option
-``accept-no-return-doc`` to ``no`` in your ``.pylintrc``.
-
-
-Prohibit builtin checker
-------------------------
-
-This used to be the ``bad-builtin`` core checker, but it was moved to
-an extension instead. It can be used for finding prohibited used builtins,
-such as ``map`` or ``filter``, for which other alternatives exists.
-
-If you want to control for what builtins the checker should warn about,
-you can use the ``bad-functions`` option::
-
- $ pylint a.py --load-plugins=pylint.extensions.bad_builtin --bad-functions=apply,reduce
- ...
-
-
-.. _mccabe_extension:
-
-Complexity checker
-------------------
-
-You can now use this plugin for finding complexity issues in your code base.
-
-Activate it through ``pylint --load-plugins=pylint.extensions.mccabe``. It introduces
-a new warning, ``too-complex``, which is emitted when a code block has a complexity
-higher than a preestablished value, which can be controlled through the
-``max-complexity`` option, such as in this example::
-
- $ cat a.py
- def f10():
- """McCabe rating: 11"""
- myint = 2
- if myint == 5:
- return myint
- elif myint == 6:
- return myint
- elif myint == 7:
- return myint
- elif myint == 8:
- return myint
- elif myint == 9:
- return myint
- elif myint == 10:
- if myint == 8:
- while True:
- return True
- elif myint == 8:
- with myint:
- return 8
- else:
- if myint == 2:
- return myint
- return myint
- return myint
- $ pylint a.py --load-plugins=pylint.extensions.mccabe
- R:1: 'f10' is too complex. The McCabe rating is 11 (too-complex)
- $ pylint a.py --load-plugins=pylint.extensions.mccabe --max-complexity=50
- $
diff --git a/doc/exts/pylint_extensions.py b/doc/exts/pylint_extensions.py
new file mode 100755
index 000000000..e1d4f67ff
--- /dev/null
+++ b/doc/exts/pylint_extensions.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Script used to generate the extensions file before building the actual documentation."""
+
+import os
+import re
+import sys
+
+import pkg_resources
+import six
+import sphinx
+
+from pylint.lint import PyLinter
+
+
+# Some modules have been renamed and deprecated under their old names.
+# Skip documenting these modules since:
+# 1) They are deprecated, why document them moving forward?
+# 2) We can't load the deprecated module and the newly renamed module at the
+# same time without getting naming conflicts
+DEPRECATED_MODULES = [
+ 'check_docs', # ==> docparams
+]
+
+def builder_inited(app):
+ """Output full documentation in ReST format for all extension modules"""
+ # PACKAGE/docs/exts/pylint_extensions.py --> PACKAGE/
+ base_path = os.path.dirname(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+ # PACKAGE/ --> PACKAGE/pylint/extensions
+ ext_path = os.path.join(base_path, 'pylint', 'extensions')
+ modules = []
+ doc_files = {}
+ for filename in os.listdir(ext_path):
+ name, ext = os.path.splitext(filename)
+ if name[0] == '_' or name in DEPRECATED_MODULES:
+ continue
+ if ext == '.py':
+ modules.append('pylint.extensions.%s' % name)
+ elif ext == '.rst':
+ doc_files['pylint.extensions.' + name] = os.path.join(ext_path,
+ filename)
+ if not modules:
+ sys.exit("No Pylint extensions found?")
+
+ linter = PyLinter()
+ linter.load_plugin_modules(modules)
+
+ extensions_doc = os.path.join(base_path, 'doc', 'extensions.rst')
+ with open(extensions_doc, 'w') as stream:
+ stream.write("Optional Pylint checkers in the extensions module\n")
+ stream.write("=================================================\n\n")
+ stream.write("Pylint provides the following optional plugins:\n\n")
+ for module in modules:
+ stream.write("- :ref:`{0}`\n".format(module))
+ stream.write("\n")
+ stream.write("You can activate any or all of these extensions "
+ "by adding a ``load-plugins`` line to the ``MASTER`` "
+ "section of your ``.pylintrc``, for example::\n")
+ stream.write("\n load-plugins=pylint.extensions.docparams,"
+ "pylint.extensions.docstyle\n\n")
+ by_module = get_plugins_info(linter, doc_files)
+ for module, info in sorted(six.iteritems(by_module)):
+ linter._print_checker_doc(info['name'], info, stream=stream)
+
+
+def get_plugins_info(linter, doc_files):
+ by_module = {}
+
+ for checker in linter.get_checkers():
+ if checker.name == 'master':
+ continue
+ module = checker.__module__
+ # Plugins only - skip over core checkers
+ if re.match("pylint.checkers", module):
+ continue
+
+ # Find any .rst documentation associated with this plugin
+ doc = ""
+ doc_file = doc_files.get(module)
+ if doc_file:
+ with open(doc_file, 'r') as f:
+ doc = f.read()
+
+ try:
+ by_module[module]['options'] += checker.options_and_values()
+ by_module[module]['msgs'].update(checker.msgs)
+ by_module[module]['reports'] += checker.reports
+ by_module[module]['doc'] += doc
+ by_module[module]['name'] += checker.name
+ by_module[module]['module'] += module
+ except KeyError:
+ by_module[module] = {
+ 'options': list(checker.options_and_values()),
+ 'msgs': dict(checker.msgs),
+ 'reports': list(checker.reports),
+ 'doc': doc,
+ 'name': checker.name,
+ 'module': module,
+ }
+
+ return by_module
+
+
+def setup(app):
+ app.connect('builder-inited', builder_inited)
+ return {'version': sphinx.__display_version__}
+
+
+if __name__ == "__main__":
+ builder_inited(None)
diff --git a/doc/exts/pylint_features.py b/doc/exts/pylint_features.py
index 30207f5bb..b81d1e4b6 100644..100755
--- a/doc/exts/pylint_features.py
+++ b/doc/exts/pylint_features.py
@@ -1,31 +1,34 @@
+#!/usr/bin/env python
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
"""Script used to generate the features file before building the actual documentation."""
import os
-import subprocess
import sys
import sphinx
+from pylint.lint import PyLinter
def builder_inited(app):
- popen = subprocess.Popen([sys.executable, "-m", "pylint", "--full-documentation"],
- stdout=subprocess.PIPE)
- output, _ = popen.communicate()
+ # PACKAGE/docs/exts/pylint_extensions.py --> PACKAGE/
+ base_path = os.path.dirname(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+ linter = PyLinter()
+ linter.load_default_plugins()
- if not output:
- print("Pylint might not be available.")
- return
-
- features = os.path.join(os.path.dirname('.'), 'features.rst')
- with open(features, 'wb') as stream:
- stream.write(b"Pylint features\n")
- stream.write(b"===============\n\n")
- stream.write(output)
+ features = os.path.join(base_path, 'doc', 'features.rst')
+ with open(features, 'w') as stream:
+ stream.write("Pylint features\n")
+ stream.write("===============\n\n")
+ stream.write(".. generated by pylint --full-documentation\n\n")
+ linter.print_full_documentation(stream)
def setup(app):
app.connect('builder-inited', builder_inited)
return {'version': sphinx.__display_version__}
+
+if __name__ == "__main__":
+ builder_inited(None)
diff --git a/doc/generated_features.rst b/doc/generated_features.rst
deleted file mode 100644
index 8ab6b9c88..000000000
--- a/doc/generated_features.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-.. toctree::
- :titlesonly:
- :maxdepth: 1
-
- features \ No newline at end of file
diff --git a/doc/index.rst b/doc/index.rst
index 0d677c47b..6cbc3d1c2 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -15,7 +15,7 @@ Pylint User Manual
run
output
message-control
- generated_features
+ features
extensions
options
ide-integration
diff --git a/doc/whatsnew/1.6.rst b/doc/whatsnew/1.6.rst
index fff378b8d..3c543d1ea 100644
--- a/doc/whatsnew/1.6.rst
+++ b/doc/whatsnew/1.6.rst
@@ -57,7 +57,7 @@ New checkers
$ pylint module_or_project --load-plugins=pylint.extensions.mccabe
- See more at :ref:`mccabe_extension`
+ See more at :ref:`pylint.extensions.mccabe`
New features
diff --git a/pylint/extensions/bad_builtin.rst b/pylint/extensions/bad_builtin.rst
new file mode 100644
index 000000000..e87e6843c
--- /dev/null
+++ b/pylint/extensions/bad_builtin.rst
@@ -0,0 +1,9 @@
+This used to be the ``bad-builtin`` core checker, but it was moved to
+an extension instead. It can be used for finding prohibited used builtins,
+such as ``map`` or ``filter``, for which other alternatives exists.
+
+If you want to control for what builtins the checker should warn about,
+you can use the ``bad-functions`` option::
+
+ $ pylint a.py --load-plugins=pylint.extensions.bad_builtin --bad-functions=apply,reduce
+ ...
diff --git a/pylint/extensions/check_elif.py b/pylint/extensions/check_elif.py
index 341710adc..79083aae7 100644
--- a/pylint/extensions/check_elif.py
+++ b/pylint/extensions/check_elif.py
@@ -12,7 +12,7 @@ class ElseifUsedChecker(BaseTokenChecker):
"""
__implements__ = (ITokenChecker, IAstroidChecker)
- name = 'elseifused'
+ name = 'else_if_used'
msgs = {'R5501': ('Consider using "elif" instead of "else if"',
'else-if-used',
'Used when an else statement is immediately followed by '
diff --git a/pylint/extensions/docparams.py b/pylint/extensions/docparams.py
index 0c545bd44..f3da7fe1e 100644
--- a/pylint/extensions/docparams.py
+++ b/pylint/extensions/docparams.py
@@ -29,7 +29,7 @@ class DocstringParameterChecker(BaseChecker):
Activate this checker by adding the line::
- load-plugins=pylint.extensions.check_docs
+ load-plugins=pylint.extensions.docparams
to the ``MASTER`` section of your ``.pylintrc``.
@@ -38,7 +38,7 @@ class DocstringParameterChecker(BaseChecker):
"""
__implements__ = IAstroidChecker
- name = 'docstring_params'
+ name = 'parameter_documentation'
msgs = {
'W9003': ('"%s" missing or differing in parameter documentation',
'missing-param-doc',
@@ -74,19 +74,19 @@ class DocstringParameterChecker(BaseChecker):
}),
('accept-no-raise-doc',
{'default': True, 'type' : 'yn', 'metavar' : '<y or n>',
- 'help': 'Whether to accept totally missing raises'
- 'documentation in the docstring of a function that'
+ 'help': 'Whether to accept totally missing raises '
+ 'documentation in the docstring of a function that '
'raises an exception.'
}),
('accept-no-return-doc',
{'default': True, 'type' : 'yn', 'metavar' : '<y or n>',
- 'help': 'Whether to accept totally missing return'
- 'documentation in the docstring of a function that'
+ 'help': 'Whether to accept totally missing return '
+ 'documentation in the docstring of a function that '
'returns a statement.'
}),
('accept-no-yields-doc',
{'default': True, 'type' : 'yn', 'metavar': '<y or n>',
- 'help': 'Whether to accept totally missing yields'
+ 'help': 'Whether to accept totally missing yields '
'documentation in the docstring of a generator.'
}),
)
diff --git a/pylint/extensions/docparams.rst b/pylint/extensions/docparams.rst
new file mode 100644
index 000000000..e46c4dcd7
--- /dev/null
+++ b/pylint/extensions/docparams.rst
@@ -0,0 +1,152 @@
+If you document the parameters of your functions, methods and constructors and
+their types systematically in your code this optional component might
+be useful for you. Sphinx style, Google style, and Numpy style are supported.
+(For some examples, see https://pypi.python.org/pypi/sphinxcontrib-napoleon .)
+
+You can activate this checker by adding the line::
+
+ load-plugins=pylint.extensions.docparams
+
+to the ``MASTER`` section of your ``.pylintrc``.
+
+This checker verifies that all function, method, and constructor docstrings
+include documentation of the
+
+* parameters and their types
+* return value and its type
+* exceptions raised
+
+and can handle docstrings in
+
+* Sphinx style (``param``, ``type``, ``return``, ``rtype``,
+ ``raise`` / ``except``)::
+
+ def function_foo(x, y, z):
+ '''function foo ...
+
+ :param x: bla x
+ :type x: int
+
+ :param y: bla y
+ :type y: float
+
+ :param int z: bla z
+
+ :return: sum
+ :rtype: float
+
+ :raises OSError: bla
+ '''
+ return x + y + z
+
+* or the Google style (``Args:``, ``Returns:``, ``Raises:``)::
+
+ def function_foo(x, y, z):
+ '''function foo ...
+
+ Args:
+ x (int): bla x
+ y (float): bla y
+
+ z (int): bla z
+
+ Returns:
+ float: sum
+
+ Raises:
+ OSError: bla
+ '''
+ return x + y + z
+
+* or the Numpy style (``Parameters``, ``Returns``, ``Raises``)::
+
+ def function_foo(x, y, z):
+ '''function foo ...
+
+ Parameters
+ ----------
+ x: int
+ bla x
+ y: float
+ bla y
+
+ z: int
+ bla z
+
+ Returns
+ -------
+ float
+ sum
+
+ Raises
+ ------
+ OSError
+ bla
+ '''
+ return x + y + z
+
+
+You'll be notified of **missing parameter documentation** but also of
+**naming inconsistencies** between the signature and the documentation which
+often arise when parameters are renamed automatically in the code, but not in
+the documentation.
+
+Constructor parameters can be documented in either the class docstring or
+the ``__init__`` docstring, but not both::
+
+ class ClassFoo(object):
+ '''Sphinx style docstring foo
+
+ :param float x: bla x
+
+ :param y: bla y
+ :type y: int
+ '''
+ def __init__(self, x, y):
+ pass
+
+ class ClassBar(object):
+ def __init__(self, x, y):
+ '''Google style docstring bar
+
+ Args:
+ x (float): bla x
+ y (int): bla y
+ '''
+ pass
+
+In some cases, having to document all parameters is a nuisance, for instance if
+many of your functions or methods just follow a **common interface**. To remove
+this burden, the checker accepts missing parameter documentation if one of the
+following phrases is found in the docstring:
+
+* For the other parameters, see
+* For the parameters, see
+
+(with arbitrary whitespace between the words). Please add a link to the
+docstring defining the interface, e.g. a superclass method, after "see"::
+
+ def callback(x, y, z):
+ '''Sphinx style docstring for callback ...
+
+ :param x: bla x
+ :type x: int
+
+ For the other parameters, see
+ :class:`MyFrameworkUsingAndDefiningCallback`
+ '''
+ return x + y + z
+
+ def callback(x, y, z):
+ '''Google style docstring for callback ...
+
+ Args:
+ x (int): bla x
+
+ For the other parameters, see
+ :class:`MyFrameworkUsingAndDefiningCallback`
+ '''
+ return x + y + z
+
+Naming inconsistencies in existing parameter and their type documentations are
+still detected.
diff --git a/pylint/extensions/mccabe.rst b/pylint/extensions/mccabe.rst
new file mode 100644
index 000000000..e63d5b20f
--- /dev/null
+++ b/pylint/extensions/mccabe.rst
@@ -0,0 +1,37 @@
+You can now use this plugin for finding complexity issues in your code base.
+
+Activate it through ``pylint --load-plugins=pylint.extensions.mccabe``. It introduces
+a new warning, ``too-complex``, which is emitted when a code block has a complexity
+higher than a preestablished value, which can be controlled through the
+``max-complexity`` option, such as in this example::
+
+ $ cat a.py
+ def f10():
+ """McCabe rating: 11"""
+ myint = 2
+ if myint == 5:
+ return myint
+ elif myint == 6:
+ return myint
+ elif myint == 7:
+ return myint
+ elif myint == 8:
+ return myint
+ elif myint == 9:
+ return myint
+ elif myint == 10:
+ if myint == 8:
+ while True:
+ return True
+ elif myint == 8:
+ with myint:
+ return 8
+ else:
+ if myint == 2:
+ return myint
+ return myint
+ return myint
+ $ pylint a.py --load-plugins=pylint.extensions.mccabe
+ R:1: 'f10' is too complex. The McCabe rating is 11 (too-complex)
+ $ pylint a.py --load-plugins=pylint.extensions.mccabe --max-complexity=50
+ $
diff --git a/pylint/extensions/redefined_variable_type.py b/pylint/extensions/redefined_variable_type.py
index 6775b1d4e..4904fd456 100644
--- a/pylint/extensions/redefined_variable_type.py
+++ b/pylint/extensions/redefined_variable_type.py
@@ -19,6 +19,7 @@ class MultipleTypesChecker(BaseChecker):
At a function, method, class or module scope
This rule could be improved:
+
- Currently, if an attribute is set to different types in 2 methods of a
same class, it won't be detected (see functional test)
- One could improve the support for inference on assignment with tuples,
diff --git a/pylint/test/unittest_lint.py b/pylint/test/unittest_lint.py
index 562f27279..72051e118 100644
--- a/pylint/test/unittest_lint.py
+++ b/pylint/test/unittest_lint.py
@@ -4,6 +4,7 @@
from contextlib import contextmanager
import sys
import os
+import re
import tempfile
from shutil import rmtree
from os import getcwd, chdir
@@ -463,6 +464,22 @@ class PyLinterTC(unittest.TestCase):
checker_names = [c.name for c in self.linter.prepare_checkers()]
self.assertIn('python3', checker_names)
+ def test_full_documentation(self):
+ out = six.StringIO()
+ self.linter.print_full_documentation(out)
+ output = out.getvalue()
+ # A few spot checks only
+ for re_str in [
+ # autogenerated text
+ "^Pylint global options and switches$",
+ "Verbatim name of the checker is ``python3``",
+ # messages
+ "^:old-octal-literal \(E1608\):",
+ # options
+ "^:dummy-variables-rgx:",
+ ]:
+ regexp = re.compile(re_str, re.MULTILINE)
+ self.assertRegexpMatches(output, regexp)
class ConfigTC(unittest.TestCase):
diff --git a/pylint/utils.py b/pylint/utils.py
index 68ea2c064..53d31f388 100644
--- a/pylint/utils.py
+++ b/pylint/utils.py
@@ -7,6 +7,7 @@ main pylint class
from __future__ import print_function
import collections
+from inspect import cleandoc
import os
from os.path import dirname, basename, splitext, exists, isdir, join, normpath
import re
@@ -416,13 +417,16 @@ class MessagesHandlerMixIn(object):
Message(msgid, symbol,
(abspath, path, module, obj, line or 1, col_offset or 0), msg, confidence))
- def print_full_documentation(self):
+ def print_full_documentation(self, stream=None):
"""output a full documentation in ReST format"""
- print("Pylint global options and switches")
- print("----------------------------------")
- print("")
- print("Pylint provides global options and switches.")
- print("")
+ if not stream:
+ stream = sys.stdout
+
+ print("Pylint global options and switches", file=stream)
+ print("----------------------------------", file=stream)
+ print("", file=stream)
+ print("Pylint provides global options and switches.", file=stream)
+ print("", file=stream)
by_checker = {}
for checker in self.get_checkers():
@@ -433,63 +437,94 @@ class MessagesHandlerMixIn(object):
title = 'General options'
else:
title = '%s options' % section.capitalize()
- print(title)
- print('~' * len(title))
- _rest_format_section(sys.stdout, None, options)
- print("")
+ print(title, file=stream)
+ print('~' * len(title), file=stream)
+ _rest_format_section(stream, None, options)
+ print("", file=stream)
else:
+ name = checker.name
try:
- by_checker[checker.name][0] += checker.options_and_values()
- by_checker[checker.name][1].update(checker.msgs)
- by_checker[checker.name][2] += checker.reports
+ by_checker[name]['options'] += checker.options_and_values()
+ by_checker[name]['msgs'].update(checker.msgs)
+ by_checker[name]['reports'] += checker.reports
except KeyError:
- by_checker[checker.name] = [list(checker.options_and_values()),
- dict(checker.msgs),
- list(checker.reports)]
-
- print("Pylint checkers' options and switches")
- print("-------------------------------------")
- print("")
- print("Pylint checkers can provide three set of features:")
- print("")
- print("* options that control their execution,")
- print("* messages that they can raise,")
- print("* reports that they can generate.")
- print("")
- print("Below is a list of all checkers and their features.")
- print("")
-
- for checker, (options, msgs, reports) in six.iteritems(by_checker):
- title = '%s checker' % (checker.replace("_", " ").title())
- print(title)
- print('~' * len(title))
- print("")
- print("Verbatim name of the checker is ``%s``." % checker)
- print("")
- if options:
- title = 'Options'
- print(title)
- print('^' * len(title))
- _rest_format_section(sys.stdout, None, options)
- print("")
- if msgs:
- title = 'Messages'
- print(title)
- print('~' * len(title))
- for msgid, msg in sorted(six.iteritems(msgs),
- key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1])):
- msg = build_message_def(checker, msgid, msg)
- print(msg.format_help(checkerref=False))
- print("")
- if reports:
- title = 'Reports'
- print(title)
- print('~' * len(title))
- for report in reports:
- print(':%s: %s' % report[:2])
- print("")
- print("")
-
+ by_checker[name] = {
+ 'options': list(checker.options_and_values()),
+ 'msgs': dict(checker.msgs),
+ 'reports': list(checker.reports),
+ }
+
+ print("Pylint checkers' options and switches", file=stream)
+ print("-------------------------------------", file=stream)
+ print("", file=stream)
+ print("Pylint checkers can provide three set of features:", file=stream)
+ print("", file=stream)
+ print("* options that control their execution,", file=stream)
+ print("* messages that they can raise,", file=stream)
+ print("* reports that they can generate.", file=stream)
+ print("", file=stream)
+ print("Below is a list of all checkers and their features.", file=stream)
+ print("", file=stream)
+
+ for checker, info in six.iteritems(by_checker):
+ self._print_checker_doc(checker, info, stream=stream)
+
+ @staticmethod
+ def _print_checker_doc(checker_name, info, stream=None):
+ """Helper method for print_full_documentation.
+
+ Also used by doc/exts/pylint_extensions.py.
+ """
+ if not stream:
+ stream = sys.stdout
+
+ doc = info.get('doc')
+ module = info.get('module')
+ msgs = info.get('msgs')
+ options = info.get('options')
+ reports = info.get('reports')
+
+ title = '%s checker' % (checker_name.replace("_", " ").title())
+
+ if module:
+ # Provide anchor to link against
+ print(".. _%s:\n" % module, file=stream)
+ print(title, file=stream)
+ print('~' * len(title), file=stream)
+ print("", file=stream)
+ if module:
+ print("This checker is provided by ``%s``." % module, file=stream)
+ print("Verbatim name of the checker is ``%s``." % checker_name, file=stream)
+ print("", file=stream)
+ if doc:
+ title = 'Documentation'
+ print(title, file=stream)
+ print('^' * len(title), file=stream)
+ print(cleandoc(doc), file=stream)
+ print("", file=stream)
+ if options:
+ title = 'Options'
+ print(title, file=stream)
+ print('^' * len(title), file=stream)
+ _rest_format_section(stream, None, options)
+ print("", file=stream)
+ if msgs:
+ title = 'Messages'
+ print(title, file=stream)
+ print('^' * len(title), file=stream)
+ for msgid, msg in sorted(six.iteritems(msgs),
+ key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1])):
+ msg = build_message_def(checker_name, msgid, msg)
+ print(msg.format_help(checkerref=False), file=stream)
+ print("", file=stream)
+ if reports:
+ title = 'Reports'
+ print(title, file=stream)
+ print('^' * len(title), file=stream)
+ for report in reports:
+ print(':%s: %s' % report[:2], file=stream)
+ print("", file=stream)
+ print("", file=stream)
class FileState(object):
"""Hold internal state specific to the currently analyzed file"""