1#!/usr/bin/env python 2 3r""" 4This module provides valuable argument processing functions like 5gen_get_options and sprint_args. 6""" 7 8import os 9import gen_print as gp 10 11exit_on_error = False 12 13 14def set_exit_on_error(value): 15 r""" 16 Set the exit_on_error value to either True or False. 17 18 If exit_on_error is set, validation functions like valid_value will exit 19 the program on error instead of returning False. 20 21 Description of argument(s): 22 value Value to set global exit_on_error to. 23 """ 24 25 global exit_on_error 26 exit_on_error = value 27 28 29def get_var_name(var_name): 30 r""" 31 If var_name has a value, simply return it. Otherwise, get the variable 32 name of the first argument used to call the validation function (e.g. 33 valid_value, valid_integer, etc.) and return it. 34 35 This function is designed solely for use by other functions in this file. 36 37 Example: 38 39 A programmer codes this: 40 41 valid_value(last_name) 42 43 Which results in the following call stack: 44 45 valid_value(last_name) 46 -> svalid_value(var_value...) 47 -> get_var_name(var_name) 48 49 In this example, this function will return "last_name". 50 51 Example: 52 53 err_msg = svalid_value(last_name, var_name="some_other_name") 54 55 Which results in the following call stack: 56 57 svalid_value(var_value, var_name="some_other_name") 58 -> get_var_name(var_name) 59 60 In this example, this function will return "some_other_name". 61 62 Description of argument(s): 63 var_name The name of the variable. 64 """ 65 66 if var_name != "": 67 return var_name 68 # Calculate stack_frame_ix. The validation functions in this file come 69 # in pairs. There is an "s" version of each validation function (e.g. 70 # svalid_value) whose job is to return an error message string. Then 71 # there is a wrapper function (e.g. valid_value) that will call the "s" 72 # version and print the result if there is an error. See examples 1 and 2 73 # above for illustration. This function must be cognizant of both 74 # scenarios to accurately determine the name of the variable being 75 # validated. Where the "s" function is being called directly, the 76 # stack_frame_ix should be set to 3. Where the wrapper function is being 77 # called, the stack_frame_ix should be incremented to 4. 78 stack_frame_ix = 3 79 parent_func_name = gp.sprint_func_name(2) 80 grandparent_func_name = gp.sprint_func_name(3) 81 if parent_func_name == "s" + grandparent_func_name: 82 stack_frame_ix += 1 83 var_name = gp.get_arg_name(0, 1, stack_frame_ix) 84 return var_name 85 86 87def process_error_message(error_message): 88 r""" 89 Process the error_message as follows: 90 - If the error_message is blank, return True. 91 - If the error_message contains a value: 92 - Print the error_message as part of a full error report. 93 - If global exit_on_error is set, then exit the program with a return 94 code of 1. 95 - If exit_on_error is not set, return False. 96 97 This function is designed solely for use by wrapper functions in this file 98 (e.g. "valid_value"). 99 100 Description of argument(s): 101 error_message An error message. 102 """ 103 104 if error_message == "": 105 return True 106 107 gp.print_error_report(error_message) 108 if exit_on_error: 109 exit(1) 110 return False 111 112 113def svalid_value(var_value, 114 invalid_values=[], 115 valid_values=[], 116 var_name=""): 117 r""" 118 Return an empty string if var_value is a valid value. Otherwise, return 119 an error string. 120 121 Description of arguments: 122 var_value The value being validated. 123 invalid_values A list of invalid values. If var_value is 124 equal to any of these, it is invalid. 125 Note that if you specify anything for 126 invalid_values (below), the valid_values 127 list is not even processed. If you 128 specify nothing for both invalid_values 129 and valid_values, invalid_values will be 130 set to a default value of [""]. 131 valid_values A list of valid values. var_value must be 132 equal to one of these values to be 133 considered valid. 134 var_name The name of the variable whose value is 135 passed in var_value. This parameter is 136 normally unnecessary as this function can 137 figure out the var_name. This is provided 138 for Robot callers. In this scenario, we 139 are unable to get the variable name 140 ourselves. 141 """ 142 143 success_message = "" 144 error_message = "" 145 146 # Validate this function's arguments. 147 len_valid_values = len(valid_values) 148 len_invalid_values = len(invalid_values) 149 if len_valid_values > 0 and len_invalid_values > 0: 150 error_message += "Programmer error - You must provide either an" +\ 151 " invalid_values list or a valid_values" +\ 152 " list but NOT both.\n" +\ 153 gp.sprint_var(invalid_values) +\ 154 gp.sprint_var(valid_values) 155 return error_message 156 157 if len_valid_values > 0: 158 # Processing the valid_values list. 159 if var_value in valid_values: 160 return success_message 161 error_message += "The following variable has an invalid" +\ 162 " value:\n" +\ 163 gp.sprint_varx(get_var_name(var_name), var_value, 164 gp.blank() | gp.verbose()) +\ 165 "\nIt must be one of the following values:\n" +\ 166 gp.sprint_var(valid_values, gp.blank()) 167 return error_message 168 169 if len_invalid_values == 0: 170 # Assign default value. 171 invalid_values = [""] 172 173 # Assertion: We have an invalid_values list. Processing it now. 174 if var_value not in invalid_values: 175 return success_message 176 177 error_message += "The following variable has an invalid value:\n" +\ 178 gp.sprint_varx(get_var_name(var_name), var_value, 179 gp.blank() | gp.verbose()) +\ 180 "\nIt must NOT be one of the following values:\n" +\ 181 gp.sprint_var(invalid_values, gp.blank()) 182 return error_message 183 184 185def valid_value(var_value, 186 invalid_values=[], 187 valid_values=[], 188 var_name=""): 189 r""" 190 Return True if var_value is valid. Otherwise, print an error message and 191 either return False or exit(1) depending on the value of exit_on_error. 192 193 Description of arguments: 194 (See description of arguments for svalid_value (above)). 195 """ 196 197 error_message = svalid_value(var_value, invalid_values, valid_values, 198 var_name) 199 return process_error_message(error_message) 200 201 202def svalid_integer(var_value, 203 var_name=""): 204 r""" 205 Return an empty string if var_value is a valid integer. Otherwise, return 206 an error string. 207 208 Description of arguments: 209 var_value The value being validated. 210 var_name The name of the variable whose value is 211 passed in var_value. This parameter is 212 normally unnecessary as this function can 213 figure out the var_name. This is provided 214 for Robot callers. In this scenario, we 215 are unable to get the variable name 216 ourselves. 217 """ 218 219 success_message = "" 220 error_message = "" 221 try: 222 if isinstance(int(str(var_value), 0), int): 223 return success_message 224 except ValueError: 225 pass 226 227 # If we get to this point, the validation has failed. 228 error_message +=\ 229 "Invalid integer value:\n" +\ 230 gp.sprint_varx(get_var_name(var_name), var_value, gp.blank()) 231 232 return error_message 233 234 235def valid_integer(var_value, 236 var_name=""): 237 r""" 238 Return True if var_value is a valid integer. Otherwise, print an error 239 message and either return False or exit(1) depending on the value of 240 exit_on_error. 241 242 Description of arguments: 243 (See description of arguments for svalid_integer (above)). 244 """ 245 246 error_message = svalid_integer(var_value, var_name) 247 return process_error_message(error_message) 248 249 250def svalid_dir_path(var_value, 251 var_name=""): 252 r""" 253 Return an empty string if var_value is a valid directory path. Otherwise, 254 return an error string. 255 256 Description of arguments: 257 var_value The value being validated. 258 var_name The name of the variable whose value is 259 passed in var_value. This parameter is 260 normally unnecessary as this function can 261 figure out the var_name. This is provided 262 for Robot callers. In this scenario, we 263 are unable to get the variable name 264 ourselves. 265 """ 266 267 error_message = "" 268 if not os.path.isdir(str(var_value)): 269 error_message += "The following directory does not exist:\n" +\ 270 gp.sprint_varx(get_var_name(var_name), var_value) 271 272 return error_message 273 274 275def valid_dir_path(var_value, 276 var_name=""): 277 r""" 278 Return True if var_value is a valid directory path. Otherwise, print an 279 error message and either return False or exit(1) depending on the value of 280 exit_on_error. 281 282 Valid means that the directory path exists. 283 284 Description of arguments: 285 (See description of arguments for svalid_dir_path (above)). 286 """ 287 288 error_message = svalid_dir_path(var_value, var_name) 289 return process_error_message(error_message) 290 291 292def svalid_file_path(var_value, 293 var_name=""): 294 r""" 295 Return an empty string if var_value is a valid file path. Otherwise, 296 return an error string. 297 298 Description of arguments: 299 var_value The value being validated. 300 var_name The name of the variable whose value is 301 passed in var_value. This parameter is 302 normally unnecessary as this function can 303 figure out the var_name. This is provided 304 for Robot callers. In this scenario, we 305 are unable to get the variable name 306 ourselves. 307 """ 308 309 error_message = "" 310 if not os.path.isfile(str(var_value)): 311 error_message += "Invalid file (does not exist):\n" +\ 312 gp.sprint_varx(get_var_name(var_name), var_value) 313 314 return error_message 315 316 317def valid_file_path(var_value, 318 var_name=""): 319 r""" 320 Return True if var_value is a valid file path. Otherwise, print an error 321 message and either return False or exit(1) depending on the value of 322 exit_on_error. 323 324 Valid means that the file exists. 325 326 Description of arguments: 327 (See description of arguments for svalid_file_path (above)). 328 """ 329 330 error_message = svalid_file_path(var_value, var_name) 331 return process_error_message(error_message) 332 333 334def svalid_path(var_value, 335 var_name=""): 336 r""" 337 Return an empty string if var_value is either a valid file path or 338 directory path. Otherwise, return an error string. 339 340 Description of arguments: 341 var_value The value being validated. 342 var_name The name of the variable whose value is 343 passed in var_value. This parameter is 344 normally unnecessary as this function can 345 figure out the var_name. This is provided 346 for Robot callers. In this scenario, we 347 are unable to get the variable name 348 ourselves. 349 """ 350 351 error_message = "" 352 if not (os.path.isfile(str(var_value)) or os.path.isdir(str(var_value))): 353 error_message = "Invalid path (file or directory does not exist):\n" +\ 354 gp.sprint_varx(get_var_name(var_name), var_value) 355 356 return error_message 357 358 359def valid_path(var_value, 360 var_name=""): 361 r""" 362 Return True if var_value is a valid file path. Otherwise, print an error 363 message and either return False or exit(1) depending on the value of 364 exit_on_error. 365 366 Valid means that the file exists. 367 368 Description of arguments: 369 (See description of arguments for svalid_path (above)). 370 """ 371 372 error_message = svalid_path(var_value, var_name) 373 return process_error_message(error_message) 374 375 376def svalid_range(var_value, 377 valid_range=[], 378 var_name=""): 379 r""" 380 Return an empty string if var_value is within the range. Otherwise, 381 return an error string. 382 383 Description of arguments: 384 var_value The value being validated. This value 385 must be an integer. 386 valid_range A list comprised of one or two elements 387 which are the lower and upper ends of a 388 range. These values must be integers 389 except where noted. Valid specifications 390 may be of the following forms: [lower, 391 upper], [lower] or [None, upper]. 392 var_name The name of the variable whose value is 393 passed in var_value. This parameter is 394 normally unnecessary as this function can 395 figure out the var_name. This is provided 396 for Robot callers. In this scenario, we 397 are unable to get the variable name 398 ourselves. 399 """ 400 401 error_message = "" 402 403 # Validate this function's parms: 404 # First, ensure that the value is an integer. 405 error_message = svalid_integer(var_value, var_name) 406 if not error_message == "": 407 return error_message 408 var_value = int(var_value) 409 410 len_valid_range = len(valid_range) 411 if len_valid_range == 0 or len_valid_range > 2: 412 error_message += "Programmer error - For the valid_range parameter," +\ 413 " you must provide a list consisting of one or two" +\ 414 " elements.\n" +\ 415 gp.sprint_var(valid_range) 416 return error_message 417 418 if len_valid_range == 1 or valid_range[0] is not None: 419 # Make sure lower valid_range value is an integer. 420 error_message = svalid_integer(valid_range[0], "valid_range[0]") 421 if not error_message == "": 422 error_message = "Programmer error:\n" + error_message 423 return error_message 424 if valid_range[0] is not None: 425 valid_range[0] = int(valid_range[0]) 426 if len_valid_range == 2: 427 # Make sure upper valid_range value is an integer. 428 error_message = svalid_integer(valid_range[1], "valid_range[1]") 429 if not error_message == "": 430 error_message = "Programmer error:\n" + error_message 431 return error_message 432 valid_range[1] = int(valid_range[1]) 433 if valid_range[0] is not None and valid_range[0] > valid_range[1]: 434 error_message = "Programmer error - In the following range, the" +\ 435 " lower limit is greater than the upper" +\ 436 " limit:\n" + gp.sprint_var(valid_range) 437 return error_message 438 439 if len_valid_range == 1: 440 if var_value < valid_range[0]: 441 error_message += "The following variable is not within the" +\ 442 " expected range:\n" +\ 443 gp.sprint_varx(get_var_name(var_name), 444 var_value) +\ 445 gp.sprint_varx("valid_range", 446 str(valid_range[0]) + "..") 447 return error_message 448 return error_message 449 450 if valid_range[0] is None: 451 if var_value > valid_range[1]: 452 error_message += "The following variable is not within the" +\ 453 " expected range:\n" +\ 454 gp.sprint_varx(get_var_name(var_name), 455 var_value) +\ 456 gp.sprint_varx("valid_range", 457 ".." + str(valid_range[1])) 458 return error_message 459 460 if var_value < valid_range[0] or var_value > valid_range[1]: 461 error_message += "The following variable is not within the expected" +\ 462 " range:\n" +\ 463 gp.sprint_varx(get_var_name(var_name), var_value) +\ 464 gp.sprint_varx("valid_range", 465 str(valid_range[0]) + ".." 466 + str(valid_range[1])) 467 return error_message 468 469 return error_message 470 471 472def valid_range(var_value, 473 valid_range=[], 474 var_name=""): 475 r""" 476 Return True if var_value is within range. Otherwise, print an error 477 message and either return False or exit(1) depending on the value of 478 exit_on_error. 479 480 Description of arguments: 481 (See description of arguments for svalid_range (above)). 482 """ 483 484 error_message = svalid_range(var_value, valid_range, var_name) 485 return process_error_message(error_message) 486 487 488def svalid_list(var_value, 489 valid_values=[], 490 var_name=""): 491 r""" 492 Return an empty string if var_value is a valid list. Otherwise, return an 493 error string. 494 495 Description of arguments: 496 var_value The value (i.e. list) being validated. 497 valid_values A list of valid values. Each element in 498 the var_value list must be equal to one of 499 these values to be considered valid. 500 var_name The name of the variable whose value is 501 passed in var_value. This parameter is 502 normally unnecessary as this function can 503 figure out the var_name. This is provided 504 for Robot callers. In this scenario, we 505 are unable to get the variable name 506 ourselves. 507 """ 508 509 error_message = "" 510 if len(var_value) == 0: 511 error_message += "The \"" + get_var_name(var_name) 512 error_message += "\" list is empty and is therefore invalid:\n" 513 return error_message 514 515 found_error = 0 516 display_var_value = list(var_value) 517 for ix in range(0, len(var_value)): 518 if var_value[ix] not in valid_values: 519 found_error = 1 520 display_var_value[ix] = var_value[ix] + "*" 521 522 if found_error: 523 error_message += "The list entries marked with \"*\" are not valid:\n" 524 error_message += gp.sprint_varx(get_var_name(var_name), 525 display_var_value, gp.blank()) 526 error_message += gp.sprint_var(valid_values) 527 return error_message 528 529 return "" 530 531 532def valid_list(var_value, 533 valid_values=[], 534 var_name=""): 535 r""" 536 Return True if var_value is a valid list. Otherwise, print an error 537 message and either return False or exit(1) depending on the value of 538 exit_on_error. 539 540 Description of arguments: 541 (See description of arguments for svalid_list (above)). 542 """ 543 544 error_message = svalid_list(var_value, valid_values, var_name) 545 return process_error_message(error_message) 546 547 548def svalid_dict(var_value, 549 required_keys=[], 550 var_name=""): 551 r""" 552 Return an empty string if var_value is a valid dictionary. Otherwise, 553 return an error string. 554 555 Description of arguments: 556 var_value The value (i.e. dictionary) being 557 validated. 558 required_keys A list of keys which must be found in the 559 dictionary for it to be considered valid. 560 var_name The name of the variable whose value is 561 passed in var_value. This parameter is 562 normally unnecessary as this function can 563 figure out the var_name. This is provided 564 for Robot callers. In this scenario, we 565 are unable to get the variable name 566 ourselves. 567 """ 568 569 error_message = "" 570 571 keys_missing = list(set(required_keys) - set(var_value.keys())) 572 if len(keys_missing) > 0: 573 var_name = get_var_name(var_name) 574 error_message = "The following key fields are missing from " 575 error_message += var_name + ":\n" 576 error_message += gp.sprint_var(keys_missing) 577 error_message += gp.sprint_varx(var_name, var_value, gp.blank()) 578 return error_message 579 580 return "" 581 582 583def valid_dict(var_value, 584 required_keys=[], 585 var_name=""): 586 r""" 587 Return True if var_value is a valid dictionary. Otherwise, print an error 588 message and either return False or exit(1) depending on the value of 589 exit_on_error. 590 591 Description of arguments: 592 (See description of arguments for svalid_list (above)). 593 """ 594 595 error_message = svalid_dict(var_value, required_keys, var_name) 596 return process_error_message(error_message) 597