Skip to content

v1.57

Choose a tag to compare

@ussoewwin ussoewwin released this 30 Oct 18:18
· 665 commits to main since this release

v1.57 — Duplicate installation prevention; backup consolidation; comprehensive block removal

Summary

  • Fixes a critical bug where running the installer multiple times would create duplicate integration blocks in ComfyUI-nunchaku/__init__.py, breaking the plugin.
  • Adds three comprehensive safety mechanisms:
    • Marker-based duplicate detection → skip if already installed
    • Backup consolidation → single .qwen_image_backup created only once
    • Legacy block removal → handles old-style blocks without markers
  • Normal installation/uninstallation flows remain unchanged.

Problem

  • Some users see duplicate integration blocks in ComfyUI-nunchaku/__init__.py after running the installer multiple times (e.g., during troubleshooting, updates, or accidental double-clicks).
  • Root cause:
    • No duplicate detection existed, so every installer run appended the same integration code
    • Backup file was created by both batch scripts and Python script, causing two backups (.backup and .qwen_image_backup)
    • Backup could be overwritten on second run, making recovery impossible
    • Old-style blocks (without BEGIN/END markers) couldn't be removed by uninstaller

Technical Solution

  • Marker-based duplicate detection: Check for both BEGIN_MARKER and END_MARKER before appending; skip if found
  • Backup consolidation: Remove backup creation from batch files; only Python script creates .qwen_image_backup once
  • Backup creation order: Check for duplicates BEFORE creating backup, ensure backup contains original state
  • Legacy block removal: Regex pattern to detect and remove old-style blocks without markers
  • Uninstaller strategy: Prefer backup restoration, fallback to marker-based removal

Code Changes

File 1: append_integration.py

Key modifications:

  • Added marker-based duplicate detection using BEGIN_MARKER and END_MARKER constants
  • Implemented idempotent backup creation with .qwen_image_backup extension
  • Fixed backup creation order: check duplicates BEFORE backup, to ensure backup always contains original state
  • Added graceful error handling for backup creation failures

Code-level Explanation:

  1. Marker-based duplicate detection (Lines 49-51)
if BEGIN_MARKER in content and END_MARKER in content:
    print("Integration code already present. Skipping.")
    return True

Purpose: Before attempting any modification, check if BEGIN_MARKER and END_MARKER both exist in the file. If both are present, it means this script has already run successfully, so skip the installation immediately.

Benefit: Ensures the script is idempotent—running it multiple times produces the same result as running it once. No duplicate blocks are created.

  1. Idempotent backup creation (Lines 54-57)
backup_path = init_py_path + ".qwen_image_backup"
if not os.path.exists(backup_path):
    try:
        shutil.copy2(init_py_path, backup_path)
        print(f"Backup created: {backup_path}")

Purpose: Create a backup only if it doesn't already exist. The .qwen_image_backup extension is unique to this loader, avoiding conflicts with other backups (e.g., .backup from batch files).

Benefit: Backup file is only created on the FIRST installation, never overwritten on subsequent runs. Original __init__.py state is preserved permanently.

  1. Append integration code (Lines 65-72)
with open(init_py_path, 'a', encoding='utf-8') as f:
    if not content.endswith('\n'):
        f.write('\n')
    f.write(integration_code)

print(f"Successfully appended integration code to: {init_py_path}")
return True

Purpose: Only append the integration block if we passed the duplicate check. Ensure file ends with newline before appending to avoid formatting issues.

Benefit: Clean, predictable append operation. No orphaned code.


File 2: remove_integration.py (NEW FILE)

Purpose: Provide robust block removal functionality that handles both new-style blocks (with markers) and legacy old-style blocks (without markers).

Key modifications:

  • Implemented _remove_legacy_block() function using regex to detect and remove old-style integration blocks
  • Implemented _remove_all_blocks() function that removes both marker-based and legacy blocks in two stages
  • Added proper whitespace trimming to leave clean file structure

Code-level Explanation:

  1. Legacy block removal function (Lines 40-66)
def _remove_legacy_block(text: str) -> tuple[str, int]:
    """Remove old-style integration block without markers."""
    old_comment = "# ComfyUI-QwenImageLoraLoader Integration"
    removed = 0
    
    start = text.find(old_comment)
    if start == -1:
        return text, 0
    
    pattern = r'# ComfyUI-QwenImageLoraLoader Integration\ntry:.*?except ImportError:.*?logger\.exception\(.*?\)'
    
    match = re.search(pattern, text, re.DOTALL)
    if match:
        start_pos = match.start()
        end_pos = match.end()
        
        # Trim leading whitespace from the block
        while start_pos > 0 and text[start_pos - 1] in "\r\n\t ":
            start_pos -= 1
        
        # Remove the block
        text = text[:start_pos].rstrip() + "\n\n" + text[end_pos:].lstrip()
        removed = 1
    
    return text, removed

