Skip to content

Commit 892467a

Browse files
authored
docs: update and clarify docstrings with examples and type hints (#971)
docs: update and clarify docstrings with examples and type hints - Expanded docstrings with usage examples and improved clarity - Added missing type hints - Addressed review feedback and cleaned up documentation
1 parent 65b8ecc commit 892467a

File tree

1 file changed

+109
-26
lines changed
  • hydrolib/tools/extforce_convert

1 file changed

+109
-26
lines changed

hydrolib/tools/extforce_convert/cli.py

Lines changed: 109 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,19 @@
1616
)
1717

1818

19-
def valid_file(path_str):
20-
"""Check if the file exists, has a .mdu extension, and return its path."""
19+
def valid_file(path_str: str):
20+
"""Validate an .mdu file path and return it as a Path object.
21+
22+
Args:
23+
path_str (str):
24+
The path to the MDU file as provided on the command line.
25+
26+
Returns:
27+
Path: The validated path to the file.
28+
29+
Raises:
30+
argparse.ArgumentTypeError: If the file does not exist or does not have a .mdu extension.
31+
"""
2132
path = Path(path_str)
2233
if not str(path).lower().endswith(".mdu"):
2334
raise ArgumentTypeError(f"File must have a .mdu extension: {path}")
@@ -27,8 +38,21 @@ def valid_file(path_str):
2738
return path
2839

2940

30-
def _validator(path_str, extension):
31-
"""Validate that the file exists and has the given extension."""
41+
def _validator(path_str: str, extension: str):
42+
"""Validate that a file exists and matches the expected extension.
43+
44+
Args:
45+
path_str (str):
46+
The path to the file to validate.
47+
extension (str):
48+
The required file extension (including the dot), e.g. ".ext".
49+
50+
Returns:
51+
Path: The validated file path.
52+
53+
Raises:
54+
argparse.ArgumentTypeError: If the file does not exist or the extension does not match.
55+
"""
3256
path = Path(path_str)
3357
if not path.exists():
3458
raise ArgumentTypeError(f"File not found: {path}")
@@ -37,13 +61,36 @@ def _validator(path_str, extension):
3761
return path
3862

3963

40-
def valid_file_with_extension(extension):
41-
"""Create a validator for files with a specific extension for argparse."""
64+
def valid_file_with_extension(extension: str):
65+
"""Create a validator callable for argparse that enforces a file extension.
66+
67+
Args:
68+
extension (str):
69+
The required file extension (including the dot), e.g. ".mdu".
70+
71+
Returns:
72+
Callable[[str], Path]:
73+
A function that validates a path string and returns a Path when invoked.
74+
75+
Raises:
76+
argparse.ArgumentTypeError: Raised by the returned validator if the provided path does not exist or
77+
does not end with the required extension.
78+
"""
4279
return lambda path_str: _validator(path_str, extension)
4380

4481

45-
def valid_dir(path_str):
46-
"""Validate that the path exists and is a directory."""
82+
def valid_dir(path_str: str):
83+
"""Validate that the given path exists and is a directory.
84+
85+
Args:
86+
path_str (str): The path to validate.
87+
88+
Returns:
89+
Path: The validated directory path.
90+
91+
Raises:
92+
argparse.ArgumentTypeError: If the path does not exist or is not a directory.
93+
"""
4794
path = Path(path_str)
4895
if not path.exists():
4996
raise ArgumentTypeError(f"Directory not found: {path}")
@@ -53,6 +100,11 @@ def valid_dir(path_str):
53100

54101

