Repository navigation
v1.57
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_backupcreated 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__.pyafter 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 (
.backupand.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_backuponce - 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_backupextension - 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:
- Marker-based duplicate detection (Lines 49-51)
if BEGIN_MARKER in content and END_MARKER in content:
print("Integration code already present. Skipping.")
return TruePurpose: 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.
- 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.
- 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 TruePurpose: 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:
- 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, removedPurpose: 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.DOTALLflag 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.
- 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, removedPurpose: 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
findstrduplicate 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>&1redirects output to nowhere (silent search)%errorlevel% equ 0means 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_backuponly) - Removed fallback to old
.backupfile
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 0Explanation:
- First: Check if
.qwen_image_backupexists - If yes: Copy it over
__init__.pyto restore original state, then exit - If no: Call Python script to remove blocks using regex detection
where pychecks ifpycommand 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 0Difference 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: