fix(hardening): address PR #157 review findings

Critical fixes:
- node_runtime.py: change outer handler in _load_module() from except Exception to except BaseException with sys.modules.pop() cleanup, preventing non-Exception BaseExceptions (SystemExit, KeyboardInterrupt) from violating the no-raise contract
- app.py: remove incorrect reassignment of base_node_class_mappings after custom node import; keep it as pre-custom-node baseline so WorkflowPlanner correctly treats extras/custom nodes as NODE_CLASS_MAPPINGS dict lookups instead of direct imports
- Regenerate stale test fixtures (text-to-image.py, upscale-model-loader.py) from current source: updated _load_module() with exec failure cleanup pattern, 3-marker structural verification, and try/finally sys.path restoration in import_custom_nodes()

Suggestions addressed:
- node_runtime.py: _is_comfyui_directory() now checks 3 structural markers (nodes.py, main.py, comfy/) instead of just nodes.py
- generator/render.py: added None guard for getattr on generated_helpers.__all__ items with log warning for missing helpers
- test_import_path_resolution.py: fixed test_sys_path_restored_on_crash to target the server module specifically after sys.path manipulation, so the try/finally restoration path is actually exercised

Nits addressed:
- .gitignore: added missing trailing newline

New test coverage (tests/test_app_base_mappings.py):
- base_node_class_mappings unchanged after custom node import
- deep copy independence of base_node_class_mappings
- render output contains hardened _load_module with except BaseException pattern
This commit is contained in:
Peyton DeNiro
2026-05-10 11:29:02 -05:00
parent ae8d5a2f2a
commit 29defc5ef1
8 changed files with 417 additions and 164 deletions
+9 -1
View File
@@ -1,9 +1,12 @@
import inspect
import logging
from pprint import pformat
from typing import Any
import black
log = logging.getLogger(__name__)
from ..node_runtime import import_custom_nodes
from . import generated_helpers
from .model import GenerationPlan
@@ -25,7 +28,12 @@ class WorkflowRenderer:
# are picked up without manually updating this list.
func_strings = []
for name in generated_helpers.__all__:
func = getattr(generated_helpers, name)
func = getattr(generated_helpers, name, None)
if func is None:
log.warning(
"Helper '%s' missing from generated_helpers — skipping", name
)
continue
func_strings.append(f"\n{inspect.getsource(func)}")
static_imports = [