diff options
author | Daniel van Noord <13665637+DanielNoord@users.noreply.github.com> | 2023-02-20 20:19:00 +0100 |
---|---|---|
committer | Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com> | 2023-02-20 23:04:52 +0100 |
commit | d7f307252a04f2135d9692cd75bb2e280566d475 (patch) | |
tree | fa18e108b49eaa0d0efaecafc06f485b77ce37e7 | |
parent | 490c40525c286483e8b7405870a8d2f0ae627de4 (diff) | |
download | pylint-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>
-rw-r--r-- | doc/exts/pylint_messages.py | 137 |
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:: |