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.
- Enable GitHub Actions and GitHub Pages for your project.
- In the GitHub Pages settings, select GitHub Actions as your build and deployment source.
- 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- do not start with an underscore,
- don't have
@private
in their docstring, - 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 or add
@private
to their docstring, - 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.
...link to other identifiers?
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:
- Run
pdoc --docformat ...
to enable a particular docstring flavor globally, or - 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.
...render Mermaid diagrams?
Run pdoc --mermaid
, and pdoc will render mermaid diagrams in your docstrings. See
mermaid_demo
for details.
...add my project's logo?
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.
...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__
forem
andstrong
. - 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()
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 180- If `__all__` is defined in the module, then all identifiers in that list will be considered public. 181 No other identifiers will be considered public. 182- If `__all__` is not defined, then pdoc will consider all members public that 183 1. do not start with an underscore, 184 2. don't have `@private` in their docstring, 185 2. and are defined in the current module (i.e. they are not imported). 186 187In general, we recommend keeping these conventions: 188 189- If you want to document a private member, consider making it public. 190- If you want to hide a public member, consider making it private or add `@private` to their docstring, 191- If you want to document a special `__dunder__` method, the recommended way to do so is 192 to not document the dunder method specifically, but to add some usage examples in the class documentation. 193 194As a last resort, you can override pdoc's behavior with a custom module template (see 195[*How can I edit pdoc's HTML template?*](#edit-pdocs-html-template)). 196You can find an example at 197[`examples/custom-template/module.html.jinja2`](https://github.com/mitmproxy/pdoc/blob/main/examples/custom-template/module.html.jinja2). 198 199## ...exclude submodules from being documented? 200 201If you would like to exclude specific submodules from the documentation, the recommended way is to specify `__all__` as 202shown in the previous section. Alternatively, you can pass negative regular expression `!patterns` as part of the 203module specification. Each pattern removes all previously specified (sub)module names that match. For example, the following 204invocation documents `foo` and all submodules of `foo`, but not `foo.bar`: 205 206``` 207pdoc foo !foo.bar 208``` 209 210Likewise, `pdoc pdoc !pdoc.` would document the pdoc module itself, but none of its submodules. Patterns are always 211matched on the final module name, even if modules are passed as file paths. 212 213 214## ...link to other identifiers? 215 216In your documentation, you can link to other identifiers by enclosing them in backticks: 217<code>\`pdoc\`</code> will link to `pdoc`. 218When linking to identifiers in other modules, the identifier name must be fully qualified. 219For example, <code>\`pdoc.doc.Doc\`</code> will be automatically linked to `pdoc.doc.Doc`, 220while <code>\`Doc\`</code> only works within the `pdoc.doc` module. 221 222pdoc will link all identifiers that are rendered in the current run. 223This means that you need to run `pdoc module_a module_b` to have interlinking between module_a and module_b. 224If you run `pdoc module_a` followed by `pdoc module_b`, there will be no cross-linking between the two modules. 225 226 227## ...change the item order? 228 229By default, documentation items are sorted in order of (first) appearance in the source code. 230This means that if you want to move a particular function to the beginning of your documentation, 231you need to move it there in your source code. This is not only useful to the readers of your documentation, 232but also useful to the consumers of your source code. 233 234 235## ...use numpydoc or Google docstrings? 236 237While pdoc prefers docstrings that are plain Markdown, it also understands numpydoc and Google-style docstrings. 238If your documentation follows one of these styles, you can: 239 2401. Run `pdoc --docformat ...` to enable a particular docstring flavor globally, or 2412. Add `__docformat__ = "..."` at the top-level of the module you are documenting. 242 243The following values are supported: 244 245- `markdown`: Process Markdown syntax only. 246- `restructuredtext`: Process reStructuredText elements, then Markdown (default setting). 247- `google`: Process reStructuredText elements, then Google-style syntax, then Markdown. 248- `numpy`: Process reStructuredText elements, then Numpydoc syntax, then Markdown. 249 250pdoc only interprets a subset of the reStructuredText specification. 251Adding additional syntax elements is usually easy. If you feel that pdoc doesn't parse a docstring element properly, 252please amend `pdoc.docstrings` and send us a pull request! 253 254 255## ...render math formulas? 256 257Run `pdoc --math`, and pdoc will render formulas in your docstrings. See 258[`math_demo`](https://pdoc.dev/docs/math/math_demo.html) for details. 259 260 261## ...render Mermaid diagrams? 262 263Run `pdoc --mermaid`, and pdoc will render mermaid diagrams in your docstrings. See 264[`mermaid_demo`](https://pdoc.dev/docs/mermaid/mermaid_demo.html) for details. 265 266 267## ...add my project's logo? 268 269See [*Customizing pdoc*](#customizing-pdoc). 270 271 272## ...include Markdown files? 273 274You can include external Markdown files in your documentation by using reStructuredText's 275`.. include::` directive. For example, a common pattern is to include your project's README in your top-level `__init__.py` like this: 276 277```python 278""" 279.. include:: ../README.md 280""" 281``` 282 283Since version 11, pdoc processes such reStructuredText elements by default. 284 285 286## ...add a title page? 287 288The landing page for your documentation is your project's top-level `<modulename>/__init__.py` file. 289Adding a module-level docstring here is a great way to introduce users to your project. 290For example, the documentation you are reading right now is sourced from 291[`pdoc/__init__.py`](https://github.com/mitmproxy/pdoc/blob/main/pdoc/__init__.py). 292You can also include your title page from a [Markdown file](#include-markdown-files). 293 294If you have multiple top-level modules, a custom title page requires modifying the `index.html.jinja2` template. 295You can find an example in [#410](https://github.com/mitmproxy/pdoc/issues/410). 296 297## ...edit pdoc's HTML template? 298 299For more advanced customization, we can edit pdoc's 300[default HTML template](https://github.com/mitmproxy/pdoc/blob/main/pdoc/templates/default/module.html.jinja2), 301which uses the 302[Jinja2](https://jinja.palletsprojects.com/) templating language. 303 304Let's assume you want to replace the logo with a custom button. We first find the right location in the template by searching 305for "logo", which shows us that the logo is defined in a Jinja2 block named `nav_title`. 306We now extend the default template by creating a file titled `module.html.jinja2` in the current directory 307 with the following contents: 308 309```html+jinja 310{% extends "default/module.html.jinja2" %} 311{% block nav_title %} 312<button>Donate dog food</button> 313{% endblock %} 314``` 315 316We then specify our custom template directory when invoking pdoc... 317 318```shell 319pdoc -t . ./demo.py 320``` 321 322...and the updated documentation – with button – renders! 🎉 323 324See [`examples/`](https://github.com/mitmproxy/pdoc/tree/main/examples/) 325for more examples. 326 327 328## ...pass arguments to the Jinja2 template? 329 330If you need to pass additional data to pdoc's Jinja2 templates, 331you can use system environment variables. 332For example, 333[`examples/custom-template/module.html.jinja2`](https://github.com/mitmproxy/pdoc/blob/main/examples/custom-template/module.html.jinja2) 334shows how to include a version number in the rendered HTML. 335 336 337## ...integrate pdoc into other systems? 338 339pdoc's HTML and CSS are written in a way that the default template can be easily adjusted 340to produce standalone HTML fragments that can be embedded in other systems. 341This makes it possible to integrate pdoc with almost every CMS or static site generator. 342The only limitation is that you need to retain pdoc's directory structure 343if you would like to link between modules. 344 345To do so, [create a custom `frame.html.jinja2` template](#edit-pdocs-html-template) which only emits CSS and the main 346page contents instead of a full standalone HTML document: 347```html+jinja 348{% block content %}{% endblock %} 349 350{% filter minify_css %} 351 {% block style %} 352 {# The same CSS files as in pdoc's default template, except for layout.css. 353 You may leave out Bootstrap Reboot, which corrects inconsistences across browsers 354 but may conflict with you website's stylesheet. #} 355 <style>{% include "resources/bootstrap-reboot.min.css" %}</style> 356 <style>{% include "syntax-highlighting.css" %}</style> 357 <style>{% include "theme.css" %}</style> 358 <style>{% include "content.css" %}</style> 359 {% endblock %} 360{% endfilter %} 361``` 362 363This should be enough to produce HTML files that can be embedded into other pages. 364All CSS selectors are prefixed with `.pdoc` so that pdoc's page style does not interfere with the rest of your website. 365 366You can find a full example for mkdocs in [`examples/mkdocs`](https://github.com/mitmproxy/pdoc/tree/main/examples/mkdocs/). 367 368 369# Docstring Inheritance 370 371pdoc extends the standard use of docstrings in two important ways: 372by introducing variable docstrings (see [*How can I document variables?*](#document-variables)), 373and by allowing functions and classes to inherit docstrings and type annotations. 374 375This is useful to not unnecessarily repeat information. Consider this example: 376 377```python 378class Dog: 379 def bark(self, loud: bool) -> None: 380 """ 381 Make the dog bark. If `loud` is True, 382 use full volume. Not supported by all breeds. 383 """ 384 385class GoldenRetriever(Dog): 386 def bark(self, loud: bool) -> None: 387 print("Woof Woof") 388``` 389 390In Python, the docstring for `GoldenRetriever.bark` is empty, even though one was 391defined in `Dog.bark`. If pdoc generates documentation for the above 392code, then it will automatically attach the docstring for `Dog.bark` to 393`GoldenRetriever.bark` if it does not have a docstring. 394 395 396# Limitations 397 398 - **Scope:** pdoc main use case is API documentation. 399 If you have substantially more complex documentation needs, we recommend using [Sphinx](https://www.sphinx-doc.org/)! 400 - **Dynamic analysis:** pdoc makes heavy use of dynamic analysis to extract docstrings. 401 This means your Python modules will be executed/imported when pdoc runs. 402 - **HTML Output:** pdoc only supports HTML as an output format. If you want to use pdoc with a static site 403 generator that only accepts Markdown, that may work nonetheless – take a look at 404 [integrating pdoc into other systems](https://pdoc.dev/docs/pdoc.html#integrate-pdoc-into-other-systems). 405 406 407# Markdown Support 408 409[Markdown](https://guides.github.com/features/mastering-markdown/) is a lightweight and popular markup language for text 410formatting. There are many versions or *"flavors"* of Markdown. 411pdoc uses the [markdown2](https://github.com/trentm/python-markdown2) library, which closely matches 412the behavior of the original [Markdown 1.0.1 spec][]. 413In addition, the following extra syntax elements are enabled: 414 415 - **[code-friendly][]:** Disable `_` and `__` for `em` and `strong`. 416 - **[cuddled-lists][]:** Allow lists to be cuddled to the preceding 417 paragraph. 418 - **[fenced-code-blocks][]:** Allows a code block to not have to be 419 indented by fencing it with <code>```</code> on a line before and after. 420 Based on [GitHub-Flavored Markdown][] with support for syntax highlighting. 421 - **[footnotes][]:** Support footnotes as in use on daringfireball.net 422 and implemented in other Markdown processors. 423 - **[header-ids][]:** Adds "id" attributes to headers. The id value 424 is a slug of the header text. 425 - **[markdown-in-html][]:** Allow the use of `markdown="1"` in a 426 block HTML tag to have markdown processing be done on its contents. 427 Similar to [PHP-Markdown Extra][] but with some limitations. 428 - **[mermaid][]:** Allows rendering Mermaid diagrams from included Markdown files using <code>```mermaid</code> fence blocks. 429 - **[pyshell][]:** Treats unindented Python interactive shell 430 sessions as `<code>` blocks. 431 - **strike:** Parse `~~strikethrough~~` formatting. 432 - **[tables][]:** Tables using the same format as [GitHub-Flavored Markdown][] and 433 [PHP-Markdown Extra][]. 434 - **task_list:** Allows GitHub-style task lists (i.e. check boxes) 435 - **toc:** The returned HTML string gets a new "toc_html" 436 attribute which is a Table of Contents for the document. 437 438[Markdown 1.0.1 spec]: https://daringfireball.net/projects/markdown/ 439[code-friendly]: https://github.com/trentm/python-markdown2/wiki/code-friendly 440[cuddled-lists]: https://github.com/trentm/python-markdown2/wiki/cuddled-lists 441[fenced-code-blocks]: https://github.com/trentm/python-markdown2/wiki/fenced-code-blocks 442[footnotes]: https://github.com/trentm/python-markdown2/wiki/footnotes 443[header-ids]: https://github.com/trentm/python-markdown2/wiki/header-ids 444[markdown-in-html]: https://github.com/trentm/python-markdown2/wiki/markdown-in-html 445[mermaid]: https://github.com/trentm/python-markdown2/wiki/mermaid 446[pyshell]: https://github.com/trentm/python-markdown2/wiki/pyshell 447[tables]: https://github.com/trentm/python-markdown2/wiki/tables 448[GitHub-Flavored Markdown]: https://github.github.com/gfm/ 449[PHP-Markdown Extra]: https://michelf.ca/projects/php-markdown/extra/#table 450 451It is possible (but not recommended) to use another Markdown library with pdoc. 452See [#401](https://github.com/mitmproxy/pdoc/issues/401#issuecomment-1148661829) for details. 453 454# Using pdoc as a library 455 456pdoc provides the high-level `pdoc.pdoc()` interface explained below. This makes it possible to do custom adjustments 457to your Python code before pdoc is used. 458 459It is also possible to create `pdoc.doc.Module` objects directly and modify them before rendering. 460You can find an example in [`examples/library-usage`](https://github.com/mitmproxy/pdoc/tree/main/examples/library-usage). 461''' 462from __future__ import annotations 463 464__docformat__ = "markdown" # explicitly disable rST processing in the examples above. 465__version__ = "14.1.0" # this is read from setup.py 466 467from pathlib import Path 468from typing import overload 469 470from pdoc import doc 471from pdoc import extract 472from pdoc import render 473 474 475@overload 476def pdoc( 477 *modules: Path | str, 478 output_directory: None = None, 479) -> str: 480 pass 481 482 483@overload 484def pdoc( 485 *modules: Path | str, 486 output_directory: Path, 487) -> None: 488 pass 489 490 491def pdoc( 492 *modules: Path | str, 493 output_directory: Path | None = None, 494) -> str | None: 495 """ 496 Render the documentation for a list of modules. 497 498 - If `output_directory` is `None`, returns the rendered documentation 499 for the first module in the list. 500 - If `output_directory` is set, recursively writes the rendered output 501 for all specified modules and their submodules to the target destination. 502 503 Rendering options can be configured by calling `pdoc.render.configure` in advance. 504 """ 505 all_modules: dict[str, doc.Module] = {} 506 for module_name in extract.walk_specs(modules): 507 all_modules[module_name] = doc.Module.from_name(module_name) 508 509 for module in all_modules.values(): 510 out = render.html_module(module, all_modules) 511 if not output_directory: 512 return out 513 else: 514 outfile = output_directory / f"{module.fullname.replace('.', '/')}.html" 515 outfile.parent.mkdir(parents=True, exist_ok=True) 516 outfile.write_bytes(out.encode()) 517 518 assert output_directory 519 520 index = render.html_index(all_modules) 521 if index: 522 (output_directory / "index.html").write_bytes(index.encode()) 523 524 search = render.search_index(all_modules) 525 if search: 526 (output_directory / "search.js").write_bytes(search.encode()) 527 528 return None
492def pdoc( 493 *modules: Path | str, 494 output_directory: Path | None = None, 495) -> str | None: 496 """ 497 Render the documentation for a list of modules. 498 499 - If `output_directory` is `None`, returns the rendered documentation 500 for the first module in the list. 501 - If `output_directory` is set, recursively writes the rendered output 502 for all specified modules and their submodules to the target destination. 503 504 Rendering options can be configured by calling `pdoc.render.configure` in advance. 505 """ 506 all_modules: dict[str, doc.Module] = {} 507 for module_name in extract.walk_specs(modules): 508 all_modules[module_name] = doc.Module.from_name(module_name) 509 510 for module in all_modules.values(): 511 out = render.html_module(module, all_modules) 512 if not output_directory: 513 return out 514 else: 515 outfile = output_directory / f"{module.fullname.replace('.', '/')}.html" 516 outfile.parent.mkdir(parents=True, exist_ok=True) 517 outfile.write_bytes(out.encode()) 518 519 assert output_directory 520 521 index = render.html_index(all_modules) 522 if index: 523 (output_directory / "index.html").write_bytes(index.encode()) 524 525 search = render.search_index(all_modules) 526 if search: 527 (output_directory / "search.js").write_bytes(search.encode()) 528 529 return None
Render the documentation for a list of modules.
- If
output_directory
isNone
, 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.