1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
|
Theming and other customizations
================================
Configure sidebar expanding/collapsing
--------------------------------------
By default, the sidebar only lists one level of objects (always expanded),
to allow objects to expand/collapse and show first nested content, use the following option::
--sidebar-expand-depth=2
This value describe how many nested modules and classes should be expandable.
.. note::
Careful, a value higher than ``1`` (which is the default) can make your HTML files
significantly larger if you have many modules or classes.
To disable completely the sidebar, use option ``--no-sidebar``
Theming
-------
Currently, there are 2 main themes packaged with pydoctor: ``classic`` and ``readthedocs``.
Choose your theme with option::
--theme
.. note::
Additionnaly, the ``base`` theme can be used as a base for customizations.
Tweak HTML templates
--------------------
They are 3 special files designed to be included in specific places of each pages.
- ``header.html``: at the very beginning of the body
- ``subheader.html``: after the main header, before the page title
- ``extra.css``: extra CSS sheet for layout customization
To include a file, write your custom HTML or CSS files to a directory
and use the following option::
--template-dir=./pydoctor_templates
If you want more customization, you can override the default templates in
`pydoctor/themes/base <https://github.com/twisted/pydoctor/tree/master/pydoctor/themes/base>`_
with the same method.
HTML templates have their own versioning system and warnings will be triggered when an outdated custom template is used.
.. admonition:: Demo theme example
There is a demo template inspired by Twisted web page for which the source code is `here <https://github.com/twisted/pydoctor/tree/master/docs/sample_template>`_.
You can try the result by checking `this page <custom_template_demo/pydoctor.html>`_.
.. note::
This example is using the ``base`` theme.
.. _customize-privacy:
Override objects privacy (show/hide)
------------------------------------
Pydoctor supports 3 types of privacy.
Below is the description of each type and the default association:
- ``PRIVATE``: By default for objects whose name starts with an underscore and are not a dunder method.
Rendered in HTML, but hidden via CSS by default.
- ``PUBLIC``: By default everything else that is not private.
Always rendered and visible in HTML.
- ``HIDDEN``: Nothing is hidden by default.
Not rendered at all and no links can be created to hidden objects.
Not present in the search index nor the intersphinx inventory.
Basically excluded from API documentation. If a module/package/class is hidden, then all it's members are hidden as well.
When the default rules regarding privacy doesn't fit your use case,
use the ``--privacy`` command line option.
It can be used multiple times to define multiple privacy rules::
--privacy=<PRIVACY>:<PATTERN>
where ``<PRIVACY>`` can be one of ``PUBLIC``, ``PRIVATE`` or ``HIDDEN`` (case insensitive), and ``<PATTERN>`` is fnmatch-like
pattern matching objects fullName.
Privacy tweak examples
^^^^^^^^^^^^^^^^^^^^^^
- ``--privacy="PUBLIC:**"``
Makes everything public.
- ``--privacy="HIDDEN:twisted.test.*" --privacy="PUBLIC:twisted.test.proto_helpers"``
Makes everything under ``twisted.test`` hidden except ``twisted.test.proto_helpers``, which will be public.
- ``--privacy="PRIVATE:**.__*__" --privacy="PUBLIC:**.__init__"``
Makes all dunder methods private except ``__init__``.
.. important:: The order of arguments matters. Pattern added last have priority over a pattern added before,
but an exact match wins over a fnmatch.
.. note:: See :py:mod:`pydoctor.qnmatch` for more informations regarding the pattern syntax.
.. note:: Quotation marks should be added around each rule to avoid shell expansions.
Unless the arguments are passed directly to pydoctor, like in Sphinx's ``conf.py``, in this case you must not quote the privacy rules.
Use a custom system class
-------------------------
You can subclass the :py:class:`pydoctor.model.System`
and pass your custom class dotted name with the following argument::
--system-class=mylib._pydoctor.CustomSystem
System class allows you to customize certain aspect of the system and configure the enabled extensions.
If what you want to achieve has something to do with the state of some objects in the Documentable tree,
it's very likely that you can do it without the need to override any system method,
by using the extension mechanism described below.
Brief on pydoctor extensions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The AST builder can now be customized with extension modules.
This is how we handle Zope Interfaces declarations and :py:mod:`twisted.python.deprecate` warnings.
Each pydocotor extension is a Python module with at least a ``setup_pydoctor_extension()`` function.
This function is called at initialization of the system with one argument,
the :py:class:`pydoctor.extensions.ExtRegistrar` object representing the system.
An extension can register multiple kind of components:
- AST builder visitors
- Mixin classes for :py:class:`pydoctor.model.Documentable`
- Post processors
Take a look at built-in extensions :py:mod:`pydoctor.extensions.zopeinterface` and :py:mod:`pydoctor.extensions.deprecate`.
Navigate to the source code for a better overview.
A concrete example
^^^^^^^^^^^^^^^^^^
Let's say you want to write a extension for simple pydantic classes like this one:
.. code:: python
from typing import ClassVar
from pydantic import BaseModel
class Model(BaseModel):
a: int
b: int = Field(...)
name:str = 'Jane Doe'
kind:ClassVar = 'person'
First, we need to create a new module that will hold our extension code: ``mylib._pydoctor``.
This module will contain visitor code that visits ``ast.AnnAssign`` nodes after the main visitor.
It will check if the current context object is a class derived from ``pydantic.BaseModel`` and
transform each class variable into instance variables accordingly.
.. code:: python
# Module mylib._pydoctor
import ast
from pydoctor import astutils, extensions, model
class PydanticModVisitor(extensions.ModuleVisitorExt):
def depart_AnnAssign(self, node: ast.AnnAssign) -> None:
"""
Called after an annotated assignment definition is visited.
"""
ctx = self.visitor.builder.current
if not isinstance(ctx, model.Class):
# check if the current context object is a class
return
if not any(ctx.expandName(b) == 'pydantic.BaseModel' for b in ctx.bases):
# check if the current context object if a class derived from ``pydantic.BaseModel``
return
dottedname = astutils.node2dottedname(node.target)
if not dottedname or len(dottedname)!=1:
# check if the assignment is a simple name, otherwise ignore it
return
# Get the attribute from current context
attr = ctx.contents[dottedname[0]]
assert isinstance(attr, model.Attribute)
# All class variables that are not annotated with ClassVar will be transformed to instance variables.
if astutils.is_using_typing_classvar(attr.annotation, attr):
return
if attr.kind == model.DocumentableKind.CLASS_VARIABLE:
attr.kind = model.DocumentableKind.INSTANCE_VARIABLE
def setup_pydoctor_extension(r:extensions.ExtRegistrar) -> None:
r.register_astbuilder_visitor(PydanticModVisitor)
class PydanticSystem(model.System):
# Declare that this system should load this additional extension
custom_extensions = ['mylib._pydoctor']
Then, we would pass our custom class dotted name with the argument ``--system-class``::
--system-class=mylib._pydoctor.PydanticSystem
Et voilĂ .
If this extension mechanism doesn't support the tweak you want, you can consider overriding some
:py:class:`pydoctor.model.System` methods. For instance, overriding :py:meth:`pydoctor.model.System.__init__` method could be useful,
if some want to write a custom :py:class:`pydoctor.sphinx.SphinxInventory`.
.. important::
If you feel like other users of the community might benefit from your extension as well, please
don't hesitate to open a pull request adding your extension module to the package :py:mod:`pydoctor.extensions`.
Use a custom writer class
-------------------------
You can subclass the :py:class:`pydoctor.templatewriter.TemplateWriter` (or the abstract super class :py:class:`pydoctor.templatewriter.IWriter`)
and pass your custom class dotted name with the following argument::
--html-writer=mylib._pydoctor.CustomTemplateWriter
The option is actually badly named because, theorically one could write a subclass
of :py:class:`pydoctor.templatewriter.IWriter` (to be used alongside option ``--template-dir``)
that would output Markdown, reStructuredText or JSON.
.. warning:: Pydoctor does not have a stable API yet. Code customization is prone
to break in future versions.
|