Purpose: Find and remove old-style integration blocks that lack explicit BEGIN/END markers. These blocks were created by older versions of the installer.

How it works:

  • Search for the old comment marker # ComfyUI-QwenImageLoraLoader Integration
  • If found, use regex pattern to match the entire try-except block
  • Regex pattern re.DOTALL flag allows matching across multiple lines
  • Calculate exact boundaries of the block including leading whitespace
  • Remove the entire block and trim excess whitespace

Benefit: Handles upgrades from older versions smoothly. Users who ran old installers can uninstall cleanly.

  1. Two-stage block removal function (Lines 70-89)
def _remove_all_blocks(text: str) -> tuple[str, int]:
    # First, remove new-style blocks with markers
    removed = 0
    start = text.find(BEGIN_MARKER)
    while start != -1:
        stop = text.find(END_MARKER, start)
        if stop == -1:
            break
        stop += len(END_MARKER)
        while stop < len(text) and text[stop] in "\r\n":
            stop += 1
        text = (text[:start].rstrip() + "\n\n" + text[stop:].lstrip())
        removed += 1
        start = text.find(BEGIN_MARKER)
    
    # Then, remove old-style blocks without markers
    text, legacy_removed = _remove_legacy_block(text)
    removed += legacy_removed
    
    return text, removed

Purpose: Orchestrate complete removal of all integration blocks, both new and legacy.

Strategy:

  • Stage 1: Remove all marker-based blocks (new-style, created by v1.57+)
  • Stage 2: Remove legacy blocks (old-style, no markers)
  • Return total count of blocks removed

Benefit: Comprehensive uninstallation works regardless of installation history.


File 3: install_qwen_lora.bat (Global Python Installer)

Key modifications:

  • Added findstr duplicate check before calling Python script
  • Removed redundant backup creation logic (moved to Python script)
  • Simplified batch file to focus on detection and delegation

Code section (Lines 48-64):

REM Check if already installed
findstr /C:"ComfyUI-QwenImageLoraLoader Integration" "%NUNCHAKU_PATH%\__init__.py" >nul 2>&1
if %errorlevel% equ 0 (
    echo Already installed. Integration code already exists in __init__.py
    pause
    exit /b 0
)

echo Adding LoRA loader integration code...

REM Append integration code using Python script
py -3 "%LORA_LOADER_PATH%\append_integration.py" "%NUNCHAKU_PATH%\__init__.py"

Explanation:

  • findstr /C: searches for exact string "ComfyUI-QwenImageLoraLoader Integration"
  • >nul 2>&1 redirects output to nowhere (silent search)
  • %errorlevel% equ 0 means search succeeded (code already installed)
  • If already installed, exit immediately with success message
  • Otherwise, call Python script to perform actual installation

Benefit: Quick early exit before Python startup. Faster feedback for users running installer multiple times.


File 4: install_qwen_lora_portable.bat (Embedded Python Installer)

Key modifications:

  • Same duplicate detection logic as global version
  • Uses embedded Python path instead of py -3
  • Maintains compatibility with portable ComfyUI installations

Code section (Lines 75-91):

REM Check if already installed
findstr /C:"ComfyUI-QwenImageLoraLoader Integration" "%NUNCHAKU_PATH%\__init__.py" >nul 2>&1
if %errorlevel% equ 0 (
    echo Already installed. Integration code already exists in __init__.py
    pause
    exit /b 0
)

echo Adding LoRA loader integration code...

REM Append integration code using embedded Python
"%PYTHON_CMD%" "%LORA_LOADER_PATH%\append_integration.py" "%NUNCHAKU_PATH%\__init__.py"

Difference from global: Uses %PYTHON_CMD% (embedded Python path) instead of py -3 command.


File 5: uninstall_qwen_lora.bat (Global Python Uninstaller)

Key modifications:

  • Two-stage uninstallation strategy: backup restoration first, block removal as fallback
  • Unified to single backup file (.qwen_image_backup only)
  • Removed fallback to old .backup file

Code section (Lines 23-43):

echo Checking for backups...

REM Try new-style backup first
if exist "%NUNCHAKU_PATH%\__init__.py.qwen_image_backup" (
    copy "%NUNCHAKU_PATH%\__init__.py.qwen_image_backup" "%NUNCHAKU_PATH%\__init__.py" >nul
    echo Restored from backup: __init__.py.qwen_image_backup
    echo [OK] Qwen Image LoRA Loader integration removed.
    pause
    exit /b 0
)

REM No backup found - remove integration blocks using Python script
echo No backup found. Removing integration blocks...
where py >nul 2>&1
if not errorlevel 1 (
    call py -3 "%SCRIPT_DIR%remove_integration.py"
)

echo [OK] Qwen Image LoRA Loader integration removed.
pause
exit /b 0

