From 3e417c1ac51694bf7b3e4e45113063d81d6bfb92 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 16 Aug 2026 17:25:49 +0800 Subject: [PATCH] =?UTF-8?q?refactor(chain):=20=5Fmixins.py=20=E6=9B=B4?= =?UTF-8?q?=E5=90=8D=20=5Ftransfer.py=EF=BC=8C=E7=BB=9F=E4=B8=80=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=9F=9F=20mixin=20=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TransferChain 的 7 个功能域 mixin 集合文件按域更名 _transfer.py, 与 _recognition/_messaging/_interaction/_music 的单域命名约定对齐; 同步更新 transfer.py 导入、9 个测试文件 patch 目标与架构文档。 另含 chain 基类导入排序优化与未使用导入清理(copy/datetime 已随 dispatch 方法迁入 _messaging.py)。 --- app/chain/__init__.py | 16 +++--- app/chain/{_mixins.py => _transfer.py} | 0 app/chain/transfer.py | 2 +- docs/rules/05-architecture.md | 2 +- tests/test_episode_format_helper.py | 4 +- tests/test_manual_transfer_history.py | 8 +-- tests/test_music_transfer.py | 6 +-- tests/test_transfer_custom_words.py | 2 +- tests/test_transfer_failed_retry_buttons.py | 4 +- tests/test_transfer_mounted_disk_cleanup.py | 10 ++-- tests/test_transfer_movie_collection.py | 12 ++--- tests/test_transfer_sync_extra_files.py | 58 ++++++++++----------- tests/test_transfer_tmdb_category.py | 4 +- 13 files changed, 63 insertions(+), 65 deletions(-) rename app/chain/{_mixins.py => _transfer.py} (100%) diff --git a/app/chain/__init__.py b/app/chain/__init__.py index b6f9b4749..6c6498213 100644 --- a/app/chain/__init__.py +++ b/app/chain/__init__.py @@ -1,25 +1,26 @@ from __future__ import annotations -import copy import inspect import pickle import traceback from abc import ABCMeta from collections.abc import Callable -from datetime import datetime from pathlib import Path from typing import Optional, Any, Tuple, List, Set, Union, Dict from fastapi.concurrency import run_in_threadpool -from app.runtime.cache import FileCache, AsyncFileCache +from app.application.messaging.message import MessageHelper, MessageQueueManager +from app.chain._messaging import MessageProcessingMixin, NotificationMixin +from app.chain._recognition import RecognitionMixin +from app.db.oper.message import MessageOper from app.domain.context import Context, MediaInfo, SubtitleInfo, TorrentInfo -from app.runtime.events import EventManager from app.domain.meta.metabase import MetaBase +from app.foundation.reflection import ObjectUtils +from app.runtime.cache import FileCache, AsyncFileCache +from app.runtime.events import EventManager from app.runtime.extensions.module_manager import ModuleManager from app.runtime.extensions.plugin_manager import PluginManager -from app.db.oper.message import MessageOper -from app.application.messaging.message import MessageHelper, MessageQueueManager from app.runtime.log import logger from app.schemas import ( RateLimitExceededException, @@ -41,9 +42,6 @@ from app.schemas.types import ( MediaImageType, EventType, ) -from app.foundation.reflection import ObjectUtils -from app.chain._messaging import MessageProcessingMixin, NotificationMixin -from app.chain._recognition import RecognitionMixin class ChainBase(RecognitionMixin, MessageProcessingMixin, NotificationMixin, diff --git a/app/chain/_mixins.py b/app/chain/_transfer.py similarity index 100% rename from app/chain/_mixins.py rename to app/chain/_transfer.py diff --git a/app/chain/transfer.py b/app/chain/transfer.py index 2c3c86b76..eefbc8193 100755 --- a/app/chain/transfer.py +++ b/app/chain/transfer.py @@ -57,7 +57,7 @@ from app.schemas.types import ( from app.runtime.reload import ConfigReloadMixin from app.application.transfer import (FailedRetryScheduler, JobManager, TransferQueue, TransferTask, job_lock) -from app.chain._mixins import (EpisodeFormatMixin, FailedRetryMixin, +from app.chain._transfer import (EpisodeFormatMixin, FailedRetryMixin, FileFilterMixin, FileKeyMixin, HistoryMatchMixin, ManualHistoryMixin, ScrapeBatchMixin) diff --git a/docs/rules/05-architecture.md b/docs/rules/05-architecture.md index 6e3712d71..637f9ad98 100644 --- a/docs/rules/05-architecture.md +++ b/docs/rules/05-architecture.md @@ -219,7 +219,7 @@ Underscore-prefixed files in `app/chain/` are feature-domain mixins for slash-command delegation for `remote_list` / `parse_callback` / `handle_callback_interaction` / `handle_text_interaction`), `_music.py` (`MusicSubscribeMixin`, the music single/album subscribe domain mixed into -`SubscribeChain`) and `_mixins.py` (TransferChain feature mixins). A concrete chain that exposes slash-command +`SubscribeChain`) and `_transfer.py` (TransferChain feature mixins). A concrete chain that exposes slash-command interaction inherits `InteractionChainMixin`, injects its handler class via `_interaction_handler_type` and implements only `_interaction_handler`; it must not re-export application-layer interaction managers. diff --git a/tests/test_episode_format_helper.py b/tests/test_episode_format_helper.py index 3ad3d6764..4bb22be05 100644 --- a/tests/test_episode_format_helper.py +++ b/tests/test_episode_format_helper.py @@ -722,7 +722,7 @@ def test_transfer_chain_recommend_episode_format_passes_helper_data(monkeypatch) lambda item: [sample], ) monkeypatch.setattr( - "app.chain._mixins.EpisodeFormatRuleHelper.recommend", + "app.chain._transfer.EpisodeFormatRuleHelper.recommend", lambda self, rules, sample_files: (True, "", helper_data), ) @@ -777,7 +777,7 @@ def test_transfer_chain_recommend_episode_format_uses_selected_fileitems(monkeyp lambda: [], ) monkeypatch.setattr( - "app.chain._mixins.EpisodeFormatRuleHelper.recommend", + "app.chain._transfer.EpisodeFormatRuleHelper.recommend", lambda self, rules, sample_files: (True, "", { **helper_data, "received_samples": [item.name for item in sample_files], diff --git a/tests/test_manual_transfer_history.py b/tests/test_manual_transfer_history.py index 2190497a6..12665826e 100644 --- a/tests/test_manual_transfer_history.py +++ b/tests/test_manual_transfer_history.py @@ -62,7 +62,7 @@ def _patch_transfer_planning(monkeypatch, chain, fileitem, history, planned, del "app.chain.transfer.TransferHistoryOper", lambda: history_oper, ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: history_oper) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: history_oper) monkeypatch.setattr( "app.chain.transfer.DownloadHistoryOper", lambda: SimpleNamespace( @@ -72,7 +72,7 @@ def _patch_transfer_planning(monkeypatch, chain, fileitem, history, planned, del get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -82,7 +82,7 @@ def _patch_transfer_planning(monkeypatch, chain, fileitem, history, planned, del "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr( "app.chain.transfer.StorageChain", lambda: SimpleNamespace( @@ -93,7 +93,7 @@ def _patch_transfer_planning(monkeypatch, chain, fileitem, history, planned, del or True, ), ) - monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace( exists=lambda current_fileitem: True, delete_media_file=lambda current_fileitem: deleted.append( ("target", current_fileitem.path) diff --git a/tests/test_music_transfer.py b/tests/test_music_transfer.py index 4e1d7935b..d0825c375 100644 --- a/tests/test_music_transfer.py +++ b/tests/test_music_transfer.py @@ -68,7 +68,7 @@ def test_music_retry_restores_history_entity_namespace(tmp_path, monkeypatch): title="叶惠美", ) monkeypatch.setattr("app.chain.transfer.MediaChain", lambda: media_chain) - monkeypatch.setattr("app.chain._mixins.MediaChain", lambda: media_chain) + monkeypatch.setattr("app.chain._transfer.MediaChain", lambda: media_chain) result = TransferChain()._recognize_music_retry_media( history, @@ -538,7 +538,7 @@ def test_success_file_aggregation_is_isolated_between_music_jobs_in_same_directo "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace()) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace()) monkeypatch.setattr( "app.chain.transfer.add_transfer_success", lambda **kwargs: SimpleNamespace(id=1), @@ -784,7 +784,7 @@ def test_downloader_process_forwards_music_history_type(tmp_path, monkeypatch): ), ) monkeypatch.setattr("app.chain.transfer.MediaChain", lambda: media_chain) - monkeypatch.setattr("app.chain._mixins.MediaChain", lambda: media_chain) + monkeypatch.setattr("app.chain._transfer.MediaChain", lambda: media_chain) monkeypatch.setattr(chain, "do_transfer", Mock(return_value=(True, ""))) monkeypatch.setattr(chain, "run_module", run_module) diff --git a/tests/test_transfer_custom_words.py b/tests/test_transfer_custom_words.py index 30563b137..531ae89a3 100644 --- a/tests/test_transfer_custom_words.py +++ b/tests/test_transfer_custom_words.py @@ -7,7 +7,7 @@ """ from types import SimpleNamespace -import app.chain._mixins as mixins_module +import app.chain._transfer as mixins_module from app.chain.transfer import TransferChain diff --git a/tests/test_transfer_failed_retry_buttons.py b/tests/test_transfer_failed_retry_buttons.py index b096b8891..223d9e4e5 100644 --- a/tests/test_transfer_failed_retry_buttons.py +++ b/tests/test_transfer_failed_retry_buttons.py @@ -150,7 +150,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase): "app.chain.transfer.TransferHistoryOper" ) as history_oper_cls, patch( # mixin 中按自身模块命名空间解析 TransferHistoryOper,需同步镜像 - "app.chain._mixins.TransferHistoryOper" + "app.chain._transfer.TransferHistoryOper" ) as mixins_history_oper_cls, patch( "app.chain.transfer.asyncio.run_coroutine_threadsafe", side_effect=_close_pending_coro, @@ -224,7 +224,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase): "app.chain.transfer.TransferHistoryOper" ) as history_oper_cls, patch( # mixin 中按自身模块命名空间解析 TransferHistoryOper,需同步镜像 - "app.chain._mixins.TransferHistoryOper" + "app.chain._transfer.TransferHistoryOper" ) as mixins_history_oper_cls, patch( "app.application.agent._agent_manager.run_background_prompt", side_effect=fake_run_background_prompt, diff --git a/tests/test_transfer_mounted_disk_cleanup.py b/tests/test_transfer_mounted_disk_cleanup.py index 1c4732c31..ad11c0629 100644 --- a/tests/test_transfer_mounted_disk_cleanup.py +++ b/tests/test_transfer_mounted_disk_cleanup.py @@ -31,7 +31,7 @@ def test_enabled_cleanup_skips_filesystem_detection(): 开关开启时应保持旧行为,且不产生额外文件系统检测。 """ with patch( - "app.chain._mixins.SystemUtils.is_network_filesystem" + "app.chain._transfer.SystemUtils.is_network_filesystem" ) as is_network_filesystem: should_delete = ( TransferChain._should_delete_empty_source_directories( @@ -50,7 +50,7 @@ def test_disabled_cleanup_keeps_mounted_local_source_directories(): 开关关闭时应保留网络或 FUSE 挂载的本地源目录。 """ with patch( - "app.chain._mixins.SystemUtils.is_network_filesystem", + "app.chain._transfer.SystemUtils.is_network_filesystem", return_value=True, ) as is_network_filesystem: should_delete = ( @@ -72,7 +72,7 @@ def test_disabled_cleanup_still_deletes_ordinary_local_source_directories(): 开关关闭时普通本地文件系统仍应删除空目录。 """ with patch( - "app.chain._mixins.SystemUtils.is_network_filesystem", + "app.chain._transfer.SystemUtils.is_network_filesystem", return_value=False, ): should_delete = ( @@ -91,7 +91,7 @@ def test_disabled_cleanup_does_not_change_remote_storage_cleanup(): 开关关闭时非本地存储仍应执行原有空目录清理。 """ with patch( - "app.chain._mixins.SystemUtils.is_network_filesystem" + "app.chain._transfer.SystemUtils.is_network_filesystem" ) as is_network_filesystem: should_delete = ( TransferChain._should_delete_empty_source_directories( @@ -111,7 +111,7 @@ def test_mounted_filesystem_detection_is_cached_by_source_directory(): """ mounted_filesystem_cache = {} with patch( - "app.chain._mixins.SystemUtils.is_network_filesystem", + "app.chain._transfer.SystemUtils.is_network_filesystem", return_value=True, ) as is_network_filesystem: for _ in range(2): diff --git a/tests/test_transfer_movie_collection.py b/tests/test_transfer_movie_collection.py index 3d00ed21b..b420cbfdc 100644 --- a/tests/test_transfer_movie_collection.py +++ b/tests/test_transfer_movie_collection.py @@ -107,7 +107,7 @@ def test_conflicting_download_history_recognizes_movie_by_file_meta(monkeypatch) "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_type_tmdbid=lambda **kwargs: None), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_type_tmdbid=lambda **kwargs: None)) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_type_tmdbid=lambda **kwargs: None)) monkeypatch.setattr( "app.chain.transfer.MediaChain", lambda: SimpleNamespace( @@ -118,7 +118,7 @@ def test_conflicting_download_history_recognizes_movie_by_file_meta(monkeypatch) supplement_tmdb_info=lambda media, _meta: media, ), ) - monkeypatch.setattr("app.chain._mixins.MediaChain", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.MediaChain", lambda: SimpleNamespace( recognize_media=lambda **kwargs: pytest.fail("不应按合集历史 ID 识别"), recognize_by_meta=lambda meta, obtain_images: ( recognized_meta.append(meta) or fallback_media @@ -190,16 +190,16 @@ def test_movie_collection_conflict_only_drops_automatic_media( "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr("app.chain.transfer.DownloadHistoryOper", lambda: history_oper) - monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: history_oper) + monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: history_oper) monkeypatch.setattr( "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain.transfer.StorageChain", lambda: SimpleNamespace()) - monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace()) + monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace()) monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda *args, **kwargs: file_meta) # 用真 MediaInfo 而非 SimpleNamespace:它会被装进 TransferTask.mediainfo, diff --git a/tests/test_transfer_sync_extra_files.py b/tests/test_transfer_sync_extra_files.py index 3b02a50e6..7e33d8269 100644 --- a/tests/test_transfer_sync_extra_files.py +++ b/tests/test_transfer_sync_extra_files.py @@ -142,7 +142,7 @@ def test_sync_extra_subtitle_inherits_matching_video_episode(monkeypatch): "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( "app.chain.transfer.DownloadHistoryOper", lambda: SimpleNamespace( @@ -152,7 +152,7 @@ def test_sync_extra_subtitle_inherits_matching_video_episode(monkeypatch): get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -162,7 +162,7 @@ def test_sync_extra_subtitle_inherits_matching_video_episode(monkeypatch): "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain.transfer.MetaInfoPath", fake_meta_info_path) state, errmsg = TransferChain.do_transfer( @@ -239,7 +239,7 @@ def test_single_subtitle_transfer_reuses_same_name_video_episode(monkeypatch): "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( "app.chain.transfer.DownloadHistoryOper", lambda: SimpleNamespace( @@ -249,7 +249,7 @@ def test_single_subtitle_transfer_reuses_same_name_video_episode(monkeypatch): get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -259,7 +259,7 @@ def test_single_subtitle_transfer_reuses_same_name_video_episode(monkeypatch): "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr( "app.chain.transfer.StorageChain", lambda: SimpleNamespace( @@ -270,7 +270,7 @@ def test_single_subtitle_transfer_reuses_same_name_video_episode(monkeypatch): ], ), ) - monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace( get_parent_item=lambda fileitem: parent_fileitem, list_files=lambda fileitem, recursion=False: [ main_fileitem, @@ -354,7 +354,7 @@ def test_single_video_transfer_lists_parent_once_for_same_name_extra(monkeypatch "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( "app.chain.transfer.DownloadHistoryOper", lambda: SimpleNamespace( @@ -364,7 +364,7 @@ def test_single_video_transfer_lists_parent_once_for_same_name_extra(monkeypatch get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -374,7 +374,7 @@ def test_single_video_transfer_lists_parent_once_for_same_name_extra(monkeypatch "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr( "app.chain.transfer.StorageChain", lambda: SimpleNamespace( @@ -382,7 +382,7 @@ def test_single_video_transfer_lists_parent_once_for_same_name_extra(monkeypatch list_files=fake_list_files, ), ) - monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace( get_parent_item=lambda fileitem: parent_fileitem, list_files=fake_list_files, )) @@ -452,7 +452,7 @@ def test_episode_format_filters_extra_files_before_sync_planning(monkeypatch): "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( "app.chain.transfer.DownloadHistoryOper", lambda: SimpleNamespace( @@ -462,7 +462,7 @@ def test_episode_format_filters_extra_files_before_sync_planning(monkeypatch): get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -472,7 +472,7 @@ def test_episode_format_filters_extra_files_before_sync_planning(monkeypatch): "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda path, custom_words=None, **kwargs: FakeMeta(1)) state, errmsg = TransferChain.do_transfer( @@ -539,7 +539,7 @@ def test_episode_format_keeps_matching_extra_files_following_main(monkeypatch): "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( "app.chain.transfer.DownloadHistoryOper", lambda: SimpleNamespace( @@ -549,7 +549,7 @@ def test_episode_format_keeps_matching_extra_files_following_main(monkeypatch): get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -559,7 +559,7 @@ def test_episode_format_keeps_matching_extra_files_following_main(monkeypatch): "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda path, custom_words=None, **kwargs: FakeMeta(1)) state, errmsg = TransferChain.do_transfer( @@ -635,7 +635,7 @@ def test_single_matching_subtitle_uses_unmatched_video_only_as_context(monkeypat "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( "app.chain.transfer.DownloadHistoryOper", lambda: SimpleNamespace( @@ -645,7 +645,7 @@ def test_single_matching_subtitle_uses_unmatched_video_only_as_context(monkeypat get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -655,7 +655,7 @@ def test_single_matching_subtitle_uses_unmatched_video_only_as_context(monkeypat "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr( "app.chain.transfer.StorageChain", lambda: SimpleNamespace( @@ -666,7 +666,7 @@ def test_single_matching_subtitle_uses_unmatched_video_only_as_context(monkeypat ], ), ) - monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace( get_parent_item=lambda fileitem: parent_fileitem, list_files=lambda fileitem, recursion=False: [ main_fileitem, @@ -731,7 +731,7 @@ def test_cleanup_dest_fileitem_is_deleted_only_after_allowed_items_exist(monkeyp "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( "app.chain.transfer.DownloadHistoryOper", lambda: SimpleNamespace( @@ -741,7 +741,7 @@ def test_cleanup_dest_fileitem_is_deleted_only_after_allowed_items_exist(monkeyp get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._mixins.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -751,14 +751,14 @@ def test_cleanup_dest_fileitem_is_deleted_only_after_allowed_items_exist(monkeyp "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr( "app.chain.transfer.StorageChain", lambda: SimpleNamespace( delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True, ), ) - monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace( delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True, )) monkeypatch.setattr("app.chain.transfer.MetaInfoPath", lambda path, custom_words=None, **kwargs: FakeMeta(1)) @@ -799,14 +799,14 @@ def test_cleanup_dest_fileitem_is_kept_when_episode_format_matches_nothing(monke "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) monkeypatch.setattr( "app.chain.transfer.StorageChain", lambda: SimpleNamespace( delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True, ), ) - monkeypatch.setattr("app.chain._mixins.StorageChain", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.StorageChain", lambda: SimpleNamespace( delete_media_file=lambda fileitem: delete_calls.append(fileitem.path) or True, )) @@ -841,7 +841,7 @@ def test_episode_format_matched_but_filtered_by_size_returns_failure(monkeypatch "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) state, errmsg = TransferChain.do_transfer( chain, @@ -882,7 +882,7 @@ def test_candidate_collection_checks_continue_callback(monkeypatch): "app.chain.transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None), ) - monkeypatch.setattr("app.chain._mixins.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) + monkeypatch.setattr("app.chain._transfer.SystemConfigOper", lambda: SimpleNamespace(get=lambda key: None)) state, errmsg = TransferChain.do_transfer( chain, diff --git a/tests/test_transfer_tmdb_category.py b/tests/test_transfer_tmdb_category.py index bae4ed2ed..6a7818951 100644 --- a/tests/test_transfer_tmdb_category.py +++ b/tests/test_transfer_tmdb_category.py @@ -79,14 +79,14 @@ def test_transfer_stops_when_automatic_category_has_no_tmdb_result(monkeypatch) "app.chain.transfer.TransferHistoryOper", lambda: SimpleNamespace(), ) - monkeypatch.setattr("app.chain._mixins.TransferHistoryOper", lambda: SimpleNamespace()) + monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace()) monkeypatch.setattr( "app.chain.transfer.MediaChain", lambda: SimpleNamespace( supplement_tmdb_info=lambda media, _meta: media, ), ) - monkeypatch.setattr("app.chain._mixins.MediaChain", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.MediaChain", lambda: SimpleNamespace( supplement_tmdb_info=lambda media, _meta: media, )) task = TransferTask(