Custom sections¶
docshare recognizes a fixed set of sections. A docstring may have others,
and a document that a library’s own conventions describe may need them read
rather than merely preserved. custom= says so, for one call.
Declaring a section¶
A declaration maps each title to the recognized section it resembles:
from docshare import docparse, docwrap
def fit(w, x):
"""Fit a model.
Parameters
----------
w : array
The fitted weights.
Inputs
------
x : array
The observed data.
"""
pass
doc = docparse(fit, custom={'Inputs': 'Parameters'})
assert doc.section('Inputs').items[0].names == ('x',)
assert doc.section('Parameters').items[0].names == ('w',)
Inputs is not another name for Parameters. The two coexist and mean
different things — a model’s parameters are fitted, its inputs are
observed — so a declared section is a section of its own that borrows
how its body is read. It keeps its own title in both formats, is never
merged into what it resembles, and is never checked against the signature.
What it borrows is whether the body holds items, how those items are
identified, and what type stands in for one the author left out. Declare a
section like Parameters or Attributes for named items, like Returns
for positional ones, like Raises for ones named by an exception type, or
like Notes for prose.
Declaring a section without a kind¶
A title given on its own declares the title to be a section and says nothing about what it means. The body is preserved exactly and never interpreted, as an unrecognized NumPy section is:
def sender(x):
"""Send.
Efferents:
Connections to downstream objects.
"""
pass
doc = docparse(sender, custom=['Efferents'])
assert doc.section('Efferents').opaque
assert doc.section('Efferents').text == ('Connections to downstream objects.',)
This matters mostly for Google style, which has no syntax of its own for an unrecognized section — see Sections docshare does not recognize. A NumPy underline needs no declaration to be a section.
Nothing is registered¶
A declaration lasts for the call that makes it. Reading the same docstring without it reads it the way it always did:
assert docparse(fit).section('Inputs').opaque
assert docparse(fit, custom={'Inputs': 'Parameters'}).section('Inputs').items
That is deliberate. A global registry would let one library’s declaration change how another library’s docstrings are read, which is exactly the kind of action at a distance a documentation tool should not have.
Inheriting a declared section¶
custom= on docwrap declares a section on the decorated object and
decides the rest of the call’s vocabulary. When the title is a Python name,
it gets the same three arguments a recognized section has:
@docwrap(custom={'Inputs': 'Parameters'}, format='numpy', inheritinputs=fit)
def refit(w, x):
"""Refit the model.
Parameters
----------
w : array
Our own weights.
"""
pass
assert 'Our own weights.' in refit.__doc__
assert 'Inputs\n------\nx : array' in refit.__doc__
assert 'The observed data.' in refit.__doc__
Naming fit in inheritinputs is enough to have it read with Inputs
declared: the argument could not mean anything otherwise.
A declared section is driven by its sources, as Attributes is, so its
exclusion names one of the source’s items and is spelled ignore:
inheritinputs, ignoreinputs, and inputmap.
Titles that are not Python names¶
Model Inputs and Type-Hints are perfectly good section titles and no
argument can be named after them. Every declared section is therefore also
addressable by title:
def measured(a):
"""Measure.
Model Inputs
------------
a : array
The a.
"""
pass
@docwrap(
custom={'Model Inputs': 'Parameters'},
format='numpy',
inheritcustom={'Model Inputs': measured},
)
def remeasure(a):
"""Remeasure."""
pass
assert 'Model Inputs\n------------\na : array' in remeasure.__doc__
ignorecustom and custommap are its companions. A section declared
without a kind has no items to exclude or rename, so it accepts only
inheritcustom, which takes it whole.
Sources: sourcecustom and sourceformat¶
An argument that names a declared section says which source needs it.
inheritall names no section, so it needs telling:
@docwrap(
custom={'Inputs': 'Parameters'},
format='numpy',
inheritall=fit,
sourcecustom=True,
)
def refit_all(w, x):
"""Refit everything."""
pass
assert 'The fitted weights.' in refit_all.__doc__
assert 'The observed data.' in refit_all.__doc__
sourcecustom=True gives every source the whole declaration. Naming sources
directly — sourcecustom=(fn1, fn2) — gives those sources the whole
declaration, and a mapping — sourcecustom={'Inputs': (fn1, fn2)} —
gives named sources one section of it.
sourceformat says which format a source is written in, for a docstring
whose format cannot be detected:
def google_fit(w, x):
"""Fit, in Google style.
Inputs:
x (array): The observed data.
"""
pass
@docwrap(
custom={'Inputs': 'Parameters'},
format='numpy',
inheritinputs=google_fit,
sourceformat={'google': google_fit},
)
def from_google(w, x):
"""Ours."""
pass
assert 'Inputs\n------\nx : array' in from_google.__doc__
Give one format for all sources, or a mapping from a format to the sources written in it.
Neither argument accepts an already-parsed Document: a
document was read when it was parsed and cannot be read again. This is
particular to these two arguments — an ordinary inheritance source may be a
Document, as Inheriting describes. Pass the
object it came from, or parse it the way you want it and pass that.
The three format arguments¶
Argument |
What it is about |
|---|---|
|
The format the decorated object’s own docstring is written in. The default detects it. |
|
The format the composed documentation is written out in. The default is |
|
The format a source is written in. There is no default: a source’s format is detected, so that inheriting across the two formats keeps working. |
What a declaration cannot do¶
A rendered docstring does not carry its own declaration. After docwrap
writes an Inputs section, anything that reads that docstring afresh
without declaring Inputs sees an opaque section in NumPy style, or prose
in Google style. Within a session the cache hides this, because a composed
document is recorded under the plain key as well; it is a convenience and
not a guarantee. A function that inherits from a decorated one needs the
declaration too:
@docwrap(
custom={'Inputs': 'Parameters'},
format='numpy',
inheritinputs=refit,
sourcecustom=True,
)
def refit_again(w, x):
"""Again."""
pass
assert 'The observed data.' in refit_again.__doc__
A declared section also cannot be something neither format can express. A
title is written with letters, digits, spaces and hyphens and begins with a
letter, in both styles; Model I/O is refused rather than silently lost.
Nor can a declaration redefine a section docshare already recognizes.