mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-24 06:32:12 -03:00
refactor(model_scanner): normalize path comparisons for model roots
fix(example_images_download_manager): re-raise specific exception on download error refactor(usage_stats): define constants locally to avoid conditional imports test(example_images_download_manager): update exception handling in download tests test(example_images_file_manager): differentiate between os.startfile and subprocess.Popen in tests test(example_images_paths): ensure valid example images root with single-library mode test(usage_stats): use string literals for metadata payload to avoid conditional imports
This commit is contained in:
@@ -620,8 +620,12 @@ class ModelScanner:
|
|||||||
try:
|
try:
|
||||||
# Find the appropriate root path for this file
|
# Find the appropriate root path for this file
|
||||||
root_path = None
|
root_path = None
|
||||||
for potential_root in self.get_model_roots():
|
model_roots = self.get_model_roots()
|
||||||
if path.startswith(potential_root):
|
for potential_root in model_roots:
|
||||||
|
# Normalize both paths for comparison
|
||||||
|
normalized_path = os.path.normpath(path)
|
||||||
|
normalized_root = os.path.normpath(potential_root)
|
||||||
|
if normalized_path.startswith(normalized_root):
|
||||||
root_path = potential_root
|
root_path = potential_root
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@@ -202,6 +202,11 @@ class DownloadManager:
|
|||||||
)
|
)
|
||||||
|
|
||||||
snapshot = self._progress.snapshot()
|
snapshot = self._progress.snapshot()
|
||||||
|
except ExampleImagesDownloadError:
|
||||||
|
# Re-raise our own exception types without wrapping
|
||||||
|
self._is_downloading = False
|
||||||
|
self._download_task = None
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._is_downloading = False
|
self._is_downloading = False
|
||||||
self._download_task = None
|
self._download_task = None
|
||||||
|
|||||||
@@ -13,9 +13,19 @@ from ..services.service_registry import ServiceRegistry
|
|||||||
# Check if running in standalone mode
|
# Check if running in standalone mode
|
||||||
standalone_mode = os.environ.get("LORA_MANAGER_STANDALONE", "0") == "1" or os.environ.get("HF_HUB_DISABLE_TELEMETRY", "0") == "0"
|
standalone_mode = os.environ.get("LORA_MANAGER_STANDALONE", "0") == "1" or os.environ.get("HF_HUB_DISABLE_TELEMETRY", "0") == "0"
|
||||||
|
|
||||||
|
# Define constants locally to avoid dependency on conditional imports
|
||||||
|
MODELS = "models"
|
||||||
|
LORAS = "loras"
|
||||||
|
|
||||||
if not standalone_mode:
|
if not standalone_mode:
|
||||||
from ..metadata_collector.metadata_registry import MetadataRegistry
|
from ..metadata_collector.metadata_registry import MetadataRegistry
|
||||||
from ..metadata_collector.constants import MODELS, LORAS
|
# Import constants from metadata_collector to ensure consistency, but we have fallbacks defined above
|
||||||
|
try:
|
||||||
|
from ..metadata_collector.constants import MODELS as _MODELS, LORAS as _LORAS
|
||||||
|
MODELS = _MODELS
|
||||||
|
LORAS = _LORAS
|
||||||
|
except ImportError:
|
||||||
|
pass # Use the local definitions
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,12 @@ def restore_settings() -> None:
|
|||||||
async def test_start_download_requires_configured_path(monkeypatch: pytest.MonkeyPatch) -> None:
|
async def test_start_download_requires_configured_path(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
manager = download_module.DownloadManager(ws_manager=RecordingWebSocketManager())
|
manager = download_module.DownloadManager(ws_manager=RecordingWebSocketManager())
|
||||||
|
|
||||||
with pytest.raises(download_module.ExampleImagesDownloadError) as exc_info:
|
# Ensure example_images_path is not configured
|
||||||
|
settings.settings.pop('example_images_path', None)
|
||||||
|
|
||||||
|
with pytest.raises(download_module.DownloadConfigurationError) as exc_info:
|
||||||
await manager.start_download({})
|
await manager.start_download({})
|
||||||
|
|
||||||
assert isinstance(exc_info.value.__cause__, download_module.DownloadConfigurationError)
|
|
||||||
assert "not configured" in str(exc_info.value)
|
assert "not configured" in str(exc_info.value)
|
||||||
|
|
||||||
result = await manager.start_download({"auto_mode": True})
|
result = await manager.start_download({"auto_mode": True})
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
@@ -37,20 +38,30 @@ async def test_open_folder_requires_existing_model_directory(monkeypatch: pytest
|
|||||||
(model_folder / "image.png").write_text("data", encoding="utf-8")
|
(model_folder / "image.png").write_text("data", encoding="utf-8")
|
||||||
|
|
||||||
popen_calls: list[list[str]] = []
|
popen_calls: list[list[str]] = []
|
||||||
|
startfile_calls: list[str] = []
|
||||||
|
|
||||||
class DummyPopen:
|
class DummyPopen:
|
||||||
def __init__(self, cmd, *_args, **_kwargs):
|
def __init__(self, cmd, *_args, **_kwargs):
|
||||||
popen_calls.append(cmd)
|
popen_calls.append(cmd)
|
||||||
|
|
||||||
|
def dummy_startfile(path):
|
||||||
|
startfile_calls.append(path)
|
||||||
|
|
||||||
monkeypatch.setattr("subprocess.Popen", DummyPopen)
|
monkeypatch.setattr("subprocess.Popen", DummyPopen)
|
||||||
|
monkeypatch.setattr("os.startfile", dummy_startfile)
|
||||||
|
|
||||||
request = JsonRequest({"model_hash": model_hash})
|
request = JsonRequest({"model_hash": model_hash})
|
||||||
response = await ExampleImagesFileManager.open_folder(request)
|
response = await ExampleImagesFileManager.open_folder(request)
|
||||||
body = json.loads(response.text)
|
body = json.loads(response.text)
|
||||||
|
|
||||||
assert body["success"] is True
|
assert body["success"] is True
|
||||||
assert popen_calls
|
# On Windows, os.startfile is used; on other platforms, subprocess.Popen
|
||||||
assert model_hash in popen_calls[0][-1]
|
if os.name == 'nt':
|
||||||
|
assert startfile_calls
|
||||||
|
assert model_hash in startfile_calls[0]
|
||||||
|
else:
|
||||||
|
assert popen_calls
|
||||||
|
assert model_hash in popen_calls[0][-1]
|
||||||
|
|
||||||
|
|
||||||
async def test_open_folder_rejects_invalid_paths(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None:
|
async def test_open_folder_rejects_invalid_paths(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None:
|
||||||
|
|||||||
@@ -104,6 +104,10 @@ def test_iter_library_roots_returns_all_configured(tmp_path):
|
|||||||
|
|
||||||
def test_is_valid_example_images_root_accepts_hash_directories(tmp_path):
|
def test_is_valid_example_images_root_accepts_hash_directories(tmp_path):
|
||||||
settings.settings['example_images_path'] = str(tmp_path)
|
settings.settings['example_images_path'] = str(tmp_path)
|
||||||
|
# Ensure single-library mode (not multi-library mode)
|
||||||
|
settings.settings['libraries'] = {'default': {}}
|
||||||
|
settings.settings['active_library'] = 'default'
|
||||||
|
|
||||||
hash_folder = tmp_path / ('d' * 64)
|
hash_folder = tmp_path / ('d' * 64)
|
||||||
hash_folder.mkdir()
|
hash_folder.mkdir()
|
||||||
(hash_folder / 'image.png').write_text('data', encoding='utf-8')
|
(hash_folder / 'image.png').write_text('data', encoding='utf-8')
|
||||||
@@ -112,4 +116,6 @@ def test_is_valid_example_images_root_accepts_hash_directories(tmp_path):
|
|||||||
|
|
||||||
invalid_folder = tmp_path / 'not_hash'
|
invalid_folder = tmp_path / 'not_hash'
|
||||||
invalid_folder.mkdir()
|
invalid_folder.mkdir()
|
||||||
|
# Add a non-hash file to make it clearly invalid
|
||||||
|
(invalid_folder / 'invalid.txt').write_text('invalid', encoding='utf-8')
|
||||||
assert is_valid_example_images_root(str(tmp_path)) is False
|
assert is_valid_example_images_root(str(tmp_path)) is False
|
||||||
|
|||||||
@@ -113,11 +113,12 @@ async def test_usage_stats_background_processor_handles_pending_prompts(tmp_path
|
|||||||
stats, tasks, _ = _prepare_usage_stats(tmp_path, monkeypatch, sleep_override=fast_sleep)
|
stats, tasks, _ = _prepare_usage_stats(tmp_path, monkeypatch, sleep_override=fast_sleep)
|
||||||
|
|
||||||
metadata_calls = []
|
metadata_calls = []
|
||||||
|
# Use string literals directly to avoid dependency on conditional imports
|
||||||
metadata_payload = {
|
metadata_payload = {
|
||||||
MODELS: {
|
"models": {
|
||||||
"1": {"type": "checkpoint", "name": "model.ckpt"},
|
"1": {"type": "checkpoint", "name": "model.ckpt"},
|
||||||
},
|
},
|
||||||
LORAS: {
|
"loras": {
|
||||||
"2": {"lora_list": [{"name": "awesome_lora.safetensors"}]},
|
"2": {"lora_list": [{"name": "awesome_lora.safetensors"}]},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user