Explanation:

  • First: Check if .qwen_image_backup exists
  • If yes: Copy it over __init__.py to restore original state, then exit
  • If no: Call Python script to remove blocks using regex detection
  • where py checks if py command is available

Benefit: Fastest uninstallation path (simple file copy) when backup exists. Fallback ensures uninstallation works even without backup.


File 6: uninstall_qwen_lora_portable.bat (Embedded Python Uninstaller)

Key modifications:

  • Same two-stage strategy as global version
  • Uses embedded Python path instead of py -3
  • NEW FILE: Did not exist in v1.56

Code section (Lines 47-74):

echo Checking for backups...

REM Try new-style backup first
if exist "%NUNCHAKU_PATH%\__init__.py.qwen_image_backup" (
    copy "%NUNCHAKU_PATH%\__init__.py.qwen_image_backup" "%NUNCHAKU_PATH%\__init__.py" >nul
    echo Restored from backup: __init__.py.qwen_image_backup
    echo [OK] Qwen Image LoRA Loader integration removed.
    pause
    exit /b 0
)

REM No backup found - remove integration blocks using Python script
echo No backup found. Removing integration blocks...
"%PYTHON_CMD%" "%SCRIPT_DIR%remove_integration.py"

echo.
echo [OK] Qwen Image LoRA Loader integration removed.
pause
exit /b 0

Difference from global: Uses %PYTHON_CMD% (embedded Python path) instead of where py + py -3.


Design Rationale

1. Why Marker-Based Detection?

Problem: Without markers, detecting whether code has been installed requires parsing Python syntax. Error-prone and fragile.

Solution: Bracket integration code with explicit BEGIN_MARKER and END_MARKER. Simple string search is 100% reliable.

Benefit: Fast, deterministic, language-agnostic detection.

2. Why Consolidate Backups?

Problem: Multiple backup files (.backup from batch, .qwen_image_backup from Python) cause confusion and potential corruption.

Solution: Single backup file .qwen_image_backup, created only by Python script, never overwritten.

Benefit: Clear, predictable state. Users know exactly where original file is stored.

3. Why Two-Stage Uninstallation?

Problem: Uninstallation must work even if user runs uninstaller multiple times or with missing backup.

Solution:

  • Stage 1: Fastest path (backup exists) → restore from backup
  • Stage 2: Fallback path (no backup) → remove blocks programmatically

Benefit: Robust uninstallation in all scenarios.

4. Why Legacy Block Removal?

Problem: Users upgrading from old versions have blocks without markers. Uninstaller can't remove them without legacy support.

Solution: Regex pattern to detect and remove old-style blocks.

Benefit: Seamless upgrade path. No manual intervention required.


Testing Verification

Scenario 1: Clean Installation → Multiple Reinstallations

1. First run: install_qwen_lora.bat
   ✓ Integration code appended
   ✓ .qwen_image_backup created
   
2. Second run: install_qwen_lora.bat
   ✓ Duplicate detected
   ✓ Skips installation
   ✓ No new blocks added
   
3. Third run: install_qwen_lora.bat
   ✓ Duplicate detected again
   ✓ Still skips installation
   ✓ File unchanged

Scenario 2: Backup-Based Uninstallation

1. uninstall_qwen_lora.bat
   ✓ .qwen_image_backup found
   ✓ Restored from backup (fastest)
   ✓ __init__.py reverted to original

Scenario 3: Block-Based Uninstallation (No Backup)

1. Delete .qwen_image_backup manually
2. uninstall_qwen_lora.bat
   ✓ No backup found
   ✓ Calls remove_integration.py
   ✓ Removes all integration blocks
   ✓ Trims excess whitespace
   ✓ File clean and usable

Scenario 4: Legacy Block Removal

1. Old installation created block without markers
2. uninstall_qwen_lora.bat
   ✓ No new markers found
   ✓ Regex detects old comment
   ✓ Matches entire try-except block
   ✓ Removes block cleanly

Backward Compatibility

  • ✓ Installations using v1.56 or earlier: Uninstaller with legacy block detection still works
  • ✓ Fresh installations: Only use new marker-based approach
  • ✓ Mixed environments: Both detection methods coexist without conflict
  • ✓ No breaking changes to UI, nodes, or normal workflows

Expected Impact

  • Installation reliability: 100% idempotent. Multiple runs = one installation.
  • Uninstallation reliability: Works with or without backup. No stranded blocks.
  • User experience: Clear messages. Fast feedback. Predictable behavior.
  • Maintenance: Cleaner code. Easier to debug and update.

Related Issues & References

  • Resolves: Installation script duplicate block creation bug
  • Prevents: Backup file corruption and loss
  • Supports: Seamless upgrades from v1.56 and earlier versions

Credits

  • Designed and implemented by: @ussoewwin
  • Tested by: @ussoewwin
  • Based on: Community feedback on installation reliability

Links: