| #!/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/main/LICENSE |
| |
| """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(f"pylint.extensions.{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) |