refactor: unify model path identity

This commit is contained in:
Dariusz L
2026-08-05 05:02:49 +02:00
parent 535d8ad3ce
commit 75cc40cfb0
6 changed files with 42 additions and 35 deletions
+4 -3
View File
@@ -13,7 +13,7 @@ from concurrent.futures import CancelledError, ThreadPoolExecutor, as_completed
from typing import Any, Callable, Dict, List, Optional, Tuple
from .log_system import create_module_logger
from .metadata_model_utils import dedupe_models, is_model_file_path, model_identity_key
from .metadata_model_utils import dedupe_models, is_model_file_path
from .path_utils import (
MODEL_RESOLVER_METADATA_SCHEMA,
MODEL_RESOLVER_METADATA_SCHEMA_VERSION,
@@ -22,6 +22,7 @@ from .path_utils import (
extract_safetensors_header_metadata,
find_external_metadata_sidecar_path,
get_filename_from_path,
get_model_path_identity,
get_model_resolver_sidecar_path,
normalize_metadata_file_path,
read_json_safe,
@@ -579,7 +580,7 @@ def _build_missing_local_metadata_parallel(
def run_one_model(model: Dict[str, Any]) -> Dict[str, Any]:
model_path = str(model.get("path") or "").strip()
filename = get_filename_from_path(model_path)
model_key = model_identity_key(model) or model_path
model_key = get_model_path_identity(model.get("path")) or model_path
def child_progress(data: Dict[str, Any]) -> None:
if not isinstance(data, dict):
@@ -632,7 +633,7 @@ def _build_missing_local_metadata_parallel(
model = futures[future]
model_path = str(model.get("path") or "").strip()
filename = get_filename_from_path(model_path)
model_key = model_identity_key(model) or model_path
model_key = get_model_path_identity(model.get("path")) or model_path
try:
item_result = future.result()
except CancelledError:
+2 -10
View File
@@ -3,7 +3,7 @@
import os
from typing import Any, Dict, List
from .path_utils import get_filename_from_path, get_path_identity
from .path_utils import get_filename_from_path, get_model_path_identity
from .type_utils import MODEL_EXTENSIONS
@@ -19,14 +19,6 @@ def is_model_file_path(path: str) -> bool:
return os.path.splitext(filename)[1].lower() in MODEL_EXTENSIONS
def model_identity_key(model: Dict[str, Any]) -> str:
"""Return the stable filesystem identity for a model entry."""
model_path = str(model.get("path") or "").strip()
if not model_path:
return ""
return get_path_identity(model_path)
def dedupe_models(models: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Keep the first valid model entry for every filesystem identity."""
result: List[Dict[str, Any]] = []
@@ -34,7 +26,7 @@ def dedupe_models(models: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
for model in models or []:
if not isinstance(model, dict):
continue
identity = model_identity_key(model)
identity = get_model_path_identity(model.get("path"))
if not identity or identity in seen:
continue
seen.add(identity)
+6
View File
@@ -55,6 +55,12 @@ def get_path_identity(path: str) -> str:
return os.path.normcase(os.path.abspath(path))
def get_model_path_identity(path_value: Any) -> str:
"""Return a stable filesystem identity for a model path value."""
path = str(path_value or "").strip()
return get_path_identity(path)
def normalize_absolute_path(path_value: Any, path_module=os.path) -> str:
"""Return an absolute, normalized and symlink-resolved path."""
return path_module.realpath(
+2 -9
View File
@@ -13,7 +13,7 @@ from .log_system import create_module_logger
log = create_module_logger(__name__)
from .path_utils import get_filename_from_path, get_path_identity
from .path_utils import get_filename_from_path, get_model_path_identity, get_path_identity
# Import folder_paths lazily - it may not be available until ComfyUI is initialized
try:
@@ -37,13 +37,6 @@ def _directory_identity(path: str) -> str:
return _path_identity(path)
def _model_identity(model: Dict[str, str]) -> str:
path = model.get("path", "")
if not path:
return ""
return _path_identity(path)
def get_model_directories() -> Dict[str, Tuple[List[str], set]]:
"""
Get all configured model directories from folder_paths.
@@ -251,7 +244,7 @@ def scan_all_directories() -> List[Dict[str, str]]:
models = scan_directory(directory_path, extensions, category)
for model in models:
identity = _model_identity(model)
identity = get_model_path_identity(model.get("path"))
model_key = (model.get("category", category), identity)
if identity and model_key in seen_models:
continue
+13 -6
View File
@@ -3,8 +3,8 @@ import tempfile
import unittest
from unittest.mock import patch
from core.metadata_model_utils import dedupe_models, is_model_file_path, model_identity_key
from core.scanner import _model_identity
from core.metadata_model_utils import dedupe_models, is_model_file_path
from core.path_utils import get_model_path_identity
class MetadataModelHelperTests(unittest.TestCase):
@@ -43,8 +43,11 @@ class MetadataModelHelperTests(unittest.TestCase):
empty = {"path": ""}
models = [first, duplicate, empty, "not a model"]
self.assertEqual(model_identity_key(first), model_identity_key(duplicate))
self.assertEqual("", model_identity_key(empty))
self.assertEqual(
get_model_path_identity(first["path"]),
get_model_path_identity(duplicate["path"]),
)
self.assertEqual("", get_model_path_identity(empty["path"]))
self.assertEqual([first], dedupe_models(models))
def test_scanner_and_metadata_identity_share_path_fallback(self):
@@ -52,8 +55,12 @@ class MetadataModelHelperTests(unittest.TestCase):
expected = os.path.normcase(os.path.abspath(model_path))
with patch("core.path_utils.os.path.realpath", side_effect=OSError):
self.assertEqual(expected, _model_identity({"path": model_path}))
self.assertEqual(expected, model_identity_key({"path": model_path}))
self.assertEqual(expected, get_model_path_identity(model_path))
def test_model_path_identity_rejects_empty_and_whitespace_paths(self):
self.assertEqual("", get_model_path_identity(None))
self.assertEqual("", get_model_path_identity(""))
self.assertEqual("", get_model_path_identity(" "))
if __name__ == "__main__":
+15 -7
View File
@@ -1,14 +1,22 @@
import unittest
from unittest.mock import patch, MagicMock
from core.scanner import _model_identity, scan_directory, find_local_file_path
from unittest.mock import patch
from core.scanner import find_local_file_path, scan_directory
class ScannerTests(unittest.TestCase):
def test_model_identity_empty_and_valid(self):
self.assertEqual(_model_identity({}), "")
with patch("core.scanner._path_identity", return_value="stable_id"):
self.assertEqual(_model_identity({"path": "/path/to/file"}), "stable_id")
def test_model_path_identity_empty_and_valid(self):
from core.path_utils import get_model_path_identity
self.assertEqual(get_model_path_identity(None), "")
self.assertEqual(get_model_path_identity(""), "")
with patch("core.path_utils.get_path_identity", return_value="stable_id"):
self.assertEqual(
get_model_path_identity("/path/to/file"),
"stable_id",
)
@patch("os.walk")
@patch("os.path.exists")