Edit on GitHub

pdoc.render

  1from __future__ import annotations
  2
  3import os
  4import types
  5import warnings
  6from pathlib import Path
  7from typing import cast
  8from typing import Mapping
  9
 10import jinja2
 11from jinja2 import Environment
 12from jinja2 import FileSystemLoader
 13
 14import pdoc.doc
 15import pdoc.docstrings
 16from pdoc.render_helpers import DefaultMacroExtension
 17from pdoc.render_helpers import defuse_unsafe_reprs
 18from pdoc.render_helpers import edit_url
 19from pdoc.render_helpers import format_signature
 20from pdoc.render_helpers import highlight
 21from pdoc.render_helpers import link
 22from pdoc.render_helpers import linkify
 23from pdoc.render_helpers import minify_css
 24from pdoc.render_helpers import root_module_name
 25from pdoc.render_helpers import to_html
 26from pdoc.render_helpers import to_markdown_with_context
 27from pdoc.search import make_index
 28from pdoc.search import precompile_index
 29
 30try:
 31    from typing import Literal
 32except ImportError:  # pragma: no cover
 33    # Python < 3.8 - we cannot use pdoc._compat because ruff does not like it.
 34    class Literal:  # type: ignore
 35        pass
 36
 37
 38def configure(
 39    *,
 40    docformat: Literal[
 41        "markdown", "google", "numpy", "restructuredtext"
 42    ] = "restructuredtext",
 43    edit_url_map: Mapping[str, str] | None = None,
 44    favicon: str | None = None,
 45    footer_text: str = "",
 46    logo: str | None = None,
 47    logo_link: str | None = None,
 48    math: bool = False,
 49    search: bool = True,
 50    show_source: bool = True,
 51    template_directory: Path | None = None,
 52):
 53    """
 54    Configure the rendering output.
 55
 56    - `docformat` is the docstring flavor in use.
 57      pdoc prefers plain Markdown (the default), but also supports other formats.
 58    - `edit_url_map` is a mapping from module names to URL prefixes. For example,
 59
 60        ```json
 61        {"pdoc": "https://github.com/mitmproxy/pdoc/blob/main/pdoc/"}
 62        ```
 63
 64      renders the "Edit on GitHub" button on this page. The URL prefix can be modified to pin a particular version.
 65    - `favicon` is an optional path/URL for a favicon image
 66    - `footer_text` is additional text that should appear in the navigation footer.
 67    - `logo` is an optional URL to the project's logo image
 68    - `logo_link` is an optional URL the logo should point to
 69    - `math` enables math rendering by including MathJax into the rendered documentation.
 70    - `search` controls whether search functionality is enabled and a search index is built.
 71    - `show_source` controls whether a "View Source" button should be included in the output.
 72    - `template_directory` can be used to set an additional (preferred) directory
 73      for templates. You can find an example in the main documentation of `pdoc`
 74      or in `examples/custom-template`.
 75    """
 76    searchpath = _default_searchpath
 77    if template_directory:
 78        searchpath = [template_directory] + searchpath
 79    env.loader = FileSystemLoader(searchpath)
 80
 81    env.globals["edit_url_map"] = edit_url_map or {}
 82    env.globals["docformat"] = docformat
 83    env.globals["math"] = math
 84    env.globals["show_source"] = show_source
 85    env.globals["favicon"] = favicon
 86    env.globals["logo"] = logo
 87    env.globals["logo_link"] = logo_link
 88    env.globals["footer_text"] = footer_text
 89    env.globals["search"] = search
 90
 91
 92@defuse_unsafe_reprs()
 93def html_module(
 94    module: pdoc.doc.Module,
 95    all_modules: Mapping[str, pdoc.doc.Module],
 96    mtime: str | None = None,
 97) -> str:
 98    """
 99    Renders the documentation for a `pdoc.doc.Module`.
100
101    - `all_modules` contains all modules that are rendered in this invocation.
102      This is used to determine which identifiers should be linked and which should not.
103    - If `mtime` is given, include additional JavaScript on the page for live-reloading.
104      This is only passed by `pdoc.web`.
105    """
106    return env.get_template("module.html.jinja2").render(
107        module=module,
108        all_modules=all_modules,
109        root_module_name=root_module_name(all_modules),
110        edit_url=edit_url(
111            module.modulename,
112            module.is_package,
113            cast(Mapping[str, str], env.globals["edit_url_map"]),
114        ),
115        mtime=mtime,
116    )
117
118
119@defuse_unsafe_reprs()
120def html_index(all_modules: Mapping[str, pdoc.doc.Module]) -> str:
121    """Renders the module index."""
122    return env.get_template("index.html.jinja2").render(
123        all_modules=all_modules,
124        root_module_name=root_module_name(all_modules),
125    )
126
127
128@defuse_unsafe_reprs()
129def html_error(error: str, details: str = "") -> str:
130    """Renders an error message."""
131    return env.get_template("error.html.jinja2").render(
132        error=error,
133        details=details,
134    )
135
136
137@defuse_unsafe_reprs()
138def search_index(all_modules: Mapping[str, pdoc.doc.Module]) -> str:
139    """Renders the Elasticlunr.js search index."""
140    if not env.globals["search"]:
141        return ""
142    # This is a rather terrible hack to determine if a given object is public and should be included in the index.
143    module_template: jinja2.Template = env.get_template("module.html.jinja2")
144    ctx: jinja2.runtime.Context = module_template.new_context(
145        {"module": pdoc.doc.Module(types.ModuleType("")), "all_modules": all_modules}
146    )
147    for _ in module_template.root_render_func(ctx):  # type: ignore
148        pass
149
150    def is_public(x: pdoc.doc.Doc) -> bool:
151        return bool(ctx["is_public"](x).strip())
152
153    index = make_index(
154        all_modules,
155        is_public,
156        cast(str, env.globals["docformat"]),
157    )
158
159    compile_js = Path(env.get_template("build-search-index.js").filename)  # type: ignore
160    return env.get_template("search.js.jinja2").render(
161        search_index=precompile_index(index, compile_js)
162    )
163
164
165@defuse_unsafe_reprs()
166def repr_module(module: pdoc.doc.Module) -> str:
167    """Renders `repr(pdoc.doc.Module)`, primarily used for tests and debugging."""
168    return repr(module)
169
170
171_default_searchpath = [
172    Path(os.environ.get("XDG_CONFIG_HOME", "~/.config")).expanduser() / "pdoc",
173    Path(__file__).parent / "templates",
174    Path(__file__).parent / "templates" / "default",
175    Path(__file__).parent / "templates" / "deprecated",
176]
177
178env = Environment(
179    loader=FileSystemLoader(_default_searchpath),
180    extensions=[DefaultMacroExtension],
181    autoescape=True,
182    trim_blocks=True,
183    lstrip_blocks=True,
184)
185"""
186The Jinja2 environment used to render all templates.
187You can modify this object to add custom filters and globals.
188"""
189env.filters["to_markdown"] = to_markdown_with_context
190env.filters["to_html"] = to_html
191env.filters["highlight"] = highlight
192env.filters["format_signature"] = format_signature
193env.filters["linkify"] = linkify
194env.filters["link"] = link
195env.filters["minify_css"] = minify_css
196env.globals["__version__"] = pdoc.__version__
197env.globals["env"] = os.environ
198env.globals["warn"] = warnings.warn
199configure()  # add default globals
def configure( *, docformat: Literal['markdown', 'google', 'numpy', 'restructuredtext'] = 'restructuredtext', edit_url_map: Optional[Mapping[str, str]] = None, favicon: str | None = None, footer_text: str = '', logo: str | None = None, logo_link: str | None = None, math: bool = False, search: bool = True, show_source: bool = True, template_directory: pathlib.Path | None = None):
39def configure(
40    *,
41    docformat: Literal[
42        "markdown", "google", "numpy", "restructuredtext"
43    ] = "restructuredtext",
44    edit_url_map: Mapping[str, str] | None = None,
45    favicon: str | None = None,
46    footer_text: str = "",
47    logo: str | None = None,
48    logo_link: str | None = None,
49    math: bool = False,
50    search: bool = True,
51    show_source: bool = True,
52    template_directory: Path | None = None,
53):
54    """
55    Configure the rendering output.
56
57    - `docformat` is the docstring flavor in use.
58      pdoc prefers plain Markdown (the default), but also supports other formats.
59    - `edit_url_map` is a mapping from module names to URL prefixes. For example,
60
61        ```json
62        {"pdoc": "https://github.com/mitmproxy/pdoc/blob/main/pdoc/"}
63        ```
64
65      renders the "Edit on GitHub" button on this page. The URL prefix can be modified to pin a particular version.
66    - `favicon` is an optional path/URL for a favicon image
67    - `footer_text` is additional text that should appear in the navigation footer.
68    - `logo` is an optional URL to the project's logo image
69    - `logo_link` is an optional URL the logo should point to
70    - `math` enables math rendering by including MathJax into the rendered documentation.
71    - `search` controls whether search functionality is enabled and a search index is built.
72    - `show_source` controls whether a "View Source" button should be included in the output.
73    - `template_directory` can be used to set an additional (preferred) directory
74      for templates. You can find an example in the main documentation of `pdoc`
75      or in `examples/custom-template`.
76    """
77    searchpath = _default_searchpath
78    if template_directory:
79        searchpath = [template_directory] + searchpath
80    env.loader = FileSystemLoader(searchpath)
81
82    env.globals["edit_url_map"] = edit_url_map or {}
83    env.globals["docformat"] = docformat
84    env.globals["math"] = math
85    env.globals["show_source"] = show_source
86    env.globals["favicon"] = favicon
87    env.globals["logo"] = logo
88    env.globals["logo_link"] = logo_link
89    env.globals["footer_text"] = footer_text
90    env.globals["search"] = search

