autodoctools

This module implements tools for increasing the level of automation and standardisation of the online documentation generated with Sphinx.

Module autodoctools implements the following members:


hydpy.core.autodoctools.autodoc_basemodel(module)[source]

Add an exhaustive docstring to the given module of a basemodel.

Works onlye when all modules of the basemodel are named in the standard way, e.g. lland_model, lland_control, lland_inputs.

hydpy.core.autodoctools.autodoc_applicationmodel(module)[source]

Improves the docstrings of application models when called at the bottom of the respective module.

autodoc_applicationmodel() requires, similar to autodoc_basemodel(), that both the application model and its base model are defined in the conventional way.

class hydpy.core.autodoctools.Substituter(master=None)[source]

Bases: object

Implements a HydPy specific docstring substitution mechanism.

static consider_member(name_member: str, member: Any, module, class_=None, ignore: Optional[Dict[str, any]] = None)[source]

Return True if the given member should be added to the substitutions. If not return False.

Some examples based on the site-package numpy:

>>> from hydpy.core.autodoctools import Substituter
>>> import numpy

A constant like nan should be added:

>>> Substituter.consider_member("nan", numpy.nan, numpy)
True

Members with a prefixed underscore should not be added:

>>> Substituter.consider_member("_NoValue", numpy._NoValue, numpy)
False

Members that are actually imported modules should not be added:

>>> Substituter.consider_member("warnings", numpy.warnings, numpy)
False

Members that are actually defined in other modules should not be added:

>>> numpy.Substituter = Substituter
>>> Substituter.consider_member("Substituter", numpy.Substituter, numpy)
False
>>> del numpy.Substituter

Members that are defined in submodules of a given package (either from the standard library or from site-packages) should be added…

>>> Substituter.consider_member("clip", numpy.clip, numpy)
True

…but not members defined in HydPy submodules:

>>> import hydpy
>>> Substituter.consider_member("Node", hydpy.Node, hydpy)
False

For descriptor instances (with method __get__) being members of classes should be added:

>>> from hydpy.auxs import anntools
>>> Substituter.consider_member(
...     "shape_neurons", anntools.ANN.shape_neurons, anntools, anntools.ANN)
True

You can decide to ignore certain members:

>>> Substituter.consider_member(
...     "shape_neurons", anntools.ANN.shape_neurons, anntools, anntools.ANN,
...     {"test": 1.0})
True
>>> Substituter.consider_member(
...     "shape_neurons", anntools.ANN.shape_neurons, anntools, anntools.ANN,
...     {"shape_neurons": anntools.ANN.shape_neurons})
False
static get_role(member, cython=False)[source]

Return the reStructuredText role func, class, or const best describing the given member.

Some examples based on the site-package numpy. clip() is a function:

>>> from hydpy.core.autodoctools import Substituter
>>> import numpy
>>> Substituter.get_role(numpy.clip)
'func'

ndarray is a class:

>>> Substituter.get_role(numpy.ndarray)
'class'

clip() is a method, for which also the function role is returned:

>>> Substituter.get_role(numpy.ndarray.clip)
'func'

For everything else the constant role is returned:

>>> Substituter.get_role(numpy.nan)
'const'

When analysing cython extension modules, set the option cython flag to True. Double is correctly identified as a class:

>>> from hydpy.cythons import pointerutils
>>> Substituter.get_role(pointerutils.Double, cython=True)
'class'

Only with the cython flag beeing True, for everything else the function text role is returned (doesn’t make sense here, but the numpy module is not something defined in module pointerutils anyway):

>>> Substituter.get_role(pointerutils.numpy, cython=True)
'func'
add_substitution(short, medium, long, module)[source]

Add the given substitutions both as a short2long and a medium2long mapping.

Assume variable1 is defined in the hydpy module module1 and the short and medium descriptions are var1 and mod1.var1:

>>> import types
>>> module1 = types.ModuleType("hydpy.module1")
>>> from hydpy.core.autodoctools import Substituter
>>> substituter = Substituter()
>>> substituter.add_substitution(
...     "var1", "mod1.var1", "module1.variable1", module1)
>>> print(substituter.get_commands())
.. var1 replace:: module1.variable1
.. mod1.var1 replace:: module1.variable1

Adding variable2 of module2 has no effect on the predefined substitutions:

>>> module2 = types.ModuleType("hydpy.module2")
>>> substituter.add_substitution(
...     "var2", "mod2.var2", "module2.variable2", module2)
>>> print(substituter.get_commands())
.. var1 replace:: module1.variable1
.. var2 replace:: module2.variable2
.. mod1.var1 replace:: module1.variable1
.. mod2.var2 replace:: module2.variable2

