Edit on GitHub

pdoc

What is pdoc?

pdoc auto-generates API documentation that follows your project's Python module hierarchy.

pdoc's main feature is a focus on simplicity: pdoc aims to do one thing and do it well.

  • Easy setup, no configuration necessary.
  • Documentation is plain Markdown.
  • First-class support for type annotations.
  • Builtin web server with live reloading.
  • Customizable HTML templates.
  • Understands numpydoc and Google-style docstrings.

Quickstart

As an example, we want to generate API documentation for demo.py. Our demo module already includes a bunch of docstrings:

"""
A small `pdoc` example.
"""

class Dog:
    """🐕"""
    name: str
    """The name of our dog."""
    friends: list["Dog"]
    """The friends of our dog."""

    def __init__(self, name: str):
        """Make a Dog without any friends (yet)."""
        self.name = name
        self.friends = []

    def bark(self, loud: bool = True):
        """*woof*"""

We can invoke pdoc to take our docstrings and render them into a standalone HTML document:

pdoc ./demo.py  # or: pdoc my_module_name

This opens a browser with our module documentation. Here's a copy of what you should see:

If you look closely, you'll notice that docstrings are interpreted as Markdown. For example, `pdoc` is rendered as pdoc. Additionally, identifiers such as the type annotation for Dog.friends are automatically linked.

If we edit demo.py now, the page will reload automatically. Once we are happy with everything, we can export the documentation to an HTML file:

pdoc ./demo.py -o ./docs

This will create an HTML file at docs/demo.html which contains our module documentation. 🎉

Customizing pdoc

We can optionally configure pdoc's output via command line flags. For example, we can add a project logo to the documentation:

pdoc ./demo.py --logo "https://placedog.net/300?random"

To get a list of all available rendering options, run:

pdoc --help

If you need more advanced customization options, see How can I edit pdoc's HTML template?.

Deploying to GitHub Pages

In this example we'll deploy pdoc's documentation to GitHub Pages. Of course, you can distribute the generated documentation however you want! pdoc's job is to "just" produce self-contained HTML files for you.

A very simple way to host your API documentation is to set up a continuous integration job which pushes your documentation to GitHub Pages. This keeps your docs updated automatically.

  1. Enable GitHub Actions and GitHub Pages for your project.
  2. In the GitHub Pages settings, select GitHub Actions as your build and deployment source.
  3. Copy pdoc's GitHub Actions workflow into your own repository and adjust it to how you build your docs: .github/workflows/docs.yml

That's it – no need to fiddle with any secrets or set up any gh-pages branches. 🥳

How can I ... ?

...add documentation?

In Python, objects like modules, functions and classes have a special attribute named __doc__ which contains that object's docstring. The docstring comes from a special placement of a string in your source code. For example, the following code shows how to define a function with a docstring and access the contents of that docstring:

>>> def test():
...     """This is a docstring."""
...     pass
...
>>> test.__doc__
'This is a docstring.'

Something similar can be done for classes and modules too. For classes, the docstring should come on the line immediately following class .... For modules, the docstring should start on the first line of the file. These docstrings are what you see for each module, class, function and method listed in the documentation produced by pdoc.

...document variables?

Python itself does not attach docstrings to variables. For example:

variable = "SomeValue"
"""Docstring for variable."""

The resulting variable will have no __doc__ attribute. To compensate, pdoc will read the abstract syntax tree (an abstract representation of the source code) and include all assignment statements immediately followed by a docstring. This approach is not formally standardized, but followed by many tools, including Sphinx's autodoc extension in case you ever decide to migrate off pdoc. Docstring detection is limited to the current module, docstrings for variables imported from other modules are not picked up.

Something similar is done for instance variables, which are either type-annotated in the class or defined in a class's __init__. Here is an example showing both conventions detected by pdoc:

class GoldenRetriever(Dog):
    name: str
    """Full Name"""

    def __init__(self):
        self.weight: int = 10
        """Weight in kilograms"""

If you would like to distinguish an instance variable from a class variable, you can use typing.ClassVar:

class GoldenRetriever(Dog):
    breed_code: ClassVar[str] = "GOLD"
    """International breed code (same for all instances)"""
    name: str
    """Full Name (different for each instance)"""

...control what is documented?

The public interface of a module is determined through one of two ways.

  • If __all__ is defined in the module, then all identifiers in that list will be considered public. No other identifiers will be considered public.
  • If __all__ is not defined, then pdoc will consider all items public that do not start with an underscore and that are defined in the current module (i.e. they are not imported).

If you want to override the default behavior for a particular item, you can do so by including an annotation in its docstring:

  • @private hides an item unconditionally.
  • @public shows an item unconditionally.

