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 in your project's settings.
  2. 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 members public that
    1. do not start with an underscore
    2. and are defined in the current module (i.e. they are not imported).

In general, we recommend keeping these 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.

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.

...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!

...render math formulas?

Run pdoc --math, and pdoc will render formulas in your docstrings. See math_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
"""

Since version 11, pdoc processes such reStructuredText elements by default.

...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.

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.
 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 in your project's settings.
 99 2. Copy pdoc's GitHub Actions workflow into your own repository and adjust it to how you build your docs:
100    [`.github/workflows/docs.yml`](https://github.com/mitmproxy/pdoc/blob/main/.github/workflows/docs.yml)
101
102That's it – no need to fiddle with any secrets or set up any `gh-pages` branches. 🥳
103
104# How can I ... ?
105
106## ...add documentation?
107
108In Python, objects like modules, functions and classes have
109a special attribute named `__doc__` which contains that object's
110*docstring*.  The docstring comes from a special placement of a string
111in your source code.  For example, the following code shows how to
112define a function with a docstring and access the contents of that
113docstring:
114
115```python
116>>> def test():
117...     """This is a docstring."""
118...     pass
119...
120>>> test.__doc__
121'This is a docstring.'
122```
123
124Something similar can be done for classes and modules too. For classes,
125the docstring should come on the line immediately following `class
126...`. For modules, the docstring should start on the first line of
127the file. These docstrings are what you see for each module, class,
128function and method listed in the documentation produced by pdoc.
129
130## ...document variables?
131
132Python itself [does not attach docstrings to
133variables](https://www.python.org/dev/peps/pep-0224/). For example:
134
135```python
136variable = "SomeValue"
137"""Docstring for variable."""
138```
139
140The resulting `variable` will have no `__doc__` attribute.
141To compensate, pdoc will read the abstract syntax tree (an abstract representation of the source code)
142and include all assignment statements immediately followed by a docstring. This approach is not formally standardized,
143but followed by many tools, including Sphinx's autodoc extension in case you ever decide to migrate off pdoc.
144Docstring detection is limited to the current module, docstrings for variables imported from other modules are not
145picked up.
146
147Something similar is done for instance variables, which are either type-annotated in the class
148or defined in a class's `__init__`. Here is an example showing both conventions detected by pdoc:
149
150```python
151class GoldenRetriever(Dog):
152    name: str
153    """Full Name"""
154
155    def __init__(self):
156        self.weight: int = 10
157        """Weight in kilograms"""
158```
159
160
161If you would like to distinguish an instance variable from a class variable,
162you can use [`typing.ClassVar`](https://docs.python.org/3/library/typing.html#typing.ClassVar):
163
164```python
165class GoldenRetriever(Dog):
166    breed_code: ClassVar[str] = "GOLD"
167    """International breed code (same for all instances)"""
168    name: str
169    """Full Name (different for each instance)"""
170```
171
172## ...control what is documented?
173
174The public interface of a module is determined through one of two
175ways.
176
177- If `__all__` is defined in the module, then all identifiers in that list will be considered public.
178   No other identifiers will be considered public.
179- If `__all__` is not defined, then pdoc will consider all members public that
180   1. do not start with an underscore
181   2. and are defined in the current module (i.e. they are not imported).
182
183In general, we recommend keeping these conventions:
184
185- If you want to document a private member, consider making it public.
186- If you want to hide a public member, consider making it private.
187- If you want to document a special `__dunder__` method, the recommended way to do so is
188  to not document the dunder method specifically, but to add some usage examples in the class documentation.
189
190As a last resort, you can override pdoc's behavior with a custom module template (see
191[*How can I edit pdoc's HTML template?*](#edit-pdocs-html-template)).
192You can find an example at
193[`examples/custom-template/module.html.jinja2`](https://github.com/mitmproxy/pdoc/blob/main/examples/custom-template/module.html.jinja2).
194
195### ...exclude submodules from being documented?
196
197If you would like to exclude specific submodules from the documentation, the recommended way is to specify `__all__` as
198shown in the previous section. Alternatively, you can pass negative regular expression `!patterns` as part of the
199module specification. Each pattern removes all previously specified (sub)module names that match. For example, the following
200invocation documents `foo` and all submodules of `foo`, but not `foo.bar`:
201
202```
203pdoc foo !foo.bar
204```
205
206Likewise, `pdoc pdoc !pdoc.` would document the pdoc module itself, but none of its submodules. Patterns are always
207matched on the final module name, even if modules are passed as file paths.
208
209## ...link to other identifiers?
210
211In your documentation, you can link to other identifiers by enclosing them in backticks:
212<code>\`pdoc\`</code> will link to `pdoc`.
213When linking to identifiers in other modules, the identifier name must be fully qualified.
214For example, <code>\`pdoc.doc.Doc\`</code> will be automatically linked to `pdoc.doc.Doc`,
215while <code>\`Doc\`</code> only works within the `pdoc.doc` module.
216
217pdoc will link all identifiers that are rendered in the current run.
218This means that you need to run `pdoc module_a module_b` to have interlinking between module_a and module_b.
219If you run `pdoc module_a` followed by `pdoc module_b`, there will be no cross-linking between the two modules.
220
221## ...change the item order?
222
223By default, documentation items are sorted in order of (first) appearance in the source code.
224This means that if you want to move a particular function to the beginning of your documentation,
225you need to move it there in your source code. This is not only useful to the readers of your documentation,
226but also useful to the consumers of your source code.
227
228## ...use numpydoc or Google docstrings?
229
230While pdoc prefers docstrings that are plain Markdown, it also understands numpydoc and Google-style docstrings.
231If your documentation follows one of these styles, you can:
232
2331. Run `pdoc --docformat ...` to enable a particular docstring flavor globally, or
2342. Add `__docformat__ = "..."` at the top-level of the module you are documenting.
235
236The following values are supported:
237
238- `markdown`: Process Markdown syntax only.
239- `restructuredtext`: Process reStructuredText elements, then Markdown (default setting).
240- `google`: Process reStructuredText elements, then Google-style syntax, then Markdown.
241- `numpy`: Process reStructuredText elements, then Numpydoc syntax, then Markdown.
242
243pdoc only interprets a subset of the reStructuredText specification.
244Adding additional syntax elements is usually easy. If you feel that pdoc doesn't parse a docstring element properly,
245please amend `pdoc.docstrings` and send us a pull request!
246
247
248## ...render math formulas?
249
250Run `pdoc --math`, and pdoc will render formulas in your docstrings. See
251[`math_demo`](https://pdoc.dev/docs/math/math_demo.html) for details.
252
253
254## ...add my project's logo?
255
256See [*Customizing pdoc*](#customizing-pdoc).
257
258
259## ...include Markdown files?
260
261You can include external Markdown files in your documentation by using reStructuredText's
262`.. include::` directive. For example, a common pattern is to include your project's README in your top-level `__init__.py` like this:
263
264```python
265"""
266.. include:: ../README.md
267"""
268```
269
270Since version 11, pdoc processes such reStructuredText elements by default.
271
272
273## ...edit pdoc's HTML template?
274
275For more advanced customization, we can edit pdoc's
276[default HTML template](https://github.com/mitmproxy/pdoc/blob/main/pdoc/templates/default/module.html.jinja2),
277which uses the
278[Jinja2](https://jinja.palletsprojects.com/) templating language.
279
280Let's assume you want to replace the logo with a custom button. We first find the right location in the template by searching
281for "logo", which shows us that the logo is defined in a Jinja2 block named `nav_title`.
282We now extend the default template by creating a file titled `module.html.jinja2` in the current directory
283 with the following contents:
284
285```html+jinja
286{% extends "default/module.html.jinja2" %}
287{% block nav_title %}
288<button>Donate dog food</button>
289{% endblock %}
290```
291
292We then specify our custom template directory when invoking pdoc...
293
294```shell
295pdoc -t . ./demo.py
296```
297
298...and the updated documentation – with button – renders! 🎉
299
300See [`examples/`](https://github.com/mitmproxy/pdoc/tree/main/examples/)
301for more examples.
302
303## ...pass arguments to the Jinja2 template?
304
305If you need to pass additional data to pdoc's Jinja2 templates,
306you can use system environment variables.
307For example,
308[`examples/custom-template/module.html.jinja2`](https://github.com/mitmproxy/pdoc/blob/main/examples/custom-template/module.html.jinja2)
309shows how to include a version number in the rendered HTML.
310
311
312## ...integrate pdoc into other systems?
313
314pdoc's HTML and CSS are written in a way that the default template can be easily adjusted
315to produce standalone HTML fragments that can be embedded in other systems.
316This makes it possible to integrate pdoc with almost every CMS or static site generator.
317The only limitation is that you need to retain pdoc's directory structure
318if you would like to link between modules.
319
320To do so, [create a custom `frame.html.jinja2` template](#edit-pdocs-html-template) which only emits CSS and the main
321page contents instead of a full standalone HTML document:
322```html+jinja
323{% block content %}{% endblock %}
324
325{% filter minify_css %}
326    {% block style %}
327        {# The same CSS files as in pdoc's default template, except for layout.css.
328        You may leave out Bootstrap Reboot, which corrects inconsistences across browsers
329        but may conflict with you website's stylesheet. #}
330        <style>{% include "resources/bootstrap-reboot.min.css" %}</style>
331        <style>{% include "syntax-highlighting.css" %}</style>
332        <style>{% include "theme.css" %}</style>
333        <style>{% include "content.css" %}</style>
334    {% endblock %}
335{% endfilter %}
336```
337
338This should be enough to produce HTML files that can be embedded into other pages.
339All CSS selectors are prefixed with `.pdoc` so that pdoc's page style does not interfere with the rest of your website.
340
341You can find a full example for mkdocs in [`examples/mkdocs`](https://github.com/mitmproxy/pdoc/tree/main/examples/mkdocs/).
342
343
344# Docstring Inheritance
345
346pdoc extends the standard use of docstrings in two important ways:
347by introducing variable docstrings (see [*How can I document variables?*](#document-variables)),
348and by allowing functions and classes to inherit docstrings and type annotations.
349
350This is useful to not unnecessarily repeat information. Consider this example:
351
352```python
353class Dog:
354    def bark(self, loud: bool) -> None:
355        """
356        Make the dog bark. If `loud` is True,
357        use full volume. Not supported by all breeds.
358        """
359
360class GoldenRetriever(Dog):
361    def bark(self, loud: bool) -> None:
362        print("Woof Woof")
363```
364
365In Python, the docstring for `GoldenRetriever.bark` is empty, even though one was
366defined in `Dog.bark`. If pdoc generates documentation for the above
367code, then it will automatically attach the docstring for `Dog.bark` to
368`GoldenRetriever.bark` if it does not have a docstring.
369
370
371# Limitations
372
373  - **Scope:** pdoc main use case is API documentation.
374    If you have substantially more complex documentation needs, we recommend using [Sphinx](https://www.sphinx-doc.org/)!
375  - **Dynamic analysis:** pdoc makes heavy use of dynamic analysis to extract docstrings.
376    This means your Python modules will be executed/imported when pdoc runs.
377  - **HTML Output:** pdoc only supports HTML as an output format. If you want to use pdoc with a static site
378    generator that only accepts Markdown, that may work nonetheless – take a look at
379    [integrating pdoc into other systems](https://pdoc.dev/docs/pdoc.html#integrate-pdoc-into-other-systems).
380
381
382# Using pdoc as a library
383
384pdoc provides the high-level `pdoc.pdoc()` interface explained below. This makes it possible to do custom adjustments
385to your Python code before pdoc is used.
386
387It is also possible to create `pdoc.doc.Module` objects directly and modify them before rendering.
388You can find an example in [`examples/library-usage`](https://github.com/mitmproxy/pdoc/tree/main/examples/library-usage).
389'''
390from __future__ import annotations
391
392__docformat__ = "markdown"  # explicitly disable rST processing in the examples above.
393__version__ = "12.0.0"  # this is read from setup.py
394
395import traceback
396import warnings
397from pathlib import Path
398from typing import overload
399
400from pdoc import doc, extract, render
401from pdoc._compat import Literal
402
403
404@overload
405def pdoc(
406    *modules: Path | str,
407    output_directory: None = None,
408    format: Literal["html"] = "html",
409) -> str:
410    pass
411
412
413@overload
414def pdoc(
415    *modules: Path | str,
416    output_directory: Path,
417    format: Literal["html"] = "html",
418) -> None:
419    pass
420
421
422def pdoc(
423    *modules: Path | str,
424    output_directory: Path | None = None,
425    format: Literal["html"] = "html",
426) -> str | None:
427    """
428    Render the documentation for a list of modules.
429
430     - If `output_directory` is `None`, returns the rendered documentation
431       for the first module in the list.
432     - If `output_directory` is set, recursively writes the rendered output
433       for all specified modules and their submodules to the target destination.
434
435    Rendering options can be configured by calling `pdoc.render.configure` in advance.
436    """
437    if format not in ("html", "markdown", "repr"):
438        raise ValueError(f"Invalid rendering format {format!r}.")
439    if format == "markdown":  # pragma: no cover
440        raise NotImplementedError(
441            "Markdown support is currently unimplemented, but PRs are welcome!"
442        )
443
444    all_modules: dict[str, doc.Module] = {}
445    for module_name in extract.walk_specs(modules):
446        try:
447            all_modules[module_name] = doc.Module.from_name(module_name)
448        except RuntimeError:
449            warnings.warn(f"Error importing {module_name}:\n{traceback.format_exc()}")
450
451    if not all_modules:
452        raise RuntimeError("Unable to import any modules.")
453
454    for module in all_modules.values():
455        if format == "html":
456            out = render.html_module(module, all_modules)
457        else:
458            out = render.repr_module(module)
459
460        if not output_directory:
461            return out
462        else:
463            outfile = output_directory / f"{module.fullname.replace('.', '/')}.html"
464            outfile.parent.mkdir(parents=True, exist_ok=True)
465            outfile.write_bytes(out.encode())
466
467    assert output_directory
468    if format == "html":
469        index = render.html_index(all_modules)
470        if index:
471            (output_directory / "index.html").write_bytes(index.encode())
472
473        search = render.search_index(all_modules)
474        if search:
475            (output_directory / "search.js").write_bytes(search.encode())
476
477    return None
def pdoc( *modules: pathlib.Path | str, output_directory: pathlib.Path | None = None, format: Literal['html'] = 'html') -> str | None:
423def pdoc(
424    *modules: Path | str,
425    output_directory: Path | None = None,
426    format: Literal["html"] = "html",
427) -> str | None:
428    """
429    Render the documentation for a list of modules.
430
431     - If `output_directory` is `None`, returns the rendered documentation
432       for the first module in the list.
433     - If `output_directory` is set, recursively writes the rendered output
434       for all specified modules and their submodules to the target destination.
435
436    Rendering options can be configured by calling `pdoc.render.configure` in advance.
437    """
438    if format not in ("html", "markdown", "repr"):
439        raise ValueError(f"Invalid rendering format {format!r}.")
440    if format == "markdown":  # pragma: no cover
441        raise NotImplementedError(
442            "Markdown support is currently unimplemented, but PRs are welcome!"
443        )
444
445    all_modules: dict[str, doc.Module] = {}
446    for module_name in extract.walk_specs(modules):
447        try:
448            all_modules[module_name] = doc.Module.from_name(module_name)
449        except RuntimeError:
450            warnings.warn(f"Error importing {module_name}:\n{traceback.format_exc()}")
451
452    if not all_modules:
453        raise RuntimeError("Unable to import any modules.")
454
455    for module in all_modules.values():
456        if format == "html":
457            out = render.html_module(module, all_modules)
458        else:
459            out = render.repr_module(module)
460
461        if not output_directory:
462            return out
463        else:
464            outfile = output_directory / f"{module.fullname.replace('.', '/')}.html"
465            outfile.parent.mkdir(parents=True, exist_ok=True)
466            outfile.write_bytes(out.encode())
467
468    assert output_directory
469    if format == "html":
470        index = render.html_index(all_modules)
471        if index:
472            (output_directory / "index.html").write_bytes(index.encode())
473
474        search = render.search_index(all_modules)
475        if search:
476            (output_directory / "search.js").write_bytes(search.encode())
477
478    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.