Configure the rendering output.

  • docformat is the docstring flavor in use. pdoc prefers plain Markdown (the default), but also supports other formats.
  • edit_url_map is a mapping from module names to URL prefixes. For example,

    {"pdoc": "https://github.com/mitmproxy/pdoc/blob/main/pdoc/"}
    

    renders the "Edit on GitHub" button on this page. The URL prefix can be modified to pin a particular version.

  • favicon is an optional path/URL for a favicon image
  • footer_text is additional text that should appear in the navigation footer.
  • logo is an optional URL to the project's logo image
  • logo_link is an optional URL the logo should point to
  • math enables math rendering by including MathJax into the rendered documentation.
  • search controls whether search functionality is enabled and a search index is built.
  • show_source controls whether a "View Source" button should be included in the output.
  • template_directory can be used to set an additional (preferred) directory for templates. You can find an example in the main documentation of pdoc or in examples/custom-template.
@defuse_unsafe_reprs()
def html_module( module: pdoc.doc.Module, all_modules: Mapping[str, pdoc.doc.Module], mtime: str | None = None) -> str:
 93@defuse_unsafe_reprs()
 94def html_module(
 95    module: pdoc.doc.Module,
 96    all_modules: Mapping[str, pdoc.doc.Module],
 97    mtime: str | None = None,
 98) -> str:
 99    """
100    Renders the documentation for a `pdoc.doc.Module`.
101
102    - `all_modules` contains all modules that are rendered in this invocation.
103      This is used to determine which identifiers should be linked and which should not.
104    - If `mtime` is given, include additional JavaScript on the page for live-reloading.
105      This is only passed by `pdoc.web`.
106    """
107    return env.get_template("module.html.jinja2").render(
108        module=module,
109        all_modules=all_modules,
110        root_module_name=root_module_name(all_modules),
111        edit_url=edit_url(
112            module.modulename,
113            module.is_package,
114            cast(Mapping[str, str], env.globals["edit_url_map"]),
115        ),
116        mtime=mtime,
117    )

