From 75cc40cfb0724f94063d9c573d0038c0e4313dad Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Wed, 5 Aug 2026 05:02:49 +0200 Subject: [PATCH] refactor: unify model path identity --- core/metadata_builder.py | 7 ++++--- core/metadata_model_utils.py | 12 ++---------- core/path_utils.py | 6 ++++++ core/scanner.py | 11 ++--------- tests/test_metadata_model_helpers.py | 19 +++++++++++++------ tests/test_scanner.py | 22 +++++++++++++++------- 6 files changed, 42 insertions(+), 35 deletions(-) diff --git a/core/metadata_builder.py b/core/metadata_builder.py index 8f402e9..1743f44 100644 --- a/core/metadata_builder.py +++ b/core/metadata_builder.py @@ -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: diff --git a/core/metadata_model_utils.py b/core/metadata_model_utils.py index eb7bd1c..90a7355 100644 --- a/core/metadata_model_utils.py +++ b/core/metadata_model_utils.py @@ -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) diff --git a/core/path_utils.py b/core/path_utils.py index 1108c41..3cd5e59 100644 --- a/core/path_utils.py +++ b/core/path_utils.py @@ -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( diff --git a/core/scanner.py b/core/scanner.py index 75bb2ab..fe80aa9 100644 --- a/core/scanner.py +++ b/core/scanner.py @@ -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 diff --git a/tests/test_metadata_model_helpers.py b/tests/test_metadata_model_helpers.py index 0c218ce..1028de4 100644 --- a/tests/test_metadata_model_helpers.py +++ b/tests/test_metadata_model_helpers.py @@ -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__": diff --git a/tests/test_scanner.py b/tests/test_scanner.py index c312aae..28ae217 100644 --- a/tests/test_scanner.py +++ b/tests/test_scanner.py @@ -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")