2Core validation utilities for PyHelios.
4Provides decorators, type coercion, and standardized error handling
5following PyHelios's fail-fast philosophy.
10from typing
import Any, Callable, Dict, Union
12from .exceptions
import ValidationError, create_validation_error
16 type_coercions: Dict[str, Callable] =
None):
18 Decorator for comprehensive parameter validation.
20 Performs type coercion first, then validation, following the pattern:
21 1. Coerce types where safe (e.g., list to vec3)
22 2. Validate all parameters meet requirements
23 3. Call original function if validation passes
26 param_validators: Dict mapping parameter names to validation functions
27 type_coercions: Dict mapping parameter names to coercion functions
30 >>> # Using the validate_input decorator:
32 >>> # param_validators={'center': validate_vec3, 'radius': validate_positive_value},
33 >>> # type_coercions={'center': coerce_to_vec3}
35 >>> # def addSphere(self, center, radius, **kwargs):
36 >>> # # center is guaranteed to be vec3, radius is positive
39 @functools.wraps(func)
40 def wrapper(*args, **kwargs):
43 for param, coercion_func
in type_coercions.items():
46 kwargs[param] = coercion_func(kwargs[param], param_name=param)
47 except ValidationError:
49 except Exception
as e:
50 raise create_validation_error(
51 f
"Failed to coerce parameter to expected type: {str(e)}",
53 function_name=func.__name__
58 for param, validator
in param_validators.items():
61 validator(kwargs[param], param_name=param, function_name=func.__name__)
62 except ValidationError:
64 except Exception
as e:
65 raise create_validation_error(
66 f
"Parameter validation failed: {str(e)}",
68 function_name=func.__name__
71 return func(*args, **kwargs)
77 """Check if value is a finite number (not NaN or inf)."""
79 float_value = float(value)
80 return math.isfinite(float_value)
81 except (ValueError, TypeError, OverflowError):
87 Validate value is positive and finite.
90 value: Value to validate
91 param_name: Parameter name for error messages
92 function_name: Function name for error messages
95 ValidationError: If value is not positive or not finite
98 raise create_validation_error(
99 f
"Parameter must be a finite number, got {value} ({type(value).__name__})",
100 param_name=param_name,
101 function_name=function_name,
102 expected_type=
"positive finite number",
107 raise create_validation_error(
108 f
"Parameter must be positive, got {value}",
109 param_name=param_name,
110 function_name=function_name,
111 expected_type=
"positive number",
113 suggestion=
"Use a value greater than 0."
119 Validate value is non-negative and finite.
122 value: Value to validate
123 param_name: Parameter name for error messages
124 function_name: Function name for error messages
127 ValidationError: If value is negative or not finite
130 raise create_validation_error(
131 f
"Parameter must be a finite number, got {value} ({type(value).__name__})",
132 param_name=param_name,
133 function_name=function_name,
134 expected_type=
"non-negative finite number",
139 raise create_validation_error(
140 f
"Parameter must be non-negative, got {value}",
141 param_name=param_name,
142 function_name=function_name,
143 expected_type=
"non-negative number",
145 suggestion=
"Use a value >= 0."
149def coerce_to_vec3(value: Any, param_name: str =
"parameter") ->
'vec3':
151 Safely coerce list/tuple to vec3 with validation.
154 value: Value to coerce (vec3, list, or tuple)
155 param_name: Parameter name for error messages
161 ValidationError: If coercion fails or values are invalid
163 from ..wrappers.DataTypes
import vec3
166 if hasattr(value,
'x')
and hasattr(value,
'y')
and hasattr(value,
'z')
and hasattr(value,
'to_list'):
169 if isinstance(value, (list, tuple)):
171 raise create_validation_error(
172 f
"Parameter must have exactly 3 elements for vec3 conversion, got {len(value)} elements: {value}",
173 param_name=param_name,
174 expected_type=
"3-element list or tuple",
176 suggestion=
"Provide exactly 3 numeric values like [x, y, z] or (x, y, z)."
180 for i, component
in enumerate(value):
182 raise create_validation_error(
183 f
"Parameter element [{i}] must be a finite number, got {component} ({type(component).__name__})",
184 param_name=f
"{param_name}[{i}]",
185 expected_type=
"finite number",
186 actual_value=component,
187 suggestion=
"Ensure all coordinate values are finite numbers (not NaN or infinity)."
190 return vec3(float(value[0]), float(value[1]), float(value[2]))
192 raise create_validation_error(
193 f
"Parameter must be a vec3, list, or tuple, got {type(value).__name__}",
194 param_name=param_name,
195 expected_type=
"vec3, list, or tuple",
197 suggestion=
"Use vec3(x, y, z), [x, y, z], or (x, y, z) format."
201def coerce_to_vec2(value: Any, param_name: str =
"parameter") ->
'vec2':
203 Safely coerce list/tuple to vec2 with validation.
206 value: Value to coerce (vec2, list, or tuple)
207 param_name: Parameter name for error messages
213 ValidationError: If coercion fails or values are invalid
215 from ..wrappers.DataTypes
import vec2
218 if hasattr(value,
'x')
and hasattr(value,
'y')
and hasattr(value,
'to_list')
and not hasattr(value,
'z'):
221 if isinstance(value, (list, tuple)):
223 raise create_validation_error(
224 f
"Parameter must have exactly 2 elements for vec2 conversion, got {len(value)} elements: {value}",
225 param_name=param_name,
226 expected_type=
"2-element list or tuple",
228 suggestion=
"Provide exactly 2 numeric values like [x, y] or (x, y)."
232 for i, component
in enumerate(value):
234 raise create_validation_error(
235 f
"Parameter element [{i}] must be a finite number, got {component} ({type(component).__name__})",
236 param_name=f
"{param_name}[{i}]",
237 expected_type=
"finite number",
238 actual_value=component,
239 suggestion=
"Ensure all coordinate values are finite numbers (not NaN or infinity)."
242 return vec2(float(value[0]), float(value[1]))
244 raise create_validation_error(
245 f
"Parameter must be a vec2, list, or tuple, got {type(value).__name__}",
246 param_name=param_name,
247 expected_type=
"vec2, list, or tuple",
249 suggestion=
"Use vec2(x, y), [x, y], or (x, y) format."
'vec3' coerce_to_vec3(Any value, str param_name="parameter")
Safely coerce list/tuple to vec3 with validation.
validate_positive_value(Any value, str param_name="value", str function_name=None)
Validate value is positive and finite.
'vec2' coerce_to_vec2(Any value, str param_name="parameter")
Safely coerce list/tuple to vec2 with validation.
validate_non_negative_value(Any value, str param_name="value", str function_name=None)
Validate value is non-negative and finite.
validate_input(Dict[str, Callable] param_validators=None, Dict[str, Callable] type_coercions=None)
Decorator for comprehensive parameter validation.
bool is_finite_numeric(Any value)
Check if value is a finite number (not NaN or inf).