Coverage for src/beautyspot/__init__.py: 96%

1# src/beautyspot/__init__.py

2

3 import logging

4 from importlib.metadata import version, PackageNotFoundError

5 from pathlib import Path

6 from typing import Optional, Callable

7

8 from beautyspot.core import Spot as _Spot

9 from beautyspot.cache import CacheManager as _CacheManager

10

11 from beautyspot.types import (

12 SaveErrorContext,

13 PreExecuteContext,

14 CacheHitContext,

15 CacheMissContext,

16)

17 from beautyspot.cachekey import KeyGen

18 from beautyspot.lifecycle import LifecyclePolicy, Rule, Retention

19 from beautyspot.limiter import TokenBucket, LimiterProtocol

20 from beautyspot.content_types import ContentType

21 from beautyspot.db import TaskDBBase, TaskDBCore, TaskDBMaintenable, SQLiteTaskDB

22 from beautyspot.exceptions import (

23 BeautySpotError,

24 CacheCorruptedError,

25 SerializationError,

26 ConfigurationError,

27 ValidationError,

28 IncompatibleProviderError,

29)

30 from beautyspot.storage import (

31 BlobStorageBase,

32 BlobStorageCore,

33 BlobStorageMaintenable,

34 LocalStorage,

35 StoragePolicyProtocol,

36 WarningOnlyPolicy,

37 ThresholdStoragePolicy,

38 AlwaysBlobPolicy,

39)

40 from beautyspot.serializer import SerializerProtocol, MsgpackSerializer

41 from beautyspot.hooks import HookBase, ThreadSafeHookBase

42

43 try:

44 __version__ = version("beautyspot")

45 except PackageNotFoundError:

46 __version__ = "0.0.0+unknown"

47

48

49 def Spot(

50 name: str,

51 db: Optional[TaskDBMaintenable] = None,

52 serializer: Optional[SerializerProtocol] = None,

53 limiter: Optional[LimiterProtocol] = None,

54 storage_backend: Optional[BlobStorageMaintenable] = None,

55 storage_policy: Optional[StoragePolicyProtocol] = None,

56 cache: Optional[_CacheManager] = None,

57 # --- Configuration Options ---

58 lifecycle_policy: Optional[LifecyclePolicy] = None,

59 eviction_rate: float = 0.0,

60 blob_warning_threshold: int = 1024 * 1024,

61 save_blob: bool = False,

62 tpm: int = 10000,

63 save_sync: bool = True,

64 drain_timeout: float = 5.0,

65 drain_poll_interval: float = 0.5,

66 on_background_error: Optional[Callable[[BaseException, SaveErrorContext], None]] = None,

67) -> _Spot:

68 """

69 Beautyspotのメインエントリポイント（Factory Function）。

70 依存関係の解決とデフォルト設定の適用を行います。

71 """

72

73 # 0. デフォルトパス使用時のみワークスペースをセットアップ

74 _default_workspace = Path(".beautyspot")

75

76 # 1. コンポーネントの解決 (DI)

77 resolved_db = db or SQLiteTaskDB(_default_workspace / f"{name}.db")

78 resolved_ser = serializer or MsgpackSerializer()

79 resolved_stg = storage_backend or LocalStorage(_default_workspace / "blobs" / name)

80 resolved_limiter = limiter or TokenBucket(tokens_per_minute=tpm)

81

82 # 2. Storage Policy の解決

83 resolved_policy: StoragePolicyProtocol

84 if storage_policy is not None:

85 resolved_policy = storage_policy

86 elif save_blob:

87 resolved_policy = AlwaysBlobPolicy()

88 else:

89 logger = logging.getLogger("beautyspot")

90 resolved_policy = WarningOnlyPolicy(

91 warning_threshold=blob_warning_threshold, logger=logger

92 )

93

94 # 3. CacheManager の組み立て (Composition)

95 resolved_cache = cache or _CacheManager(

96 db=resolved_db,

97 storage=resolved_stg,

98 serializer=resolved_ser,

99 storage_policy=resolved_policy,

100 lifecycle_policy=lifecycle_policy,

101 )

102

103 # 4. Core Spot の生成

104 spot = _Spot(

105 name=name,

106 cache=resolved_cache,

107 limiter=resolved_limiter,

108 # その他のオプション

109 save_sync=save_sync,

110 eviction_rate=eviction_rate,

111 drain_timeout=drain_timeout,

112 drain_poll_interval=drain_poll_interval,

113 on_background_error=on_background_error,

114 )

115

116 # ファクトリ関数で内部的に DB を生成した場合のみ、GC 時にシャットダウンする。

117 # ユーザーが明示的に db= を渡した場合はライフサイクルを呼び出し元に委ねる。

118 if db is None:

119 spot._owns_db = True

120

121 return spot

122

123

124# isinstance(spot, bs.SpotType) のための型エクスポート

125 SpotType: type[_Spot] = _Spot

126

127 __all__ = [

128 # --- Core ---

129 "Spot",

130 "SpotType",

131 "KeyGen",

132 "ContentType",

133 "SaveErrorContext",

134 # --- Exceptions ---

135 "BeautySpotError",

136 "CacheCorruptedError",

137 "SerializationError",

138 "ConfigurationError",

139 "ValidationError",

140 "IncompatibleProviderError",

141 # --- Protocols & Base Classes (for custom implementations) ---

142 "TaskDBCore",

143 "TaskDBMaintenable",

144 "TaskDBBase",

145 "BlobStorageCore",

146 "BlobStorageMaintenable",

147 "BlobStorageBase",

148 "SerializerProtocol",

149 "StoragePolicyProtocol",

150 "LimiterProtocol",

151 # --- Default Implementations ---

152 "SQLiteTaskDB",

153 "LocalStorage",

154 "MsgpackSerializer",

155 "TokenBucket",

156 "ThresholdStoragePolicy",

157 "WarningOnlyPolicy",

158 "AlwaysBlobPolicy",

159 # --- Lifecycle ---

160 "LifecyclePolicy",

161 "Rule",

162 "Retention",

163 # --- Hooks ---

164 "HookBase",

165 "ThreadSafeHookBase",

166 "PreExecuteContext",

167 "CacheHitContext",

168 "CacheMissContext",

169]