Files
diodiogod-TTS-Audio-Suite/utils/robust_import.py
T
diodiogod a32bc82b63 Version 4.6.8
Implement robust import system for cross-environment compatibility

Major fix for #12 - comprehensive solution for module discovery issues

Technical details:
- Add utils/robust_import.py with multi-strategy import system
- Smart project root detection using characteristic files
- Handle conda cache invalidation, Windows path issues, and partial module loading
- Multi-layered fallback: normal import → path fix + cache clear → force reload
- Zero performance impact on working installations
- Update ChatterBox nodes to use robust import system
2025-08-30 00:28:43 -03:00

101 lines
3.9 KiB
Python

"""
Robust Import Helper for TTS Audio Suite
Ensures consistent module loading across all environments (conda, venv, Windows, etc.)
"""
import sys
import os
import importlib
from typing import Any
def ensure_project_path():
"""Ensure project root is in sys.path - works from any subdirectory"""
# Find project root by looking for characteristic files
current_path = os.path.dirname(os.path.abspath(__file__))
# Walk up directories to find project root
while current_path and current_path != os.path.dirname(current_path): # Not at filesystem root
# Check for project markers
if (os.path.exists(os.path.join(current_path, "nodes.py")) and
os.path.exists(os.path.join(current_path, "utils")) and
os.path.exists(os.path.join(current_path, "engines"))):
# Found project root
if current_path not in sys.path:
sys.path.insert(0, current_path)
return current_path
current_path = os.path.dirname(current_path)
# Fallback: assume we're in the project somewhere
current_dir = os.path.dirname(os.path.abspath(__file__)) # utils/
project_root = os.path.dirname(current_dir) # project root
if project_root not in sys.path:
sys.path.insert(0, project_root)
return project_root
def robust_import(module_name: str, retry_count: int = 2) -> Any:
"""
Robust import that handles path issues across all environments
Args:
module_name: Module to import (e.g., 'utils.audio.audio_hash')
retry_count: Number of retry attempts with different strategies
Returns:
Imported module
Raises:
ImportError: If all strategies fail
"""
# Strategy 1: Normal import
try:
return importlib.import_module(module_name)
except (ImportError, ModuleNotFoundError) as first_error:
if retry_count <= 0:
raise first_error
# Strategy 2: Ensure path and invalidate cache
try:
ensure_project_path()
importlib.invalidate_caches()
return importlib.import_module(module_name)
except (ImportError, ModuleNotFoundError) as second_error:
if retry_count <= 1:
raise second_error
# Strategy 3: Force reload of sys.modules if already partially loaded
try:
if module_name in sys.modules:
# Remove from cache and retry
del sys.modules[module_name]
importlib.invalidate_caches()
return importlib.import_module(module_name)
except (ImportError, ModuleNotFoundError) as final_error:
raise ImportError(f"Failed to import '{module_name}' after all strategies. "
f"First error: {first_error}, Final error: {final_error}")
def robust_from_import(module_name: str, attr_names: list, retry_count: int = 2) -> dict:
"""
Robust 'from module import attr' that handles path issues
Args:
module_name: Module to import from (e.g., 'utils.audio.audio_hash')
attr_names: List of attribute names to import (e.g., ['generate_stable_audio_component'])
retry_count: Number of retry attempts
Returns:
Dict mapping attribute names to imported objects
Example:
attrs = robust_from_import('utils.audio.audio_hash', ['generate_stable_audio_component'])
generate_stable_audio_component = attrs['generate_stable_audio_component']
"""
module = robust_import(module_name, retry_count)
result = {}
for attr_name in attr_names:
if not hasattr(module, attr_name):
raise ImportError(f"Module '{module_name}' has no attribute '{attr_name}'")
result[attr_name] = getattr(module, attr_name)
return result
# Initialize path on module load
ensure_project_path()