serializer
beautyspot.serializer モジュールは、計算結果やオブジェクトツリーを永続化可能なバイナリ形式に変換するための、安全で拡張可能なシリアライゼーション機構を提供します。
beautyspot.serializer
MsgpackSerializer
Bases: SerializerProtocol, TypeRegistryProtocol
A secure and extensible serializer based on MessagePack.
Allows registering custom types via register().
Automatically handles packing/unpacking of custom type payloads.
Thread Safety (No-GIL Compatible):
This class is entirely thread-safe and avoids lock contention on the critical
path (during serialization/deserialization). It achieves this by using:
1. Copy-on-Write (CoW) for the shared type registry (_encoders, _decoders).
Registrations are rare, but reads happen per-node. CoW ensures readers always
see a consistent, immutable snapshot of the registry without locking.
2. Thread-Local Storage for the LRU subclass cache (_local.subclass_cache).
This eliminates lock contention completely when traversing deep object trees
concurrently across multiple threads.
Note
To prevent memory leaks in environments where types are generated dynamically (e.g., namedtuples, dynamic Pydantic models), subclass resolution results are cached using an LRU strategy with a configurable maximum size per thread.
Source code in src/beautyspot/serializer.py
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | |
プロトコル定義
beautyspot では、特定のクラスに依存しない疎結合な設計を維持するため、2つのプロトコルを定義しています。
1. SerializerProtocol
シリアライザーが実装すべき最小限のインターフェースです。
dumps(obj: Any) -> bytes: オブジェクトをバイナリに変換します。loads(data: bytes) -> Any: バイナリをオブジェクトに復元します。
2. TypeRegistryProtocol
カスタム型の登録を受け入れるためのインターフェースです。
register(type_class, code, encoder, decoder): 特定の型に対して、一意の識別コード(0-127)と変換ロジックを紐付けます。
MsgpackSerializer クラス
MessagePack をバックエンドに使用した、本ライブラリの標準シリアライザーです。
技術的特徴
- スレッドセーフ設計:
内部的な
threading.Lockにより、レジストリの更新、LRU キャッシュの操作、サブクラス解決が保護されています。これにより、バックグラウンドでの非同期保存中であっても安全に共有・利用が可能です。 - 知的なサブクラス解決: 登録されていない型が渡された場合、そのクラスの MRO (Method Resolution Order) をスキャンして、登録済みの基底クラスが存在するかを確認します。
- LRU キャッシュによる最適化: サブクラス解決の結果は内部でキャッシュされます。動的な型生成(namedtuples や Pydantic モデルなど)によるメモリ肥大化を防ぐため、最大サイズ(デフォルト 1024)を超えると古いエントリから自動的に破棄されます。
カスタム型の登録手順
from beautyspot.serializer import MsgpackSerializer
serializer = MsgpackSerializer()
# numpy.ndarray などを登録する例
serializer.register(
type_class=MyCustomClass,
code=10, # 0-127 の一意な数値
encoder=lambda obj: obj.to_dict(),
decoder=lambda data: MyCustomClass.from_dict(data)
)
例外ハンドリング
シリアライズ過程で発生する問題は、すべて beautyspot.exceptions.SerializationError として集約されます。
- エンコード失敗: カスタムエンコーダ内で例外が発生した場合、原因となった型名と共に報告されます。
- 非シリアライズ型: 登録されていない型をシリアライズしようとした場合、修正のヒント(
spot.registerの使用推奨)を含む詳細なメッセージが表示されます。 - データ破損: デコード時にデータが不整合な場合、キャッシュの破損(Corrupted)として扱われます。