summaryrefslogtreecommitdiff
path: root/doc/exts/pylint_extensions.py
blob: c06c90e08fa094591ca45450cbc7581599b7cd24 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/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 sphinx

from pylint.constants import MAIN_CHECKER_NAME
from pylint.lint import PyLinter
from pylint.utils import get_rst_title

# 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)
    modules.sort()
    if not modules:
        sys.exit("No Pylint extensions found?")

    linter = PyLinter()
    linter.load_plugin_modules(modules)

    extensions_doc = os.path.join(
        base_path, "doc", "technical_reference", "extensions.rst"
    )
    with open(extensions_doc, "w") as stream:
        stream.write(
            get_rst_title("Optional Pylint checkers in the extensions module", "=")
        )
        stream.write("Pylint provides the following optional plugins:\n\n")
        for module in modules:
            stream.write(f"- :ref:`{module}`\n")
        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_checker = get_plugins_info(linter, doc_files)
        for checker, information in sorted(by_checker.items()):
            linter._print_checker_doc(information, stream=stream)


def get_plugins_info(linter, doc_files):
    by_checker = {}
    for checker in linter.get_checkers():
        if checker.name == MAIN_CHECKER_NAME:
            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) as f:
                doc = f.read()
        try:
            by_checker[checker]["checker"] = checker
            by_checker[checker]["options"] += checker.options_and_values()
            by_checker[checker]["msgs"].update(checker.msgs)
            by_checker[checker]["reports"] += checker.reports
            by_checker[checker]["doc"] += doc
            by_checker[checker]["module"] += module
        except KeyError:
            by_checker[checker] = {
                "checker": checker,
                "options": list(checker.options_and_values()),
                "msgs": dict(checker.msgs),
                "reports": list(checker.reports),
                "doc": doc,
                "module": module,
            }
    return by_checker


def setup(app):
    app.connect("builder-inited", builder_inited)
    return {"version": sphinx.__display_version__}


if __name__ == "__main__":
    builder_inited(None)