diff --git a/docs/source/cli.rst b/docs/source/cli.rst index fbf898fedeba5f962efd1928f7224cef1aba290e..eb3907b61c5937b7d75ef29d4847c589ec46bf16 100644 --- a/docs/source/cli.rst +++ b/docs/source/cli.rst @@ -8,7 +8,7 @@ All features are then provided as sub-commands as documented here. .. note:: You can get help for each (sub-)command with the option ``-h`` or ``--help``. -.. argparse:: - :ref: mhm_tools._cli._main._get_parser - :prog: mhm-tools - :nodefaultconst: +.. sphinx_argparse_cli:: + :module: mhm_tools._cli._main + :func: _get_parser + :prog: mhm-tools diff --git a/docs/source/conf.py b/docs/source/conf.py index 754f7384eb86b9a10b1b83a22bdbebbab0901c90..0a29666eaab1d719a1e21573f92dcc7c6d38a0da 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -25,7 +25,7 @@ extensions = [ "sphinx.ext.viewcode", "sphinx.ext.napoleon", # parameters look better than with numpydoc only "numpydoc", - "sphinxarg.ext", # documentation of the CLI + "sphinx_argparse_cli", # documentation of the CLI ] # autosummaries from source-files @@ -75,8 +75,8 @@ html_logo = "_static/logo_large.png" html_favicon = "_static/logo.png" html_theme_options = { - "page_sidebar_items": ["page-toc"], - "footer_items": ["copyright"], + "secondary_sidebar_items": ["page-toc"], + "footer_start": ["copyright"], "show_nav_level": 2, "show_toc_level": 2, "icon_links": [ diff --git a/pyproject.toml b/pyproject.toml index 61652ace5961ff8fc018626f80e08e2e3ef6e8aa..245819d35a366d3e29124aa5996c8bb42a06e211 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ dependencies = [ "numpy>=1.17.3", "netCDF4", "xarray", + "pandas<2; python_version=='3.8'", ] [project.urls] @@ -50,14 +51,12 @@ Changelog = "https://git.ufz.de/mhm/mhm/-/blob/main/CHANGELOG.md" [project.optional-dependencies] doc = [ "sphinx>=5", - "pydata-sphinx-theme==0.11", + "pydata-sphinx-theme>=0.13", "numpydoc>=1.1", "sphinx-design>=0.3", "myst-parser>=0.18", - "sphinxcontrib-mermaid>=0.7", - "ablog>=0.10", "docutils>=0.18", # mdinclude with myst - "sphinx-argparse>=0.4.0", # CLI doc + "sphinx-argparse-cli>=1.11.0", # CLI doc ] test = ["pytest-cov>=3"] check = [ @@ -108,7 +107,7 @@ omit = [ "*docs*", "*examples*", "*tests*", - "*_cli*", + "*/_cli/*", ] [tool.coverage.report] diff --git a/src/mhm_tools/_cli/_bankfull.py b/src/mhm_tools/_cli/_bankfull.py index 305dcbb49761f4c68c6f246da767302d6817c531..52263033f3ac6cfb1f2253aa805d9162ba23d0bd 100644 --- a/src/mhm_tools/_cli/_bankfull.py +++ b/src/mhm_tools/_cli/_bankfull.py @@ -1,5 +1,10 @@ -"""Calculate the river discharge at bankfull conditions and the bankfull width.""" -from ..post.bankfull_discharge import gen_bankfull_discharge +""" +Calculate the river discharge at bankfull conditions and the bankfull width. + +Bankfull discharge is determined as the yearly peak flow from monthly average discharge +with a recurrence interval given by ``return_period``, which is 1.5 years by default. +""" +from ..post.bankfull import bankfull_discharge def add_args(parser): @@ -11,11 +16,11 @@ def add_args(parser): the main argument parser """ parser.add_argument( - "-p", + "-r", "--return_period", type=float, default=1.5, - help="The return period of the flood.", + help="The return period of the flood in years.", ) parser.add_argument( "-w", @@ -31,7 +36,7 @@ def add_args(parser): default="Qrouted", help="Variable name for routed streamflow in the NetCDF file", ) - required_args = parser.add_argument_group("Required Named Arguments") + required_args = parser.add_argument_group("required arguments") required_args.add_argument( "-i", "--input", @@ -48,7 +53,7 @@ def add_args(parser): ) -def bankfull(args): +def run(args): """Calculate the bankfull discharge. Parameters @@ -56,7 +61,7 @@ def bankfull(args): args : argparse.Namespace parsed command line arguments """ - gen_bankfull_discharge( + bankfull_discharge( ncin_path=args.in_file, ncout_path=args.out_file, return_period=args.return_period, diff --git a/src/mhm_tools/_cli/_main.py b/src/mhm_tools/_cli/_main.py index cb84e6ce5054971ff3c2912229dc55089516f5e6..d35843c7a85e4a02c6005a841999dcddade322a7 100644 --- a/src/mhm_tools/_cli/_main.py +++ b/src/mhm_tools/_cli/_main.py @@ -5,16 +5,39 @@ from .. import __version__ from . import _bankfull -class CustomFormatter( +class Formatter( argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter ): """Custom formatter for argparse with help and raw text.""" +def add_command_from_module(subparsers, name, module): + """ + Add a subcommand from a given module. + + Parameters + ---------- + subparsers : subparsers + Subparser to add the command to. + name : str + Name of the command to add. + module : module + Module containing the `add_args` and `run` functions defining the command. + """ + desc = module.__doc__ + kwargs = {"description": desc} + if desc: + kwargs["help"] = desc.splitlines()[0] + parser = subparsers.add_parser(name, formatter_class=Formatter, **kwargs) + module.add_args(parser) + parser.set_defaults(func=module.run) + + def _get_parser(): parent_parser = argparse.ArgumentParser( + prog="mhm-tools", description=__doc__, - formatter_class=CustomFormatter, + formatter_class=Formatter, ) parent_parser.add_argument( @@ -22,7 +45,7 @@ def _get_parser(): "--version", action="version", version=__version__, - help="display version information", + help="Display version information.", ) sub_help = ( @@ -35,14 +58,9 @@ def _get_parser(): # all sub-parsers should be added here # documentation taken from docstring of respective cli module (first line summary) + # module needs two functions: add_args and run - desc = _bankfull.__doc__ - help = desc.splitlines()[0] - parser = subparsers.add_parser( - "bankfull", description=desc, help=help, formatter_class=CustomFormatter - ) - _bankfull.add_args(parser) - parser.set_defaults(func=_bankfull.bankfull) + add_command_from_module(subparsers, "bankfull", _bankfull) # return the parser return parent_parser diff --git a/src/mhm_tools/post/__init__.py b/src/mhm_tools/post/__init__.py index 7cdd1bdca5bc42fa48596cc1170ae248df5ab393..97ba5d375d6b2393b998136bab38792eddfafde7 100644 --- a/src/mhm_tools/post/__init__.py +++ b/src/mhm_tools/post/__init__.py @@ -6,11 +6,11 @@ Bankfull discharge .. autosummary:: :toctree: - gen_bankfull_discharge + bankfull_discharge """ -from . import bankfull_discharge -from .bankfull_discharge import gen_bankfull_discharge +from . import bankfull +from .bankfull import bankfull_discharge -__all__ = ["bankfull_discharge"] -__all__ += ["gen_bankfull_discharge"] +__all__ = ["bankfull"] +__all__ += ["bankfull_discharge"] diff --git a/src/mhm_tools/post/bankfull_discharge.py b/src/mhm_tools/post/bankfull.py similarity index 97% rename from src/mhm_tools/post/bankfull_discharge.py rename to src/mhm_tools/post/bankfull.py index fe93fb19b09754fc591485abb5d7509e3548a36c..b56ff9735544a55b9d37b6268541f1ecf9828f39 100644 --- a/src/mhm_tools/post/bankfull_discharge.py +++ b/src/mhm_tools/post/bankfull.py @@ -89,7 +89,7 @@ def process_grid(q_monthly, return_period): return q_bkfl -def gen_bankfull_discharge( +def bankfull_discharge( ncin_path, ncout_path, return_period=1.5, peri_bkfl=False, var="Qrouted" ): """Calculate bankfull discharge [1]_ [2]_. @@ -131,8 +131,9 @@ def gen_bankfull_discharge( ds["Q_bkfl"] = q_bkfl # perimeter if peri_bkfl: + # "4.8" from Savenije, H. H. G.: + # The width of a bankfull channel; Lacey's formula explained p_bkfl_data = np.copy(q_bkfl_data) - # "4.8" from Savenije, H. H. G.: The width of a bankfull channel; Lacey's formula explained p_bkfl_data[q_bkfl_data > 0] = 4.8 * np.sqrt(q_bkfl_data[q_bkfl_data > 0]) p_bkfl = q_bkfl.copy(data=p_bkfl_data) p_bkfl.attrs["long_name"] = "Perimeter at bankfull conditions" diff --git a/tests/test_bankfull.py b/tests/test_bankfull.py index 5adea39166e1e9c7d08992f26ab874eb00953d30..35e09a65e58dc4ab9c1a9e1d1ce579f0723a57b4 100644 --- a/tests/test_bankfull.py +++ b/tests/test_bankfull.py @@ -44,7 +44,7 @@ class TestBankfull(unittest.TestCase): ) def test_bankfull(self): - mt.post.gen_bankfull_discharge(self.in_file, self.out_file, peri_bkfl=True) + mt.post.bankfull_discharge(self.in_file, self.out_file, peri_bkfl=True) self.assertTrue(self.out_file.is_file()) ds = xr.load_dataset(self.out_file) self.assertIn("Q_bkfl", ds.variables)