simetri.helpers.validation
Validation functions for the user entered argument values and kwargs.
1"""Validation functions for the user entered argument values and kwargs.""" 2 3import re 4from strenum import enum 5from typing import Any, Dict 6 7from numpy import ndarray 8from ..graphics import all_enums, __version__ 9from ..graphics.all_enums import * 10from ..colors.colors import Color 11 12# Validation functions. They return True if the value is valid, False otherwise. 13 14 15class VersionConflict(Exception): 16 """Exception raised for version conflicts.""" 17 18 19def check_version(required_version: str) -> bool: 20 """ 21 Check if the current version is compatible with the required version. 22 23 Args: 24 required_version (str): The required version as a string. 25 26 Raises: 27 VersionConflict: If the current version is lower than the required version. 28 29 Returns: 30 bool: True if the current version is compatible. 31 """ 32 def version_value(str_version: str) -> int: 33 digits = str_version.split(".") 34 return int(digits[0]) * 100 + int(digits[1]) * 10 + int(digits[2]) 35 36 if version_value(required_version) > version_value(__version__): 37 msg = ( 38 f"Version conflict: Minimum required version is {required_version}. " 39 f"This version is {__version__}\n" 40 "Please update the simetri package using: pip install -U simetri" 41 ) 42 raise VersionConflict(msg) 43 44 return True 45 46 47def check_str(value: Any) -> bool: 48 """ 49 Check if the value is a string. 50 51 Args: 52 value (Any): The value to check. 53 54 Returns: 55 bool: True if the value is a string, False otherwise. 56 """ 57 return isinstance(value, str) 58 59 60def check_int(value: Any) -> bool: 61 """ 62 Check if the value is an integer. 63 64 Args: 65 value (Any): The value to check. 66 67 Returns: 68 bool: True if the value is an integer, False otherwise. 69 """ 70 return isinstance(value, int) 71 72 73def check_number(number: Any) -> bool: 74 """ 75 Check if the number is a valid number. 76 77 Args: 78 number (Any): The number to check. 79 80 Returns: 81 bool: True if the number is a valid number, False otherwise. 82 """ 83 return isinstance(number, (int, float)) 84 85 86def check_color(color: Any) -> bool: 87 """ 88 Check if the color is a valid color. 89 90 Args: 91 color (Any): The color to check. 92 93 Returns: 94 bool: True if the color is a valid color, False otherwise. 95 """ 96 return isinstance(color, (Color, str, tuple, list, ndarray)) 97 98 99def check_dash_array(dash_array: Any) -> bool: 100 """ 101 Check if the dash array is a list of numbers or predefined. 102 103 Args: 104 dash_array (Any): The dash array to check. 105 106 Returns: 107 bool: True if the dash array is valid, False otherwise. 108 """ 109 if isinstance(dash_array, (list, tuple, ndarray)): 110 res = all([isinstance(x, (int, float)) for x in dash_array]) 111 elif dash_array in LineDashArray: 112 res = True 113 elif dash_array is None: 114 res = True 115 116 117 return res 118 119 120def check_bool(value: Any) -> bool: 121 """ 122 Check if the value is a boolean. 123 124 Boolean values need to be explicitly set to True or False. 125 None is not a valid boolean value. 126 127 Args: 128 value (Any): The value to check. 129 130 Returns: 131 bool: True if the value is a boolean, False otherwise. 132 """ 133 return isinstance(value, bool) 134 135 136def check_enum(value: Any, enum: Any) -> bool: 137 """ 138 Check if the value is a valid enum value. 139 140 Args: 141 value (Any): The value to check. 142 enum (Any): The enum to check against. 143 144 Returns: 145 bool: True if the value is a valid enum value, False otherwise. 146 """ 147 return value in enum 148 149 150def check_blend_mode(blend_mode: Any) -> bool: 151 """ 152 Check if the blend mode is a valid blend mode. 153 154 Args: 155 blend_mode (Any): The blend mode to check. 156 157 Returns: 158 bool: True if the blend mode is valid, False otherwise. 159 """ 160 return blend_mode in BlendMode 161 162 163def check_position(pos: Any) -> bool: 164 """ 165 Check if the position is a valid position. 166 167 Args: 168 pos (Any): The position to check. 169 170 Returns: 171 bool: True if the position is valid, False otherwise. 172 """ 173 return ( 174 isinstance(pos, (list, tuple, ndarray)) 175 and len(pos) >= 2 176 and all(isinstance(x, (int, float)) for x in pos) 177 ) 178 179 180def check_points(points: Any) -> bool: 181 """ 182 Check if the points are a valid list of points. 183 184 Args: 185 points (Any): The points to check. 186 187 Returns: 188 bool: True if the points are valid, False otherwise. 189 """ 190 return isinstance(points, (list, tuple, ndarray)) and all( 191 isinstance(x, (list, tuple, ndarray)) for x in points 192 ) 193 194 195def check_xform_matrix(matrix: Any) -> bool: 196 """ 197 Check if the matrix is a valid transformation matrix. 198 199 Args: 200 matrix (Any): The matrix to check. 201 202 Returns: 203 bool: True if the matrix is valid, False otherwise. 204 """ 205 return isinstance(matrix, (list, tuple, ndarray)) 206 207 208def check_subtype(subtype: Any) -> bool: 209 """ 210 This check is done in Shape class. 211 212 Args: 213 subtype (Any): The subtype to check. 214 215 Returns: 216 bool: True 217 """ 218 return True 219 220 221def check_mask(mask: Any) -> bool: 222 """ 223 This check is done in Batch class. 224 225 Args: 226 mask (Any): The mask to check. 227 228 Returns: 229 bool: True if the mask is valid, False otherwise. 230 """ 231 return mask.type == Types.Shape 232 233 234def check_line_width(line_width: Any) -> bool: 235 """ 236 Check if the line width is a valid line width. 237 238 Args: 239 line_width (Any): The line width to check. 240 241 Returns: 242 bool: True if the line width is valid, False otherwise. 243 """ 244 if isinstance(line_width, (int, float)): 245 res = line_width >= 0 246 elif line_width in all_enums.LineWidth: 247 res = True 248 else: 249 res = False 250 251 return res 252 253 254def check_anchor(anchor: Any) -> bool: 255 """ 256 Check if the anchor is a valid anchor. 257 258 Args: 259 anchor (Any): The anchor to check. 260 261 Returns: 262 bool: True if the anchor is valid, False otherwise. 263 """ 264 return anchor in Anchor 265 266 267# Create a dictionary of enums for validation. 268items = (item for item in all_enums.__dict__.items() if item[0][0] != "_") 269# from https://stackoverflow.com/questions/1175208/elegant-python-function-to- 270# convert-camelcase-to-snake-case 271pattern = re.compile(r"(?<!^)(?=[A-Z])") # convert CamelCase to snake_case 272enum_map = {} 273exclude = [ 274 "TypeAlias", 275 "Union", 276 "StrEnum", 277 "CI_StrEnum", 278 "Comparable", 279 "IUC", 280 "drawable_types", 281 "shape_types", 282] 283for item in items: 284 name = item[0] 285 if isinstance(item[1], enum.EnumMeta) and name not in exclude: 286 key_ = pattern.sub("_", name).lower() 287 enum_map[key_] = item[1] 288 289d_validators = { 290 "alpha": check_number, 291 "clip": check_bool, 292 "color": check_color, 293 "dist_tol": check_number, 294 "dist_tol2": check_number, 295 "double_distance": check_number, 296 "double_lines": check_bool, 297 "draw_fillets": check_bool, 298 "draw_frame": check_bool, 299 "draw_markers": check_bool, 300 "even_odd_rule": check_bool, 301 "fill": check_bool, 302 "fill_alpha": check_number, 303 "fill_blend_mode": check_blend_mode, 304 "fill_color": check_color, 305 "fillet_radius": check_number, 306 "font_color": check_color, 307 "font_family": check_str, 308 "frame_inner_sep": check_number, 309 "frame_inner_xsep": check_number, 310 "frame_inner_ysep": check_number, 311 "frame_min_height": check_number, 312 "frame_min_width": check_number, 313 "grid_alpha": check_number, 314 "grid_back_color": check_color, 315 "grid_line_color": check_color, 316 "grid_line_width": check_number, 317 "line_alpha": check_number, 318 "line_blend_mode": check_blend_mode, 319 "line_color": check_color, 320 "line_dash_array": check_dash_array, 321 "line_dash_phase": check_number, 322 "line_miter_limit": check_number, 323 "line_width": check_line_width, 324 "marker_color": check_color, 325 "marker_radius": check_number, 326 "marker_size": check_number, 327 "markers_only": check_bool, 328 "mask": check_mask, 329 "pattern_angle": check_number, 330 "pattern_color": check_color, 331 "pattern_distance": check_number, 332 "pattern_line_width": check_number, 333 "pattern_points": check_int, 334 "pattern_radius": check_number, 335 "pattern_xshift": check_number, 336 "pattern_yshift": check_number, 337 "points": check_points, 338 "pos": check_position, 339 "radius": check_number, 340 "shade_axis_angle": check_number, 341 "shade_color_wheel": check_number, 342 "shade_color_wheel_black": check_bool, 343 "shade_color_wheel_white": check_bool, 344 "shade_bottom_color": check_color, 345 "shade_inner_color": check_color, 346 "shade_left_color": check_color, 347 "shade_lower_left_color": check_color, 348 "shade_lower_right_color": check_color, 349 "shade_middle_color": check_color, 350 "shade_outer_color": check_color, 351 "shade_right_color": check_color, 352 "shade_top_color": check_color, 353 "shade_upper_left_color": check_color, 354 "shade_upper_right_color": check_color, 355 "smooth": check_bool, 356 "stroke": check_bool, 357 "xform_matrix": check_xform_matrix, 358 "subtype": check_subtype, 359 "text_alpha": check_number, 360 "transparency_group": check_bool, 361} 362 363 364def validate_args(args: Dict[str, Any], valid_args: list[str]) -> None: 365 """ 366 Validate the user entered arguments. 367 368 Args: 369 args (Dict[str, Any]): The arguments to validate. 370 valid_args (list[str]): The list of valid argument keys. 371 372 Raises: 373 ValueError: If an invalid key or value is found. 374 375 Returns: 376 None 377 """ 378 for key, value in args.items(): 379 if (key not in valid_args) and (key not in d_validators): 380 raise ValueError(f"Invalid key: {key}") 381 if key in d_validators: 382 if not d_validators[key](value): 383 raise ValueError(f"Invalid value for {key}: {value}") 384 elif key in enum_map: 385 if value not in enum_map[key]: 386 raise ValueError(f"Invalid value for {key}: {value}") 387 elif not d_validators[key](value): 388 raise ValueError(f"Invalid value for {key}: {value}")
Exception raised for version conflicts.
20def check_version(required_version: str) -> bool: 21 """ 22 Check if the current version is compatible with the required version. 23 24 Args: 25 required_version (str): The required version as a string. 26 27 Raises: 28 VersionConflict: If the current version is lower than the required version. 29 30 Returns: 31 bool: True if the current version is compatible. 32 """ 33 def version_value(str_version: str) -> int: 34 digits = str_version.split(".") 35 return int(digits[0]) * 100 + int(digits[1]) * 10 + int(digits[2]) 36 37 if version_value(required_version) > version_value(__version__): 38 msg = ( 39 f"Version conflict: Minimum required version is {required_version}. " 40 f"This version is {__version__}\n" 41 "Please update the simetri package using: pip install -U simetri" 42 ) 43 raise VersionConflict(msg) 44 45 return True
Check if the current version is compatible with the required version.
Arguments:
- required_version (str): The required version as a string.
Raises:
- VersionConflict: If the current version is lower than the required version.
Returns:
bool: True if the current version is compatible.
48def check_str(value: Any) -> bool: 49 """ 50 Check if the value is a string. 51 52 Args: 53 value (Any): The value to check. 54 55 Returns: 56 bool: True if the value is a string, False otherwise. 57 """ 58 return isinstance(value, str)
Check if the value is a string.
Arguments:
- value (Any): The value to check.
Returns:
bool: True if the value is a string, False otherwise.
61def check_int(value: Any) -> bool: 62 """ 63 Check if the value is an integer. 64 65 Args: 66 value (Any): The value to check. 67 68 Returns: 69 bool: True if the value is an integer, False otherwise. 70 """ 71 return isinstance(value, int)
Check if the value is an integer.
Arguments:
- value (Any): The value to check.
Returns:
bool: True if the value is an integer, False otherwise.
74def check_number(number: Any) -> bool: 75 """ 76 Check if the number is a valid number. 77 78 Args: 79 number (Any): The number to check. 80 81 Returns: 82 bool: True if the number is a valid number, False otherwise. 83 """ 84 return isinstance(number, (int, float))
Check if the number is a valid number.
Arguments:
- number (Any): The number to check.
Returns:
bool: True if the number is a valid number, False otherwise.
87def check_color(color: Any) -> bool: 88 """ 89 Check if the color is a valid color. 90 91 Args: 92 color (Any): The color to check. 93 94 Returns: 95 bool: True if the color is a valid color, False otherwise. 96 """ 97 return isinstance(color, (Color, str, tuple, list, ndarray))
Check if the color is a valid color.
Arguments:
- color (Any): The color to check.
Returns:
bool: True if the color is a valid color, False otherwise.
100def check_dash_array(dash_array: Any) -> bool: 101 """ 102 Check if the dash array is a list of numbers or predefined. 103 104 Args: 105 dash_array (Any): The dash array to check. 106 107 Returns: 108 bool: True if the dash array is valid, False otherwise. 109 """ 110 if isinstance(dash_array, (list, tuple, ndarray)): 111 res = all([isinstance(x, (int, float)) for x in dash_array]) 112 elif dash_array in LineDashArray: 113 res = True 114 elif dash_array is None: 115 res = True 116 117 118 return res
Check if the dash array is a list of numbers or predefined.
Arguments:
- dash_array (Any): The dash array to check.
Returns:
bool: True if the dash array is valid, False otherwise.
121def check_bool(value: Any) -> bool: 122 """ 123 Check if the value is a boolean. 124 125 Boolean values need to be explicitly set to True or False. 126 None is not a valid boolean value. 127 128 Args: 129 value (Any): The value to check. 130 131 Returns: 132 bool: True if the value is a boolean, False otherwise. 133 """ 134 return isinstance(value, bool)
Check if the value is a boolean.
Boolean values need to be explicitly set to True or False. None is not a valid boolean value.
Arguments:
- value (Any): The value to check.
Returns:
bool: True if the value is a boolean, False otherwise.
137def check_enum(value: Any, enum: Any) -> bool: 138 """ 139 Check if the value is a valid enum value. 140 141 Args: 142 value (Any): The value to check. 143 enum (Any): The enum to check against. 144 145 Returns: 146 bool: True if the value is a valid enum value, False otherwise. 147 """ 148 return value in enum
Check if the value is a valid enum value.
Arguments:
- value (Any): The value to check.
- enum (Any): The enum to check against.
Returns:
bool: True if the value is a valid enum value, False otherwise.
151def check_blend_mode(blend_mode: Any) -> bool: 152 """ 153 Check if the blend mode is a valid blend mode. 154 155 Args: 156 blend_mode (Any): The blend mode to check. 157 158 Returns: 159 bool: True if the blend mode is valid, False otherwise. 160 """ 161 return blend_mode in BlendMode
Check if the blend mode is a valid blend mode.
Arguments:
- blend_mode (Any): The blend mode to check.
Returns:
bool: True if the blend mode is valid, False otherwise.
164def check_position(pos: Any) -> bool: 165 """ 166 Check if the position is a valid position. 167 168 Args: 169 pos (Any): The position to check. 170 171 Returns: 172 bool: True if the position is valid, False otherwise. 173 """ 174 return ( 175 isinstance(pos, (list, tuple, ndarray)) 176 and len(pos) >= 2 177 and all(isinstance(x, (int, float)) for x in pos) 178 )
Check if the position is a valid position.
Arguments:
- pos (Any): The position to check.
Returns:
bool: True if the position is valid, False otherwise.
181def check_points(points: Any) -> bool: 182 """ 183 Check if the points are a valid list of points. 184 185 Args: 186 points (Any): The points to check. 187 188 Returns: 189 bool: True if the points are valid, False otherwise. 190 """ 191 return isinstance(points, (list, tuple, ndarray)) and all( 192 isinstance(x, (list, tuple, ndarray)) for x in points 193 )
Check if the points are a valid list of points.
Arguments:
- points (Any): The points to check.
Returns:
bool: True if the points are valid, False otherwise.
196def check_xform_matrix(matrix: Any) -> bool: 197 """ 198 Check if the matrix is a valid transformation matrix. 199 200 Args: 201 matrix (Any): The matrix to check. 202 203 Returns: 204 bool: True if the matrix is valid, False otherwise. 205 """ 206 return isinstance(matrix, (list, tuple, ndarray))
Check if the matrix is a valid transformation matrix.
Arguments:
- matrix (Any): The matrix to check.
Returns:
bool: True if the matrix is valid, False otherwise.
209def check_subtype(subtype: Any) -> bool: 210 """ 211 This check is done in Shape class. 212 213 Args: 214 subtype (Any): The subtype to check. 215 216 Returns: 217 bool: True 218 """ 219 return True
This check is done in Shape class.
Arguments:
- subtype (Any): The subtype to check.
Returns:
bool: True
222def check_mask(mask: Any) -> bool: 223 """ 224 This check is done in Batch class. 225 226 Args: 227 mask (Any): The mask to check. 228 229 Returns: 230 bool: True if the mask is valid, False otherwise. 231 """ 232 return mask.type == Types.Shape
This check is done in Batch class.
Arguments:
- mask (Any): The mask to check.
Returns:
bool: True if the mask is valid, False otherwise.
235def check_line_width(line_width: Any) -> bool: 236 """ 237 Check if the line width is a valid line width. 238 239 Args: 240 line_width (Any): The line width to check. 241 242 Returns: 243 bool: True if the line width is valid, False otherwise. 244 """ 245 if isinstance(line_width, (int, float)): 246 res = line_width >= 0 247 elif line_width in all_enums.LineWidth: 248 res = True 249 else: 250 res = False 251 252 return res
Check if the line width is a valid line width.
Arguments:
- line_width (Any): The line width to check.
Returns:
bool: True if the line width is valid, False otherwise.
255def check_anchor(anchor: Any) -> bool: 256 """ 257 Check if the anchor is a valid anchor. 258 259 Args: 260 anchor (Any): The anchor to check. 261 262 Returns: 263 bool: True if the anchor is valid, False otherwise. 264 """ 265 return anchor in Anchor
Check if the anchor is a valid anchor.
Arguments:
- anchor (Any): The anchor to check.
Returns:
bool: True if the anchor is valid, False otherwise.
365def validate_args(args: Dict[str, Any], valid_args: list[str]) -> None: 366 """ 367 Validate the user entered arguments. 368 369 Args: 370 args (Dict[str, Any]): The arguments to validate. 371 valid_args (list[str]): The list of valid argument keys. 372 373 Raises: 374 ValueError: If an invalid key or value is found. 375 376 Returns: 377 None 378 """ 379 for key, value in args.items(): 380 if (key not in valid_args) and (key not in d_validators): 381 raise ValueError(f"Invalid key: {key}") 382 if key in d_validators: 383 if not d_validators[key](value): 384 raise ValueError(f"Invalid value for {key}: {value}") 385 elif key in enum_map: 386 if value not in enum_map[key]: 387 raise ValueError(f"Invalid value for {key}: {value}") 388 elif not d_validators[key](value): 389 raise ValueError(f"Invalid value for {key}: {value}")
Validate the user entered arguments.
Arguments:
- args (Dict[str, Any]): The arguments to validate.
- valid_args (list[str]): The list of valid argument keys.
Raises:
- ValueError: If an invalid key or value is found.
Returns:
None