From 3409909b056bb4443de1aed26771508ed7b91116 Mon Sep 17 00:00:00 2001 From: Brandon Wallace Date: Thu, 19 Mar 2026 00:53:29 -0500 Subject: [PATCH 1/2] test: add failing tests for NaN/undefined variable integer comparison bug Add 5 tests covering the ValueError crash in __eval_basiccondition() when int() is called on empty/non-numeric variable values during integer comparisons. Tests verify expected behavior for warn mode (comparison evaluates to false) and stop mode (proper PPPInterrupt). --- tests/tests.py | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/tests.py b/tests/tests.py index 7489c0d..c037fef 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1328,6 +1328,82 @@ class TestPromptPostProcessor(TestPromptPostProcessorBase): ppp=self.nocupppp, ) + # NaN/undefined variable integer comparison tests + + def test_cmd_if_undefined_var_int_compare_warn(self): # undefined var integer compare with on_warning=warn + self.process( + PromptPair( + "YESNO", + "", + ), + PromptPair("NO", ""), + ppp=PromptPostProcessor( + self.ppp_logger, + self.interrupt, + self.def_env_info, + {**self.defopts, "on_warning": PromptPostProcessor.ONWARNING_CHOICES.warn.value}, + self.grammar_content, + self.wildcards_obj, + self.extranetwork_maps_obj, + ), + ) + + def test_cmd_if_undefined_var_int_compare_stop(self): # undefined var integer compare with on_warning=stop + self.process( + PromptPair( + "YESNO", + "", + ), + PromptPair("", ""), + interrupted=True, + ) + + def test_cmd_if_nonnumeric_var_int_compare_warn(self): # non-numeric var integer compare with on_warning=warn + self.process( + PromptPair( + "abcYESNO", + "", + ), + PromptPair("NO", ""), + ppp=PromptPostProcessor( + self.ppp_logger, + self.interrupt, + self.def_env_info, + {**self.defopts, "on_warning": PromptPostProcessor.ONWARNING_CHOICES.warn.value}, + self.grammar_content, + self.wildcards_obj, + self.extranetwork_maps_obj, + ), + ) + + def test_cmd_if_nonnumeric_var_int_compare_stop(self): # non-numeric var integer compare with on_warning=stop + self.process( + PromptPair( + "abcYESNO", + "", + ), + PromptPair("", ""), + interrupted=True, + ) + + def test_cmd_if_empty_var_int_compare(self): # empty string var integer compare with on_warning=warn + self.process( + PromptPair( + "YESNO", + "", + ), + PromptPair("NO", ""), + ppp=PromptPostProcessor( + self.ppp_logger, + self.interrupt, + self.def_env_info, + {**self.defopts, "on_warning": PromptPostProcessor.ONWARNING_CHOICES.warn.value}, + self.grammar_content, + self.wildcards_obj, + self.extranetwork_maps_obj, + ), + ) + if __name__ == "__main__": unittest.main() From cad7a52ed0a7647dc27e7bb141663b31f187d8da Mon Sep 17 00:00:00 2001 From: Brandon Wallace Date: Thu, 19 Mar 2026 01:28:24 -0500 Subject: [PATCH 2/2] fix: handle NaN/undefined variable in integer comparisons Wrap int(var_value) in __eval_basiccondition() with try/except to prevent ValueError crash when a variable is undefined, empty, or contains non-numeric text. In warn mode the comparison evaluates to false; in stop mode a PPPInterrupt is raised with a descriptive message. --- docs/CONFIG.md | 2 +- docs/SYNTAX.md | 2 ++ ppp.py | 25 +++++++++++++------------ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 1e47c11..be83619 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -34,7 +34,7 @@ With this prompt: `__quality__, 1girl, ${head:__eyes__, __hair__, __expression__ ### General settings * **Debug level**: what to write to the console. Note: in *SD.Next* debug messages only show if you launch it with the `--debug` argument. -* **What to do on invalid content warnings?**: warn on the console or stop the generation. +* **What to do on invalid content warnings?**: warn on the console or stop the generation. This also affects integer comparisons with undefined or non-numeric variables: in *warn* mode the comparison evaluates to false, in *stop* mode the generation is stopped with an error. * **Model variant definitions**: definitions for model variants to be recognized based on strings found in the full filename. The format for each line is (with *kind* being one of the base model identifiers or not defined): diff --git a/docs/SYNTAX.md b/docs/SYNTAX.md index 309824d..4233f85 100644 --- a/docs/SYNTAX.md +++ b/docs/SYNTAX.md @@ -211,6 +211,8 @@ The `conditionN` can be: For a simple value the allowed operations are `eq`, `ne`, `gt`, `lt`, `ge`, `le`, `contains` and the value can be a quoted string or an integer. For a list of values the allowed operations are `contains`, `in` and the value of the variable is checked against all the elements of the list until one matches. The operation can be preceded by `not` for readability, instead of using it in the front. +When a variable used in an integer comparison is undefined or cannot be converted to an integer (e.g. it contains non-numeric text or is an empty string), the behavior depends on the `on_warning` setting: in `warn` mode the comparison evaluates to false, and in `stop` mode an error is raised. + You can also build complex conditions joining them with boolean operators and/or/not and parentheses. The variable can be one set with the `set` or `add` commands (user variables) or you can use system variables like these (names starting with an underscore are reserved for system variables): diff --git a/ppp.py b/ppp.py index 655f739..7b1e144 100644 --- a/ppp.py +++ b/ppp.py @@ -1139,19 +1139,20 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in ) result = False for c in cond_value_adjusted: - var_value_adjusted = ( - var_value - if isinstance(c, str) - else ( - True - if isinstance(c, bool) and var_value != "false" and var_value != "" and var_value is not False - else ( - False - if isinstance(c, bool) and (var_value != "true" or var_value is False) - else int(var_value) + if isinstance(c, str): + var_value_adjusted = var_value + elif isinstance(c, bool) and var_value != "false" and var_value != "" and var_value is not False: + var_value_adjusted = True + elif isinstance(c, bool) and (var_value != "true" or var_value is False): + var_value_adjusted = False + else: + try: + var_value_adjusted = int(var_value) + except (ValueError, TypeError): + self.warn_or_stop( + f"Cannot convert variable value '{var_value}' to integer for comparison" ) - ) - ) + return False result = comp_ops[cond_comp](var_value_adjusted, c) if result: break