55102
def _get_parser() -> argparse.ArgumentParser:
103+
"""Create and configure the argument parser for the extforce_convert CLI.
104+
105+
Returns:
106+
argparse.ArgumentParser: The configured argument parser.
107+
"""
56108
parser = argparse.ArgumentParser(
57109
prog="extforce_convert",
58110
description="Convert D-Flow FM legacy external forcings files to current external forcings file/initial fields file/structures file.",
@@ -137,32 +189,63 @@ def _get_parser() -> argparse.ArgumentParser:
137189

138190
def main(args=None):
139191
"""
140-
Entry point for extforce_convert tool.
192+
Entry point for the extforce_convert command-line tool.
141193
142194
CLI argument combinations:
143195
144196
Required (mutually exclusive, pick one):
145-
--mdufile MDUFILE Use MDUFILE to determine input/output files automatically.
146-
--extoldfile EXTOLDFILE Convert a specific legacy external forcing file.
147-
--dir DIR Recursively find and convert all .mdu files in DIR.
197+
--mdufile, -m MDUFILE Use MDUFILE to determine input/output files automatically.
198+
--extoldfile, -e EXTOLDFILE Convert a specific legacy external forcing file.
199+
--dir, -d DIR Recursively find and convert all .mdu files in DIR.
148200
149201
Optional:
150-
--outfiles EXTFILE INIFIELDFILE STRUCTUREFILE
202+
--outfiles, -o EXTFILE INIFIELDFILE STRUCTUREFILE
151203
Specify output filenames for forcings, initial fields, and structures (only with --mdufile or --extoldfile).
152-
--no-backup Do not create a backup of overwritten files (mutually exclusive).
153-
--remove-legacy-files -r Remove legacy/old files (e.g. .tim) after conversion.
154-
--verbose, -v Print diagnostic information.
155-
--version Print version and exit.
204+
Note: requires exactly three paths and is only valid for single-file conversions
205+
(i.e., when using --mdufile or --extoldfile). Using --outfiles with --dir is invalid
206+
and will result in an error.
207+
--no-backup Do not create a backup of overwritten files.
208+
--remove-legacy-files, -r Remove legacy/old files (e.g. .tim) after conversion.
209+
--debug-mode Convert only supported quantities; leave unsupported quantities in the legacy external forcing file (default: False).
210+
--verbose, -v Print diagnostic information.
211+
--version Print version and exit.
156212
--path-style {unix,windows}
157-
Handle absolute paths in input/output files according to the specified style (unix/windows).
158-
Use this when converting models that use unix paths but you are running the converter
159-
on a Windows machine and the opposite.
160-
161-
Example usages:
162-
extforce_convert --mdufile model.mdu
163-
extforce_convert --extoldfile old.ext --outfiles new.ext new.ini new.str
164-
extforce_convert --dir ./models --no-backup --remove-legacy-files
165-
extforce_convert --mdufile model.mdu --path-style unix
213+
Handle absolute paths in input/output files according to the specified style (unix/windows).
214+
Use this when converting models that use unix paths but you are running the converter
215+
on a Windows machine, or the opposite.
216+
217+
Args:
218+
args (Optional[List[str]]): Optional list of argument strings to parse instead of sys.argv. Useful for testing.
219+
220+
Notes:
221+
- `--outfiles` cannot be combined with `--dir`.
222+
- `--outfiles` applies only to a single conversion target (from --mdufile or --extoldfile) and must provide three
223+
filenames, in this order: EXTFILE INIFIELDFILE STRUCTUREFILE.
224+
- When `--debug-mode` is provided, only supported quantities are converted; unsupported quantities remain in the
225+
legacy external forcing file. Without this flag, encountering unsupported quantities results in a failure.
226+
227+
Examples (valid):
228+
- Use an MDU file and let the tool determine I/O automatically
229+
```shell
230+
>>> extforce_convert --mdufile model.mdu # doctest: +SKIP
231+
```
232+
- Convert a specific legacy .ext and explicitly set output filenames
233+
```shell
234+
>>> extforce_convert --extoldfile old.ext --outfiles new.ext new.ini new.str # doctest: +SKIP
235+
```
236+
- Recursively convert all models in a directory (no --outfiles here)
237+
```shell
238+
>>> extforce_convert --dir ./models --no-backup --remove-legacy-files # doctest: +SKIP
239+
```
240+
- Convert with explicit path style handling
241+
```shell
242+
>>> extforce_convert --mdufile model.mdu --path-style unix # doctest: +SKIP
243+
```
244+
- invalid flags combinations that will raise an error:
245+
--outfiles only works with single-file modes, not with --dir
246+
```shell
247+
>>> extforce_convert --dir ./models --outfiles a.ext b.ini c.str # doctest: +SKIP
248+
```
166249
"""
167250
parser = _get_parser()
168251
args = parser.parse_args(args)

0 commit comments

Comments
 (0)