From 8f872baf0bed944138ff439970509de7de511ce4 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Thu, 2 Jul 2026 14:56:13 +0200 Subject: [PATCH] Invalidate inputs from class definition-time mutations --- .../test_generate_popular_node_signatures.py | 95 +++++++++++++++++++ tools/generate_popular_node_signatures.py | 2 + 2 files changed, 97 insertions(+) diff --git a/tests/test_generate_popular_node_signatures.py b/tests/test_generate_popular_node_signatures.py index d18768c..bb0d381 100644 --- a/tests/test_generate_popular_node_signatures.py +++ b/tests/test_generate_popular_node_signatures.py @@ -684,6 +684,101 @@ NODE_CLASS_MAPPINGS = { self.assertEqual({}, result["nodes"]) self.assertEqual("no_static_nodes", result["pack"]["status"]) + def test_class_body_function_default_mutation_invalidates_static_input_env(self): + source = ''' +INPUTS = { + "required": { + "image": ("IMAGE",), + }, +} + + +class ClassDefaultMutatedInputEnvNode: + RETURN_TYPES = ("IMAGE",) + + def helper(x=INPUTS.clear()): + pass + + @classmethod + def INPUT_TYPES(cls): + return INPUTS + + +NODE_CLASS_MAPPINGS = { + "ClassDefaultMutatedInputEnvNode": ClassDefaultMutatedInputEnvNode, +} +''' + result = self._extract_source(source, "class-default-mutated-input-env-pack") + + self.assertEqual({}, result["nodes"]) + self.assertEqual("no_static_nodes", result["pack"]["status"]) + + def test_class_body_function_decorator_mutation_invalidates_static_input_env(self): + source = ''' +def decorator(value): + def wrap(fn): + return fn + return wrap + + +INPUTS = { + "required": { + "image": ("IMAGE",), + }, +} + + +class ClassDecoratorMutatedInputEnvNode: + RETURN_TYPES = ("IMAGE",) + + @decorator(INPUTS.clear()) + def helper(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return INPUTS + + +NODE_CLASS_MAPPINGS = { + "ClassDecoratorMutatedInputEnvNode": ClassDecoratorMutatedInputEnvNode, +} +''' + result = self._extract_source(source, "class-decorator-mutated-input-env-pack") + + self.assertEqual({}, result["nodes"]) + self.assertEqual("no_static_nodes", result["pack"]["status"]) + + def test_class_body_function_body_mutation_does_not_invalidate_static_input_env(self): + source = ''' +INPUTS = { + "required": { + "image": ("IMAGE",), + }, +} + + +class RuntimeBodyMutatedInputEnvNode: + RETURN_TYPES = ("IMAGE",) + + def helper(self): + INPUTS.clear() + + @classmethod + def INPUT_TYPES(cls): + return INPUTS + + +NODE_CLASS_MAPPINGS = { + "RuntimeBodyMutatedInputEnvNode": RuntimeBodyMutatedInputEnvNode, +} +''' + result = self._extract_source(source, "runtime-body-mutated-input-env-pack") + + self.assertIn("RuntimeBodyMutatedInputEnvNode", result["nodes"]) + self.assertEqual({"image": "IMAGE"}, result["nodes"]["RuntimeBodyMutatedInputEnvNode"]["inputs"]) + self.assertEqual("ok", result["pack"]["status"]) + def test_nested_mutable_env_literal_skips_static_node(self): source = ''' REQ = { diff --git a/tools/generate_popular_node_signatures.py b/tools/generate_popular_node_signatures.py index a87cd0e..f1266f4 100644 --- a/tools/generate_popular_node_signatures.py +++ b/tools/generate_popular_node_signatures.py @@ -205,6 +205,8 @@ def _mutating_call_target_names(stmt): self.visit(keyword.value) for type_param in getattr(node, "type_params", ()): self.visit(type_param) + for child in node.body: + self.visit(child) def visit_Lambda(self, node): self.visit(node.args)