docfiller#
Note
docfiller
assumes Numpy style docstrings
It is common to want to share documentation information across functions/methods. The module docfiller
provides a way to do this:
For example, if you have these function:
def a_function(x, y, z):
"""
A function.
A longer description
Parameters
----------
x : float
An x param
y : int
A y param
z : str
A z param
Returns
-------
output : float
An output
"""
def b_function(x, y, z):
"""
A different function.
A longer description of b_function
Parameters
----------
x : float
An x param
y : float
A different y param
z : str
A z param
Returns
-------
output : float
An different output
"""
It would be nice to share info. DocFiller
provides a few ways to do this.
First, we can define common docstrings like so:
from module_utilities.docfiller import DocFiller
docstrings = """
Parameters
----------
x : float
An x param
z : str
A z param
"""
d = DocFiller.from_docstring(docstrings, combine_keys="parameters")
d.data
{'summary': '',
'extended_summary': '',
'parameters': {'x': 'x : float\n An x param',
'z': 'z : str\n A z param'},
'returns': '',
'yields': '',
'notes': '',
'warnings': '',
'other_parameters': '',
'attributes': '',
'methods': '',
'references': '',
'examples': '',
'x': 'x : float\n An x param',
'z': 'z : str\n A z param'}
This parses the docstring
(using numpydoc). Then, we can define functions as:
from textwrap import dedent
@d()
def a_function2(x, y, z):
"""
A function.
A longer description
Parameters
----------
{x}
y : int
A y param
{z}
Returns
-------
output : float
An output
"""
@d()
def b_function2(x, y, z):
"""
A different function.
A longer description of b_function
Parameters
----------
{x}
y : float
A different y param
{z}
Returns
-------
output : float
An different output
"""
assert dedent(a_function.__doc__) == a_function2.__doc__
assert dedent(b_function.__doc__) == b_function2.__doc__
print(a_function2.__doc__)
print(b_function2.__doc__)
A function.
A longer description
Parameters
----------
x : float
An x param
y : int
A y param
z : str
A z param
Returns
-------
output : float
An output
A different function.
A longer description of b_function
Parameters
----------
x : float
An x param
y : float
A different y param
z : str
A z param
Returns
-------
output : float
An different output
Using templates#
Better still is to generalize the template:
@ (
d.update(
summary="A function.",
extended_summary="""
A longer description
""",
)
.assign_param("y", "int", "A y param")
.assign_param("output", "float", "An output")
).dedent().decorate
def a_function3(x, y, z):
"""
{summary}
{extended_summary}
Parameters
----------
{x}
{y}
{z}
Returns
-------
{output}
"""
assert a_function3.__doc__ == a_function2.__doc__
@ (
d.update(
summary="A different function.",
extended_summary="A longer description of b_function",
)
.assign_param("y", "float", "A different y param")
.assign_param("output", "float", "An different output")
).dedent()(a_function3)
def b_function3(x, y, z):
pass
assert b_function3.__doc__ == b_function2.__doc__
Note that for b_function3
, we used the template/docstring from a_function3
. DocFiller
defaults to using
the functions docstring for replacement. But you can pass a string or function to use as the template to be filled. Above, we also used DocFiller.assign_param()
to create a correctly formatted parameter.
Define parameters/returns that have different key values#
You may find that you can define a host of shared parameter documentation, except that there are a few edge cases.
For example, say we have a parameter beta
that in some cases is an array, and in others is a float. You could handle this as follows
docstring = """
Parameters
----------
beta_float | beta : float
A float value for beta
beta_array | beta : array-like
An array of beta values
x : float
x parameter
y : float
y parameter
"""
d = DocFiller.from_docstring(docstring, combine_keys="parameters")
# using `.decorate` uses the decorator without template or parameters
@d.decorate
def func_float(x, y, beta):
"""
A thing
Parameters
----------
{x}
{y}
{beta_float}
"""
@d.decorate
def func_array(x, y, beta):
"""
An array thing
Parameters
----------
{x}
{y}
{beta_array}
"""
print(func_float.__doc__)
print(func_array.__doc__)
A thing
Parameters
----------
x : float
x parameter
y : float
y parameter
beta : float
A float value for beta
An array thing
Parameters
----------
x : float
x parameter
y : float
y parameter
beta : array-like
An array of beta values
Or, if everyting else is the same, you could do:
# back to call because passing parameters
@d(beta=d["beta_float"], summary="A thing")
def func_float2(x, y, beta):
"""
{summary}
Parameters
----------
{x}
{y}
{beta}
"""
@d(func_float2, beta=d["beta_array"], summary="An array thing")
def func_array2(x, y, beta):
pass
assert func_float2.__doc__ == func_float.__doc__
assert func_array2.__doc__ == func_array.__doc__
Better still, you can use DocFiller.assign_keys()
:
@d.assign_keys(beta="beta_float")(summary="A thing")
def func_float3(x, y, beta):
"""
{summary}
Parameters
----------
{x}
{y}
{beta}
"""
@d.assign_keys(beta="beta_array")(func_float3, summary="An array thing")
def func_array3(x, y, beta):
pass
assert func_float3.__doc__ == func_float.__doc__
assert func_array3.__doc__ == func_array.__doc__
from module_utilities.docfiller import DocFiller
d = DocFiller.from_docstring(
"""
Parameters
----------
x : int
x param
y : float
y param
z0 | z : int
z int param
z1 | z : float
z float parma
""",
combine_keys="parameters",
)
@d()
def func0():
"""
Parameters
----------
{x}
{y}
{z0}
"""
print(func0.__doc__)
Parameters
----------
x : int
x param
y : float
y param
z : int
z int param
Works with classes#
This also works with classes
expected = """
A summary
A longer summary
Parameters
----------
x : float
x param
some other stuff
y : float
y param
Returns
-------
out : float
output
"""
d = DocFiller.from_docstring(expected, combine_keys="parameters")
@d()
class hello:
"""
{summary}
{extended_summary}
Parameters
----------
{x}
{y}
Returns
-------
{returns.out}
"""
assert hello.__doc__ == expected
@d(hello)
class hello2(hello):
pass
assert hello2.__doc__ == expected
help(hello)
Help on class hello in module __main__:
class hello(builtins.object)
| A summary
|
| A longer summary
|
| Parameters
| ----------
| x : float
| x param
| some other stuff
| y : float
| y param
|
| Returns
| -------
| out : float
| output
|
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
There are cases where you’d like to update the documentation from one implementation to another. For example, say you have the following functions:
@d.decorate
def func_xy(x, y):
"""
Add numbers
Parameters
----------
{x}
{y}
Returns
-------
output : float
Sum of input
"""
return x + y
print(func_xy.__doc__)
Add numbers
Parameters
----------
x : float
x param
some other stuff
y : float
y param
Returns
-------
output : float
Sum of input
But what if you want a new version that adds three numbers func_xyz(x, y, z)
? Most everything is the same as for func_xy
above, but we need to add z
. For this, we have an interface to docstring-inheritance
@d.inherit(func_xy)
def func_xyz(x, y, z):
"""
Parameters
----------
z : float
Another parameter
"""
return x + y + z
print(func_xyz.__doc__)
Add numbers
Parameters
----------
x : float
x param
some other stuff
y : float
y param
z : float
Another parameter
Returns
-------
output : float
Sum of input
For the special case that you want to inherit from methods of a baseclass, you can use the following:
class Base:
def prop(self):
"""A property"""
return 1
@d.decorate
def meth(self, x):
"""
A method
Parameters
----------
{x}
Returns
-------
float
Double x
"""
return x * 2
d_from_base = d.factory_inherit_from_parent(Base)
class Derived:
@d_from_base()
def prop(self):
pass
@d_from_base()
def meth(self, x, y):
"""
Parameters
----------
{y}
"""
return (x + y) * 2
help(Base)
Help on class Base in module __main__:
class Base(builtins.object)
| Methods defined here:
|
| meth(self, x)
| A method
|
|
| Parameters
| ----------
| x : float
| x param
| some other stuff
|
|
| Returns
| -------
| float
| Double x
|
| prop(self)
| A property
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
help(Derived)
Help on class Derived in module __main__:
class Derived(builtins.object)
| Methods defined here:
|
| meth(self, x, y)
| A method
|
| Parameters
| ----------
| x : float
| x param
| some other stuff
| y : float
| y param
|
| Returns
| -------
| float
| Double x
|
| prop(self)
| A property
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)