Renders the documentation for a pdoc.doc.Module.

  • all_modules contains all modules that are rendered in this invocation. This is used to determine which identifiers should be linked and which should not.
  • If mtime is given, include additional JavaScript on the page for live-reloading. This is only passed by pdoc.web.
@defuse_unsafe_reprs()
def html_index(all_modules: Mapping[str, pdoc.doc.Module]) -> str:
120@defuse_unsafe_reprs()
121def html_index(all_modules: Mapping[str, pdoc.doc.Module]) -> str:
122    """Renders the module index."""
123    return env.get_template("index.html.jinja2").render(
124        all_modules=all_modules,
125        root_module_name=root_module_name(all_modules),
126    )

Renders the module index.

@defuse_unsafe_reprs()
def html_error(error: str, details: str = '') -> str:
129@defuse_unsafe_reprs()
130def html_error(error: str, details: str = "") -> str:
131    """Renders an error message."""
132    return env.get_template("error.html.jinja2").render(
133        error=error,
134        details=details,
135    )

Renders an error message.

@defuse_unsafe_reprs()
def search_index(all_modules: Mapping[str, pdoc.doc.Module]) -> str:
138@defuse_unsafe_reprs()
139def search_index(all_modules: Mapping[str, pdoc.doc.Module]) -> str:
140    """Renders the Elasticlunr.js search index."""
141    if not env.globals["search"]:
142        return ""
143    # This is a rather terrible hack to determine if a given object is public and should be included in the index.
144    module_template: jinja2.Template = env.get_template("module.html.jinja2")
145    ctx: jinja2.runtime.Context = module_template.new_context(
146        {"module": pdoc.doc.Module(types.ModuleType("")), "all_modules": all_modules}
147    )
148    for _ in module_template.root_render_func(ctx):  # type: ignore
149        pass
150
151    def is_public(x: pdoc.doc.Doc) -> bool:
152        return bool(ctx["is_public"](x).strip())
153
154    index = make_index(
155        all_modules,
156        is_public,
157        cast(str, env.globals["docformat"]),
158    )
159
160    compile_js = Path(env.get_template("build-search-index.js").filename)  # type: ignore
161    return env.get_template("search.js.jinja2").render(
162        search_index=precompile_index(index, compile_js)
163    )

Renders the Elasticlunr.js search index.

@defuse_unsafe_reprs()
def repr_module(module: pdoc.doc.Module) -> str:
166@defuse_unsafe_reprs()
167def repr_module(module: pdoc.doc.Module) -> str:
168    """Renders `repr(pdoc.doc.Module)`, primarily used for tests and debugging."""
169    return repr(module)

Renders repr(pdoc.doc.Module), primarily used for tests and debugging.

env = <jinja2.environment.Environment object>

The Jinja2 environment used to render all templates. You can modify this object to add custom filters and globals.