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:

  • AUXS Priority level for members defined in different sources.

  • BUILTINS Priority level for members defined in different sources.

  • CORE Priority level for members defined in different sources.

  • CYTHONS Priority level for members defined in different sources.

  • ELSE Priority level for members defined in different sources.

  • EXE Priority level for members defined in different sources.

  • HYDPY Priority level for members defined in different sources.

  • MODELS Priority level for members defined in different sources.

  • Priority Priority level for members defined in different sources.

  • autodoc_basemodel() Add an exhaustive docstring to the given module of a basemodel.

  • autodoc_applicationmodel() Improves the docstrings of application models.

  • insert_docname_substitutions() Insert model-specific substitutions based on the definitions provided by the available DocName member.

  • Substituter Implements a HydPy specific docstring substitution mechanism.

  • prepare_mainsubstituter() Prepare and return a Substituter object for the main __init__ file of HydPy.

  • autodoc_module() Add a short summary of all implemented members to a module’s docstring.

  • autodoc_tuple2doc() Include tuples as CLASSES of ControlParameters and RUN_METHODS of Models into the respective docstring.

  • Directory Helper for representing directory structures.

  • ProjectStructure Analyser and visualiser of the file structure of HydPy projects.

  • make_modellink() Create an internal HTML reference pointing to an application model.

  • make_interfacelink() Create an internal HTML reference pointing to a submodel interface.

  • SubmodelGraph Analyser and visualiser of the advisable connections between main models and submodels.


