Doc generators » Python docs

A mod­ern, mo­bile-friend­ly Sphinx-com­pat­i­ble Python doc­u­men­ta­tion gen­er­a­tor with a first-class search func­tion­al­i­ty. Gen­er­at­ed by in­spect­ing Python mod­ules and us­ing ei­ther em­bed­ded doc­strings or ex­ter­nal reST files to pop­u­late the doc­u­men­ta­tion.

One of the de­sign goals is pro­vid­ing a sim­i­lar us­er ex­pe­ri­ence to the Doxy­gen doc­u­men­ta­tion theme.

Ba­sic us­age

The base is con­tained in a sin­gle Python script and re­lat­ed style/tem­plate files, for ad­vanced fea­tures such as math ren­der­ing it’ll make use of in­ter­nals of some m.css plug­ins. Clone the m.css GitHub re­pos­i­to­ry and look in­to the documentation/ di­rec­to­ry:

git clone git://github.com/mosra/m.css
cd m.css/documentation

The script re­quires Python 3.6 and de­pends on Jin­ja2 for tem­plat­ing and do­cu­tils for reST markup ren­der­ing. You can in­stall the de­pen­den­cies via pip or your dis­tri­bu­tion pack­age man­ag­er, in most cas­es you’ll prob­a­bly have them al­ready in­stalled:

# You may need sudo here
pip3 install docutils jinja2

Next, you need a con­fig­u­ra­tion file which tells the script what mod­ules to in­spect, how to name the project and where to put the out­put. In this ex­am­ple, we’ll gen­er­ate doc­u­men­ta­tion for the Python builtin math mod­ule:

PROJECT_TITLE = "Python math"
INPUT_MODULES = ['math']

Now, run the script and pass path to the con­fig­u­ra­tion file to it:

./python.py path/to/conf.py

This will gen­er­ate an output/ di­rec­to­ry next to the conf.py file and fill it with the gen­er­at­ed out­put. Open index.html to see the re­sult.

Fea­tures

  • Theme tai­lored from scratch for Python-spe­cif­ic lan­guage fea­tures
  • Us­es code in­spec­tion to query mod­ules, class­es, da­ta, func­tions and their sig­na­tures, does not re­ly on er­ror-prone source code pars­ing
  • Does not force the doc­u­men­ta­tion writ­er to ex­plic­it­ly list all sym­bols in or­der to have them doc­u­ment­ed
  • Can use both in-code doc­strings and ex­ter­nal reST files to de­scribe the APIs, giv­ing the us­er a con­trol over the code size vs doc­u­men­ta­tion ver­bosi­ty trade­off

Con­fig­u­ra­tion

To­geth­er with the above PROJECT_TITLE and INPUT_MODULES vari­ables men­tioned above, the con­fig­u­ra­tion file sup­ports the fol­low­ing vari­ables. The op­tions are sim­i­lar to the Doxy­gen con­fig, but free of the Doxy­gen-spe­cif­ic nam­ing and con­straints.

