summaryrefslogtreecommitdiff
path: root/doc/exts
diff options
context:
space:
mode:
authorDaniel van Noord <13665637+DanielNoord@users.noreply.github.com>2023-02-20 20:19:00 +0100
committerDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2023-02-20 23:04:52 +0100
commitd7f307252a04f2135d9692cd75bb2e280566d475 (patch)
treefa18e108b49eaa0d0efaecafc06f485b77ce37e7 /doc/exts
parent490c40525c286483e8b7405870a8d2f0ae627de4 (diff)
downloadpylint-git-d7f307252a04f2135d9692cd75bb2e280566d475.tar.gz
Add support for more than one code file in the documentation examples
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Diffstat (limited to 'doc/exts')
-rw-r--r--doc/exts/pylint_messages.py137
1 files changed, 85 insertions, 52 deletions
diff --git a/doc/exts/pylint_messages.py b/doc/exts/pylint_messages.py
index 12b6822fd..49021d3f4 100644
--- a/doc/exts/pylint_messages.py
+++ b/doc/exts/pylint_messages.py
@@ -8,6 +8,7 @@ from __future__ import annotations
import os
from collections import defaultdict
+from enum import Enum
from inspect import getmodule
from itertools import chain, groupby
from pathlib import Path
@@ -39,16 +40,18 @@ class MessageData(NamedTuple):
id: str
name: str
definition: MessageDefinition
- good_code: str
- bad_code: str
- details: str
- related_links: str
+ example_code: str
checker_module_name: str
checker_module_path: str
shared: bool = False
default_enabled: bool = True
+class ExampleType(str, Enum):
+ GOOD = "good"
+ BAD = "bad"
+
+
MessagesDict = Dict[str, List[MessageData]]
OldMessagesDict = Dict[str, DefaultDict[Tuple[str, str], List[Tuple[str, str]]]]
"""DefaultDict is indexed by tuples of (old name symbol, old name id) and values are
@@ -62,39 +65,89 @@ def _register_all_checkers_and_extensions(linter: PyLinter) -> None:
initialize_extensions(linter)
-def _get_message_data(data_path: Path) -> tuple[str, str, str, str]:
- """Get the message data from the specified path."""
- good_py_path = data_path / "good.py"
- bad_py_path = data_path / "bad.py"
- details_rst_path = data_path / "details.rst"
- related_rst_path = data_path / "related.rst"
+def _get_example_code(data_path: Path) -> str:
+ """Get the example code from the specified path."""
if not data_path.exists():
- _create_placeholders(data_path, details_rst_path, good_py_path)
- good_code = _get_titled_rst(
- title="Correct code", text=_get_python_code_as_rst(good_py_path)
- )
- bad_code = _get_titled_rst(
- title="Problematic code", text=_get_python_code_as_rst(bad_py_path)
- )
+ raise AssertionError(
+ f"Documentation examples path {data_path} does not exist. "
+ "Please create it and add an example."
+ )
+
+ good_code = _get_demo_code_for(data_path, ExampleType.GOOD)
+ bad_code = _get_demo_code_for(data_path, ExampleType.BAD)
+ pylintrc = _get_pylintrc_code(data_path)
details = _get_titled_rst(
- title="Additional details", text=_get_rst_as_str(details_rst_path)
+ title="Additional details", text=_get_rst_as_str(data_path / "details.rst")
)
related = _get_titled_rst(
- title="Related links", text=_get_rst_as_str(related_rst_path)
+ title="Related links", text=_get_rst_as_str(data_path / "related.rst")
)
- _check_placeholders(bad_code, details, good_py_path, related)
- return good_code, bad_code, details, related
+
+ _check_placeholders(data_path, bad_code, details, related)
+ return "\n".join((good_code, bad_code, pylintrc, details, related)) + "\n"
+
+
+def _get_pylintrc_code(data_path: Path) -> str:
+ if (data_path / "pylintrc").exists():
+ pylintrc = _get_titled_rst(
+ title="Configuration file", text=_get_ini_as_rst(data_path / "pylintrc")
+ )
+ else:
+ pylintrc = ""
+ return pylintrc
+
+
+def _get_demo_code_for(data_path: Path, example_type: ExampleType) -> str:
+ """Get code examples while handling multi-file code templates."""
+ single_file_path = data_path / f"{example_type.value}.py"
+ multiple_code_path = data_path / f"{example_type.value}"
+
+ if single_file_path.exists() and multiple_code_path.exists():
+ raise ValueError(
+ f"You cannot have a single file '{example_type.value}.py' and multiple files "
+ f"example '{multiple_code_path}' existing at the same time."
+ )
+
+ title = "Problematic code" if example_type is ExampleType.BAD else "Correct code"
+ if single_file_path.exists():
+ return _get_titled_rst(
+ title=title, text=_get_python_code_as_rst(single_file_path)
+ )
+
+ if multiple_code_path.exists():
+ files: list[str] = []
+ # Sort so the order of the files makes sense
+ for file_as_str in sorted([str(p) for p in multiple_code_path.iterdir()]):
+ file = Path(file_as_str)
+ if file.suffix == ".py":
+ files.append(
+ f"""\
+``{file.name}``:
+
+.. literalinclude:: /{file.relative_to(Path.cwd())}
+ :language: python
+
+"""
+ )
+ return _get_titled_rst(title=title, text="\n".join(files))
+
+ return ""
def _check_placeholders(
- bad_code: str, details: str, good_py_path: Path, related: str
+ data_path: Path, bad_code: str, details: str, related: str
) -> None:
+ # Check if the placeholder file can even be presented by checking if its path exists
+ good_path = data_path / "good.py"
+ if not good_path.exists():
+ return
+
if bad_code or related:
placeholder_details = "help us make the doc better" in details
- with open(good_py_path) as f:
+ with open(good_path, encoding="utf-8") as f:
placeholder_good = "placeholder" in f.read()
assert_msg = (
- f"Please remove placeholders in '{good_py_path.parent}' "
+ f"Please remove placeholders in '{data_path}' "
f"as you started completing the documentation"
)
assert not placeholder_good and not placeholder_details, assert_msg
@@ -127,22 +180,11 @@ def _get_python_code_as_rst(code_path: Path) -> str:
"""
-def _create_placeholders(
- data_path: Path, details_rst_path: Path, good_py_path: Path
-) -> None:
- data_path.mkdir(parents=True)
- with open(good_py_path, "w", encoding="utf-8") as file:
- file.write(
- """\
-# This is a placeholder for correct code for this message.
-"""
- )
- with open(details_rst_path, "w", encoding="utf-8") as file:
- file.write(
- """\
-You can help us make the doc better `by contributing <https://github.com/PyCQA/pylint/issues/5953>`_ !
+def _get_ini_as_rst(code_path: Path) -> str:
+ return f"""\
+.. literalinclude:: /{code_path.relative_to(Path.cwd())}
+ :language: ini
"""
- )
def _get_all_messages(
@@ -175,9 +217,7 @@ def _get_all_messages(
)
for checker, message in checker_message_mapping:
- good_code, bad_code, details, related = _get_message_data(
- _get_message_data_path(message)
- )
+ example_code = _get_example_code(_get_message_data_path(message))
checker_module = getmodule(checker)
@@ -190,10 +230,7 @@ def _get_all_messages(
message.msgid,
message.symbol,
message,
- good_code,
- bad_code,
- details,
- related,
+ example_code,
checker_module.__name__,
checker_module.__file__,
message.shared,
@@ -283,12 +320,8 @@ def _generate_single_message_body(message: MessageData) -> str:
"""
- body += f"""
-{message.bad_code}
-{message.good_code}
-{message.details}
-{message.related_links}
-"""
+ body += "\n" + message.example_code + "\n"
+
if message.checker_module_name.startswith("pylint.extensions."):
body += f"""
.. note::