In general, we recommend keeping the following conventions:

  • If you want to document a private member, consider making it public.
  • If you want to hide a public member, consider making it private.
  • If you want to document a special __dunder__ method, the recommended way to do so is to not document the dunder method specifically, but to add some usage examples in the class documentation.
Note

Hiding an item only removes it from documentation. It is still displayed in the source code when clicking the "View Source" button.

As a last resort, you can override pdoc's behavior with a custom module template (see How can I edit pdoc's HTML template?). You can find an example at examples/custom-template/module.html.jinja2.

Hiding an item only removes it from documentation. It is still displayed in the source code when clicking the "View Source" button.

...exclude submodules from being documented?

If you would like to exclude specific submodules from the documentation, the recommended way is to specify __all__ as shown in the previous section. Alternatively, you can pass negative regular expression !patterns as part of the module specification. Each pattern removes all previously specified (sub)module names that match. For example, the following invocation documents foo and all submodules of foo, but not foo.bar:

pdoc foo !foo.bar

Likewise, pdoc pdoc !pdoc. would document the pdoc module itself, but none of its submodules. Patterns are always matched on the final module name, even if modules are passed as file paths.

In your documentation, you can link to other identifiers by enclosing them in backticks: `pdoc` will link to pdoc. When linking to identifiers in other modules, the identifier name must be fully qualified. For example, `pdoc.doc.Doc` will be automatically linked to pdoc.doc.Doc, while `Doc` only works within the pdoc.doc module.

pdoc will link all identifiers that are rendered in the current run. This means that you need to run pdoc module_a module_b to have interlinking between module_a and module_b. If you run pdoc module_a followed by pdoc module_b, there will be no cross-linking between the two modules.

...change the item order?

By default, documentation items are sorted in order of (first) appearance in the source code. This means that if you want to move a particular function to the beginning of your documentation, you need to move it there in your source code. This is not only useful to the readers of your documentation, but also useful to the consumers of your source code.

...use numpydoc or Google docstrings?

While pdoc prefers docstrings that are plain Markdown, it also understands numpydoc and Google-style docstrings. If your documentation follows one of these styles, you can:

  1. Run pdoc --docformat ... to enable a particular docstring flavor globally, or
  2. Add __docformat__ = "..." at the top-level of the module you are documenting.

The following values are supported:

  • markdown: Process Markdown syntax only.
  • restructuredtext: Process reStructuredText elements, then Markdown (default setting).
  • google: Process reStructuredText elements, then Google-style syntax, then Markdown.
  • numpy: Process reStructuredText elements, then Numpydoc syntax, then Markdown.

pdoc only interprets a subset of the reStructuredText specification. Adding additional syntax elements is usually easy. If you feel that pdoc doesn't parse a docstring element properly, please amend pdoc.docstrings and send us a pull request!

...document Pydantic models?

For Pydantic models, pdoc will extract field descriptions and treat them just like documented variables. For example, the following two Pydantic models would have identical pdoc-rendered documentation:

from pydantic import BaseModel, Field

class Foo(BaseModel):
    a: int = Field(description="Docs for field a.")

class OtherFoo(BaseModel):
    a: int
    """Docs for field a."""

...render math formulas?

Run pdoc --math, and pdoc will render formulas in your docstrings. See math_demo for details.

...render Mermaid diagrams?

Run pdoc --mermaid, and pdoc will render mermaid diagrams in your docstrings. See mermaid_demo for details.

See Customizing pdoc.

...include Markdown files?

You can include external Markdown files in your documentation by using reStructuredText's .. include:: directive. For example, a common pattern is to include your project's README in your top-level __init__.py like this:

"""
.. include:: ../README.md
"""

You can also include only parts of a file with the start-line, end-line, start-after, and end-after options:

"""
.. include:: ../README.md
   :start-line: 1
   :end-before: Changelog
"""

...add a title page?

The landing page for your documentation is your project's top-level <modulename>/__init__.py file. Adding a module-level docstring here is a great way to introduce users to your project. For example, the documentation you are reading right now is sourced from pdoc/__init__.py. You can also include your title page from a Markdown file.

If you have multiple top-level modules, a custom title page requires modifying the index.html.jinja2 template. You can find an example in #410.

...edit pdoc's HTML template?

For more advanced customization, we can edit pdoc's default HTML template, which uses the Jinja2 templating language.

Let's assume you want to replace the logo with a custom button. We first find the right location in the template by searching for "logo", which shows us that the logo is defined in a Jinja2 block named nav_title. We now extend the default template by creating a file titled module.html.jinja2 in the current directory with the following contents:

{% extends "default/module.html.jinja2" %}
{% block nav_title %}
<button>Donate dog food</button>
{% endblock %}

We then specify our custom template directory when invoking pdoc...

pdoc -t . ./demo.py

