hms-commander 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hms_commander/Decorators.py +150 -0
- hms_commander/HmsBasin.py +637 -0
- hms_commander/HmsCmdr.py +512 -0
- hms_commander/HmsControl.py +405 -0
- hms_commander/HmsDss.py +628 -0
- hms_commander/HmsExamples.py +710 -0
- hms_commander/HmsGage.py +404 -0
- hms_commander/HmsGeo.py +757 -0
- hms_commander/HmsJython.py +1210 -0
- hms_commander/HmsMet.py +699 -0
- hms_commander/HmsOutput.py +710 -0
- hms_commander/HmsPrj.py +1332 -0
- hms_commander/HmsResults.py +521 -0
- hms_commander/HmsRun.py +1494 -0
- hms_commander/HmsUtils.py +667 -0
- hms_commander/LoggingConfig.py +91 -0
- hms_commander/__init__.py +143 -0
- hms_commander/_constants.py +283 -0
- hms_commander/_parsing.py +287 -0
- hms_commander/dss/__init__.py +38 -0
- hms_commander/dss/_hec_monolith.py +362 -0
- hms_commander/dss/core.py +563 -0
- hms_commander-0.1.0.dist-info/METADATA +231 -0
- hms_commander-0.1.0.dist-info/RECORD +27 -0
- hms_commander-0.1.0.dist-info/WHEEL +5 -0
- hms_commander-0.1.0.dist-info/licenses/LICENSE +21 -0
- hms_commander-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Decorators for hms-commander library.
|
|
3
|
+
|
|
4
|
+
Provides decorators for logging and input standardization for HMS file operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from functools import wraps
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Union
|
|
10
|
+
import logging
|
|
11
|
+
import inspect
|
|
12
|
+
|
|
13
|
+
# Import log_call from LoggingConfig (single source of truth)
|
|
14
|
+
from .LoggingConfig import log_call
|
|
15
|
+
|
|
16
|
+
# Re-export for backwards compatibility
|
|
17
|
+
__all__ = ['log_call', 'standardize_path']
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def standardize_path(file_type: str = 'hms'):
|
|
21
|
+
"""
|
|
22
|
+
Decorator to standardize input paths for HMS file operations.
|
|
23
|
+
|
|
24
|
+
This decorator processes various input types and converts them to a Path object
|
|
25
|
+
pointing to the correct HMS file. It handles the following input types:
|
|
26
|
+
- pathlib.Path objects
|
|
27
|
+
- Strings (file paths)
|
|
28
|
+
|
|
29
|
+
The decorator also manages HMS object references.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
file_type (str): Specifies the type of HMS file to look for:
|
|
33
|
+
- 'hms': Project file (.hms)
|
|
34
|
+
- 'basin': Basin model file (.basin)
|
|
35
|
+
- 'met': Meteorologic model file (.met)
|
|
36
|
+
- 'control': Control specification file (.control)
|
|
37
|
+
- 'gage': Time-series gage file (.gage)
|
|
38
|
+
- 'grid': Grid file (.grid)
|
|
39
|
+
- 'pdata': Paired data file (.pdata)
|
|
40
|
+
- 'run': Run file (.run)
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
A decorator that wraps the function to standardize its input to a Path object.
|
|
44
|
+
"""
|
|
45
|
+
def decorator(func):
|
|
46
|
+
@wraps(func)
|
|
47
|
+
def wrapper(*args, **kwargs):
|
|
48
|
+
logger = logging.getLogger(func.__module__)
|
|
49
|
+
|
|
50
|
+
# Check if the function expects a path parameter
|
|
51
|
+
sig = inspect.signature(func)
|
|
52
|
+
param_names = list(sig.parameters.keys())
|
|
53
|
+
|
|
54
|
+
# Handle both static method calls and regular function calls
|
|
55
|
+
if args and isinstance(args[0], type):
|
|
56
|
+
# Static method call, remove the class argument
|
|
57
|
+
args = args[1:]
|
|
58
|
+
|
|
59
|
+
# Get file_input from kwargs if provided with file_path key, or take first positional arg
|
|
60
|
+
file_input = kwargs.pop('file_path', None) if 'file_path' in kwargs else (args[0] if args else None)
|
|
61
|
+
|
|
62
|
+
# hms_object is always keyword-only, never in args
|
|
63
|
+
hms_object = kwargs.pop('hms_object', None)
|
|
64
|
+
|
|
65
|
+
# Get global hms object only if hms_object not provided
|
|
66
|
+
# Use lazy import to avoid circular dependency during HmsPrj initialization
|
|
67
|
+
hms_obj = hms_object
|
|
68
|
+
if hms_obj is None:
|
|
69
|
+
try:
|
|
70
|
+
from .HmsPrj import hms
|
|
71
|
+
hms_obj = hms
|
|
72
|
+
except ImportError:
|
|
73
|
+
hms_obj = None
|
|
74
|
+
|
|
75
|
+
# If no file_input provided, return the function unmodified
|
|
76
|
+
if file_input is None:
|
|
77
|
+
return func(*args, **kwargs)
|
|
78
|
+
|
|
79
|
+
file_path = None
|
|
80
|
+
|
|
81
|
+
# Clean and normalize string inputs
|
|
82
|
+
if isinstance(file_input, str):
|
|
83
|
+
# Clean the string (remove extra whitespace, normalize path separators)
|
|
84
|
+
file_input = file_input.strip()
|
|
85
|
+
|
|
86
|
+
# Check if it's a raw file path that exists
|
|
87
|
+
try:
|
|
88
|
+
test_path = Path(file_input)
|
|
89
|
+
if test_path.is_file():
|
|
90
|
+
file_path = test_path
|
|
91
|
+
logger.info(f"Using file from direct string path: {file_path}")
|
|
92
|
+
except Exception as e:
|
|
93
|
+
logger.debug(f"Error converting string to path: {str(e)}")
|
|
94
|
+
|
|
95
|
+
# If a valid path wasn't created from string processing, continue with normal flow
|
|
96
|
+
if file_path is None:
|
|
97
|
+
# If file_input is already a Path and exists, use it directly
|
|
98
|
+
if isinstance(file_input, Path) and file_input.is_file():
|
|
99
|
+
file_path = file_input
|
|
100
|
+
logger.info(f"Using existing Path object file: {file_path}")
|
|
101
|
+
|
|
102
|
+
# Handle string inputs that are file names (not full paths) from HMS project
|
|
103
|
+
elif isinstance(file_input, str) and hms_obj is not None:
|
|
104
|
+
try:
|
|
105
|
+
hms_obj.check_initialized()
|
|
106
|
+
|
|
107
|
+
# Look up file in appropriate DataFrame based on file_type
|
|
108
|
+
if file_type == 'basin':
|
|
109
|
+
matching = hms_obj.basin_df[hms_obj.basin_df['name'] == file_input]
|
|
110
|
+
if not matching.empty:
|
|
111
|
+
file_path = Path(matching.iloc[0]['full_path'])
|
|
112
|
+
|
|
113
|
+
elif file_type == 'met':
|
|
114
|
+
matching = hms_obj.met_df[hms_obj.met_df['name'] == file_input]
|
|
115
|
+
if not matching.empty:
|
|
116
|
+
file_path = Path(matching.iloc[0]['full_path'])
|
|
117
|
+
|
|
118
|
+
elif file_type == 'control':
|
|
119
|
+
matching = hms_obj.control_df[hms_obj.control_df['name'] == file_input]
|
|
120
|
+
if not matching.empty:
|
|
121
|
+
file_path = Path(matching.iloc[0]['full_path'])
|
|
122
|
+
|
|
123
|
+
elif file_type == 'run':
|
|
124
|
+
matching = hms_obj.run_df[hms_obj.run_df['name'] == file_input]
|
|
125
|
+
if not matching.empty:
|
|
126
|
+
file_path = Path(matching.iloc[0]['full_path'])
|
|
127
|
+
|
|
128
|
+
except Exception as e:
|
|
129
|
+
logger.debug(f"Error looking up file in HMS project: {str(e)}")
|
|
130
|
+
|
|
131
|
+
# Final verification that the path exists
|
|
132
|
+
if file_path is None or not file_path.exists():
|
|
133
|
+
error_msg = f"HMS {file_type} file not found: {file_input}"
|
|
134
|
+
logger.error(error_msg)
|
|
135
|
+
raise FileNotFoundError(error_msg)
|
|
136
|
+
|
|
137
|
+
logger.info(f"Final validated file path: {file_path}")
|
|
138
|
+
|
|
139
|
+
# Pass all original arguments and keywords, replacing file_input with standardized file_path
|
|
140
|
+
# If the original input was positional, replace the first argument
|
|
141
|
+
if args and 'file_path' not in kwargs:
|
|
142
|
+
new_args = (file_path,) + args[1:]
|
|
143
|
+
else:
|
|
144
|
+
new_args = args
|
|
145
|
+
kwargs['file_path'] = file_path
|
|
146
|
+
|
|
147
|
+
return func(*new_args, **kwargs)
|
|
148
|
+
|
|
149
|
+
return wrapper
|
|
150
|
+
return decorator
|