Vari­able De­scrip­tion
PROJECT_TITLE: str Project ti­tle. Ren­dered in top navbar, page ti­tle and fine print. If not set, "My Python Project" is used.
PROJECT_SUBTITLE: str Project sub­ti­tle. If set, ap­pend­ed in a thin­ner font to PROJECT_TITLE.
MAIN_PROJECT_URL: str If set and PROJECT_SUBTITLE is al­so set, then PROJECT_TITLE in the top navbar will link to this URL and PROJECT_SUBTITLE to the doc­u­men­ta­tion main page, sim­i­lar­ly as shown here.
INPUT: str Base in­put di­rec­to­ry. If not set, con­fig file base dir is used. Rel­a­tive paths are rel­a­tive to con­fig file base dir.
OUTPUT: str Where to save the out­put. Rel­a­tive paths are rel­a­tive to INPUT; if not set, output/ is used.
INPUT_MODULES: List[Any] List of mod­ules to gen­er­ate the docs from. Val­ues can be ei­ther strings or mod­ule ob­jects. See Mod­ule in­spec­tion for more in­for­ma­tion.
INPUT_PAGES: List[str] List of reST files for stand­alone pages. See Pages for more in­for­ma­tion.
THEME_COLOR: str Col­or for <meta name="theme-color" />, cor­re­spond­ing to the CSS style. If emp­ty, no <meta> tag is ren­dered. See Theme se­lec­tion for more in­for­ma­tion.
FAVICON: str Fav­i­con URL, used to pop­u­late <link rel="icon" />. If emp­ty, no <link> tag is ren­dered. Rel­a­tive paths are searched rel­a­tive to INPUT and to the python.py script dir as a fall­back. See Theme se­lec­tion for more in­for­ma­tion.
STYLESHEETS: List[str] List of CSS files to in­clude. Rel­a­tive paths are searched rel­a­tive to INPUT and to the python.py script dir as a fall­back. See Theme se­lec­tion for more in­for­ma­tion.
HTML_HEADER: str HTML code to put at the end of the <head> el­e­ment. Use­ful for link­ing ar­bi­trary JavaScript code or, for ex­am­ple, adding <link> CSS stylesheets with ad­di­tion­al prop­er­ties and IDs that are oth­er­wise not pos­si­ble with just STYLESHEETS.
EXTRA_FILES: List[str] List of ex­tra files to copy (for ex­am­ple ad­di­tion­al CSS files that are @imported from the pri­ma­ry one). Rel­a­tive paths are searched rel­a­tive to INPUT and to the python.py script dir as a fall­back.
LINKS_NAVBAR1: List[Any] Left navbar col­umn links. See Navbar links for more in­for­ma­tion.
LINKS_NAVBAR2: List[Any] Right navbar col­umn links. See Navbar links for more in­for­ma­tion.
PAGE_HEADER: str reST markup to put at the top of ev­ery page. If not set, noth­ing is added any­where. The {url} place­hold­er is re­placed with cur­rent file URL.
FINE_PRINT: str reST markup to put in­to the foot­er. If not set, a de­fault gener­ic text is used. If emp­ty, no foot­er is ren­dered at all.
FORMATTED_METADATA: List[str] Which meata­da­ta fields should be for­mat­ted in doc­u­men­ta­tion pages. By de­fault on­ly the summary field is.
PLUGINS: List[str] List of plug­ins to use. See Plug­ins for more in­for­ma­tion.
PLUGIN_PATHS: List[str] Ad­di­tion­al plug­in search paths. Rel­a­tive paths are rel­a­tive to INPUT.
CLASS_INDEX_EXPAND_LEVELS How many lev­els of the class in­dex tree to ex­pand. 0 means on­ly the top-lev­el sym­bols are shown. If not set, 1 is used.
CLASS_INDEX_EXPAND_INNER Whether to ex­pand in­ner class­es in the class in­dex. If not set, False is used.
PYBIND11_COMPATIBILITY En­able some ad­di­tion­al tricks for bet­ter com­pat­i­bil­i­ty with py­bind11. If not set, False is used. See py­bind11 com­pat­i­bil­i­ty for more in­for­ma­tion.
SEARCH_DISABLED: bool Dis­able search func­tion­al­i­ty. If this op­tion is set, no search da­ta is com­piled and the ren­dered HTML does not con­tain search-re­lat­ed UI or sup­port. If not set, False is used.
SEARCH_DOWNLOAD_BINARY: bool Down­load search da­ta as a bi­na­ry to save band­width and ini­tial pro­cess­ing time. If not set, False is used. See Search op­tions for more in­for­ma­tion.
SEARCH_HELP: str reST markup to dis­play as help text on emp­ty search pop­up. If not set, a de­fault mes­sage is used. Has ef­fect on­ly if SEARCH_DISABLED is not True.
SEARCH_BASE_URL: str Base URL for OpenSearch-based search en­gine sug­ges­tions for web browsers. See Search op­tions for more in­for­ma­tion. Has ef­fect on­ly if SEARCH_DISABLED is not True.
SEARCH_EXTERNAL_URL: str URL for ex­ter­nal search. The {query} place­hold­er is re­placed with ur­len­cod­ed search string. If not set, no ex­ter­nal search is of­fered. See Search op­tions for more in­for­ma­tion. Has ef­fect on­ly if SEARCH_DISABLED is not True.
DOCUTILS_SETTINGS: Dict[Any] Ad­di­tion­al do­cu­tils set­tings. Key/val­ue pairs as de­scribed in the docs.
URL_FORMATTER: Callable Func­tion for cre­at­ing file­names and URLs for mod­ules, class­es, pages and in­dex pages. See Cus­tom URL for­mat­ters for more in­for­ma­tion.
ID_FORMATTER: Callable Func­tion for cre­at­ing link an­chors for mod­ule and class mem­bers. See Cus­tom URL for­mat­ters for more in­for­ma­tion.

Theme se­lec­tion

By de­fault, the dark m.css theme to­geth­er with doc­u­men­ta­tion-theme-spe­cif­ic ad­di­tions is used, which cor­re­sponds to the fol­low­ing con­fig­u­ra­tion:

STYLESHEETS = [
    'https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600',
    '../css/m-dark+documentation.compiled.css']
THEME_COLOR = '#22272e'
FAVICON = 'favicon-dark.png'

If you have a site al­ready us­ing the m-dark.compiled.css file, there’s an­oth­er file called m-dark.documentation.compiled.css, which con­tains just the doc­u­men­ta­tion-theme-spe­cif­ic ad­di­tions so you can re­use the al­ready cached m-dark.compiled.css file from your main site:

STYLESHEETS = [
    'https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600',
    '../css/m-dark.compiled.css',
    '../css/m-dark.documentation.compiled.css']
THEME_COLOR = '#22272e'
FAVICON = 'favicon-dark.png'

