From 3f0359e1f4530783942d7a793577de319f633664 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Sun, 2 Aug 2026 03:04:29 +0200 Subject: [PATCH] refactor(context): require hash and download dependencies --- core/services/download_service.py | 66 ++++++++++++++-------------- core/services/hash_service.py | 52 +++++++++++----------- tests/test_route_context_contract.py | 18 ++++++++ 3 files changed, 77 insertions(+), 59 deletions(-) diff --git a/core/services/download_service.py b/core/services/download_service.py index 22e966c..ffabb06 100644 --- a/core/services/download_service.py +++ b/core/services/download_service.py @@ -10,55 +10,55 @@ class DownloadService: """Coordinate download requests without embedding business logic in routes.""" def __init__(self, context: RouteContext): - self.aria2_install_error = context.get("Aria2InstallError") - self.unsafe_url_error = context.get("UnsafeUrlError") - self.asyncio = context.get("asyncio") - self.cancel_download_fn = context.get("cancel_download") - self.clear_completed_downloads_fn = context.get( + self.aria2_install_error = context.require("Aria2InstallError") + self.unsafe_url_error = context.require("UnsafeUrlError") + self.asyncio = context.require("asyncio") + self.cancel_download_fn = context.require("cancel_download") + self.clear_completed_downloads_fn = context.require( "clear_completed_downloads" ) - self.first_non_empty = context.get("first_non_empty") - self.get_all_progress_fn = context.get("get_all_progress") - self.get_aria2_status_fn = context.get("get_aria2_status") - self.get_civarchive_model_details = context.get( + self.first_non_empty = context.require("first_non_empty") + self.get_all_progress_fn = context.require("get_all_progress") + self.get_aria2_status_fn = context.require("get_aria2_status") + self.get_civarchive_model_details = context.require( "get_civarchive_model_details" ) - self.get_civitai_model_details = context.get("get_civitai_model_details") - self.get_default_root_for_category = context.get( + self.get_civitai_model_details = context.require("get_civitai_model_details") + self.get_default_root_for_category = context.require( "get_default_root_for_category" ) - self.get_download_directory = context.get("get_download_directory") - self.get_override_settings_from_request = context.get( + self.get_download_directory = context.require("get_download_directory") + self.get_override_settings_from_request = context.require( "get_override_settings_from_request" ) - self.get_progress_fn = context.get("get_progress") - self.host_matches_domain = context.get("host_matches_domain") - self.install_aria2_engine = context.get("install_aria2_engine") - self.is_allowed_model_download_filename = context.get( + self.get_progress_fn = context.require("get_progress") + self.host_matches_domain = context.require("host_matches_domain") + self.install_aria2_engine = context.require("install_aria2_engine") + self.is_allowed_model_download_filename = context.require( "is_allowed_model_download_filename" ) - self.load_resolver_settings = context.get("load_resolver_settings") - self.normalize_download_category = context.get( + self.load_resolver_settings = context.require("load_resolver_settings") + self.normalize_download_category = context.require( "normalize_download_category" ) - self.pause_download_fn = context.get("pause_download") - self.resolve_download_subfolder = context.get( + self.pause_download_fn = context.require("pause_download") + self.resolve_download_subfolder = context.require( "resolve_download_subfolder" ) - self.resume_download_fn = context.get("resume_download") - self.save_resolver_settings = context.get("save_resolver_settings") - self.sanitize_download_filename = context.get( + self.resume_download_fn = context.require("resume_download") + self.save_resolver_settings = context.require("save_resolver_settings") + self.sanitize_download_filename = context.require( "sanitize_download_filename" ) - self.extension = context.get("self") - self.split_path_segments = context.get("split_path_segments") - self.start_aria2_daemon = context.get("start_aria2_daemon") - self.start_background_download = context.get("start_background_download") - self.stop_aria2_daemon = context.get("stop_aria2_daemon") - self.to_bool = context.get("to_bool") - self.to_int = context.get("to_int") - self.validate_public_http_url = context.get("validate_public_http_url") - self.web = context.get("web") + self.extension = context.require("self") + self.split_path_segments = context.require("split_path_segments") + self.start_aria2_daemon = context.require("start_aria2_daemon") + self.start_background_download = context.require("start_background_download") + self.stop_aria2_daemon = context.require("stop_aria2_daemon") + self.to_bool = context.require("to_bool") + self.to_int = context.require("to_int") + self.validate_public_http_url = context.require("validate_public_http_url") + self.web = context.require("web") @property def logger(self): diff --git a/core/services/hash_service.py b/core/services/hash_service.py index 2a48f88..bb43a85 100644 --- a/core/services/hash_service.py +++ b/core/services/hash_service.py @@ -12,55 +12,55 @@ class HashService: """Implement local model hash operations used by HTTP routes.""" def __init__(self, context: RouteContext): - self.file_manager_error = context.get("FileManagerError") - self.file_manager_unavailable_error = context.get( + self.file_manager_error = context.require("FileManagerError") + self.file_manager_unavailable_error = context.require( "FileManagerUnavailableError" ) - self.metadata_schema = context.get("MODEL_RESOLVER_METADATA_SCHEMA") - self.metadata_schema_version = context.get( + self.metadata_schema = context.require("MODEL_RESOLVER_METADATA_SCHEMA") + self.metadata_schema_version = context.require( "MODEL_RESOLVER_METADATA_SCHEMA_VERSION" ) - self.unsupported_file_manager_error = context.get( + self.unsupported_file_manager_error = context.require( "UnsupportedFileManagerPlatformError" ) - self.asyncio = context.get("asyncio") - self.cancel_progress_response = context.get("cancel_progress_response") + self.asyncio = context.require("asyncio") + self.cancel_progress_response = context.require("cancel_progress_response") self.get_existing_model_preview_path = context.get( "get_existing_model_preview_path" ) - self.get_filename_from_path = context.get("get_filename_from_path") - self.get_local_model_hash_metadata = context.get( + self.get_filename_from_path = context.require("get_filename_from_path") + self.get_local_model_hash_metadata = context.require( "get_local_model_hash_metadata" ) - self.get_progress_response = context.get("get_progress_response") - self.get_safe_model_resolver_sidecar_path = context.get( + self.get_progress_response = context.require("get_progress_response") + self.get_safe_model_resolver_sidecar_path = context.require( "get_safe_model_resolver_sidecar_path" ) - self.get_workflow_model_inventory = context.get( + self.get_workflow_model_inventory = context.require( "get_workflow_model_inventory" ) - self.is_path_in_configured_model_roots = context.get( + self.is_path_in_configured_model_roots = context.require( "is_path_in_configured_model_roots" ) - self.load_resolver_settings = context.get("load_resolver_settings") - self.normalize_file_manager_path = context.get( + self.load_resolver_settings = context.require("load_resolver_settings") + self.normalize_file_manager_path = context.require( "normalize_file_manager_path" ) - self.normalize_sha256 = context.get("normalize_sha256") - self.open_in_file_manager = context.get("open_in_file_manager") + self.normalize_sha256 = context.require("normalize_sha256") + self.open_in_file_manager = context.require("open_in_file_manager") self.os = context.get("os") or os - self.read_json_safe = context.get("read_json_safe") - self.resolver_bool_setting = context.get("resolver_bool_setting") - self.run_in_background_thread = context.get("run_in_background_thread") - self.search_local_matches_by_hash = context.get( + self.read_json_safe = context.require("read_json_safe") + self.resolver_bool_setting = context.require("resolver_bool_setting") + self.run_in_background_thread = context.require("run_in_background_thread") + self.search_local_matches_by_hash = context.require( "search_local_matches_by_hash" ) - self.extension = context.get("self") + self.extension = context.require("self") self.time = context.get("time") or time - self.to_bool = context.get("to_bool") - self.to_int = context.get("to_int") - self.web = context.get("web") - self.write_json_atomic = context.get("write_json_atomic") + self.to_bool = context.require("to_bool") + self.to_int = context.require("to_int") + self.web = context.require("web") + self.write_json_atomic = context.require("write_json_atomic") @property def hash_tracker(self): diff --git a/tests/test_route_context_contract.py b/tests/test_route_context_contract.py index 64cbd20..010c64a 100644 --- a/tests/test_route_context_contract.py +++ b/tests/test_route_context_contract.py @@ -2,6 +2,8 @@ import pytest from core.routes.context import RouteContext from core.services.directory_service import DirectoryService +from core.services.download_service import DownloadService +from core.services.hash_service import HashService from core.services.loaded_models_service import LoadedModelsService from core.services.metadata_service import MetadataService from core.services.scanner_service import ScannerService @@ -60,3 +62,19 @@ def test_loaded_models_service_requires_mandatory_route_dependencies(): def test_metadata_service_requires_mandatory_route_dependencies(): with pytest.raises(KeyError, match="Missing route dependency: asyncio"): MetadataService(RouteContext({})) + + +def test_hash_service_requires_mandatory_route_dependencies(): + with pytest.raises( + KeyError, + match="Missing route dependency: FileManagerError", + ): + HashService(RouteContext({})) + + +def test_download_service_requires_mandatory_route_dependencies(): + with pytest.raises( + KeyError, + match="Missing route dependency: Aria2InstallError", + ): + DownloadService(RouteContext({}))