Coverage for src / beautyspot / __init__.py: 89%
53 statements
« prev ^ index » next coverage.py v7.13.2, created at 2026-03-18 18:18 +0900
« prev ^ index » next coverage.py v7.13.2, created at 2026-03-18 18:18 +0900
1# src/beautyspot/__init__.py
3import logging
4import warnings
5from importlib.metadata import version, PackageNotFoundError
6from pathlib import Path
7from typing import Any, Optional, Callable
9from beautyspot.core import Spot as _Spot
10from beautyspot.cache import CacheManager as _CacheManager
12from beautyspot.types import (
13 SaveErrorContext,
14 PreExecuteContext,
15 CacheHitContext,
16 CacheMissContext,
17)
18from beautyspot.cachekey import KeyGen
19from beautyspot.lifecycle import LifecyclePolicy, Rule, Retention
20from beautyspot.limiter import Gcra, LimiterProtocol
21from beautyspot.content_types import ContentType
22from beautyspot.db import TaskDBBase, TaskDBCore, TaskDBMaintenable, SQLiteTaskDB
23from beautyspot.exceptions import (
24 BeautySpotError,
25 CacheCorruptedError,
26 SerializationError,
27 ConfigurationError,
28 ValidationError,
29 IncompatibleProviderError,
30)
31from beautyspot.storage import (
32 BlobStorageBase,
33 BlobStorageCore,
34 BlobStorageMaintenable,
35 LocalStorage,
36 StoragePolicyProtocol,
37 WarningOnlyPolicy,
38 ThresholdStoragePolicy,
39 AlwaysBlobPolicy,
40)
41from beautyspot.serializer import SerializerProtocol, MsgpackSerializer
42from beautyspot.hooks import HookBase, ThreadSafeHookBase
44try:
45 __version__ = version("beautyspot")
46except PackageNotFoundError:
47 __version__ = "0.0.0+unknown"
50_UNSET: Any = object()
53def _resolve_renamed(
54 new_val: Any,
55 old_val: Any,
56 new_name: str,
57 old_name: str,
58 default: Any,
59) -> Any:
60 """新旧パラメータ名を解決するヘルパー。旧名使用時は DeprecationWarning を発行する。"""
61 if new_val is not _UNSET and old_val is not _UNSET: 61 ↛ 62line 61 didn't jump to line 62 because the condition on line 61 was never true
62 raise IncompatibleProviderError(
63 f"Cannot specify both '{new_name}' and deprecated '{old_name}'."
64 )
65 if old_val is not _UNSET: 65 ↛ 66line 65 didn't jump to line 66 because the condition on line 65 was never true
66 warnings.warn(
67 f"'{old_name}' is deprecated, use '{new_name}' instead.",
68 DeprecationWarning,
69 stacklevel=3,
70 )
71 return old_val
72 if new_val is not _UNSET:
73 return new_val
74 return default
77def Spot(
78 name: str,
79 db: Optional[TaskDBMaintenable] = None,
80 serializer: Optional[SerializerProtocol] = None,
81 limiter: Optional[LimiterProtocol] = None,
82 storage_backend: Optional[BlobStorageMaintenable] = None,
83 storage_policy: Optional[StoragePolicyProtocol] = None,
84 cache: Optional[_CacheManager] = None,
85 # --- Configuration Options ---
86 lifecycle_policy: Optional[LifecyclePolicy] = None,
87 gc_probability: float = 0.0,
88 blob_warning_threshold: int = 1024 * 1024,
89 save_blob: bool = False,
90 tokens_per_minute: int = 10000,
91 save_sync: bool = True,
92 flush_timeout: float = 5.0,
93 flush_poll_interval: float = 0.5,
94 on_save_error: Optional[Callable[[BaseException, SaveErrorContext], None]] = None,
95 # --- Deprecated aliases (backward compat) ---
96 eviction_rate: Any = _UNSET,
97 tpm: Any = _UNSET,
98 drain_timeout: Any = _UNSET,
99 drain_poll_interval: Any = _UNSET,
100 on_background_error: Any = _UNSET,
101) -> _Spot:
102 """
103 Beautyspotのメインエントリポイント(Factory Function)。
104 依存関係の解決とデフォルト設定の適用を行います。
105 """
107 # 0. 非推奨パラメータの解決
108 effective_gc_prob: float = _resolve_renamed(
109 gc_probability if gc_probability != 0.0 else _UNSET,
110 eviction_rate, "gc_probability", "eviction_rate", 0.0,
111 )
112 effective_tpm: int = _resolve_renamed(
113 tokens_per_minute if tokens_per_minute != 10000 else _UNSET,
114 tpm, "tokens_per_minute", "tpm", 10000,
115 )
116 effective_flush_timeout: float = _resolve_renamed(
117 flush_timeout if flush_timeout != 5.0 else _UNSET,
118 drain_timeout, "flush_timeout", "drain_timeout", 5.0,
119 )
120 effective_flush_poll: float = _resolve_renamed(
121 flush_poll_interval if flush_poll_interval != 0.5 else _UNSET,
122 drain_poll_interval, "flush_poll_interval", "drain_poll_interval", 0.5,
123 )
124 effective_on_save_error = _resolve_renamed(
125 on_save_error if on_save_error is not None else _UNSET,
126 on_background_error, "on_save_error", "on_background_error", None,
127 )
129 # 1. デフォルトパス使用時のみワークスペースをセットアップ
130 _default_workspace = Path(".beautyspot")
132 # 2. コンポーネントの解決 (DI)
133 resolved_db = db or SQLiteTaskDB(_default_workspace / f"{name}.db")
134 resolved_ser = serializer or MsgpackSerializer()
135 resolved_stg = storage_backend or LocalStorage(_default_workspace / "blobs" / name)
136 resolved_limiter = limiter or Gcra(tokens_per_minute=effective_tpm)
138 # 3. Storage Policy の解決
139 resolved_policy: StoragePolicyProtocol
140 if storage_policy is not None:
141 resolved_policy = storage_policy
142 elif save_blob:
143 resolved_policy = AlwaysBlobPolicy()
144 else:
145 logger = logging.getLogger("beautyspot")
146 resolved_policy = WarningOnlyPolicy(
147 warning_threshold=blob_warning_threshold, logger=logger
148 )
150 # 4. CacheManager の組み立て (Composition)
151 resolved_cache = cache or _CacheManager(
152 db=resolved_db,
153 storage=resolved_stg,
154 serializer=resolved_ser,
155 storage_policy=resolved_policy,
156 lifecycle_policy=lifecycle_policy,
157 )
159 # 5. Core Spot の生成
160 # _owns_db: ファクトリ関数で内部的に DB を生成した場合のみ True。
161 # GC 時のファイナライザが DB シャットダウンするかを決定する。
162 # コンストラクタ引数で渡すことで、ファイナライザのキャプチャタイミング問題を防ぐ。
163 spot = _Spot(
164 name=name,
165 cache=resolved_cache,
166 limiter=resolved_limiter,
167 # その他のオプション
168 save_sync=save_sync,
169 gc_probability=effective_gc_prob,
170 flush_timeout=effective_flush_timeout,
171 flush_poll_interval=effective_flush_poll,
172 on_save_error=effective_on_save_error,
173 _owns_db=(db is None),
174 )
176 return spot
179# isinstance(spot, bs.SpotType) のための型エクスポート
180SpotType: type[_Spot] = _Spot
182__all__ = [
183 # --- Core ---
184 "Spot",
185 "SpotType",
186 "KeyGen",
187 "ContentType",
188 "SaveErrorContext",
189 # --- Exceptions ---
190 "BeautySpotError",
191 "CacheCorruptedError",
192 "SerializationError",
193 "ConfigurationError",
194 "ValidationError",
195 "IncompatibleProviderError",
196 # --- Protocols & Base Classes (for custom implementations) ---
197 "TaskDBCore",
198 "TaskDBMaintenable",
199 "TaskDBBase",
200 "BlobStorageCore",
201 "BlobStorageMaintenable",
202 "BlobStorageBase",
203 "SerializerProtocol",
204 "StoragePolicyProtocol",
205 "LimiterProtocol",
206 # --- Default Implementations ---
207 "SQLiteTaskDB",
208 "LocalStorage",
209 "MsgpackSerializer",
210 "Gcra",
211 "ThresholdStoragePolicy",
212 "WarningOnlyPolicy",
213 "AlwaysBlobPolicy",
214 # --- Lifecycle ---
215 "LifecyclePolicy",
216 "Rule",
217 "Retention",
218 # --- Hooks ---
219 "HookBase",
220 "ThreadSafeHookBase",
221 "PreExecuteContext",
222 "CacheHitContext",
223 "CacheMissContext",
224]