From a0234e7d5492495c5959f365ef667bb8c633ccd0 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Thu, 16 Jan 2025 12:39:20 +0100 Subject: [PATCH] Add module jwutils.cast Signed-off-by: Jan Lindemann --- test/cast/main.py | 44 +++++++++++++++++++ tools/python/jwutils/cast.py | 82 ++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 test/cast/main.py create mode 100644 tools/python/jwutils/cast.py diff --git a/test/cast/main.py b/test/cast/main.py new file mode 100644 index 0000000..cdb1115 --- /dev/null +++ b/test/cast/main.py @@ -0,0 +1,44 @@ +from jwutils.cast import * +from jwutils.log import * + +set_level(DEBUG) +set_flags('console,color,position,prio') + +test_data = [ + (bool, 'true', True, True), + (bool, 'False', True, False), + (bool, 'yes', True, True), + (bool, 'nah', None, None), + (bool, 'duh', None, None), + (bool, '11', 11, None), + (bool, '22', 22, None), + (bool, '0', False,False), + (bool, '', None, None), + (bool, None, Exception(), Exception()), + (bool, '-1', -1, None), + (bool, '1.6', None, None), + (int, '1', 1, None), + (int, '22', 22, 22), + (int, '0', 0, 0), + (int, '-1', -1, -1), + (int, '-32', -32, -32), + (int, 123, 132, 132), + (str, '', '', ''), + (str, None, Exception(), Exception()), + (str, 'This is a string', 'This is a string', 'This is a string') +] + +status = 0 +for tp, s, guessed_val, cast_val in test_data: + try: + slog(INFO, f'guessed type of "{s}" = "{guess_type(s)}", should be "{tp}"') + slog(INFO, f'cast_str("{s}") = "{cast_str(s)}"') + slog(INFO, f'cast_str("{s}, {tp}") = "{cast_str(s, target_type=tp)}"') + except Exception as e: + if s is None: + slog(INFO, f'guessed type of "{s}" = Exception, should be "Exception"') + continue + slog(ERR, f'Test failed for "{s}": ({e})') + status = 1 + +exit(status) diff --git a/tools/python/jwutils/cast.py b/tools/python/jwutils/cast.py new file mode 100644 index 0000000..2bff110 --- /dev/null +++ b/tools/python/jwutils/cast.py @@ -0,0 +1,82 @@ +from collections import OrderedDict + +from .log import * + +_int_chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] + +def _strip(s_, throw=True, log_level=ERR): + s = s_.strip() + if len(s) != 0: + return s + msg = f'Tried to strip empty string "{s_}" to int' + if throw: + raise Exception(msg) + slog(log_level, msg) + return None + +def cast_str_to_int(s_: str, throw=True, log_level=DEBUG): # export + s = _strip(s_, throw=throw, log_level=log_level) + if s[0] == '-': + s = s[1:] + for c in s: + if not c in _int_chars: + break + else: + return (True, int(s_)) + msg = f'Could not convert string "{s_}" to int' + if throw: + raise Exception(msg) + slog(log_level, msg) + return (False, None) + +def cast_str_to_bool(s_: str, throw=True, log_level=DEBUG): # export + s = _strip(s_, throw=throw, log_level=log_level).lower() + if s in ['true', 'yes', '1']: + return (True, True) + if s in ['false', 'no', '0']: + return (True, False) + msg = f'Could not convert string "{s_}" to bool' + if throw: + raise Exception(msg) + slog(log_level, msg) + return (False, None) + +_str_cast_functions = OrderedDict({ + bool: cast_str_to_bool, + int: cast_str_to_int +}) + +def guess_type(s: str, default=None, log_level=DEBUG, throw=False): # export + if s is None: + raise Exception('None string passed to guess_type()') + for tp, func in _str_cast_functions.items(): + try: + success, value = func(s, log_level=OFF, throw=False) + if success: + return tp + except: + continue + msg = f'Failed to guess type of string "{s}"' + if throw: + raise Exception(msg) + slog(log_level, msg) + return default + +def cast_str(s: str, target_type=None, default_type=None, throw=True, log_level=WARNING, caller=None): # export + if target_type is None: + target_type = guess_type(s, default_type) + if target_type is None: + msg = f'Could not deduce type to cast to from string "{s}"' + if throw: + raise Exception(msg) + slog(log_level, msg) + return None + result = _str_cast_functions[target_type](s, throw=throw, log_level=log_level) + if result[0]: + return result[1] + msg = f'Failed to cast string "{s}" to type {target_type}' + if throw: + raise Exception(msg) + slog(log_level, msg) + return None +