From ea747080667338b76da46b48464f16238dd079c9 Mon Sep 17 00:00:00 2001 From: Anselm Hahn Date: Mon, 1 Feb 2021 23:13:01 +0100 Subject: [PATCH] Finished docs Documentation is finished. Missing test and CI/CD --- .pre-commit-config.yaml | 13 ++- docs/generate_docs.py | 18 ++-- docs/mkdocs.yml | 8 +- docs/theme/main.html | 22 ++++ mkdocstrings_sourcelink/auto_generator.py | 73 +++++++------ mkdocstrings_sourcelink/test/__init__.py | 1 + mkdocstrings_sourcelink/toolbox.py | 124 ++++++++-------------- setup.cfg | 30 +++++- 8 files changed, 162 insertions(+), 127 deletions(-) create mode 100644 docs/theme/main.html diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index edacc54..6650b8b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,6 +11,14 @@ repos: rev: '' hooks: - id: check-added-large-files + - repo: https://github.com/asottile/setup-cfg-fmt + rev: v1.16.0 + hooks: + - id: setup-cfg-fmt + - repo: https://github.com/pycqa/pydocstyle + rev: 5.1.1 # pick a git hash / tag to point to + hooks: + - id: pydocstyle - repo: local hooks: - id: pylint @@ -19,9 +27,4 @@ repos: language: system always_run: true pass_filenames: false - - id: pydocstyle - name: pydocstyle - entry: pydocstyle mkdocstrings_sourcelink - language: python - types: [python] \ No newline at end of file diff --git a/docs/generate_docs.py b/docs/generate_docs.py index eac071b..a5d16cf 100644 --- a/docs/generate_docs.py +++ b/docs/generate_docs.py @@ -1,3 +1,4 @@ +"""Generate Docstring via MkDocGenerator of mkdocstrings-sourcelink.""" from pathlib import Path from mkdocstrings_sourcelink import MkDocGenerator @@ -6,25 +7,29 @@ "Documentation": { "auto_generator.md": [ "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.__init__", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._render", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._initialize_generate", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._generate_docs", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._generate_static", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.render_to_markdown", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.initialize_generate", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.generate_docs", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.generate_static", "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.generate", ] }, "Tools": { "toolbox.md": [ - # "mkdocstrings_sourcelink.toolbox.Utilities", "mkdocstrings_sourcelink.toolbox.Utilities.insert_in_file", "mkdocstrings_sourcelink.toolbox.Utilities.element_to_mkdocstrings", "mkdocstrings_sourcelink.toolbox.Utilities.make_source_link", "mkdocstrings_sourcelink.toolbox.Utilities.make_title", - "mkdocstrings_sourcelink.toolbox.Utilities.ismethod", "mkdocstrings_sourcelink.toolbox.Utilities.import_object", "mkdocstrings_sourcelink.toolbox.Utilities.return_as_Path", ], }, + "API": { + "api.md": [ + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator", + "mkdocstrings_sourcelink.toolbox.Utilities", + ], + }, } markdown_files = {"HOME": {"index.md": ["../README.md"]}} root = Path(__file__).resolve().parents[1] @@ -32,6 +37,7 @@ root / "docs" / "src", pages, "https://github.com/AI2Business/mkdocstrings-sourcelink", + root / "docs" / "tmp", markdown_files=markdown_files, underline_title=True, source=":material-github::material-source-branch:", diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 77fc82d..894ec13 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -130,6 +130,8 @@ plugins: nav: - Home: index.md - - Documentation: auto_generator.md - - Tools: toolbox.md - #- Tools: toolbox.md \ No newline at end of file + - Documentation: + - About: about.md + - Automatic Doc Generator: auto_generator.md + - Tools: toolbox.md + - API: api.md \ No newline at end of file diff --git a/docs/theme/main.html b/docs/theme/main.html new file mode 100644 index 0000000..4877557 --- /dev/null +++ b/docs/theme/main.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% block libs %} +{{ super() }} +{% include "partials/libs.html" ignore missing %} +{% endblock %} + +{% block announce %}{% include "announce.html" ignore missing %}{% endblock %} + +{% block content %} + {{ super() }} + +{% endblock %} diff --git a/mkdocstrings_sourcelink/auto_generator.py b/mkdocstrings_sourcelink/auto_generator.py index 740cb5d..a134870 100644 --- a/mkdocstrings_sourcelink/auto_generator.py +++ b/mkdocstrings_sourcelink/auto_generator.py @@ -8,7 +8,7 @@ class BuilderMkDoc(ABC): - """BuilderMkDoc is the Metaclass of MkDocGenerator. + """BuilderMkDoc is the Metaclass of `MkDocGenerator`. Args: ABC (class): Helper class that provides a standard way to create an ABC using @@ -16,20 +16,20 @@ class BuilderMkDoc(ABC): """ @abstractmethod - def _render(self, element: str) -> str: - """Abstract the internal method of `_render`.""" + def render_to_markdown(self, element: str) -> str: + """Abstract the internal method of `render_to_markdown`.""" @abstractmethod - def _initialize_generate(self) -> None: - """Abstract the internal method of `_initialize_generate`.""" + def initialize_generate(self) -> None: + """Abstract the internal method of `initialize_generate`.""" @abstractmethod - def _generate_docs(self) -> None: - """Abstract the internal method of `_generate_docs`.""" + def generate_docs(self) -> None: + """Abstract the internal method of `generate_docs`.""" @abstractmethod - def _generate_static(self) -> None: - """Abstract the internal method of `_generate_static`.""" + def generate_static(self) -> None: + """Abstract the internal method of `generate_static`.""" @abstractproperty def generate(self) -> None: @@ -37,15 +37,21 @@ def generate(self) -> None: class MkDocGenerator(Utilities, BuilderMkDoc): - """Generates the documentation withthe links to the source.""" + """The `MkDocGenerator` generates the documentation with the links to the source code. + + Args: + Utilities (class): [description] + BuilderMkDoc (class): Builder class of the abstract methods and property of + `MkDocGenerator`. + """ def __init__( self, dest_dir: Union[str, Path], documentation: Dict[str, Dict[str, List[str]]] = {}, project_url: Union[str, Dict[str, str]] = None, - template_dir: str = None, - examples_dir: str = None, + template_dir: Union[str, Path] = None, + examples_dir: Union[str, Path] = None, markdown_files: Dict[str, Dict[str, List[str]]] = None, titles_size: str = "#", underline_title: bool = False, @@ -61,11 +67,11 @@ def __init__( `pages = {'page_title':{'filename.md': ['package.module.function']}}`. project_url (Union[str, Dict[str, str]], optional): The URL, where the project is hosted and where it should be linked to. - template_dir (str, optional): Directory of template files. If template has + template_dir (Union[str, Path], optional): Directory of template files. If template has to be automatically filled out, then the keyword **{{autogenerated}}** has to be used. - examples_dir (str, optional): Directory of examples files, especially suitable for - `Jupyter-Notebooks`. + examples_dir (Union[str, Path], optional): Directory of examples files, especially + suitable for `Jupyter-Notebooks`. markdown_files (Dict[str, Dict[str, List[str]]], optional): A nested dictionary with the page title, the page filename, and the link to already existing markdown files like **README.md** or **LICENSE**. The dictionary should look like: @@ -84,10 +90,10 @@ def __init__( "Documentation": { "auto_generator.md": [ "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.__init__", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._render", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._initialize_generate", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._generate_docs", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._generate_static", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.render_to_markdown", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.initialize_generate", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.generate_docs", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.generate_static", "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.generate", ] } @@ -126,12 +132,12 @@ def __init__( self.underline_title = underline_title self.source = source - def _render(self, element: str) -> str: + def render_to_markdown(self, element: str) -> str: """Rendering the element path to mkdocstrings. Args: element (str): String of they python class, function, or method, which has to be - converted to string for markdown. + converted to a string in the mkdocstrings format. Returns: str: Return of the initial string which looks like @@ -156,7 +162,7 @@ def _render(self, element: str) -> str: subblocks.append(Utilities.element_to_mkdocstrings(signature, self.titles_size)) return "\n\n".join(subblocks) + "\n\n" - def _initialize_generate(self) -> None: + def initialize_generate(self) -> None: """Initialization of the auto documentation generatorion. 1. Firs removing a possible existing target directory (`dest_dir`). @@ -179,15 +185,17 @@ def _initialize_generate(self) -> None: ) shutil.copytree(self.example_dir, self.dest_dir) - def _generate_docs(self) -> None: + def generate_docs(self) -> None: """Generated *dynamic* documentation based on calling the elements via dictionary.""" for title, documentation in self.documentation.items(): markdown_text = f"{self.titles_size} {title}\n\n---\n\n" for file_path, elements in documentation.items(): - markdown_text += "".join(self._render(element) for element in elements) + markdown_text += "".join( + self.render_to_markdown(element) for element in elements + ) Utilities.insert_in_file(markdown_text, self.dest_dir.joinpath(file_path)) - def _generate_static(self) -> None: + def generate_static(self) -> None: """Generate *static* documentation based on existing markdown files.""" if self.markdown_files: for _, markdown_files in self.markdown_files.items(): @@ -215,10 +223,10 @@ def generate(self) -> None: "Documentation": { "auto_generator.md": [ "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.__init__", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._render", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._initialize_generate", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._generate_docs", - "mkdocstrings_sourcelink.auto_generator.MkDocGenerator._generate_static", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.render_to_markdown", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.initialize_generate", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.generate_docs", + "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.generate_static", "mkdocstrings_sourcelink.auto_generator.MkDocGenerator.generate", ] }, @@ -228,7 +236,6 @@ def generate(self) -> None: "mkdocstrings_sourcelink.toolbox.Utilities.element_to_mkdocstrings", "mkdocstrings_sourcelink.toolbox.Utilities.make_source_link", "mkdocstrings_sourcelink.toolbox.Utilities.make_title", - "mkdocstrings_sourcelink.toolbox.Utilities.ismethod", "mkdocstrings_sourcelink.toolbox.Utilities.import_object", "mkdocstrings_sourcelink.toolbox.Utilities.return_as_Path", ], @@ -249,6 +256,6 @@ def generate(self) -> None: >>> ... ``` """ - self._initialize_generate() - self._generate_docs() - self._generate_static() + self.initialize_generate() + self.generate_docs() + self.generate_static() diff --git a/mkdocstrings_sourcelink/test/__init__.py b/mkdocstrings_sourcelink/test/__init__.py index e69de29..c6a6571 100644 --- a/mkdocstrings_sourcelink/test/__init__.py +++ b/mkdocstrings_sourcelink/test/__init__.py @@ -0,0 +1 @@ +"""Test module of mkdocstrings-sourcelink.""" diff --git a/mkdocstrings_sourcelink/toolbox.py b/mkdocstrings_sourcelink/toolbox.py index ac4c880..879a097 100644 --- a/mkdocstrings_sourcelink/toolbox.py +++ b/mkdocstrings_sourcelink/toolbox.py @@ -1,4 +1,4 @@ -""" [summary] """ +"""Tools for the automatic source link generation for mkdocstrings.""" import importlib import inspect from abc import ABC, abstractmethod @@ -7,12 +7,13 @@ class BuilderUtilities(ABC): - """BuilderUtilities [summary] + """BuilderUtilities is the Metaclass of Utilities. - [extended_summary] + The Utilities are essential for building the mkdocstrings and hyperlinks to the source code. Args: - ABC ([type]): [description] + ABC (class): Helper class that provides a standard way to create an ABC using + inheritance. """ @abstractmethod @@ -31,41 +32,32 @@ def make_source_link(self) -> None: def make_title(self) -> None: """Abstract method of `make_title`.""" - @abstractmethod - def ismethod(self) -> None: - """Abstract method of `ismethod`.""" - @abstractmethod def import_object(self) -> None: """Abstract method of `import_object`.""" @abstractmethod def return_as_Path(self) -> None: - """Abstract method of `return_as_Path`""" + """Abstract method of `return_as_Path`.""" class Utilities(BuilderUtilities): - """Utilities [summary] - - [extended_summary] + """The `Utilities` build the mkdocstrings and generate the hyperlinks to the source code. Args: - AbstractUtilities (class): [description] + AbstractUtilities (class): Builder class of the abstract methods of `Utilities`. """ def insert_in_file(markdown_text: str, file_path: Path) -> None: - """insert_in_file [summary] + """Insert the markdown formatted text into a new or existing file. - Save module page. - - Either insert content into existing page, - or create page otherwise. Args: - markdown_text (str): [description] - file_path (Path): [description] + markdown_text (str): Text as string, which follows the markdown format. + file_path (Path): Filename and path as Path object. Raises: - RuntimeError: [description] + RuntimeError: If {{autogenerated}} is not provided in a template file, it will cause a + a **RuntimeError**. """ if file_path.exists(): template = file_path.read_text(encoding="utf-8") @@ -82,34 +74,46 @@ def insert_in_file(markdown_text: str, file_path: Path) -> None: file_path.write_text(markdown_text, encoding="utf-8") def element_to_mkdocstrings(element: str, titles_size: str) -> str: - """element_to_mkdocstrings [summary] + """Converts point separated string into the mkdocstrings format. - [extended_summary] + For converting the elements to mkdocstrings, the element will added **:::** in front of the + element string. In addition to that, the the new mkdocstrings will get subheadings. Args: - element (str): [description] + element (str): String of they python class, function, or method, which has to be + converted to a string in the mkdocstrings format. + titles_size (str): Current title size in the style of '#', which defines the headings. Returns: - str: [description] + str: String of they python class, function, or method, which is converted to a string + in the mkdocstrings format. """ return f"##{titles_size} :::{element}\n" def make_source_link( - cls: Any, + cls: classmethod, project_url: Union[str, Dict[str, str]], source: str = "**source code**", ) -> str: - """make_source_link [summary] - - [extended_summary] + """Make a source link to the code basis including the linestart. Args: - cls (Any): [description] - project_url (Union[str, Dict[str, str]]): [description] - source (str, optional): [description]. Defaults to "**source code**". + cls (classmethod): Convert a function to be a class method. + project_url (Union[str, Dict[str, str]]): URL to the repository like GitHub + https://github.com/AI2Business/mkdocstrings-sourcelink/. + source (str, optional): [description]. Returns: - str: [description] + str: Hyperlink in html format with link to the repository. + + !!! tip "About *source*" + Instead of using a string for `source = "**source code**"`, icons can be used instead + or as combination of string + icon(s) like. + ```python + source = ":material-github::material-source-branch: source-code" + ``` + In case of using material-icons, please check https://pictogrammers.github.io/@mdi/font/5.4.55/ + and replace `mdl` by `material`. """ if isinstance(project_url, dict): base_module = cls.__module__.split(".")[0] @@ -128,56 +132,24 @@ def make_source_link( f"" ) - def make_title(cls: Any, titles_size: str, underline_title: bool) -> str: - """make_title [summary] - - [extended_summary] + def make_title(cls: classmethod, titles_size: str, underline_title: bool) -> str: + """Make the title of the class, function, or method. Args: - cls (Any): [description] - titles_size (str): [description] + cls (classmethod): Convert a function to be a class method. In case of class properties + `fget` is used to read out the name of the module. + titles_size (str): Current title size in the style of '#', which defines the headings. Returns: - str: [description] + str: The name of the class, function, or method in a markdown conformed title. """ title_underline = "\n---\n" if underline_title else "\n" if isinstance(cls, property): return f"#{titles_size} {cls.fget.__name__}{title_underline}" return f"#{titles_size} {cls.__name__}{title_underline}" - def ismethod(function: Any) -> Union[Any, bool]: - """ismethod [summary] - - [extended_summary] - - Args: - function (Any): [description] - - Returns: - Union[Any, bool]: [description] - """ - if inspect.ismethod(function): - for cls in inspect.getmro(function.__self__.__class__): - if cls.__dict__.get(function.__name__) is function: - return cls - function = function.__func__ # fallback to __qualname__ parsing - if inspect.isfunction(function): - cls = getattr( - inspect.getmodule(function), - function.__qualname__.split(".", 1)[0].rsplit(".", 1)[0], - ) - if isinstance(cls, type): - return cls - - return bool(getattr(function, "__objclass__", None)) - def import_object(element: str) -> object: - """import_object [summary] - - Import an object from a string. - - The object can be a function, class or method. - For example: `'keras.layers.Dense.get_weights'` is valid. + """Import an object like class, function, or method from a string. Args: element (str): [description] @@ -197,15 +169,13 @@ def import_object(element: str) -> object: return last_object_got def return_as_Path(path: str = None) -> Optional[Path]: - """return_as_Path [summary] - - [extended_summary] + """Converts strings to Path of pathlib. Args: - path (str, optional): [description]. Defaults to None. + path (str, optional): String of a filename. Returns: - Optional[Path]: [description] + Optional[Path]: Path object of the intial filename. """ if path: return Path(path) diff --git a/setup.cfg b/setup.cfg index 21b7715..07861b8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,26 @@ +[metadata] +name = mkdocstrings_sourcelink +version = 0.3.0 +description = Automatic source link generation for mkdocstrings. +long_description = file: README.md +long_description_content_type = text/markdown +license = Apache-2.0 +license_file = LICENSE +classifiers = + License :: OSI Approved :: Apache Software License + License :: OSI Approved :: MIT License + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: PyPy + +[options] +python_requires = >=3.6 + [tool.isort] multi_line_output = 3 include_trailing_comma = true @@ -9,16 +32,17 @@ line_length = 88 [pylint] max-line-length = 88 disable = all -enable = missing-docstring +enable = missing-docstring ignore = test [pylint.messages_control] disable = all -enable = missing-docstring +enable = missing-docstring [pydocstyle] convention = google match = (?!test_).*\.py +match-dir = mkdocstrings_sourcelink/ [pycodestyle] -ax-line-length = 88 \ No newline at end of file +ax-line-length = 88