Unify shared path normalization

This commit is contained in:
Dariusz L
2026-08-03 12:59:04 +02:00
parent 0ff8057576
commit e1e725792b
7 changed files with 41 additions and 9 deletions
+2 -2
View File
@@ -12,6 +12,7 @@ from ..path_utils import (
MODEL_RESOLVER_METADATA_SCHEMA_VERSION,
find_metadata_sidecar_path,
get_filename_from_path,
normalize_metadata_file_path,
read_merged_model_metadata,
)
from ..resolver import normalize_sha256
@@ -116,8 +117,7 @@ def _coerce_size(value: Any) -> int:
return 0
def _normalise_metadata_file_path(path_value: str) -> str:
return str(path_value or "").replace(os.sep, "/")
_normalise_metadata_file_path = normalize_metadata_file_path
def _resolve_lora_manager_model_type(category: str, source_type: Any = "") -> str:
+2 -2
View File
@@ -31,6 +31,7 @@ from .path_utils import (
find_external_metadata_sidecar_path,
get_filename_from_path,
get_model_resolver_sidecar_path,
normalize_metadata_file_path,
read_json_safe,
read_safetensors_header,
write_json_atomic,
@@ -255,8 +256,7 @@ def _merge_unique_strings(*values: Any) -> List[str]:
return result
def _normalise_metadata_file_path(path_value: str) -> str:
return str(path_value or "").replace(os.sep, "/")
_normalise_metadata_file_path = normalize_metadata_file_path
def _model_type_for_category(category: str) -> str:
+12
View File
@@ -55,6 +55,18 @@ def get_path_identity(path: str) -> str:
return os.path.normcase(os.path.abspath(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(
path_module.abspath(path_module.normpath(str(path_value)))
)
def normalize_metadata_file_path(path_value: Any) -> str:
"""Normalize metadata paths to portable forward-slash separators."""
return str(path_value or "").replace(os.sep, "/")
def get_path_abs(path_value: Any) -> str:
"""Return absolute path safely, handling OSError/ValueError."""
try:
+4 -2
View File
@@ -1,5 +1,6 @@
"""CivitAI model search service."""
from .. import path_utils
from ..routes.context import RouteContext
from ..sources.common import is_remote_link_marked_dead
from .model_utils import CivitAISearchDependencies, ModelServiceDependencies
@@ -99,8 +100,9 @@ class CivitAISearchService(ModelServiceDependencies):
file_path = find_local_file_path(filename, category)
if file_path:
file_path = _os.path.realpath(
_os.path.abspath(_os.path.normpath(str(file_path)))
file_path = path_utils.normalize_absolute_path(
file_path,
path_module=_os.path,
)
if not is_path_in_configured_model_roots(file_path):
if resolved_path:
+3 -2
View File
@@ -71,8 +71,9 @@ class HashService:
return self.extension.logger
def _normalize_path(self, path):
return self.os.path.realpath(
self.os.path.abspath(self.os.path.normpath(str(path)))
return path_utils.normalize_absolute_path(
path,
path_module=self.os.path,
)
async def local_model_hashes(self, request):
+2
View File
@@ -5,6 +5,7 @@ from types import SimpleNamespace
from unittest.mock import MagicMock, patch
from core.download.api import context as downloader
from core.path_utils import normalize_metadata_file_path
class DownloaderBoundaryTests(unittest.TestCase):
@@ -60,6 +61,7 @@ class DownloaderBoundaryTests(unittest.TestCase):
self.assertEqual(12, downloader._coerce_size("12.9"))
self.assertEqual(0, downloader._coerce_size("not-a-size"))
self.assertEqual("models/subfolder", downloader._normalise_metadata_file_path("models\\subfolder"))
self.assertEqual("models/subfolder", normalize_metadata_file_path("models\\subfolder"))
def test_metadata_source_and_model_type_helpers_cover_provider_variants(self):
self.assertEqual(
+16 -1
View File
@@ -8,7 +8,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from aiohttp import web
from core.path_utils import HashCalculationCancelled
from core.path_utils import HashCalculationCancelled, normalize_absolute_path
from core.routes.context import RouteContext
from core.routes.hashes import register_hash_routes
from core.services.hash_service import HashService
@@ -134,6 +134,21 @@ async def test_local_model_hashes_uses_model_path_and_returns_metadata():
)
def test_hash_service_path_normalization_uses_shared_helper_and_injected_path_module():
_, values = _build_hash_routes()
service = HashService(RouteContext(values))
model_path = "models/checkpoints/../loras/model.safetensors"
with patch(
"core.path_utils.normalize_absolute_path",
wraps=normalize_absolute_path,
) as normalize_path:
result = service._normalize_path(model_path)
assert result == normalize_absolute_path(model_path)
normalize_path.assert_called_once_with(model_path, path_module=os.path)
@pytest.mark.asyncio
async def test_hash_routes_validate_local_metadata_and_preview_paths():
handlers, values = _build_hash_routes()