But when adding variable1 of module2, the short2long mapping of variable1 would become inconclusive, which is why the new one (related to module2) is not stored and the old one (related to module1) is removed:

>>> substituter.add_substitution(
...     "var1", "mod2.var1", "module2.variable1", module2)
>>> print(substituter.get_commands())
.. var2 replace:: module2.variable2
.. mod1.var1 replace:: module1.variable1
.. mod2.var1 replace:: module2.variable1
.. mod2.var2 replace:: module2.variable2

Adding variable2 of module2 accidentally again, does not result in any undesired side-effects:

>>> substituter.add_substitution(
...     "var2", "mod2.var2", "module2.variable2", module2)
>>> print(substituter.get_commands())
.. var2 replace:: module2.variable2
.. mod1.var1 replace:: module1.variable1
.. mod2.var1 replace:: module2.variable1
.. mod2.var2 replace:: module2.variable2

In order to reduce the risk of name conflicts, only the medium2long mapping is supported for modules not part of the HydPy package:

>>> module3 = types.ModuleType("module3")
>>> substituter.add_substitution(
...     "var3", "mod3.var3", "module3.variable3", module3)
>>> print(substituter.get_commands())
.. var2 replace:: module2.variable2
.. mod1.var1 replace:: module1.variable1
.. mod2.var1 replace:: module2.variable1
.. mod2.var2 replace:: module2.variable2
.. mod3.var3 replace:: module3.variable3

The only exception to this rule is builtins, for which only the short2long mapping is supported (note also, that the module name builtins is removed from string long):

>>> import builtins
>>> substituter.add_substitution(
...     "str", "blt.str", ":func:`~builtins.str`", builtins)
>>> print(substituter.get_commands())
.. str replace:: :func:`str`
.. var2 replace:: module2.variable2
.. mod1.var1 replace:: module1.variable1
.. mod2.var1 replace:: module2.variable1
.. mod2.var2 replace:: module2.variable2
.. mod3.var3 replace:: module3.variable3
add_module(module, cython=False)[source]

Add the given module, its members, and their submembers.

The first examples are based on the site-package numpy: which is passed to method add_module():

>>> from hydpy.core.autodoctools import Substituter
>>> substituter = Substituter()
>>> import numpy
>>> substituter.add_module(numpy)

First, the module itself is added:

>>> substituter.find("|numpy|")
|numpy| :mod:`~numpy`

Second, constants like nan are added:

>>> substituter.find("|numpy.nan|")
|numpy.nan| :const:`~numpy.nan`

Third, functions like clip() are added:

>>> substituter.find("|numpy.clip|")
|numpy.clip| :func:`~numpy.clip`

Fourth, clases line ndarray are added:

>>> substituter.find("|numpy.ndarray|")
|numpy.ndarray| :class:`~numpy.ndarray`

Method add_module() also searches for available annotations:

>>> from hydpy.core import timetools
>>> substituter.add_module(timetools)
>>> substituter.find("Timegrids.init")
|Timegrids.initindices| :const:`~hydpy.core.timetools.Timegrids.initindices`
|Timegrids.init| :attr:`~hydpy.core.timetools.Timegrids.init`
|timetools.Timegrids.initindices| :const:`~hydpy.core.timetools.Timegrids.initindices`
|timetools.Timegrids.init| :attr:`~hydpy.core.timetools.Timegrids.init`
>>> from hydpy.auxs import calibtools
>>> substituter.add_module(calibtools)
>>> substituter.find("ReplaceIUH.update_parameters")
|ReplaceIUH.update_parameters| :attr:`~hydpy.auxs.calibtools.ReplaceIUH.update_parameters`
|calibtools.ReplaceIUH.update_parameters| :attr:`~hydpy.auxs.calibtools.ReplaceIUH.update_parameters`

When adding Cython modules, the cython flag should be set True:

>>> from hydpy.cythons import pointerutils
>>> substituter.add_module(pointerutils, cython=True)
>>> substituter.find("set_pointer")
|PPDouble.set_pointer| :func:`~hydpy.cythons.autogen.pointerutils.PPDouble.set_pointer`
|pointerutils.PPDouble.set_pointer| :func:`~hydpy.cythons.autogen.pointerutils.PPDouble.set_pointer`
add_modules(package)[source]

Add the modules of the given package without their members.

update_masters()[source]

