From 2ebd5068948f937718a95d30d962e6c1dc695734 Mon Sep 17 00:00:00 2001 From: TheSateWarco Date: Thu, 23 Apr 2026 23:43:14 -0700 Subject: [PATCH 1/4] fixed config mismatch types --- bin/deepstate/core/base.py | 30 +++++++++++++++++++++++------- tests/config/test_config.ini | 5 +++++ tests/config/test_config_types.py | 15 +++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 tests/config/test_config.ini create mode 100644 tests/config/test_config_types.py diff --git a/bin/deepstate/core/base.py b/bin/deepstate/core/base.py index 4c3aa5e8..dde2c85d 100644 --- a/bin/deepstate/core/base.py +++ b/bin/deepstate/core/base.py @@ -179,13 +179,20 @@ def parse_args(cls) -> Optional[argparse.Namespace]: target_args_parsed.append((key, val)) _args['target_args'] = target_args_parsed - - # if configuration is specified, parse and replace argument instantiations if args.config: _args.update(cls.build_from_config(args.config)) # type: ignore + # Re-apply argparse types to values read from config, since configparser + # returns everything as strings (e.g. timeout="36000" instead of 36000). + for action in parser._actions: + if action.dest in _args and action.type is not None: + try: + _args[action.dest] = action.type(_args[action.dest]) + except (ValueError, TypeError): + pass + # Cleanup: force --no_exit_compile to be on, meaning if user specifies a `[test]` section, - # execution will continue. Delete config as well + # execution will continue. Delete config as well. _args["no_exit_compile"] = True # type: ignore del _args["config"] @@ -200,7 +207,7 @@ def parse_args(cls) -> Optional[argparse.Namespace]: logger.setLevel(LOG_LEVEL_INT_TO_STR[_args["min_log_level"]]) else: L.debug("Using log level from $DEEPSTATE_LOG.") - + cls._ARGS = args return cls._ARGS @@ -236,7 +243,8 @@ def build_from_config(config: str, allowed_keys: Optional[List[str]] = None, inc "test" # configurations for harness execution under analysis tool ] - parser = configparser.SafeConfigParser() + # ConfigParser replaces the deprecated SafeConfigParser removed in Python 3.12 + parser = configparser.ConfigParser() parser.read(config) for section, kv in parser._sections.items(): # type: ignore @@ -264,7 +272,15 @@ def build_from_config(config: str, allowed_keys: Optional[List[str]] = None, inc if isinstance(val, list): _context[key].append(val) else: - _context[key] = val + # configparser returns all values as strings. Try casting to int + # then float, falling back to string for non-numeric values. + try: + _context[key] = int(val) + except (ValueError, TypeError): + try: + _context[key] = float(val) + except (ValueError, TypeError): + _context[key] = val return context # type: ignore @@ -278,4 +294,4 @@ def init_from_dict(self, _args: Optional[Dict[str, str]] = None) -> None: """ args: Dict[str, str] = vars(self._ARGS) if _args is None else _args for key, value in args.items(): - setattr(self, key, value) + setattr(self, key, value) \ No newline at end of file diff --git a/tests/config/test_config.ini b/tests/config/test_config.ini new file mode 100644 index 00000000..ba0da983 --- /dev/null +++ b/tests/config/test_config.ini @@ -0,0 +1,5 @@ +[test] +timeout = 36000 +mem_limit = 100 +min_log_level = 2 +output_test_dir = /tmp/deepstate_out diff --git a/tests/config/test_config_types.py b/tests/config/test_config_types.py new file mode 100644 index 00000000..36efa179 --- /dev/null +++ b/tests/config/test_config_types.py @@ -0,0 +1,15 @@ +from deepstate.core.base import AnalysisBackend + +# Load the config directly using build_from_config +result = AnalysisBackend.build_from_config("tests/config/test_config.ini") + +print("Values and their types:") +for key, val in result.items(): + print(f" {key} = {val!r} (type: {type(val).__name__})") + +# Assert numeric fields came back as ints, not strings +assert isinstance(result["timeout"], int), f"timeout should be int, got {type(result['timeout'])}" +assert isinstance(result["mem_limit"], int), f"mem_limit should be int, got {type(result['mem_limit'])}" +assert isinstance(result["min_log_level"], int), f"min_log_level should be int, got {type(result['min_log_level'])}" + +print("\nAll assertions passed! Types are correctly cast.") From 7abd4d06b847e69d43d39ec9bc36ae2bc9baa394 Mon Sep 17 00:00:00 2001 From: AshCat31 <31ccat520@gmail.com> Date: Fri, 24 Apr 2026 19:48:36 -0700 Subject: [PATCH 2/4] move config tests out of config dir; inline ini mock instead of real file; change to DeepStateTestCase --- tests/config/test_config.ini | 5 ----- tests/config/test_config_types.py | 15 --------------- tests/test_config_types.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 20 deletions(-) delete mode 100644 tests/config/test_config.ini delete mode 100644 tests/config/test_config_types.py create mode 100644 tests/test_config_types.py diff --git a/tests/config/test_config.ini b/tests/config/test_config.ini deleted file mode 100644 index ba0da983..00000000 --- a/tests/config/test_config.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test] -timeout = 36000 -mem_limit = 100 -min_log_level = 2 -output_test_dir = /tmp/deepstate_out diff --git a/tests/config/test_config_types.py b/tests/config/test_config_types.py deleted file mode 100644 index 36efa179..00000000 --- a/tests/config/test_config_types.py +++ /dev/null @@ -1,15 +0,0 @@ -from deepstate.core.base import AnalysisBackend - -# Load the config directly using build_from_config -result = AnalysisBackend.build_from_config("tests/config/test_config.ini") - -print("Values and their types:") -for key, val in result.items(): - print(f" {key} = {val!r} (type: {type(val).__name__})") - -# Assert numeric fields came back as ints, not strings -assert isinstance(result["timeout"], int), f"timeout should be int, got {type(result['timeout'])}" -assert isinstance(result["mem_limit"], int), f"mem_limit should be int, got {type(result['mem_limit'])}" -assert isinstance(result["min_log_level"], int), f"min_log_level should be int, got {type(result['min_log_level'])}" - -print("\nAll assertions passed! Types are correctly cast.") diff --git a/tests/test_config_types.py b/tests/test_config_types.py new file mode 100644 index 00000000..5147f313 --- /dev/null +++ b/tests/test_config_types.py @@ -0,0 +1,31 @@ +from __future__ import print_function +import configparser +from unittest.mock import patch + +from deepstate.core.base import AnalysisBackend +import deepstate_base + + +INI_DATA = """ +[test] +timeout = 36000 +mem_limit = 100 +min_log_level = 2 +output_test_dir = /tmp/deepstate_out +""" + + +def fake_read(self, *args, **kwargs): + self.read_string(INI_DATA) + return [] + +class ConfigParseTest(deepstate_base.DeepStateTestCase): + def run_deepstate(self, deepstate): + # Not actually invoking deepstate, just reusing the test harness structure + + with patch("deepstate.core.base.configparser.ConfigParser.read", fake_read): + result = AnalysisBackend.build_from_config("dummy_path") + + self.assertIsInstance(result["timeout"], int) + self.assertIsInstance(result["mem_limit"], int) + self.assertIsInstance(result["min_log_level"], int) From 893d2268fabb2bde646290789bf78ec6c6fe39ff Mon Sep 17 00:00:00 2001 From: AshCat31 <31ccat520@gmail.com> Date: Fri, 24 Apr 2026 19:51:36 -0700 Subject: [PATCH 3/4] add value tests --- tests/test_config_types.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_config_types.py b/tests/test_config_types.py index 5147f313..d9552088 100644 --- a/tests/test_config_types.py +++ b/tests/test_config_types.py @@ -29,3 +29,7 @@ def run_deepstate(self, deepstate): self.assertIsInstance(result["timeout"], int) self.assertIsInstance(result["mem_limit"], int) self.assertIsInstance(result["min_log_level"], int) + + self.assertEqual(result["timeout"], 36000) + self.assertEqual(result["mem_limit"], 100) + self.assertEqual(result["min_log_level"], 2) From f7abb57d08409ec985236b3dd65e046df29051cb Mon Sep 17 00:00:00 2001 From: AshCat31 <31ccat520@gmail.com> Date: Fri, 24 Apr 2026 19:53:21 -0700 Subject: [PATCH 4/4] add tests for string and float config parsing --- tests/test_config_types.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_config_types.py b/tests/test_config_types.py index d9552088..c2b4e091 100644 --- a/tests/test_config_types.py +++ b/tests/test_config_types.py @@ -11,6 +11,7 @@ timeout = 36000 mem_limit = 100 min_log_level = 2 +float_value = 3.14 output_test_dir = /tmp/deepstate_out """ @@ -29,7 +30,11 @@ def run_deepstate(self, deepstate): self.assertIsInstance(result["timeout"], int) self.assertIsInstance(result["mem_limit"], int) self.assertIsInstance(result["min_log_level"], int) + self.assertIsInstance(result["float_value"], float) + self.assertIsInstance(result["output_test_dir"], str) self.assertEqual(result["timeout"], 36000) self.assertEqual(result["mem_limit"], 100) self.assertEqual(result["min_log_level"], 2) + self.assertEqual(result["float_value"], 3.14) + self.assertEqual(result["output_test_dir"], "/tmp/deepstate_out")