If you pre­fer the light m.css theme in­stead, use the fol­low­ing con­fig­u­ra­tion (and, sim­i­lar­ly, you can use m-light.compiled.css to­geth­er with m-light.documentation.compiled-css in place of m-light+documentation.compiled.css:

STYLESHEETS = [
    'https://fonts.googleapis.com/css?family=Libre+Baskerville:400,400i,700,700i%7CSource+Code+Pro:400,400i,600',
    '../css/m-light+documentation.compiled.css']
THEME_COLOR = '#cb4b16'
FAVICON = 'favicon-light.png'

See the CSS files sec­tion be­low for more in­for­ma­tion about cus­tomiz­ing the CSS files.

Search op­tions

Sym­bol search is im­ple­ment­ed us­ing JavaScript Typed Ar­rays and does not need any serv­er-side func­tion­al­i­ty to per­form well — the client au­to­mat­i­cal­ly down­loads a tight­ly packed bi­na­ry con­tain­ing search da­ta and per­forms search di­rect­ly on it.

How­ev­er, due to re­stric­tions of Chro­mi­um-based browsers, it’s not pos­si­ble to down­load da­ta us­ing XMLHttpRequest when served from a lo­cal file-sys­tem. Be­cause of that, the search de­faults to pro­duc­ing a Base85-en­cod­ed rep­re­sen­ta­tion of the search bi­na­ry and load­ing that asyn­chronous­ly as a plain JavaScript file. This re­sults in the search da­ta be­ing 25% larg­er, but since this is for serv­ing from a lo­cal filesys­tem, it’s not con­sid­ered a prob­lem. If your docs are ac­cessed through a serv­er (or you don’t need Chrome sup­port), en­able the SEARCH_DOWNLOAD_BINARY op­tion.

The site can pro­vide search en­gine meta­da­ta us­ing the OpenSearch spec­i­fi­ca­tion. On sup­port­ed browsers this means you can add the search field to search en­gines and search di­rect­ly from the ad­dress bar. To en­able search en­gine meta­da­ta, point SEARCH_BASE_URL to base URL of your doc­u­men­ta­tion, for ex­am­ple:

SEARCH_BASE_URL = 'https://doc.magnum.graphics/magnum/'

In gen­er­al, even with­out the above set­ting, ap­pend­ing ?q={query}#search to the URL will di­rect­ly open the search pop­up with re­sults for {query}.

If SEARCH_EXTERNAL_URL is spec­i­fied, full-text search us­ing an ex­ter­nal search en­gine is of­fered if noth­ing is found for giv­en string or if the us­er has JavaScript dis­abled. It’s rec­om­mend­ed to re­strict the search to a par­tic­u­lar do­main or add ad­di­tion­al key­words to the search query to fil­ter out ir­rel­e­vant re­sults. Ex­am­ple, us­ing Google search en­gine and re­strict­ing the search to a sub­do­main:

SEARCH_EXTERNAL_URL = 'https://google.com/search?q=site:doc.magnum.graphics+{query}'

Cus­tom URL for­mat­ters

The URL_FORMATTER op­tion al­lows you to con­trol how all file­names and gen­er­at­ed URLs look like. It takes an en­try type and a “path” as a list of strings (so for ex­am­ple my.module.Class is rep­re­sent­ed as ['my', 'module', 'Class']), re­turn­ing a tu­ple a file­name and an URL. Those can be the same, but al­so dif­fer­ent (for ex­am­ple a file get­ting saved in­to my/module/Class/index.html but the ac­tu­al URL be­ing https://docs.my.module/Class/). The de­fault im­ple­men­ta­tion looks like this, pro­duc­ing both file­names and URLs in the form of my.module.Class.html:

def default_url_formatter(type: EntryType, path: List[str]) -> Tuple[str, str]:
    url = '.'.join(path) + '.html'
    return url, url

The type is an enum, if you don’t want to fid­dle with im­ports, com­pare str(type) against a string, which is one of 'PAGE', 'MODULE', 'CLASS' or 'SPECIAL'. The 'SPECIAL' is for in­dex pages and in that case the path has al­ways just one item, one of 'pages', 'modules' or 'classes'.

The ID_FORMATTER han­dles for­mat­ting of an­chors on a page. Again it takes an en­try type (which in this case is al­ways one of 'ENUM', 'ENUM_VALUE', 'FUNCTION', 'PROPERTY', 'DATA' or, in case of py­bind11 code, 'OVERLOADED_FUNCTION'. The sec­ond pa­ram­e­ter is again a path, be­ing al­ways just one item ex­cept for 'ENUM_VALUE' (in which case it’s enum name and val­ue name to­geth­er) and for 'OVERLOADED_FUNCTION', in which case it con­tains al­so a llist of ar­gu­ment types. The de­fault im­ple­men­ta­tion sim­ply re­turns the the path con­cate­nat­ed with dash­es:

def default_id_formatter(type: EntryType, path: List[str]) -> str:
    return '-'.join(path)

Mod­ule in­spec­tion

By de­fault, if a mod­ule con­tains the __all__ at­tribute, all names list­ed there are ex­posed in the doc­u­men­ta­tion. Oth­er­wise, all mod­ule (and class) mem­bers are ex­tract­ed us­ing inspect.getmembers(), skip­ping names imported from else­where and un­der­scored names.

De­tect­ing if a mod­ule is a sub­mod­ule of the cur­rent pack­age or if it’s imported from else­where is tricky, the script thus in­cludes on­ly sub­mod­ules that have their __package__ prop­er­ty the same or one lev­el be­low the par­ent pack­age. If a mod­ule’s __package__ is emp­ty, it’s as­sumed to be a plain mod­ule (in­stead of a pack­age) and since those can’t have sub­mod­ules, all found sub­mod­ules in it are ig­nored.

Doc­strings

The first para­graph of a mod­ule-lev­el, class-lev­el and func­tion-lev­el doc­string is used as a doc sum­ma­ry, copied as-is to the out­put with­out for­mat­ting it in any way.

"""Module summary"""

class Foo:
    """Class summary"""

    def bar(self):
        """Function summary"""

Func­tion and vari­able an­no­ta­tions

The script us­es inspect.signature() to query func­tion pa­ram­e­ter / re­turn type an­no­ta­tions to­geth­er with de­fault val­ues and dis­plays them in the out­put. Sim­i­lar is for mod­ule and class vari­ables, ex­tract­ed from the __annotations__ prop­er­ty. If a vari­able type im­ple­ments __repr__(), a repr() of it is print­ed as the val­ue, oth­er­wise the val­ue is omit­ted.

from typing import Tuple, List

def foo(a: str, be_nice: bool = True) -> Tuple[int, str]:
    pass

SETTINGS: List[Tuple[str, bool]] = []

For bet­ter read­abil­i­ty, if the func­tion sig­na­ture con­tains type an­no­ta­tions or a de­fault val­ue, the ar­gu­ments are print­ed each on one line. Oth­er­wise, to avoid wast­ing ver­ti­cal space, the ar­gu­ments are list­ed on a sin­gle line.

Sim­i­lar­ly to how the builtin help() in Python 3.7 start­ed an­no­tat­ing bound­aries be­tween po­si­tion-on­ly, po­si­tion-or-key­word and key­word-on­ly ar­gu­ments with / and *, the same is done here — it’s es­pe­cial­ly help­ful for na­tive func­tions, where you can for ex­am­ple call math.sin(0.3) but not math.sin(x=0.3), be­cause the x ar­gu­ment is po­si­tion­al-on­ly. Cur­rent­ly, po­si­tion­al-on­ly ar­gu­ments are pos­si­ble on­ly with na­tive func­tions, PEP570 adds them for pure Python func­tions as well.

In some cas­es, es­pe­cial­ly when doc­u­ment­ing na­tive func­tions, the sig­na­ture can’t be ex­tract­ed and the func­tion sig­na­ture shows just an el­lip­sis () in­stead of the ac­tu­al ar­gu­ment list.

Class meth­ods, stat­ic meth­ods, dun­der meth­ods, prop­er­ties

Meth­ods dec­o­rat­ed with @classmethod are put in­to a “Class meth­ods” sec­tion, @staticmethods in­to a “Stat­ic meth­ods” sec­tion. Dou­ble-un­der­scored meth­ods ex­plic­it­ly im­ple­ment­ed in the class are put in­to a “Spe­cial meth­ods” sec­tion, oth­er­wise they’re ig­nored — by de­fault, Python adds a large col­lec­tion of dun­der meth­ods to each class and the on­ly way to know if the method is us­er-pro­vid­ed or im­plic­it is by check­ing the doc­string.

class MyClass:
    @classmethod
    def a_classmethod(cls):
        """A class method"""

    @staticmethod
    def a_staticmethod():
        """A static method"""

    def __init__(self, foo, bar):
        """A constructor"""

Prop­er­ties added to class­es ei­ther us­ing the @property dec­o­ra­tor or cre­at­ed with the property() builtin are added to the “Prop­er­ties” sec­tion. Each prop­er­ty is an­no­tat­ed with get set del if it has a get­ter, a set­ter and a deleter or with get and oth­er vari­ants if it has just some. The doc­string and type an­no­ta­tion is ex­tract­ed from the prop­er­ty get­ter.

from typing import Tuple

class MyClass:
    @property
    def a_read_write_property(self) -> Tuple[int, int]:
        """A read-write tuple property"""

    @a_read_write_property.setter
    def a_read_write_property(self, a):
        # Docstring and type annotation taken from the getter, no need to
        # have it repeated here too
        pass

Doc­u­ment­ing enum val­ues

Python sup­plies an im­plic­it doc­strings for enums de­rived from enum.Enum and enum val­ues im­plic­it­ly in­her­it the doc­string of the enum class. If ei­ther is de­tect­ed to be the case, doc­string of the enum or the val­ue is ig­nored. While it’s pos­si­ble to doc­u­ment enum class­es the usu­al way, there’s a non-ob­vi­ous way to doc­u­ment enum val­ues as well.

import enum

class MyEnum(enum.Enum):
    """My enum"""

    ZERO = 0
    TWO = 3
    CONSISTENCY = -73

MyEnum.ZERO.__doc__ = "Zero value"
MyEnum.TWO.__doc__ = "Three, but named TWO for compatibility"

The doc­u­men­ta­tion out­put for enums in­cludes enum val­ue val­ues and the class it was de­rived from, so it’s pos­si­ble to know whether it’s an enum or a flag.

Pages

In ad­di­tion to doc­u­men­ta­tion gen­er­at­ed by in­spect­ing par­tic­u­lar mod­ule, it’s pos­si­ble to add ded­i­cat­ed doc­u­men­ta­tion pages. Con­tent is writ­ten in reST (see Writ­ing reST con­tent for a short in­tro­duc­tion) and tak­en from files spec­i­fied in INPUT_PAGES. File­names are in­ter­pret­ed rel­a­tive to con­fig­u­ra­tion file path, out­put file­name is in­put base­name with ex­ten­sion re­placed to .html. In par­tic­u­lar, con­tent of a index.rst file is used for the doc­u­men­ta­tion main page. Ex­am­ple:

INPUT_PAGES = ['pages/index.rst']
My Python library
=================

:summary: Welcome on the main page!

This is a documentation of the mypythonlib module. You can use it like
this:

.. code:: py

    import mypythonlib
    mypythonlib.foo()

Apart from :summary:, the page can have any num­ber of meta­da­ta, with all of them ex­posed as prop­er­ties of page in the out­put tem­plates. Fields list­ed in FORMATTED_METADATA (the :summary: is among them) are ex­pect­ed to be for­mat­ted as reST and ex­posed as HTML, oth­er­wise as a plain text.

All ref­er­enced im­ages are ex­pect­ed to have ei­ther an ab­so­lute URL or be rel­a­tive to INPUT, the ones with rel­a­tive paths are then copied di­rect­ly to OUTPUT with the lead­ing dirs stripped from the path.

Plug­ins

The reST con­tent is not lim­it­ed to just the builtin func­tion­al­i­ty and it’s pos­si­ble to ex­tend it via plug­ins eit­er from m.css it­self or 3rd par­ty ones. See doc­u­men­ta­tion of each plug­in to see its us­age; the m.html­san­i­ty plug­in is used un­con­di­tion­al­ly while all oth­ers are op­tion­al. For ex­am­ple, en­abling the com­mon m.css plug­ins might look like this:

PLUGINS = ['m.code', 'm.components', 'm.dox']

Im­ple­ment­ing cus­tom plug­ins

Oth­er plug­ins can be load­ed from paths spec­i­fied in PLUGIN_PATHS. Cus­tom plug­ins need to im­ple­ment a reg­is­tra­tion func­tion named register_mcss(). It gets passed the fol­low­ing named ar­gu­ments and the plug­in might or might not use them.

Key­word ar­gu­ment Con­tent
mcss_settings Dict con­tain­ing all m.css set­tings
jinja_environment Jin­ja2 en­vi­ron­ment. Use­ful for adding new fil­ters etc.
module_doc_contents Mod­ule doc­u­men­ta­tion con­tents
class_doc_contents Class doc­u­men­ta­tion con­tents
enum_doc_contents Enum doc­u­men­ta­tion con­tents
function_doc_contents Func­tion doc­u­men­ta­tion con­tents
property_doc_contents Prop­er­ty doc­u­men­ta­tion con­tents
data_doc_contents Da­ta doc­u­men­ta­tion con­tents
hooks_pre_page Hooks to call be­fore each page gets ren­dered
hooks_post_run Hooks to call at the very end of the script run

The module_doc_contents, class_doc_contents, function_doc_contents, property_doc_contents and data_doc_contents vari­ables are Dict[str, Dict[str, str]], where the first lev­el is a name and sec­ond lev­el are key/val­ue pairs of the ac­tu­al HTML doc­u­men­ta­tion con­tent. Plug­ins that parse ex­tra doc­u­men­ta­tion in­puts (such as m.sphinx) are sup­posed to add to the dict, which is then used to fill the ac­tu­al doc­u­men­ta­tion con­tents. The fol­low­ing cor­re­sponds to the doc­u­men­ta­tion source shown in the Ex­ter­nal doc­u­men­ta­tion con­tent sec­tion be­low.

class_doc_contents['mymodule.sub.Class'] = {
    'summary': "A pretty class",
    'details': "This class is *pretty*."
}

The hooks_pre_page and hooks_post_run vari­ables are lists of pa­ram­e­ter-less func­tions. Plug­ins that need to do some­thing be­fore each page of out­put gets ren­dered (for ex­am­ple, re­set­ting an some in­ter­nal counter for page-wide unique el­e­ment IDs) or af­ter the whole run is done (for ex­am­ple to se­ri­al­ize cached in­ter­nal state) are sup­posed to add func­tions to the list.

Reg­is­tra­tion func­tion for a plug­in that needs to query the OUTPUT set­ting might look like this — the re­main­ing key­word ar­gu­ments will col­lapse in­to the **kwargs pa­ram­e­ter. See code of var­i­ous m.css plug­ins for ac­tu­al ex­am­ples. The be­low ex­am­ple shows reg­is­tra­tion of a hy­po­thet­ic HTML val­ida­tor plug­in — it saves the out­put path from set­tings and reg­is­ters a post-run hook that val­i­dates ev­ery­thing in giv­en out­put di­rec­to­ry.

output_dir = None



def _validate_output():
    validate_all_html_files(output_dir)

def register_mcss(mcss_settings, hooks_post_run, **kwargs):
    global output_dir
    output_dir = mcss_settings['OUTPUT']
    hooks_post_run += [_validate_output]

Ex­ter­nal doc­u­men­ta­tion con­tent

Be­cause it’s of­ten not fea­si­ble to have the whole doc­u­men­ta­tion stored in Python doc­strings, the gen­er­a­tor al­lows you to sup­ply doc­u­men­ta­tion from ex­ter­nal files. Sim­i­lar­ly to pages, the INPUT_DOCS set­ting is a list of reST files that con­tain doc­u­men­ta­tion for par­tic­u­lar names us­ing cus­tom di­rec­tives. This is han­dled by the bun­dled m.sphinx plug­in. See its doc­u­men­ta­tion for de­tailed de­scrip­tion of all fea­tures, be­low is a sim­ple ex­am­ple of us­ing it to doc­u­ment a class:

PLUGINS = ['m.sphinx']
.. py:class:: mymodule.sub.Class
    :summary: A pretty class

    This class is *pretty*.

py­bind11 com­pat­i­bil­i­ty

C++ bind­ings gen­er­at­ed us­ing py­bind11 do not have all in­for­ma­tion ac­ces­si­ble through in­tro­spec­tion and thus the script has to do a few py­bind11-spe­cif­ic work­arounds to gen­er­ate ex­pect­ed out­put. This be­hav­ior is not en­abled by de­fault as it might have un­want­ed con­se­quences in pure Python code, en­able it us­ing the PYBIND11_COMPATIBILITY op­tion.

Func­tion sig­na­tures, prop­er­ty an­no­ta­tions

For rea­sons ex­plained in py­bind/py­bind11#990, py­bind11 is not able to pro­vide func­tion sig­na­tures through in­tro­spec­tion and thus the script falls back to pars­ing ar­gu­ment names, type an­no­ta­tions and de­fault val­ues from the doc­string in­stead. By de­fault, un­less py::arg() is used, func­tion ar­gu­ments are po­si­tion­al-on­ly (shown as arg0, arg1, …) and marked as such in the out­put.

Sim­i­lar­ly, prop­er­ty types are ex­tract­ed from get­ter doc­strings.

Un­like Python, py­bind11 has a builtin sup­port for over­load­ed func­tions — de­pend­ing on types passed to a func­tion, it dis­patch­es to a par­tic­u­lar C++ over­load. The over­loads are ex­pand­ed in the out­put as well, mean­ing you can see one func­tion men­tioned more than once with dif­fer­ent sig­na­tures.

Be­cause stat­ic meth­ods in py­bind11 are not dec­o­rat­ed with @staticmethod, they are de­tect­ed based on pres­ence of self as the first pa­ram­e­ter — if it’s there, it’s an in­stance method, oth­er­wise it’s a stat­ic method.

Enums

Enums in py­bind11 are not de­rived from enum.Enum, but rather are plain class­es. The on­ly re­li­able way to de­tect a py­bind11 enum is by look­ing for a __members__ mem­ber, which is a dict pro­vid­ing string names and their cor­re­spond­ing val­ues. With py­bind 2.2, it’s on­ly pos­si­ble to doc­u­ment the enum class it­self, not the val­ues.

Com­mand-line op­tions

./python.py [-h] [--templates TEMPLATES] [--debug] conf

Ar­gu­ments:

  • conf — con­fig­u­ra­tion file

Op­tions:

  • -h, --help — show this help mes­sage and ex­it
  • --templates TEMPLATES — tem­plate di­rec­to­ry. De­faults to the templates/python/ sub­di­rec­to­ry if not set.
  • --debug — ver­bose log­ging out­put. Use­ful for de­bug­ging.

Cus­tomiz­ing the tem­plate

The rest of the doc­u­men­ta­tion ex­plains how to cus­tom­ize the builtin tem­plate to bet­ter suit your needs. Each doc­u­men­ta­tion file is gen­er­at­ed from one of the tem­plate files that are bun­dled with the script. How­ev­er, it’s pos­si­ble to pro­vide your own Jin­ja2 tem­plate files for cus­tom­ized ex­pe­ri­ence as well as mod­i­fy the CSS styling.

CSS files

By de­fault, com­piled CSS files are used to re­duce amount of HTTP re­quests and band­width need­ed for view­ing the doc­u­men­ta­tion. How­ev­er, for eas­i­er cus­tomiza­tion and de­bug­ging it’s bet­ter to use the un­pro­cessed stylesheets. The STYLESHEETS op­tion lists all files that go to the <link rel="stylesheet" /> in the re­sult­ing HTML markup, while EXTRA_FILES list the in­di­rect­ly ref­er­enced files that need to be copied to the out­put as well. Be­low is an ex­am­ple con­fig­u­ra­tion cor­re­spond­ing to the dark theme:

STYLESHEETS = [
    'https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600',
    '../css/m-dark.css',
    '../css/m-documentation.css']
EXTRA_FILES = [
    '../css/m-grid.css',
    '../css/m-components.css',
    '../css/pygments-dark.css',
    '../css/pygments-console.css']
THEME_COLOR = '#22272e'

Af­ter mak­ing de­sired changes to the source files, it’s pos­si­ble to post­pro­cess them back to the com­piled ver­sion us­ing the postprocess.py util­i­ty as ex­plained in the CSS themes doc­u­men­ta­tion. In case of the dark theme, the m-dark+documentation.compiled.css and m-dark.documentation.compiled.css files are pro­duced like this:

cd css
./postprocess.py m-dark.css m-documentation.css -o m-dark+documentation.compiled.css
./postprocess.py m-dark.css m-documentation.css --no-import -o m-dark.documentation.compiled.css

Out­put tem­plates

Each out­put file is ren­dered with one of these tem­plates:

File­name Use
module.html Mod­ule doc­u­men­ta­tion
class.html Class doc­u­men­ta­tion
page.html Ex­plic­it doc­u­men­ta­tion pages, in­clud­ing the main page

Each tem­plate gets passed all con­fig­u­ra­tion val­ues from the Con­fig­u­ra­tion ta­ble as-is, to­geth­er with a URL vari­able with URL of giv­en out­put file. In ad­di­tion to builtin Jin­ja2 fil­ters, the basename_or_url fil­ter re­turns ei­ther a base­name of file path, if the path is rel­a­tive; or a full URL, if the ar­gu­ment is an ab­so­lute URL. It’s use­ful in cas­es like this:

{% for css in HTML_EXTRA_STYLESHEET %}
<link rel="stylesheet" href="{{ css|basename_or_url }}" />
{% endfor %}

The ac­tu­al page con­tents are pro­vid­ed in a page ob­ject, which has the fol­low­ing prop­er­ties. All ex­posed da­ta are meant to be passed di­rect­ly to the HTML markup with­out any ad­di­tion­al es­cap­ing.

Prop­er­ty De­scrip­tion
page.summary Doc sum­ma­ry
page.filename File name 3
page.url File URL 3
page.breadcrumb List of (title, URL) tu­ples for bread­crumb nav­i­ga­tion.
page.content De­tailed doc­u­men­ta­tion, if any

Each mod­ule page, ren­dered with module.html, has the fol­low­ing ad­di­tion­al prop­er­ties:

Prop­er­ty De­scrip­tion
page.prefix_wbr Ful­ly-qual­i­fied sym­bol pre­fix for giv­en com­pound with trail­ing . with <wbr/> tag af­ter ev­ery ..
page.modules List of in­ner mod­ules. See Mod­ule prop­er­ties for de­tails.
page.classes List of class­es. See Class prop­er­ties for de­tails.
page.enums List of enums. See Enum prop­er­ties for de­tails.
page.functions List of mod­ule-lev­el func­tions. See Func­tion prop­er­ties for de­tails.
page.data List of mod­ule-lev­el da­ta. See Da­ta prop­er­ties for de­tails.
page.has_enum_details If there is at least one enum with full de­scrip­tion block 2
page.has_function_details If there is at least one func­tion (or method, in case of class­es) with full de­scrip­tion block 2
page.has_data_details If there is at least one da­ta with full de­scrip­tion block 2

Each class page, ren­dered with class.html, has the fol­low­ing ad­di­tion­al prop­er­ties:

Prop­er­ty De­scrip­tion
page.classmethods List of class meth­ods (an­no­tat­ed with @classmethod). See Func­tion prop­er­ties for de­tails.
page.staticmethods List of stat­ic meth­ods (an­no­tat­ed with @staticmethod). See Func­tion prop­er­ties for de­tails.
page.methods List of meth­ods. See Func­tion prop­er­ties for de­tails.
page.dunder_methods List of dou­ble-un­der­scored spe­cial func­tions. See Func­tion prop­er­ties for de­tails.
page.properties List of prop­er­ties. See Prop­er­ty prop­er­ties for de­tails.
page.has_property_details If there is at least one prop­er­ty with full de­scrip­tion block 2

Ex­plic­it doc­u­men­ta­tion pages ren­dered with class.html have ad­di­tion­al prop­er­ties tak­en from in­put meta­da­ta. If giv­en meta­da­ta is list­ed in FORMATTED_METADATA, it’s ren­dered in­to HTML, oth­er­wise it’s ex­posed as plain text.

Mod­ule prop­er­ties

Prop­er­ty De­scrip­tion
module.url URL of de­tailed mod­ule doc­u­men­ta­tion
module.name Mod­ule name
module.summary Doc sum­ma­ry

Class prop­er­ties

Prop­er­ty De­scrip­tion
class_.url URL of de­tailed class doc­u­men­ta­tion
class_.name Class name
class_.summary Doc sum­ma­ry

Enum prop­er­ties

Prop­er­ty De­scrip­tion
enum.name Enum name
enum.id Enum ID 4
enum.summary Doc sum­ma­ry
enum.content De­tailed doc­u­men­ta­tion, if any
enum.base Base class from which the enum is de­rived. Set to None if no base class in­for­ma­tion is avail­able.
enum.values List of enum val­ues
enum.has_details If there is enough con­tent for the full de­scrip­tion block. 2
enum.has_value_details If the enum val­ues have de­scrip­tion. Imp­ies enum.has_details.

Ev­ery item of enum.values has the fol­low­ing prop­er­ties:

Prop­er­ty De­scrip­tion
value.name Val­ue name
value.id Val­ue ID 4
value.value Val­ue val­ue. Set to None if no val­ue is avail­able.
value.summary Val­ue doc sum­ma­ry

Func­tion prop­er­ties

Prop­er­ty De­scrip­tion
function.name Func­tion name
function.id Func­tion ID 4
function.summary Doc sum­ma­ry
function.content De­tailed doc­u­men­ta­tion, if any
function.type Func­tion re­turn type an­no­ta­tion 1
function.params List of func­tion pa­ram­e­ters. See be­low for de­tails.
function.has_complex_params Set to True if the pa­ram­e­ter list should be wrapped on sev­er­al lines for bet­ter read­abil­i­ty (for ex­am­ple when it con­tains type an­no­ta­tions or de­fault ar­gu­ments). Set to False when wrap­ping on mul­ti­ple lines would on­ly oc­cu­py too much ver­ti­cal space.
function.has_details If there is enough con­tent for the full de­scrip­tion block 2
function.is_classmethod Set to True if the func­tion is an­no­tat­ed with @classmethod, False oth­er­wise.
function.is_staticmethod Set to True if the func­tion is an­no­tat­ed with @staticmethod, False oth­er­wise.

The func.params is a list of func­tion pa­ram­e­ters and their de­scrip­tion. Each item has the fol­low­ing prop­er­ties:

Prop­er­ty De­scrip­tion
param.name Pa­ram­e­ter name
param.type Pa­ram­e­ter type an­no­ta­tion 1
param.default De­fault pa­ram­e­ter val­ue, if any
param.kind Pa­ram­e­ter kind, a string equiv­a­lent to one of the in­spect.Pa­ram­e­ter.kind val­ues

In some cas­es (for ex­am­ple in case of na­tive APIs), the pa­ram­e­ters can’t be in­tro­spect­ed. In that case, the pa­ram­e­ter list is a sin­gle en­try with name set to "..." and the rest be­ing emp­ty.

Prop­er­ty prop­er­ties

Prop­er­ty De­scrip­tion
property.name Prop­er­ty name
property.id Prop­er­ty ID 4
property.type Prop­er­ty get­ter re­turn type an­no­ta­tion 1
property.summary Doc sum­ma­ry
property.content De­tailed doc­u­men­ta­tion, if any
property.is_gettable If the prop­er­ty is get­table
property.is_settable If the prop­er­ty is set­table
property.is_deletable If the prop­er­ty is deletable with del
property.has_details If there is enough con­tent for the full de­scrip­tion block 2

Da­ta prop­er­ties

Prop­er­ty De­scrip­tion
data.name Da­ta name
data.id Da­ta ID 4
data.type Da­ta type
data.summary Doc sum­ma­ry
data.content De­tailed doc­u­men­ta­tion, if any
data.value Da­ta val­ue rep­re­sen­ta­tion
data.has_details If there is enough con­tent for the full de­scrip­tion block 2

In­dex page tem­plates

The fol­low­ing in­dex pages are pro­vid­ed, show­ing a ex­pand­able tree of the con­tents:

File­name Use
classes.html Class list­ing
modules.html Mod­ule list­ing
pages.html Page list­ing

Each tem­plate is passed all con­fig­u­ra­tion val­ues from the Con­fig­u­ra­tion ta­ble as-is, to­geth­er with an URL, as above. The nav­i­ga­tion tree is pro­vid­ed in an index ob­ject, which has the fol­low­ing prop­er­ties:

Prop­er­ty De­scrip­tion
index.classes List of all mod­ules + class­es
index.pages List of all pages

The form of each list en­try is the same:

Prop­er­ty De­scrip­tion
i.kind En­try kind (one of 'module', 'class' or 'page')
i.name Name
i.url URL of the file with de­tailed doc­u­men­ta­tion
i.summary Doc sum­ma­ry
i.has_nestable_children If the list has nestable chil­dren (i.e., dirs or names­paces)
i.children Re­cur­sive list of child en­tries

Mod­ule/class list is or­dered in a way that all mod­ules are be­fore all class­es.


1.
^ a b c i.type is ex­tract­ed out of func­tion an­no­ta­tion. If the types aren’t an­no­tat­ed, the an­no­ta­tion is emp­ty.
2.
^ a b c d e f g h page.has_*_details and i.has_details are True if there is de­tailed de­scrip­tion, func­tion pa­ram­e­ter doc­u­men­ta­tion or doc­u­ment­ed enum val­ue list­ing that makes it worth to ren­der the full de­scrip­tion block. If False, the mem­ber should be in­clud­ed on­ly in the sum­ma­ry list­ing on top of the page to avoid un­nec­es­sary rep­e­ti­tion.
3.
^ a b page.filename and page.url is gen­er­at­ed by an URL for­mat­ter, see Cus­tom URL for­mat­ters for more in­for­ma­tion
4.
^ a b c d e i.id is an ID used for link­ing to giv­en en­try on a page. Gen­er­at­ed by an an­chor for­mat­ter, see Cus­tom URL for­mat­ters for more in­for­ma­tion.