Update all master Substituter objects.

If a Substituter object is passed to the constructor of another Substituter object, they become master and slave:

>>> from hydpy.core.autodoctools import Substituter
>>> sub1 = Substituter()
>>> from hydpy.core import devicetools
>>> sub1.add_module(devicetools)
>>> sub2 = Substituter(sub1)
>>> sub3 = Substituter(sub2)
>>> sub3.master.master is sub1
True
>>> sub2 in sub1.slaves
True

During initialization, all mappings handled by the master object are passed to its new slave:

>>> sub3.find("Node|")
|Node| :class:`~hydpy.core.devicetools.Node`
|devicetools.Node| :class:`~hydpy.core.devicetools.Node`

Updating a slave, does not affect its master directly:

>>> from hydpy.core import hydpytools
>>> sub3.add_module(hydpytools)
>>> sub3.find("HydPy|")
|HydPy| :class:`~hydpy.core.hydpytools.HydPy`
|hydpytools.HydPy| :class:`~hydpy.core.hydpytools.HydPy`
>>> sub2.find("HydPy|")

Through calling update_masters(), the medium2long mappings are passed from the slave to its master:

>>> sub3.update_masters()
>>> sub2.find("HydPy|")
|hydpytools.HydPy| :class:`~hydpy.core.hydpytools.HydPy`

Then each master object updates its own master object also:

>>> sub1.find("HydPy|")
|hydpytools.HydPy| :class:`~hydpy.core.hydpytools.HydPy`

In reverse, subsequent updates of master objects to not affect their slaves directly:

>>> from hydpy.core import masktools
>>> sub1.add_module(masktools)
>>> sub1.find("Masks|")
|Masks| :class:`~hydpy.core.masktools.Masks`
|NodeMasks| :class:`~hydpy.core.masktools.NodeMasks`
|masktools.Masks| :class:`~hydpy.core.masktools.Masks`
|masktools.NodeMasks| :class:`~hydpy.core.masktools.NodeMasks`
>>> sub2.find("Masks|")

Through calling update_slaves(), the medium2long mappings are passed the master to all of its slaves:

>>> sub1.update_slaves()
>>> sub2.find("Masks|")
|masktools.Masks| :class:`~hydpy.core.masktools.Masks`
|masktools.NodeMasks| :class:`~hydpy.core.masktools.NodeMasks`
>>> sub3.find("Masks|")
|masktools.Masks| :class:`~hydpy.core.masktools.Masks`
|masktools.NodeMasks| :class:`~hydpy.core.masktools.NodeMasks`
update_slaves()[source]

Update all slave Substituter objects.

See method update_masters() for further information.

get_commands(source=None)[source]

Return a string containing multiple reStructuredText replacements with the substitutions currently defined.

Some examples based on the subpackage optiontools:

>>> from hydpy.core.autodoctools import Substituter
>>> substituter = Substituter()
>>> from hydpy.core import optiontools
>>> substituter.add_module(optiontools)

When calling get_commands() with the source argument, the complete short2long and medium2long mappings are translated into replacement commands (only a few of them are shown):

>>> print(substituter.get_commands())   
.. |Options.autocompile| replace:: :const:`~hydpy.core.optiontools.Options.autocompile`
.. |Options.checkseries| replace:: :const:`~hydpy.core.optiontools.Options.checkseries`
...
.. |optiontools.Options.autocompile| replace:: :const:`~hydpy.core.optiontools.Options.autocompile`
.. |optiontools.Options.checkseries| replace:: :const:`~hydpy.core.optiontools.Options.checkseries`
...

Through passing a string (usually the source code of a file to be documented), only the replacement commands relevant for this string are translated:

>>> from hydpy.core import objecttools
>>> import inspect
>>> source = inspect.getsource(objecttools)
>>> print(substituter.get_commands(source))
.. |Options.ellipsis| replace:: :const:`~hydpy.core.optiontools.Options.ellipsis`
.. |Options.reprdigits| replace:: :const:`~hydpy.core.optiontools.Options.reprdigits`
find(text)[source]

Print all substitutions that include the given text string.

hydpy.core.autodoctools.prepare_mainsubstituter()[source]

Prepare and return a Substituter object for the main __init__ file of HydPy.

hydpy.core.autodoctools.autodoc_module(module)[source]

Add a short summary of all implemented members to a modules docstring.

hydpy.core.autodoctools.autodoc_tuple2doc(module)[source]

Include tuples as CLASSES of ControlParameters and RUN_METHODS of Models into the respective docstring.