diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/_static/changelog.css | 11 | ||||
| -rw-r--r-- | docs/_static/changelog.js | 37 | ||||
| -rw-r--r-- | docs/changelog.rst | 14 | ||||
| -rw-r--r-- | docs/conf.py | 125 | ||||
| -rw-r--r-- | docs/index.rst | 6 | ||||
| -rw-r--r-- | docs/utils.py | 117 | 
6 files changed, 135 insertions, 175 deletions
diff --git a/docs/_static/changelog.css b/docs/_static/changelog.css deleted file mode 100644 index 343792a1..00000000 --- a/docs/_static/changelog.css +++ /dev/null @@ -1,11 +0,0 @@ -[data-theme='dark'] #changelog .dark, -[data-theme='light'] #changelog .light, -[data-theme='auto'] #changelog .light { -    display: inline; -} - -[data-theme='dark'] #changelog .light, -[data-theme='light'] #changelog .dark, -[data-theme='auto'] #changelog .dark { -    display: none; -} diff --git a/docs/_static/changelog.js b/docs/_static/changelog.js deleted file mode 100644 index f72d025c..00000000 --- a/docs/_static/changelog.js +++ /dev/null @@ -1,37 +0,0 @@ -/** Update the changelog colors in dark mode */ - -const changelog = document.getElementById("changelog"); - -function updateEntryColor(entry) { -    const line = entry.lastChild; -    const lightColorSpan = line.childNodes.item(1); -    const darkColorSpan = lightColorSpan.cloneNode(true); - -    line.insertBefore(darkColorSpan, lightColorSpan); - -    lightColorSpan.classList.add("light"); -    darkColorSpan.classList.add("dark"); - -    let color; -    switch (darkColorSpan.textContent) { -        case "Feature": -            color = "#5BF38E"; -            break; -        case "Support": -            color = "#55A5E7"; -            break; -        case "Bug": -            color = "#E14F4F"; -            break; -        default: -            color = null; -    } - -    darkColorSpan.style["color"] = color; -} - -if (changelog !== null) { -    for (let collection of changelog.getElementsByClassName("simple")) { -        Array.from(collection.children).forEach(updateEntryColor); -    } -} diff --git a/docs/changelog.rst b/docs/changelog.rst deleted file mode 100644 index 743fcc20..00000000 --- a/docs/changelog.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. See docs for details on formatting your entries -   https://releases.readthedocs.io/en/latest/concepts.html - - -Changelog -========= - -- :release:`1.2.0 <9th January 2022>` -- :feature:`12` Code block detection regex -- :release:`1.1.0 <2nd December 2021>` -- :support:`2` Autogenerated docs. -- :feature:`2` Regex utility. -- :release:`1.0.0 <17th November 2021>` -- :support:`1` Core package, poetry, and linting CI. diff --git a/docs/conf.py b/docs/conf.py index 4ab831d3..476a4d36 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,17 +1,20 @@  # Configuration file for the Sphinx documentation builder.  # https://www.sphinx-doc.org/en/master/usage/configuration.html -import ast -import importlib -import inspect +import functools +import os.path  import sys -import typing  from pathlib import Path  import git  import tomli  from sphinx.application import Sphinx +# Handle the path not being set correctly in actions. +sys.path.insert(0, os.path.abspath('..')) + +from docs import utils  # noqa: E402 +  # -- Project information -----------------------------------------------------  project = "Bot Core" @@ -38,11 +41,11 @@ add_module_names = False  # ones.  extensions = [      "sphinx.ext.extlinks", +    "sphinx.ext.intersphinx",      "sphinx.ext.autodoc",      "sphinx.ext.todo",      "sphinx.ext.napoleon",      "sphinx_autodoc_typehints", -    "releases",      "sphinx.ext.linkcode",      "sphinx.ext.githubpages",  ] @@ -80,52 +83,10 @@ html_logo = "https://raw.githubusercontent.com/python-discord/branding/main/logo  html_favicon = html_logo  html_css_files = [ -    "changelog.css",      "logo.css",  ] -html_js_files = [ -    "changelog.js", -] - - -# -- Autodoc cleanup --------------------------------------------------------- -# Clean up the output generated by autodoc to produce a nicer documentation tree -# This is kept in a function to avoid polluting the namespace -def __cleanup() -> None: -    for file in (PROJECT_ROOT / "docs" / "output").iterdir(): -        if file.name == "modules.rst": -            # We only have one module, so this is redundant -            # Remove it and flatten out the tree -            file.unlink() - -        elif file.name == "botcore.rst": -            # We want to bring the submodule name to the top, and remove anything that's not a submodule -            result = "" -            for line in file.read_text(encoding="utf-8").splitlines(keepends=True): -                if ".." not in line and result == "": -                    # We have not reached the first submodule, this is all filler -                    continue -                elif "Module contents" in line: -                    # We have parsed all the submodules, so let's skip the redudant module name -                    break -                result += line - -            result = "Botcore\n=======\n\n" + result -            file.write_text(result, encoding="utf-8") - -        else: -            # Clean up the submodule name so it's just the name without the top level module name -            # example: `botcore.regex module` -> `regex` -            lines = file.read_text(encoding="utf-8").splitlines() -            lines[0] = lines[0].replace("botcore.", "").replace("module", "").strip() - -            # Take the opportunity to configure autodoc -            lines = "\n".join(lines).replace("undoc-members", "special-members") -            file.write_text(lines, encoding="utf-8") - - -__cleanup() +utils.cleanup()  def skip(*args) -> bool: @@ -159,70 +120,18 @@ napoleon_numpy_docstring = False  napoleon_attr_annotations = True -# -- Options for releases extension ------------------------------------------ -releases_github_path = REPO_LINK.removeprefix("https://github.com/") - -  # -- Options for extlinks extension ------------------------------------------  extlinks = {      "repo-file": (f"{REPO_LINK}/blob/main/%s", "repo-file %s")  } +# -- Options for intersphinx extension --------------------------------------- +intersphinx_mapping = { +    "python": ("https://docs.python.org/3", None), +    "discord": ("https://discordpy.readthedocs.io/en/master/", None), +} + +  # -- Options for the linkcode extension -------------------------------------- -def linkcode_resolve(domain: str, info: dict[str, str]) -> typing.Optional[str]: -    """ -    Function called by linkcode to get the URL for a given resource. - -    See for more details: -    https://www.sphinx-doc.org/en/master/usage/extensions/linkcode.html#confval-linkcode_resolve -    """ -    if domain != "py": -        raise Exception("Unknown domain passed to linkcode function.") - -    symbol_name = info["fullname"] - -    module = importlib.import_module(info["module"]) - -    symbol = [module] -    for name in symbol_name.split("."): -        symbol.append(getattr(symbol[-1], name)) -        symbol_name = name - -    try: -        lines, start = inspect.getsourcelines(symbol[-1]) -        end = start + len(lines) -    except TypeError: -        # Find variables by parsing the ast -        source = ast.parse(inspect.getsource(symbol[-2])) -        while isinstance(source.body[0], ast.ClassDef): -            source = source.body[0] - -        for ast_obj in source.body: -            if isinstance(ast_obj, ast.Assign): -                names = [] -                for target in ast_obj.targets: -                    if isinstance(target, ast.Tuple): -                        names.extend([name.id for name in target.elts]) -                    else: -                        names.append(target.id) - -                if symbol_name in names: -                    start, end = ast_obj.lineno, ast_obj.end_lineno -                    break -        else: -            raise Exception(f"Could not find symbol `{symbol_name}` in {module.__name__}.") - -        _, offset = inspect.getsourcelines(symbol[-2]) -        if offset != 0: -            offset -= 1 -        start += offset -        end += offset - -    file = Path(inspect.getfile(module)).relative_to(PROJECT_ROOT).as_posix() - -    url = f"{SOURCE_FILE_LINK}/{file}#L{start}" -    if end != start: -        url += f"-L{end}" - -    return url +linkcode_resolve = functools.partial(utils.linkcode_resolve, SOURCE_FILE_LINK) diff --git a/docs/index.rst b/docs/index.rst index e7c25ef1..81975f35 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,11 +13,6 @@ Reference     output/botcore -.. toctree:: -   :caption: Other: - -   changelog -  Extras  ================== @@ -25,3 +20,4 @@ Extras  * :ref:`genindex`  * :ref:`search`  * :repo-file:`Information <docs/README.md>` +* :repo-file:`Changelog <CHANGELOG.md>` diff --git a/docs/utils.py b/docs/utils.py new file mode 100644 index 00000000..76b3e098 --- /dev/null +++ b/docs/utils.py @@ -0,0 +1,117 @@ +"""Utilities used in generating docs.""" + +import ast +import importlib +import inspect +import typing +from pathlib import Path + +PROJECT_ROOT = Path(__file__).parent.parent + + +def linkcode_resolve(source_url: str, domain: str, info: dict[str, str]) -> typing.Optional[str]: +    """ +    Function called by linkcode to get the URL for a given resource. + +    See for more details: +    https://www.sphinx-doc.org/en/master/usage/extensions/linkcode.html#confval-linkcode_resolve +    """ +    if domain != "py": +        raise Exception("Unknown domain passed to linkcode function.") + +    symbol_name = info["fullname"] + +    module = importlib.import_module(info["module"]) + +    symbol = [module] +    for name in symbol_name.split("."): +        symbol.append(getattr(symbol[-1], name)) +        symbol_name = name + +    try: +        lines, start = inspect.getsourcelines(symbol[-1]) +        end = start + len(lines) +    except TypeError: +        # Find variables by parsing the ast +        source = ast.parse(inspect.getsource(symbol[-2])) +        while isinstance(source.body[0], ast.ClassDef): +            source = source.body[0] + +        for ast_obj in source.body: +            if isinstance(ast_obj, ast.Assign): +                names = [] +                for target in ast_obj.targets: +                    if isinstance(target, ast.Tuple): +                        names.extend([name.id for name in target.elts]) +                    else: +                        names.append(target.id) + +                if symbol_name in names: +                    start, end = ast_obj.lineno, ast_obj.end_lineno +                    break +        else: +            raise Exception(f"Could not find symbol `{symbol_name}` in {module.__name__}.") + +        _, offset = inspect.getsourcelines(symbol[-2]) +        if offset != 0: +            offset -= 1 +        start += offset +        end += offset + +    file = Path(inspect.getfile(module)).relative_to(PROJECT_ROOT).as_posix() + +    url = f"{source_url}/{file}#L{start}" +    if end != start: +        url += f"-L{end}" + +    return url + + +def cleanup() -> None: +    """Remove unneeded autogenerated doc files, and clean up others.""" +    included = __get_included() + +    for file in (PROJECT_ROOT / "docs" / "output").iterdir(): +        if file.name in ("botcore.rst", "botcore.exts.rst", "botcore.utils.rst") and file.name in included: +            content = file.read_text(encoding="utf-8").splitlines(keepends=True) + +            # Rename the extension to be less wordy +            # Example: botcore.exts -> Botcore Exts +            title = content[0].split()[0].strip().replace("botcore.", "").replace(".", " ").title() +            title = f"{title}\n{'=' * len(title)}\n\n" +            content = title, *content[3:] + +            file.write_text("".join(content), encoding="utf-8") + +        elif file.name in included: +            # Clean up the submodule name so it's just the name without the top level module name +            # example: `botcore.regex module` -> `regex` +            lines = file.read_text(encoding="utf-8").splitlines(keepends=True) +            lines[0] = lines[0].replace("module", "").strip().split(".")[-1] + "\n" +            file.write_text("".join(lines)) + +        else: +            # These are files that have not been explicitly included in the docs via __all__ +            print("Deleted file", file.name) +            file.unlink() +            continue + +        # Take the opportunity to configure autodoc +        content = file.read_text(encoding="utf-8").replace("undoc-members", "special-members") +        file.write_text(content, encoding="utf-8") + + +def __get_included() -> set[str]: +    """Get a list of files that should be included in the final build.""" + +    def get_all_from_module(module_name: str) -> set[str]: +        module = importlib.import_module(module_name) +        _modules = {module.__name__ + ".rst"} + +        if hasattr(module, "__all__"): +            for sub_module in module.__all__: +                _modules.update(get_all_from_module(sub_module)) + +        return _modules + +    return get_all_from_module("botcore")  |