...and the updated documentation – with button – renders! 🎉

See examples/ for more examples.

...pass arguments to the Jinja2 template?

If you need to pass additional data to pdoc's Jinja2 templates, you can use system environment variables. For example, examples/custom-template/module.html.jinja2 shows how to include a version number in the rendered HTML.

...integrate pdoc into other systems?

pdoc's HTML and CSS are written in a way that the default template can be easily adjusted to produce standalone HTML fragments that can be embedded in other systems. This makes it possible to integrate pdoc with almost every CMS or static site generator. The only limitation is that you need to retain pdoc's directory structure if you would like to link between modules.

To do so, create a custom frame.html.jinja2 template which only emits CSS and the main page contents instead of a full standalone HTML document:

{% block content %}{% endblock %}

{% filter minify_css %}
    {% block style %}
        {# The same CSS files as in pdoc's default template, except for layout.css.
        You may leave out Bootstrap Reboot, which corrects inconsistences across browsers
        but may conflict with you website's stylesheet. #}
        <style>{% include "resources/bootstrap-reboot.min.css" %}</style>
        <style>{% include "syntax-highlighting.css" %}</style>
        <style>{% include "theme.css" %}</style>
        <style>{% include "content.css" %}</style>
    {% endblock %}
{% endfilter %}

This should be enough to produce HTML files that can be embedded into other pages. All CSS selectors are prefixed with .pdoc so that pdoc's page style does not interfere with the rest of your website.

You can find a full example for mkdocs in examples/mkdocs.

Docstring Inheritance

pdoc extends the standard use of docstrings in two important ways: by introducing variable docstrings (see How can I document variables?), and by allowing functions and classes to inherit docstrings and type annotations.

This is useful to not unnecessarily repeat information. Consider this example:

class Dog:
    def bark(self, loud: bool) -> None:
        """
        Make the dog bark. If `loud` is True,
        use full volume. Not supported by all breeds.
        """

class GoldenRetriever(Dog):
    def bark(self, loud: bool) -> None:
        print("Woof Woof")

In Python, the docstring for GoldenRetriever.bark is empty, even though one was defined in Dog.bark. If pdoc generates documentation for the above code, then it will automatically attach the docstring for Dog.bark to GoldenRetriever.bark if it does not have a docstring.

Limitations

  • Scope: pdoc main use case is API documentation. If you have substantially more complex documentation needs, we recommend using Sphinx!
  • Dynamic analysis: pdoc makes heavy use of dynamic analysis to extract docstrings. This means your Python modules will be executed/imported when pdoc runs.
  • HTML Output: pdoc only supports HTML as an output format. If you want to use pdoc with a static site generator that only accepts Markdown, that may work nonetheless – take a look at integrating pdoc into other systems.

Markdown Support

Markdown is a lightweight and popular markup language for text formatting. There are many versions or "flavors" of Markdown. pdoc uses the markdown2 library, which closely matches the behavior of the original Markdown 1.0.1 spec. In addition, the following extra syntax elements are enabled:

  • code-friendly: Disable _ and __ for em and strong.
  • cuddled-lists: Allow lists to be cuddled to the preceding paragraph.
  • fenced-code-blocks: Allows a code block to not have to be indented by fencing it with ``` on a line before and after. Based on GitHub-Flavored Markdown with support for syntax highlighting.
  • footnotes: Support footnotes as in use on daringfireball.net and implemented in other Markdown processors.
  • header-ids: Adds "id" attributes to headers. The id value is a slug of the header text.
  • markdown-in-html: Allow the use of markdown="1" in a block HTML tag to have markdown processing be done on its contents. Similar to PHP-Markdown Extra but with some limitations.
  • mermaid: Allows rendering Mermaid diagrams from included Markdown files using ```mermaid fence blocks.
  • pyshell: Treats unindented Python interactive shell sessions as <code> blocks.
  • strike: Parse ~~strikethrough~~ formatting.
  • tables: Tables using the same format as GitHub-Flavored Markdown and PHP-Markdown Extra.
  • task_list: Allows GitHub-style task lists (i.e. check boxes)
  • toc: The returned HTML string gets a new "toc_html" attribute which is a Table of Contents for the document.

It is possible (but not recommended) to use another Markdown library with pdoc. See #401 for details.

Using pdoc as a library

pdoc provides the high-level pdoc.pdoc() interface explained below. This makes it possible to do custom adjustments to your Python code before pdoc is used.

It is also possible to create pdoc.doc.Module objects directly and modify them before rendering. You can find an example in examples/library-usage.

  1r'''
  2# What is pdoc?
  3
  4pdoc auto-generates API documentation that follows your project's Python module hierarchy.
  5
  6pdoc's main feature is a focus on simplicity: pdoc aims to do one thing and do it well.
  7
  8 - Easy setup, no configuration necessary.
  9 - Documentation is plain [Markdown](#markdown-support).
 10 - First-class support for type annotations.
 11 - Builtin web server with live reloading.
 12 - Customizable HTML templates.
 13 - Understands numpydoc and Google-style docstrings.
 14
 15# Quickstart
 16
 17As an example, we want to generate API documentation for `demo.py`.
 18Our demo module already includes a bunch of docstrings:
 19
 20```python
 21"""
 22A small `pdoc` example.
 23"""
 24
 25class Dog:
 26    """🐕"""
 27    name: str
 28    """The name of our dog."""
 29    friends: list["Dog"]
 30    """The friends of our dog."""
 31
 32    def __init__(self, name: str):
 33        """Make a Dog without any friends (yet)."""
 34        self.name = name
 35        self.friends = []
 36
 37    def bark(self, loud: bool = True):
 38        """*woof*"""
 39```
 40
 41We can invoke pdoc to take our docstrings and render them into a standalone HTML document:
 42
 43```shell
 44pdoc ./demo.py  # or: pdoc my_module_name
 45```
 46
 47This opens a browser with our module documentation. Here's a copy of what you should see:
 48
 49<iframe style="
 50    width: 100%;
 51    height: 250px;
 52    border: solid gray 1px;
 53    display: block;
 54    margin: 1rem auto;
 55    border-radius: 5px;"
 56    title="rendered demo.py documentation"
 57    src="https://pdoc.dev/docs/demo-standalone.html"></iframe>
 58
 59If you look closely, you'll notice that docstrings are interpreted as Markdown.
 60For example, \`pdoc\` is rendered as `pdoc`. Additionally, identifiers such as the type annotation
 61for `Dog.friends` are automatically linked.
 62
 63If we edit `demo.py` now, the page will reload automatically.
 64Once we are happy with everything, we can export the documentation to an HTML file:
 65
 66```shell
 67pdoc ./demo.py -o ./docs
 68```
 69
 70This will create an HTML file at `docs/demo.html` which contains our module documentation. 🎉
 71
 72## Customizing pdoc
 73
 74We can optionally configure pdoc's output via command line flags.
 75For example, we can add a project logo to the documentation:
 76
 77```shell
 78pdoc ./demo.py --logo "https://placedog.net/300?random"
 79```
 80
 81To get a list of all available rendering options, run:
 82
 83```shell
 84pdoc --help
 85```
 86
 87If you need more advanced customization options, see [*How can I edit pdoc's HTML template?*](#edit-pdocs-html-template).
 88
 89
 90## Deploying to GitHub Pages
 91
 92*In this example we'll deploy pdoc's documentation to GitHub Pages. Of course, you can distribute
 93the generated documentation however you want! pdoc's job is to "just" produce self-contained HTML files for you.*
 94
 95A very simple way to host your API documentation is to set up a continuous integration job which
 96pushes your documentation to GitHub Pages. This keeps your docs updated automatically.
 97
 98 1. Enable GitHub Actions and GitHub Pages for your project.
 99 2. In the GitHub Pages settings, select GitHub Actions as your build and deployment source.
100 3. Copy pdoc's GitHub Actions workflow into your own repository and adjust it to how you build your docs:
101    [`.github/workflows/docs.yml`](https://github.com/mitmproxy/pdoc/blob/main/.github/workflows/docs.yml)
102
103That's it – no need to fiddle with any secrets or set up any `gh-pages` branches. 🥳
104
105# How can I ... ?
106
107## ...add documentation?
108
109In Python, objects like modules, functions and classes have
110a special attribute named `__doc__` which contains that object's
111*docstring*.  The docstring comes from a special placement of a string
112in your source code.  For example, the following code shows how to
113define a function with a docstring and access the contents of that
114docstring:
115
116```python
117>>> def test():
118...     """This is a docstring."""
119...     pass
120...
121>>> test.__doc__
122'This is a docstring.'
123```
124
125Something similar can be done for classes and modules too. For classes,
126the docstring should come on the line immediately following `class
127...`. For modules, the docstring should start on the first line of
128the file. These docstrings are what you see for each module, class,
129function and method listed in the documentation produced by pdoc.
130
131
132## ...document variables?
133
134Python itself [does not attach docstrings to
135variables](https://www.python.org/dev/peps/pep-0224/). For example:
136
137```python
138variable = "SomeValue"
139"""Docstring for variable."""
140```
141
142The resulting `variable` will have no `__doc__` attribute.
143To compensate, pdoc will read the abstract syntax tree (an abstract representation of the source code)
144and include all assignment statements immediately followed by a docstring. This approach is not formally standardized,
145but followed by many tools, including Sphinx's autodoc extension in case you ever decide to migrate off pdoc.
146Docstring detection is limited to the current module, docstrings for variables imported from other modules are not
147picked up.
148
149Something similar is done for instance variables, which are either type-annotated in the class
150or defined in a class's `__init__`. Here is an example showing both conventions detected by pdoc:
151
152```python
153class GoldenRetriever(Dog):
154    name: str
155    """Full Name"""
156
157    def __init__(self):
158        self.weight: int = 10
159        """Weight in kilograms"""
160```
161
162
163If you would like to distinguish an instance variable from a class variable,
164you can use [`typing.ClassVar`](https://docs.python.org/3/library/typing.html#typing.ClassVar):
165
166```python
167class GoldenRetriever(Dog):
168    breed_code: ClassVar[str] = "GOLD"
169    """International breed code (same for all instances)"""
170    name: str
171    """Full Name (different for each instance)"""
172```
173
174
175## ...control what is documented?
176
177The public interface of a module is determined through one of two
178ways.
179- If `__all__` is defined in the module, then all identifiers in that list will be considered public.
180   No other identifiers will be considered public.
181- If `__all__` is not defined, then pdoc will consider all items public that do not start with an
182  underscore and that are defined in the current module (i.e. they are not imported).
183
184If you want to override the default behavior for a particular item,
185you can do so by including an annotation in its docstring:
186
187- `@private` hides an item unconditionally.
188- <code>&#64;public</code> shows an item unconditionally.
189
190In general, we recommend keeping the following conventions:
191
192- If you want to document a private member, consider making it public.
193- If you want to hide a public member, consider making it private.
194- If you want to document a special `__dunder__` method, the recommended way to do so is
195  to not document the dunder method specifically, but to add some usage examples in the class documentation.
196
197> [!NOTE]
198> Hiding an item only removes it from documentation.
199> It is still displayed in the source code when clicking the "View Source" button.
200
201As a last resort, you can override pdoc's behavior with a custom module template (see
202[*How can I edit pdoc's HTML template?*](#edit-pdocs-html-template)).
203You can find an example at
204[`examples/custom-template/module.html.jinja2`](https://github.com/mitmproxy/pdoc/blob/main/examples/custom-template/module.html.jinja2).
205
206Hiding an item only removes it from documentation. It is still displayed in the source code when clicking the "View Source" button.
207
208## ...exclude submodules from being documented?
209
210If you would like to exclude specific submodules from the documentation, the recommended way is to specify `__all__` as
211shown in the previous section. Alternatively, you can pass negative regular expression `!patterns` as part of the
212module specification. Each pattern removes all previously specified (sub)module names that match. For example, the following
213invocation documents `foo` and all submodules of `foo`, but not `foo.bar`:
214
215```
216pdoc foo !foo.bar
217```
218
219Likewise, `pdoc pdoc !pdoc.` would document the pdoc module itself, but none of its submodules. Patterns are always
220matched on the final module name, even if modules are passed as file paths.
221
222
223## ...link to other identifiers?
224
225In your documentation, you can link to other identifiers by enclosing them in backticks:
226<code>\`pdoc\`</code> will link to `pdoc`.
227When linking to identifiers in other modules, the identifier name must be fully qualified.
228For example, <code>\`pdoc.doc.Doc\`</code> will be automatically linked to `pdoc.doc.Doc`,
229while <code>\`Doc\`</code> only works within the `pdoc.doc` module.
230
231pdoc will link all identifiers that are rendered in the current run.
232This means that you need to run `pdoc module_a module_b` to have interlinking between module_a and module_b.
233If you run `pdoc module_a` followed by `pdoc module_b`, there will be no cross-linking between the two modules.
234
235
236## ...change the item order?
237
238By default, documentation items are sorted in order of (first) appearance in the source code.
239This means that if you want to move a particular function to the beginning of your documentation,
240you need to move it there in your source code. This is not only useful to the readers of your documentation,
241but also useful to the consumers of your source code.
242
243
244## ...use numpydoc or Google docstrings?
245
246While pdoc prefers docstrings that are plain Markdown, it also understands numpydoc and Google-style docstrings.
247If your documentation follows one of these styles, you can:
248
2491. Run `pdoc --docformat ...` to enable a particular docstring flavor globally, or
2502. Add `__docformat__ = "..."` at the top-level of the module you are documenting.
251
252The following values are supported:
253
254- `markdown`: Process Markdown syntax only.
255- `restructuredtext`: Process reStructuredText elements, then Markdown (default setting).
256- `google`: Process reStructuredText elements, then Google-style syntax, then Markdown.
257- `numpy`: Process reStructuredText elements, then Numpydoc syntax, then Markdown.
258
259pdoc only interprets a subset of the reStructuredText specification.
260Adding additional syntax elements is usually easy. If you feel that pdoc doesn't parse a docstring element properly,
261please amend `pdoc.docstrings` and send us a pull request!
262
263## ...document Pydantic models?
264
265For [Pydantic models](https://docs.pydantic.dev/latest/concepts/models/), pdoc
266will extract [field](https://docs.pydantic.dev/latest/concepts/fields/)
267descriptions and treat them just like [documented
268variables](#document-variables). For example, the following two Pydantic models
269would have identical pdoc-rendered documentation:
270
271```python
272from pydantic import BaseModel, Field
273
274class Foo(BaseModel):
275    a: int = Field(description="Docs for field a.")
276
277class OtherFoo(BaseModel):
278    a: int
279    """Docs for field a."""
280
281```
282
283## ...render math formulas?
284
285Run `pdoc --math`, and pdoc will render formulas in your docstrings. See
286[`math_demo`](https://pdoc.dev/docs/math/math_demo.html) for details.
287
288
289## ...render Mermaid diagrams?
290
291Run `pdoc --mermaid`, and pdoc will render mermaid diagrams in your docstrings. See
292[`mermaid_demo`](https://pdoc.dev/docs/mermaid/mermaid_demo.html) for details.
293
294
295## ...add my project's logo?
296
297See [*Customizing pdoc*](#customizing-pdoc).
298
299
300## ...include Markdown files?
301
302You can include external Markdown files in your documentation by using reStructuredText's
303`.. include::` directive. For example, a common pattern is to include your project's README in your top-level `__init__.py` like this:
304
305```python
306"""
307.. include:: ../README.md
308"""
309```
310
311You can also include only parts of a file with the
312[`start-line`, `end-line`, `start-after`, and `end-after` options](https://docutils.sourceforge.io/docs/ref/rst/directives.html#including-an-external-document-fragment):
313
314```python
315"""
316.. include:: ../README.md
317   :start-line: 1
318   :end-before: Changelog
319"""
320```
321
322
323## ...add a title page?
324
325The landing page for your documentation is your project's top-level `<modulename>/__init__.py` file.
326Adding a module-level docstring here is a great way to introduce users to your project.
327For example, the documentation you are reading right now is sourced from
328[`pdoc/__init__.py`](https://github.com/mitmproxy/pdoc/blob/main/pdoc/__init__.py).
329You can also include your title page from a [Markdown file](#include-markdown-files).
330
331If you have multiple top-level modules, a custom title page requires modifying the `index.html.jinja2` template.
332You can find an example in [#410](https://github.com/mitmproxy/pdoc/issues/410).
333
334## ...edit pdoc's HTML template?
335
336For more advanced customization, we can edit pdoc's
337[default HTML template](https://github.com/mitmproxy/pdoc/blob/main/pdoc/templates/default/module.html.jinja2),
338which uses the
339[Jinja2](https://jinja.palletsprojects.com/) templating language.
340
341Let's assume you want to replace the logo with a custom button. We first find the right location in the template by searching
342for "logo", which shows us that the logo is defined in a Jinja2 block named `nav_title`.
343We now extend the default template by creating a file titled `module.html.jinja2` in the current directory
344 with the following contents:
345
346```html+jinja
347{% extends "default/module.html.jinja2" %}
348{% block nav_title %}
349<button>Donate dog food</button>
350{% endblock %}
351```
352
353We then specify our custom template directory when invoking pdoc...
354
355```shell
356pdoc -t . ./demo.py
357```
358
359...and the updated documentation – with button – renders! 🎉
360
361See [`examples/`](https://github.com/mitmproxy/pdoc/tree/main/examples/)
362for more examples.
363
364
365## ...pass arguments to the Jinja2 template?
366
367If you need to pass additional data to pdoc's Jinja2 templates,
368you can use system environment variables.
369For example,
370[`examples/custom-template/module.html.jinja2`](https://github.com/mitmproxy/pdoc/blob/main/examples/custom-template/module.html.jinja2)
371shows how to include a version number in the rendered HTML.
372
373
374## ...integrate pdoc into other systems?
375
376pdoc's HTML and CSS are written in a way that the default template can be easily adjusted
377to produce standalone HTML fragments that can be embedded in other systems.
378This makes it possible to integrate pdoc with almost every CMS or static site generator.
379The only limitation is that you need to retain pdoc's directory structure
380if you would like to link between modules.
381
382To do so, [create a custom `frame.html.jinja2` template](#edit-pdocs-html-template) which only emits CSS and the main
383page contents instead of a full standalone HTML document:
384```html+jinja
385{% block content %}{% endblock %}
386
387{% filter minify_css %}
388    {% block style %}
389        {# The same CSS files as in pdoc's default template, except for layout.css.
390        You may leave out Bootstrap Reboot, which corrects inconsistences across browsers
391        but may conflict with you website's stylesheet. #}
392        <style>{% include "resources/bootstrap-reboot.min.css" %}</style>
393        <style>{% include "syntax-highlighting.css" %}</style>
394        <style>{% include "theme.css" %}</style>
395        <style>{% include "content.css" %}</style>
396    {% endblock %}
397{% endfilter %}
398```
399
400This should be enough to produce HTML files that can be embedded into other pages.
401All CSS selectors are prefixed with `.pdoc` so that pdoc's page style does not interfere with the rest of your website.
402
403You can find a full example for mkdocs in [`examples/mkdocs`](https://github.com/mitmproxy/pdoc/tree/main/examples/mkdocs/).
404
405
406# Docstring Inheritance
407
408pdoc extends the standard use of docstrings in two important ways:
409by introducing variable docstrings (see [*How can I document variables?*](#document-variables)),
410and by allowing functions and classes to inherit docstrings and type annotations.
411
412This is useful to not unnecessarily repeat information. Consider this example:
413
414```python
415class Dog:
416    def bark(self, loud: bool) -> None:
417        """
418        Make the dog bark. If `loud` is True,
419        use full volume. Not supported by all breeds.
420        """
421
422class GoldenRetriever(Dog):
423    def bark(self, loud: bool) -> None:
424        print("Woof Woof")
425```
426
427In Python, the docstring for `GoldenRetriever.bark` is empty, even though one was
428defined in `Dog.bark`. If pdoc generates documentation for the above
429code, then it will automatically attach the docstring for `Dog.bark` to
430`GoldenRetriever.bark` if it does not have a docstring.
431
432
433# Limitations
434
435  - **Scope:** pdoc main use case is API documentation.
436    If you have substantially more complex documentation needs, we recommend using [Sphinx](https://www.sphinx-doc.org/)!
437  - **Dynamic analysis:** pdoc makes heavy use of dynamic analysis to extract docstrings.
438    This means your Python modules will be executed/imported when pdoc runs.
439  - **HTML Output:** pdoc only supports HTML as an output format. If you want to use pdoc with a static site
440    generator that only accepts Markdown, that may work nonetheless – take a look at
441    [integrating pdoc into other systems](https://pdoc.dev/docs/pdoc.html#integrate-pdoc-into-other-systems).
442
443
444# Markdown Support
445
446[Markdown](https://guides.github.com/features/mastering-markdown/) is a lightweight and popular markup language for text
447formatting. There are many versions or *"flavors"* of Markdown.
448pdoc uses the [markdown2](https://github.com/trentm/python-markdown2) library, which closely matches
449the behavior of the original [Markdown 1.0.1 spec][].
450In addition, the following extra syntax elements are enabled:
451
452  - **[code-friendly][]:** Disable `_` and `__` for `em` and `strong`.
453  - **[cuddled-lists][]:** Allow lists to be cuddled to the preceding
454    paragraph.
455  - **[fenced-code-blocks][]:** Allows a code block to not have to be
456    indented by fencing it with <code>```</code> on a line before and after.
457    Based on [GitHub-Flavored Markdown][] with support for syntax highlighting.
458  - **[footnotes][]:** Support footnotes as in use on daringfireball.net
459    and implemented in other Markdown processors.
460  - **[header-ids][]:** Adds "id" attributes to headers. The id value
461    is a slug of the header text.
462  - **[markdown-in-html][]:** Allow the use of `markdown="1"` in a
463    block HTML tag to have markdown processing be done on its contents.
464    Similar to [PHP-Markdown Extra][] but with some limitations.
465  - **[mermaid][]:** Allows rendering Mermaid diagrams from included Markdown files using <code>```mermaid</code> fence blocks.
466  - **[pyshell][]:** Treats unindented Python interactive shell
467    sessions as `<code>` blocks.
468  - **strike:** Parse `~~strikethrough~~` formatting.
469  - **[tables][]:** Tables using the same format as [GitHub-Flavored Markdown][] and
470    [PHP-Markdown Extra][].
471  - **task_list:** Allows GitHub-style task lists (i.e. check boxes)
472  - **toc:** The returned HTML string gets a new "toc_html"
473    attribute which is a Table of Contents for the document.
474
475[Markdown 1.0.1 spec]: https://daringfireball.net/projects/markdown/
476[code-friendly]: https://github.com/trentm/python-markdown2/wiki/code-friendly
477[cuddled-lists]: https://github.com/trentm/python-markdown2/wiki/cuddled-lists
478[fenced-code-blocks]: https://github.com/trentm/python-markdown2/wiki/fenced-code-blocks
479[footnotes]: https://github.com/trentm/python-markdown2/wiki/footnotes
480[header-ids]: https://github.com/trentm/python-markdown2/wiki/header-ids
481[markdown-in-html]: https://github.com/trentm/python-markdown2/wiki/markdown-in-html
482[mermaid]: https://github.com/trentm/python-markdown2/wiki/mermaid
483[pyshell]: https://github.com/trentm/python-markdown2/wiki/pyshell
484[tables]: https://github.com/trentm/python-markdown2/wiki/tables
485[GitHub-Flavored Markdown]: https://github.github.com/gfm/
486[PHP-Markdown Extra]: https://michelf.ca/projects/php-markdown/extra/#table
487
488It is possible (but not recommended) to use another Markdown library with pdoc.
489See [#401](https://github.com/mitmproxy/pdoc/issues/401#issuecomment-1148661829) for details.
490
491# Using pdoc as a library
492
493pdoc provides the high-level `pdoc.pdoc()` interface explained below. This makes it possible to do custom adjustments
494to your Python code before pdoc is used.
495
496It is also possible to create `pdoc.doc.Module` objects directly and modify them before rendering.
497You can find an example in [`examples/library-usage`](https://github.com/mitmproxy/pdoc/tree/main/examples/library-usage).
498'''
499
500from __future__ import annotations
501
502__docformat__ = "markdown"  # explicitly disable rST processing in the examples above.
503__version__ = "16.0.0"  # this is read from setup.py
504
505from pathlib import Path
506from typing import overload
507
508from pdoc import doc
509from pdoc import extract
510from pdoc import render
511
512
513@overload
514def pdoc(
515    *modules: Path | str,
516    output_directory: None = None,
517) -> str:
518    pass
519
520
521@overload
522def pdoc(
523    *modules: Path | str,
524    output_directory: Path,
525) -> None:
526    pass
527
528
529def pdoc(
530    *modules: Path | str,
531    output_directory: Path | None = None,
532) -> str | None:
533    """
534    Render the documentation for a list of modules.
535
536     - If `output_directory` is `None`, returns the rendered documentation
537       for the first module in the list.
538     - If `output_directory` is set, recursively writes the rendered output
539       for all specified modules and their submodules to the target destination.
540
541    Rendering options can be configured by calling `pdoc.render.configure` in advance.
542    """
543    all_modules: dict[str, doc.Module] = {}
544    for module_name in extract.walk_specs(modules):
545        all_modules[module_name] = doc.Module.from_name(module_name)
546
547    for module in all_modules.values():
548        out = render.html_module(module, all_modules)
549        if not output_directory:
550            return out
551        else:
552            outfile = output_directory / f"{module.fullname.replace('.', '/')}.html"
553            outfile.parent.mkdir(parents=True, exist_ok=True)
554            outfile.write_bytes(out.encode())
555
556    assert output_directory
557
558    index = render.html_index(all_modules)
559    if index:
560        (output_directory / "index.html").write_bytes(index.encode())
561
562    search = render.search_index(all_modules)
563    if search:
564        (output_directory / "search.js").write_bytes(search.encode())
565
566    return None
def pdoc( *modules: pathlib.Path | str, output_directory: pathlib.Path | None = None) -> str | None:
530def pdoc(
531    *modules: Path | str,
532    output_directory: Path | None = None,
533) -> str | None:
534    """
535    Render the documentation for a list of modules.
536
537     - If `output_directory` is `None`, returns the rendered documentation
538       for the first module in the list.
539     - If `output_directory` is set, recursively writes the rendered output
540       for all specified modules and their submodules to the target destination.
541
542    Rendering options can be configured by calling `pdoc.render.configure` in advance.
543    """
544    all_modules: dict[str, doc.Module] = {}
545    for module_name in extract.walk_specs(modules):
546        all_modules[module_name] = doc.Module.from_name(module_name)
547
548    for module in all_modules.values():
549        out = render.html_module(module, all_modules)
550        if not output_directory:
551            return out
552        else:
553            outfile = output_directory / f"{module.fullname.replace('.', '/')}.html"
554            outfile.parent.mkdir(parents=True, exist_ok=True)
555            outfile.write_bytes(out.encode())
556
557    assert output_directory
558
559    index = render.html_index(all_modules)
560    if index:
561        (output_directory / "index.html").write_bytes(index.encode())
562
563    search = render.search_index(all_modules)
564    if search:
565        (output_directory / "search.js").write_bytes(search.encode())
566
567    return None

Render the documentation for a list of modules.

  • If output_directory is None, returns the rendered documentation for the first module in the list.
  • If output_directory is set, recursively writes the rendered output for all specified modules and their submodules to the target destination.

Rendering options can be configured by calling pdoc.render.configure in advance.