class hydpy.core.autodoctools.Priority(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: Enum

Priority level for members defined in different sources.

BUILTINS = 0

Priority level for members defined in builtins.

CORE = 1

Priority level for members defined in the core subpackage.

EXE = 2

Priority level for members defined in the exe subpackage.

AUXS = 3

Priority level for members defined in the auxs subpackage.

CYTHONS = 4

Priority level for members defined in the cythons subpackage.

MODELS = 5

Priority level for members defined in the models subpackage.

HYDPY = 6

Priority level for HydPy members not falling in the other categories.

ELSE = 7

Priority level for non-HydPy and non builtins members.

classmethod get_priority(modulename: str) Priority[source]

Return the priority for the given module name.

>>> from hydpy.core.autodoctools import Priority
>>> assert Priority.get_priority("builtins") == Priority.BUILTINS
>>> assert Priority.get_priority("hydpy.core.test") == Priority.CORE
>>> assert Priority.get_priority("hydpy.exe.test") == Priority.EXE
>>> assert Priority.get_priority("hydpy.auxs.test") == Priority.AUXS
>>> assert Priority.get_priority("hydpy.cythons.test") == Priority.CYTHONS
>>> assert Priority.get_priority("hydpy.models.test") == Priority.MODELS
>>> assert Priority.get_priority("hydpy.test") == Priority.HYDPY
>>> assert Priority.get_priority("numpy.test") == Priority.ELSE
hydpy.core.autodoctools.autodoc_basemodel(module: ModuleType) None[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: ModuleType) None[source]

Improves the docstrings of application models.

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

hydpy.core.autodoctools.insert_docname_substitutions(module: ModuleType, name: str, substituter: Substituter) None[source]

Insert model-specific substitutions based on the definitions provided by the available DocName member.

>>> from hydpy.core.autodoctools import insert_docname_substitutions, Substituter
>>> from hydpy.models import wland_wag
>>> substituter = Substituter()
>>> insert_docname_substitutions(wland_wag, "wland_wag", substituter)
>>> for command in substituter.get_commands().split("\n"):
...     print(command)  
.. |wland_wag.DOCNAME.complete| replace:: HydPy-W-Wag (extended version ...
.. |wland_wag.DOCNAME.description| replace:: extended version ...
.. |wland_wag.DOCNAME.family| replace:: HydPy-W
.. |wland_wag.DOCNAME.long| replace:: HydPy-W-Wag
.. |wland_wag.DOCNAME.short| replace:: W-Wag
.. |wland_wag.Model.DOCNAME.complete| replace:: HydPy-W-Wag (extended version ...
.. |wland_wag.Model.DOCNAME.description| replace:: extended version ...
.. |wland_wag.Model.DOCNAME.family| replace:: HydPy-W
.. |wland_wag.Model.DOCNAME.long| replace:: HydPy-W-Wag
.. |wland_wag.Model.DOCNAME.short| replace:: W-Wag
>>> from hydpy.models.wland import wland_model
>>> substituter = Substituter()
>>> insert_docname_substitutions(wland_model, "wland", substituter)
>>> for command in substituter.get_commands().split("\n"):
...     print(command)   
.. |wland.DOCNAME.complete| replace:: HydPy-W (base model)
.. |wland.DOCNAME.description| replace:: base model
.. |wland.DOCNAME.family| replace:: HydPy-W
.. |wland.DOCNAME.long| replace:: HydPy-W
.. |wland.DOCNAME.short| replace:: W
.. |wland.Model.DOCNAME.complete| replace:: HydPy-W (base model)
.. |wland.Model.DOCNAME.description| replace:: base model
.. |wland.Model.DOCNAME.family| replace:: HydPy-W
.. |wland.Model.DOCNAME.long| replace:: HydPy-W
.. |wland.Model.DOCNAME.short| replace:: W
class hydpy.core.autodoctools.Substituter(master: Substituter | None = None)[source]

Bases: object

Implements a HydPy specific docstring substitution mechanism.

master: Substituter | None
slaves: list[Substituter]
short2long: dict[str, str]
short2priority: dict[str, Priority]
medium2long: dict[str, str]
static consider_member(name_member: str, member: Any, module: ModuleType, class_: type[object] | None = None, ignore: dict[str, object] | None = None) bool[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("random", numpy.random, 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

Module typingtools is unique, as it is the only one for which consider_member() returns True all explicitly exported type aliases:

>>> from hydpy.core import sequencetools, typingtools
>>> Substituter.consider_member(
...     "NDArrayFloat", typingtools.NDArrayFloat, typingtools)
True
>>> Substituter.consider_member(
...     "NDArrayFloat", typingtools.NDArrayFloat, sequencetools)
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: object, cython: bool = False) str[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: str, medium: str, long: str, module: ModuleType) None[source]

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

Assume variable1, variable2, and variable3 are defined in the HydPy module module1 of subpackage exe and the short and medium descriptions are var1 and mod1.var1 and so on:

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

If we add an object with the same, the updating of short2long depends on its priority. Builtins have the highest priority. Objects defined in the core, exe, auxs, cythons, and models subpackages have the priorities two to six. All other objects (incuding those of other site-packages) have the lowest priority. If we add variable1, said to be defined in the core subpackage (higher priority than the exe subpackage), the new short substitution replaces the old one:

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

If we add variable2, said to be defined in the auxs subpackage (lower priority than the exe subpackage), the old short substitution does not change:

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

If we add variable3, said to be also defined in the exe subpackage, the short substitution is removed to avoid ambiguity:

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

Adding variable3 of module1 accidentally again does not result in any undesired side-effects:

>>> substituter.add_substitution(
...     "var3", "mod1.var3", "hydpy.exe.module1.variable3", module1)
>>> print(substituter.get_commands())
.. var1 replace:: hydpy.core.module2.variable1
.. var2 replace:: hydpy.exe.module1.variable2
.. mod1.var1 replace:: hydpy.exe.module1.variable1
.. mod1.var2 replace:: hydpy.exe.module1.variable2
.. mod1.var3 replace:: hydpy.exe.module1.variable3
.. mod2.var1 replace:: hydpy.core.module2.variable1
.. mod3.var2 replace:: hydpy.auxs.module3.variable2
.. mod4.var3 replace:: hydpy.exe.module4.variable3

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

>>> module5 = types.ModuleType("external")
>>> substituter.add_substitution(
...     "var4", "mod5.var4", "external.variable4", module5)
>>> print(substituter.get_commands())
.. var1 replace:: hydpy.core.module2.variable1
.. var2 replace:: hydpy.exe.module1.variable2
.. mod1.var1 replace:: hydpy.exe.module1.variable1
.. mod1.var2 replace:: hydpy.exe.module1.variable2
.. mod1.var3 replace:: hydpy.exe.module1.variable3
.. mod2.var1 replace:: hydpy.core.module2.variable1
.. mod3.var2 replace:: hydpy.auxs.module3.variable2
.. mod4.var3 replace:: hydpy.exe.module4.variable3
.. mod5.var4 replace:: external.variable4

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`
.. var1 replace:: hydpy.core.module2.variable1
.. var2 replace:: hydpy.exe.module1.variable2
.. mod1.var1 replace:: hydpy.exe.module1.variable1
.. mod1.var2 replace:: hydpy.exe.module1.variable2
.. mod1.var3 replace:: hydpy.exe.module1.variable3
.. mod2.var1 replace:: hydpy.core.module2.variable1
.. mod3.var2 replace:: hydpy.auxs.module3.variable2
.. mod4.var3 replace:: hydpy.exe.module4.variable3
.. mod5.var4 replace:: external.variable4
add_module(module: ModuleType, cython: bool = False) None[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("RuleIUH.update_parameters")
|RuleIUH.update_parameters| :attr:`~hydpy.auxs.calibtools.RuleIUH.update_parameters`
|calibtools.RuleIUH.update_parameters| :attr:`~hydpy.auxs.calibtools.RuleIUH.update_parameters`

Module typingtools is unique, as it is the only one for which add_module() considers all explicitly exported type aliases:

>>> from hydpy.core import typingtools
>>> substituter.add_module(typingtools)
>>> substituter.find("|NDArrayFloat|")  
|NDArrayFloat| :...:`~hydpy.core.typingtools.NDArrayFloat`

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: ModuleType) None[source]

Add the modules of the given package without their members.

update_masters() None[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 initialisation, 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() None[source]

Update all slave Substituter objects.

See method update_masters() for further information.

get_commands(source: str | None = None) str[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())   
.. |OptionContextBase._new_value| replace:: :attr:`~hydpy.core.optiontools.OptionContextBase._new_value`
.. |OptionContextBase._old_value| replace:: :attr:`~hydpy.core.optiontools.OptionContextBase._old_value`
...
.. |optiontools.TypeOption| replace:: :const:`~hydpy.core.optiontools.TypeOption`
.. |optiontools.l1| replace:: :const:`~hydpy.core.optiontools.l1`

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: str) None[source]

Print all substitutions that include the given text string.

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

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

hydpy.core.autodoctools.autodoc_module(module: ModuleType) None[source]

Add a short summary of all implemented members to a module’s docstring.

hydpy.core.autodoctools.autodoc_tuple2doc(module: ModuleType) None[source]

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

class hydpy.core.autodoctools.Directory[source]

Bases: TypedDict

Helper for representing directory structures.

subdirectories: dict[str, Directory]

Mapping between the subdirectory names and the subdirectories of a directory.

files: list[str]

The names of all files of a directory.

class hydpy.core.autodoctools.ProjectStructure(*, projectpath: str, branch: str)[source]

Bases: object

Analyser and visualiser of the file structure of HydPy projects.

property url_prefix: str

The base GitHub URL that is common to all project files of the relevant branch.

>>> import os
>>> from hydpy import data
>>> projectpath = os.path.join(data.__path__[0], "HydPy-H-Lahn")
>>> from hydpy.core.autodoctools import ProjectStructure
>>> ProjectStructure(projectpath=projectpath, branch="master").url_prefix
'https://github.com/hydpy-dev/hydpy/blob/master/hydpy/data'
>>> ProjectStructure(projectpath=projectpath, branch="release/v6.0").url_prefix
'https://github.com/hydpy-dev/hydpy/blob/release/v6.0/hydpy/data'
property directories: Directory

A representation of the project’s file structure based on nested Directory dictionaries.

>>> import os
>>> from hydpy import data
>>> dirpath = os.path.join(data.__path__[0], "HydPy-H-Lahn")
>>> from hydpy.core.autodoctools import ProjectStructure
>>> pj = ProjectStructure(projectpath=dirpath, branch="master")
>>> from pprint import pprint
>>> pprint(pj.directories["files"])
['multiple_runs.xml',
 'multiple_runs_alpha.xml',
 'single_run.xml',
 'single_run.xmlt']
>>> list(pj.directories["subdirectories"])
['conditions', 'control', 'network', 'series']
>>> control = pj.directories["subdirectories"]["control"]
>>> pprint(control["subdirectories"]["default"]["files"])
['land.py',
 'land_dill_assl.py',
 'land_lahn_kalk.py',
 'land_lahn_leun.py',
 'land_lahn_marb.py',
 'stream_dill_assl_lahn_leun.py',
 'stream_lahn_leun_lahn_kalk.py',
 'stream_lahn_marb_lahn_leun.py']
>>> pprint(control["subdirectories"]["default"]["subdirectories"])
{}
property html: str

A representation of the project’s file structure based on nested HTML describe elements, including inline CSS instructions.

>>> import os
>>> from hydpy import data
>>> projectpath = os.path.join(data.__path__[0], "HydPy-H-Lahn")
>>> from hydpy.core.autodoctools import ProjectStructure
>>> pj = ProjectStructure(projectpath=projectpath, branch="master")
>>> print(pj.html)  
<details style="margin-left:1em; color:#20435c; font-family:'Trebuchet MS'">
  <summary><b>HydPy-H-Lahn</b></summary>
  ...
  <details style="margin-left:1em">
    <summary><b>control</b></summary>
    <details style="margin-left:1em">
      <summary><b>default</b></summary>
      <h style="margin-left:1em"><a class="reference external" href="https://github.com/hydpy-dev/hydpy/blob/master/hydpy/data/HydPy-H-Lahn/control/default/land.py">land.py</a></h><br/>
      ...
      <h style="margin-left:1em"><a class="reference external" href="https://github.com/hydpy-dev/hydpy/blob/master/hydpy/data/HydPy-H-Lahn/control/default/stream_lahn_marb_lahn_leun.py">stream_lahn_marb_lahn_leun.py</a></h><br/>
    </details>
  </details>
  ...
</details>

Create an internal HTML reference pointing to an application model.

>>> from hydpy.core.autodoctools import make_modellink
>>> from hydpy.models import hland_96
>>> make_modellink(hland_96.Model)
'<a class="reference internal" href="hland_96.html#module-hydpy.models.hland_96" title="HydPy-H-HBV96 (adoption of SMHI-IHMS-HBV96)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">hland_96</span></code></a>'

Create an internal HTML reference pointing to a submodel interface.

>>> from hydpy.core.autodoctools import make_interfacelink
>>> from hydpy.interfaces.aetinterfaces import AETModel_V1
>>> make_interfacelink(AETModel_V1)
'<a class="reference internal" href="aetinterfaces.html#hydpy.interfaces.aetinterfaces.AETModel_V1" title="hydpy.interfaces.aetinterfaces.AETModel_V1"><code class="xref py py-class docutils literal notranslate"><span class="pre">AETModel_V1</span></code></a>'
class hydpy.core.autodoctools.SubmodelGraph(*, modelname: str | None = None)[source]

Bases: object

Analyser and visualiser of the advisable connections between main models and submodels.

property interfaces: tuple[type[SubmodelInterface], ...]

All available submodel interfaces.

>>> from hydpy.core.autodoctools import SubmodelGraph
>>> for interface in SubmodelGraph().interfaces:
...     print(interface.__HYDPY_NAME__)  
AETModel_V1
ChannelModel_V1
...
TempModel_V1
TempModel_V2
WaterLevelModel_V1
property models: tuple[type[Model], ...]

All available application models.

>>> from hydpy.core.autodoctools import SubmodelGraph
>>> for model in SubmodelGraph().models:
...     print(model.__HYDPY_NAME__)  
arma_rimorido
conv_idw
...
wland_wag
wq_trapeze
wq_trapeze_strickler
wq_walrus
property rootmodels: tuple[type[Model], ...]

The models that build the roots of the submodel graph.

By default, all documentation-relevant main models:

>>> from hydpy.core.autodoctools import SubmodelGraph
>>> for model in SubmodelGraph().rootmodels:
...     print(model.__HYDPY_NAME__)  
arma_rimorido
conv_idw
...
sw1d_channel
wland_gd
wland_wag

Or only the selected model:

>>> for model in SubmodelGraph(modelname="wland_gd").rootmodels:
...     print(model.__HYDPY_NAME__)
wland_gd
property subgraphs: dict[type[Model], dict[SubmodelProperty | SubmodelsProperty, list[type[Model]]]]

Mappings from all models over their ports to their potential submodels.

>>> from hydpy.core.autodoctools import SubmodelGraph
>>> subgraphs = SubmodelGraph().subgraphs
>>> from hydpy.models import exch_branch_hbv96, hland_96
>>> subgraphs[exch_branch_hbv96.Model]
{}
>>> for port, models in subgraphs[hland_96.Model].items():
...     print(port.name, *(model.__HYDPY_NAME__ for model in models))
aetmodel evap_aet_hbv96 evap_aet_minhas evap_aet_morsim
rconcmodel rconc_nash rconc_uh
property graph: dict[type[Model], dict[SubmodelProperty | SubmodelsProperty, tuple[Graph, list[type[Model]]]]]

The complete (recursive) graph for all or the selected root models.

Example for a main model without any submodels:

>>> from hydpy.core.autodoctools import SubmodelGraph
>>> graph = SubmodelGraph(modelname="exch_branch_hbv96").graph
>>> for model, subgraph in graph.items():
...     print(model.__HYDPY_NAME__, subgraph)
exch_branch_hbv96 {}

Complex example that covers two important cases: (1) main models used as sub-submodels; (2) PET models are not suggested as submodel candidates for other PET models due to special-casing:

>>> graph = SubmodelGraph(modelname="hland_96").graph
>>> for model, subgraph in graph.items():
...     print(model.__HYDPY_NAME__, *(port.name for port in subgraph))
hland_96 aetmodel rconcmodel
>>> selected = tuple(graph.values())[0]
>>> for port, (subgraphs, mainsubmodels) in selected.items():
...     print(port.name)
...     print("main-submodels:", *(m.__HYDPY_NAME__ for m in mainsubmodels))
...     print("sub-submodels:", *(m.__HYDPY_NAME__ for m in subgraphs))
aetmodel
main-submodels:
sub-submodels: evap_aet_hbv96 evap_aet_minhas evap_aet_morsim
rconcmodel
main-submodels:
sub-submodels: rconc_nash rconc_uh
>>> selected = tuple(selected.values())[0][0]
>>> for model, subgraph in selected.items():
...     print(model.__HYDPY_NAME__, *(port.name for port in subgraph))
evap_aet_hbv96 intercmodel petmodel snowcovermodel soilwatermodel tempmodel
evap_aet_minhas intercmodel petmodel soilwatermodel
evap_aet_morsim intercmodel radiationmodel snowalbedomodel snowcovermodel snowycanopymodel soilwatermodel tempmodel
>>> selected = tuple(tuple(selected.values())[0].items())[:2]
>>> for port, (subgraphs, mainsubmodels) in selected:
...     print(port.name)
...     print("main-submodels:", *(m.__HYDPY_NAME__ for m in mainsubmodels))
...     print("sub-submodels:", *(m.__HYDPY_NAME__ for m in subgraphs))
intercmodel
main-submodels: hland_96
sub-submodels: dummy_interceptedwater
petmodel
main-submodels:
sub-submodels: evap_pet_ambav1 evap_pet_hbv96 evap_pet_m evap_pet_mlc evap_ret_fao56 evap_ret_io evap_ret_tw2002
>>> selected = selected[1][1][0]
>>> for model, subgraph in selected.items():
...     print(model.__HYDPY_NAME__, *(port.name for port in subgraph))
evap_pet_ambav1 precipmodel radiationmodel snowcovermodel tempmodel
evap_pet_hbv96 precipmodel tempmodel
evap_pet_m retmodel
evap_pet_mlc retmodel
evap_ret_fao56 radiationmodel tempmodel
evap_ret_io
evap_ret_tw2002 radiationmodel tempmodel
>>> selected = tuple(selected.values())[2]
>>> for port, (subgraphs, mainsubmodels) in selected.items():
...     print(port.name)
...     print("main-submodels:", *(m.__HYDPY_NAME__ for m in mainsubmodels))
...     print("sub-submodels:", *(m.__HYDPY_NAME__ for m in subgraphs))
retmodel
main-submodels:
sub-submodels: evap_ret_fao56 evap_ret_io evap_ret_tw2002

Example showing that “side-model” ports are ignored:

>>> graph = SubmodelGraph(modelname="sw1d_channel").graph
>>> for model, subgraph in graph.items():
...     print(model.__HYDPY_NAME__, *(port.name for port in subgraph))
sw1d_channel routingmodels storagemodels
>>> selected = tuple(graph.values())[0]
>>> for port, (subgraphs, mainsubmodels) in selected.items():
...     print(port.name)
...     print("main-submodels:", *(m.__HYDPY_NAME__ for m in mainsubmodels))
...     print("sub-submodels:", *(m.__HYDPY_NAME__ for m in subgraphs))
routingmodels
main-submodels:
sub-submodels: sw1d_gate_out sw1d_lias sw1d_lias_sluice sw1d_pump sw1d_q_in sw1d_q_out sw1d_weir_out
storagemodels
main-submodels:
sub-submodels: sw1d_storage
>>> selected = tuple(selected.values())[0][0]
>>> for model, subgraph in selected.items():
...     print(model.__HYDPY_NAME__, *(port.name for port in subgraph))
sw1d_gate_out
sw1d_lias crosssection
sw1d_lias_sluice crosssection
sw1d_pump crosssection
sw1d_q_in crosssection
sw1d_q_out crosssection
sw1d_weir_out
static make_portinfo(port: SubmodelProperty | SubmodelsProperty, mainsubmodels: list[type[Model]]) str[source]

Create an informative sentence about the given port (if available, in the context of the given main models usable as sub-submodels).

>>> from hydpy.core.autodoctools import SubmodelGraph
>>> from hydpy.models.hland_96 import Model as h
>>> SubmodelGraph.make_portinfo(h.rconcmodel, [])  
'Allows one submodel that follows the <a ...RConcModel_V1...</a> interface.'
>>> SubmodelGraph.make_portinfo(h.aetmodel, [])  
'Requires one submodel that follows the <a ...AETModel_V1...</a> interface (no default available).'
>>> from hydpy.models.evap_aet_hbv96 import Model as e
>>> SubmodelGraph.make_portinfo(e.tempmodel, [h])  
'Requires one submodel that follows the <a ...TempModel_V1...</a> or <a ...TempModel_V2...</a> interface (default is <a ...HydPy-H-HBV96...</a>).'
>>> from hydpy.models.sw1d_channel import Model as s
>>> SubmodelGraph.make_portinfo(s.storagemodels, [])  
'Allows multiple submodels that follow the <a ...StorageModel_V1...</a> interface.'
property html: str

A representation of the complete or selected part of the submodel graph based on nested HTML describe elements, including inline CSS instructions.

>>> from hydpy.core.autodoctools import SubmodelGraph
>>> print(SubmodelGraph().html)  
<details style="margin-left:1.0em; color:#20435c; font-family:'Trebuchet MS'">
  <summary><b>Complete Submodel Graph</b></summary>
  <h style="margin-left:1.9em"><a ...HydPy-ARMA-RIMO/RIDO...</a></h></br>
  ...
</details>
>>> print(SubmodelGraph(modelname="hland_96").html)  
<details style="margin-left:1.0em; color:#20435c; font-family:'Trebuchet MS'">
  <summary><b>Submodel Graph of HydPy-H-HBV96 (adoption of ...)</b></summary>
  <details style="margin-left:1.0em">
    <summary><a ...HydPy-H-HBV96...</a></summary>
    <details style="margin-left:1.0em">
      <summary>aetmodel</summary>
      <p style="margin-left:1.2em; margin-bottom:0.0em; margin-top:0.2em; font-style:italic; font-size:80%">Requires one submodel ...</p>
      <details style="margin-left:1.0em">
        <summary><a ...HydPy-Evap-AET-HBV96...</a></summary>
        <details style="margin-left:1.0em">
          <summary>intercmodel</summary>
          <p style="margin-left:1.2em; margin-bottom:0.0em; margin-top:0.2em; font-style:italic; font-size:80%">Requires one submodel ...</p>
          <h style="margin-left:1.9em"><a ...HydPy-H-HBV96...</a><br/></h>
          <h style="margin-left:1.9em"><a ...HydPy-Dummy-InterceptedWater ...</a></h></br>
...
  </details>
</details>