checkpointer 2.12.0__tar.gz → 2.14.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. checkpointer-2.14.0/PKG-INFO +243 -0
  2. checkpointer-2.14.0/README.md +227 -0
  3. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/__init__.py +3 -2
  4. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/checkpoint.py +81 -73
  5. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/fn_ident.py +45 -43
  6. checkpointer-2.14.0/checkpointer/fn_string.py +75 -0
  7. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/object_hash.py +31 -19
  8. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/storages/memory_storage.py +2 -2
  9. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/storages/pickle_storage.py +3 -3
  10. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/storages/storage.py +19 -5
  11. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/utils.py +16 -17
  12. {checkpointer-2.12.0 → checkpointer-2.14.0}/pyproject.toml +6 -8
  13. {checkpointer-2.12.0 → checkpointer-2.14.0}/uv.lock +32 -22
  14. checkpointer-2.12.0/PKG-INFO +0 -236
  15. checkpointer-2.12.0/README.md +0 -220
  16. {checkpointer-2.12.0 → checkpointer-2.14.0}/.gitignore +0 -0
  17. {checkpointer-2.12.0 → checkpointer-2.14.0}/.python-version +0 -0
  18. {checkpointer-2.12.0 → checkpointer-2.14.0}/ATTRIBUTION.md +0 -0
  19. {checkpointer-2.12.0 → checkpointer-2.14.0}/LICENSE +0 -0
  20. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/import_mappings.py +0 -0
  21. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/print_checkpoint.py +0 -0
  22. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/storages/__init__.py +0 -0
  23. {checkpointer-2.12.0 → checkpointer-2.14.0}/checkpointer/types.py +0 -0
@@ -0,0 +1,243 @@
1
+ Metadata-Version: 2.4
2
+ Name: checkpointer
3
+ Version: 2.14.0
4
+ Summary: checkpointer adds code-aware caching to Python functions, maintaining correctness and speeding up execution as your code changes.
5
+ Project-URL: Repository, https://github.com/Reddan/checkpointer.git
6
+ Author: Hampus Hallman
7
+ License-Expression: MIT
8
+ License-File: ATTRIBUTION.md
9
+ License-File: LICENSE
10
+ Keywords: async,cache,caching,data analysis,data processing,fast,hashing,invalidation,memoization,optimization,performance,workflow
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+
17
+ # checkpointer · [![License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/Reddan/checkpointer/blob/master/LICENSE) [![pypi](https://img.shields.io/pypi/v/checkpointer)](https://pypi.org/project/checkpointer/) [![pypi](https://img.shields.io/pypi/pyversions/checkpointer)](https://pypi.org/project/checkpointer/)
18
+
19
+ `checkpointer` is a Python library offering a decorator-based API for memoizing (caching) function results with code-aware cache invalidation. It works with sync and async functions, supports multiple storage backends, and invalidates caches automatically when your code or dependencies change - helping you maintain correctness, speed up execution, and smooth out your workflows by skipping redundant, costly operations.
20
+
21
+ ## 📦 Installation
22
+
23
+ ```bash
24
+ pip install checkpointer
25
+ ```
26
+
27
+ ## 🚀 Quick Start
28
+
29
+ Apply the `@checkpoint` decorator to any function:
30
+
31
+ ```python
32
+ from checkpointer import checkpoint
33
+
34
+ @checkpoint
35
+ def expensive_function(x: int) -> int:
36
+ print("Computing...")
37
+ return x ** 2
38
+
39
+ result = expensive_function(4) # Computes and stores the result
40
+ result = expensive_function(4) # Loads from the cache
41
+ ```
42
+
43
+ ## 🧠 How It Works
44
+
45
+ When you decorate a function with `@checkpoint` and call it, `checkpointer` computes a unique identifier that represents that specific call. This identifier is based on:
46
+
47
+ * The function's source code and all its user-defined dependencies,
48
+ * Global variables used by the function (if capturing is enabled or explicitly annotated),
49
+ * The actual arguments passed to the function.
50
+
51
+ `checkpointer` then looks up this identifier in its cache. If a valid cached result exists, it returns that immediately. Otherwise, it runs the original function, stores the result, and returns it.
52
+
53
+ `checkpointer` is designed to be flexible through features like:
54
+
55
+ * **Support for decorated methods**, correctly caching results bound to instances.
56
+ * **Support for decorated async functions**, compatible with any async runtime.
57
+ * **Robust hashing**, covering complex Python objects and large **NumPy**/**PyTorch** arrays via its internal `ObjectHash`.
58
+ * **Targeted hashing**, allowing you to optimize how arguments and captured variables are hashed.
59
+ * **Multi-layered caching**, letting you stack decorators for layered caching strategies without losing cache consistency.
60
+
61
+ ### 🚨 What Causes Cache Invalidation?
62
+
63
+ To ensure cache correctness, `checkpointer` tracks two types of hashes:
64
+
65
+ #### 1. Function Identity Hash (Computed Once Per Function)
66
+
67
+ This hash represents the decorated function itself and is computed once (usually on first invocation). It covers:
68
+
69
+ * **Function Code and Signature:**\
70
+ The actual logic and parameters of the function are hashed - but *not* parameter type annotations or formatting details like whitespace, newlines, comments, or trailing commas, which do **not** trigger invalidation.
71
+
72
+ * **Dependencies:**\
73
+ All user-defined functions and methods that the decorated function calls or relies on, including indirect dependencies, are included recursively. Dependencies are identified by:
74
+ * Inspecting the function's global scope for referenced functions and objects.
75
+ * Inferring from the function's argument type annotations.
76
+ * Analyzing object constructions and method calls to identify classes and methods used.
77
+
78
+ * **Exclusions:**\
79
+ Changes elsewhere in the module unrelated to the function or its dependencies do **not** cause invalidation.
80
+
81
+ #### 2. Call Hash (Computed on Every Function Call)
82
+
83
+ Every function call produces a call hash, combining:
84
+
85
+ * **Passed Arguments:**\
86
+ Includes positional and keyword arguments, combined with default values. Changing defaults alone doesn't necessarily trigger invalidation unless it affects actual call values.
87
+
88
+ * **Captured Global Variables:**\
89
+ When `capture=True` or explicit capture annotations are used, `checkpointer` includes referenced global variables in the call hash. Variables annotated with `CaptureMe` are hashed on every call, causing immediate cache invalidation if they change. Variables annotated with `CaptureMeOnce` are hashed only once per Python session, improving performance by avoiding repeated hashing.
90
+
91
+ * **Custom Argument Hashing:**\
92
+ Using `HashBy` annotations, arguments or captured variables can be transformed before hashing (e.g., sorting lists to ignore order), allowing more precise or efficient call hashes.
93
+
94
+ ## 💡 Usage
95
+
96
+ Once a function is decorated with `@checkpoint`, you can interact with its caching behavior using the following methods:
97
+
98
+ * **`expensive_function(...)`**:\
99
+ Call the function normally. This will compute and cache the result or load it from cache.
100
+
101
+ * **`expensive_function.rerun(...)`**:\
102
+ Force the original function to execute and overwrite any existing cached result.
103
+
104
+ * **`expensive_function.fn(...)`**:\
105
+ Call the undecorated function directly, bypassing the cache (useful in recursion to prevent caching intermediate steps).
106
+
107
+ * **`expensive_function.get(...)`**:\
108
+ Retrieve the cached result without executing the function. Raises `CheckpointError` if no valid cache exists.
109
+
110
+ * **`expensive_function.exists(...)`**:\
111
+ Check if a cached result exists without computing or loading it.
112
+
113
+ * **`expensive_function.delete(...)`**:\
114
+ Remove the cached entry for given arguments.
115
+
116
+ * **`expensive_function.reinit(recursive: bool = True)`**:\
117
+ Recalculate the function identity hash and recapture `CaptureMeOnce` variables, updating the cached function state within the same Python session.
118
+
119
+ ## ⚙️ Configuration & Customization
120
+
121
+ The `@checkpoint` decorator accepts the following parameters:
122
+
123
+ * **`storage`** (Type: `str` or `checkpointer.Storage`, Default: `"pickle"`)\
124
+ Storage backend to use: `"pickle"` (disk-based, persistent), `"memory"` (in-memory, non-persistent), or a custom `Storage` class.
125
+
126
+ * **`directory`** (Type: `str` or `pathlib.Path` or `None`, Default: `~/.cache/checkpoints`)\
127
+ Base directory for disk-based checkpoints (only for `"pickle"` storage).
128
+
129
+ * **`capture`** (Type: `bool`, Default: `False`)\
130
+ If `True`, includes global variables referenced by the function in call hashes (except those excluded via `NoHash`).
131
+
132
+ * **`expiry`** (Type: `Callable[[datetime.datetime], bool]` or `datetime.timedelta`, Default: `None`)\
133
+ A custom callable that receives the `datetime` timestamp of a cached result. It should return `True` if the cached result is considered expired and needs recomputation, or `False` otherwise.
134
+
135
+ * **`fn_hash_from`** (Type: `Any`, Default: `None`)\
136
+ Override the computed function identity hash with any hashable object you provide (e.g., version strings, config IDs). This gives you explicit control over the function's version and when its cache should be invalidated.
137
+
138
+ * **`when`** (Type: `bool`, Default: `True`)\
139
+ Enable or disable checkpointing dynamically, useful for environment-based toggling.
140
+
141
+ * **`verbosity`** (Type: `int` (`0`, `1`, or `2`), Default: `1`)\
142
+ Controls the level of logging output from `checkpointer`.
143
+ * `0`: No output.
144
+ * `1`: Shows when functions are computed and cached.
145
+ * `2`: Also shows when cached results are remembered (loaded from cache).
146
+
147
+ ## 🔬 Customize Argument Hashing
148
+
149
+ You can customize how arguments are hashed without modifying the actual argument values to improve cache hit rates or speed up hashing.
150
+
151
+ * **`Annotated[T, HashBy[fn]]`**:\
152
+ Transform the argument via `fn(argument)` before hashing. Useful for normalization (e.g., sorting lists) or optimized hashing for complex inputs.
153
+
154
+ * **`NoHash[T]`**:\
155
+ Exclude the argument from hashing completely, so changes to it won't trigger cache invalidation.
156
+
157
+ **Example:**
158
+
159
+ ```python
160
+ from typing import Annotated
161
+ from checkpointer import checkpoint, HashBy, NoHash
162
+ from pathlib import Path
163
+ import logging
164
+
165
+ def file_bytes(path: Path) -> bytes:
166
+ return path.read_bytes()
167
+
168
+ @checkpoint
169
+ def process(
170
+ numbers: Annotated[list[int], HashBy[sorted]], # Hash by sorted list
171
+ data_file: Annotated[Path, HashBy[file_bytes]], # Hash by file content
172
+ log: NoHash[logging.Logger], # Exclude logger from hashing
173
+ ):
174
+ ...
175
+ ```
176
+
177
+ In this example, the hash for `numbers` ignores order, `data_file` is hashed based on its contents rather than path, and changes to `log` don't affect caching.
178
+
179
+ ## 🎯 Capturing Global Variables
180
+
181
+ `checkpointer` can include **captured global variables** in call hashes - these are globals your function reads during execution that may affect results.
182
+
183
+ Use `capture=True` on `@checkpoint` to capture **all** referenced globals (except those explicitly excluded with `NoHash`).
184
+
185
+ Alternatively, you can **opt-in selectively** by annotating globals with:
186
+
187
+ * **`CaptureMe[T]`**:\
188
+ Capture the variable on every call (triggers invalidation on changes).
189
+
190
+ * **`CaptureMeOnce[T]`**:\
191
+ Capture once per Python session (for expensive, immutable globals).
192
+
193
+ You can also combine these with `HashBy` to customize how captured variables are hashed (e.g., hash by subset of attributes).
194
+
195
+ **Example:**
196
+
197
+ ```python
198
+ from typing import Annotated
199
+ from checkpointer import checkpoint, CaptureMe, CaptureMeOnce, HashBy
200
+ from pathlib import Path
201
+
202
+ def file_bytes(path: Path) -> bytes:
203
+ return path.read_bytes()
204
+
205
+ captured_data: CaptureMe[Annotated[Path, HashBy[file_bytes]]] = Path("data.txt")
206
+ session_config: CaptureMeOnce[dict] = {"mode": "prod"}
207
+
208
+ @checkpoint
209
+ def process():
210
+ # `captured_data` is included in the call hash on every call, hashed by file content
211
+ # `session_config` is hashed once per session
212
+ ...
213
+ ```
214
+
215
+ ## 🗄️ Custom Storage Backends
216
+
217
+ Implement your own storage backend by subclassing `checkpointer.Storage` and overriding required methods.
218
+
219
+ Within storage methods, `call_hash` identifies calls by arguments. Use `self.fn_id()` to get function identity (name + hash/version), important for organizing checkpoints.
220
+
221
+ **Example:**
222
+
223
+ ```python
224
+ from checkpointer import checkpoint, Storage
225
+ from datetime import datetime
226
+
227
+ class MyCustomStorage(Storage):
228
+ def exists(self, call_hash):
229
+ fn_dir = self.checkpointer.directory / self.fn_id()
230
+ return (fn_dir / call_hash).exists()
231
+
232
+ def store(self, call_hash, data):
233
+ ... # Store serialized data
234
+ return data # Must return data to checkpointer
235
+
236
+ def checkpoint_date(self, call_hash): ...
237
+ def load(self, call_hash): ...
238
+ def delete(self, call_hash): ...
239
+
240
+ @checkpoint(storage=MyCustomStorage)
241
+ def custom_cached_function(x: int):
242
+ return x ** 2
243
+ ```
@@ -0,0 +1,227 @@
1
+ # checkpointer · [![License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/Reddan/checkpointer/blob/master/LICENSE) [![pypi](https://img.shields.io/pypi/v/checkpointer)](https://pypi.org/project/checkpointer/) [![pypi](https://img.shields.io/pypi/pyversions/checkpointer)](https://pypi.org/project/checkpointer/)
2
+
3
+ `checkpointer` is a Python library offering a decorator-based API for memoizing (caching) function results with code-aware cache invalidation. It works with sync and async functions, supports multiple storage backends, and invalidates caches automatically when your code or dependencies change - helping you maintain correctness, speed up execution, and smooth out your workflows by skipping redundant, costly operations.
4
+
5
+ ## 📦 Installation
6
+
7
+ ```bash
8
+ pip install checkpointer
9
+ ```
10
+
11
+ ## 🚀 Quick Start
12
+
13
+ Apply the `@checkpoint` decorator to any function:
14
+
15
+ ```python
16
+ from checkpointer import checkpoint
17
+
18
+ @checkpoint
19
+ def expensive_function(x: int) -> int:
20
+ print("Computing...")
21
+ return x ** 2
22
+
23
+ result = expensive_function(4) # Computes and stores the result
24
+ result = expensive_function(4) # Loads from the cache
25
+ ```
26
+
27
+ ## 🧠 How It Works
28
+
29
+ When you decorate a function with `@checkpoint` and call it, `checkpointer` computes a unique identifier that represents that specific call. This identifier is based on:
30
+
31
+ * The function's source code and all its user-defined dependencies,
32
+ * Global variables used by the function (if capturing is enabled or explicitly annotated),
33
+ * The actual arguments passed to the function.
34
+
35
+ `checkpointer` then looks up this identifier in its cache. If a valid cached result exists, it returns that immediately. Otherwise, it runs the original function, stores the result, and returns it.
36
+
37
+ `checkpointer` is designed to be flexible through features like:
38
+
39
+ * **Support for decorated methods**, correctly caching results bound to instances.
40
+ * **Support for decorated async functions**, compatible with any async runtime.
41
+ * **Robust hashing**, covering complex Python objects and large **NumPy**/**PyTorch** arrays via its internal `ObjectHash`.
42
+ * **Targeted hashing**, allowing you to optimize how arguments and captured variables are hashed.
43
+ * **Multi-layered caching**, letting you stack decorators for layered caching strategies without losing cache consistency.
44
+
45
+ ### 🚨 What Causes Cache Invalidation?
46
+
47
+ To ensure cache correctness, `checkpointer` tracks two types of hashes:
48
+
49
+ #### 1. Function Identity Hash (Computed Once Per Function)
50
+
51
+ This hash represents the decorated function itself and is computed once (usually on first invocation). It covers:
52
+
53
+ * **Function Code and Signature:**\
54
+ The actual logic and parameters of the function are hashed - but *not* parameter type annotations or formatting details like whitespace, newlines, comments, or trailing commas, which do **not** trigger invalidation.
55
+
56
+ * **Dependencies:**\
57
+ All user-defined functions and methods that the decorated function calls or relies on, including indirect dependencies, are included recursively. Dependencies are identified by:
58
+ * Inspecting the function's global scope for referenced functions and objects.
59
+ * Inferring from the function's argument type annotations.
60
+ * Analyzing object constructions and method calls to identify classes and methods used.
61
+
62
+ * **Exclusions:**\
63
+ Changes elsewhere in the module unrelated to the function or its dependencies do **not** cause invalidation.
64
+
65
+ #### 2. Call Hash (Computed on Every Function Call)
66
+
67
+ Every function call produces a call hash, combining:
68
+
69
+ * **Passed Arguments:**\
70
+ Includes positional and keyword arguments, combined with default values. Changing defaults alone doesn't necessarily trigger invalidation unless it affects actual call values.
71
+
72
+ * **Captured Global Variables:**\
73
+ When `capture=True` or explicit capture annotations are used, `checkpointer` includes referenced global variables in the call hash. Variables annotated with `CaptureMe` are hashed on every call, causing immediate cache invalidation if they change. Variables annotated with `CaptureMeOnce` are hashed only once per Python session, improving performance by avoiding repeated hashing.
74
+
75
+ * **Custom Argument Hashing:**\
76
+ Using `HashBy` annotations, arguments or captured variables can be transformed before hashing (e.g., sorting lists to ignore order), allowing more precise or efficient call hashes.
77
+
78
+ ## 💡 Usage
79
+
80
+ Once a function is decorated with `@checkpoint`, you can interact with its caching behavior using the following methods:
81
+
82
+ * **`expensive_function(...)`**:\
83
+ Call the function normally. This will compute and cache the result or load it from cache.
84
+
85
+ * **`expensive_function.rerun(...)`**:\
86
+ Force the original function to execute and overwrite any existing cached result.
87
+
88
+ * **`expensive_function.fn(...)`**:\
89
+ Call the undecorated function directly, bypassing the cache (useful in recursion to prevent caching intermediate steps).
90
+
91
+ * **`expensive_function.get(...)`**:\
92
+ Retrieve the cached result without executing the function. Raises `CheckpointError` if no valid cache exists.
93
+
94
+ * **`expensive_function.exists(...)`**:\
95
+ Check if a cached result exists without computing or loading it.
96
+
97
+ * **`expensive_function.delete(...)`**:\
98
+ Remove the cached entry for given arguments.
99
+
100
+ * **`expensive_function.reinit(recursive: bool = True)`**:\
101
+ Recalculate the function identity hash and recapture `CaptureMeOnce` variables, updating the cached function state within the same Python session.
102
+
103
+ ## ⚙️ Configuration & Customization
104
+
105
+ The `@checkpoint` decorator accepts the following parameters:
106
+
107
+ * **`storage`** (Type: `str` or `checkpointer.Storage`, Default: `"pickle"`)\
108
+ Storage backend to use: `"pickle"` (disk-based, persistent), `"memory"` (in-memory, non-persistent), or a custom `Storage` class.
109
+
110
+ * **`directory`** (Type: `str` or `pathlib.Path` or `None`, Default: `~/.cache/checkpoints`)\
111
+ Base directory for disk-based checkpoints (only for `"pickle"` storage).
112
+
113
+ * **`capture`** (Type: `bool`, Default: `False`)\
114
+ If `True`, includes global variables referenced by the function in call hashes (except those excluded via `NoHash`).
115
+
116
+ * **`expiry`** (Type: `Callable[[datetime.datetime], bool]` or `datetime.timedelta`, Default: `None`)\
117
+ A custom callable that receives the `datetime` timestamp of a cached result. It should return `True` if the cached result is considered expired and needs recomputation, or `False` otherwise.
118
+
119
+ * **`fn_hash_from`** (Type: `Any`, Default: `None`)\
120
+ Override the computed function identity hash with any hashable object you provide (e.g., version strings, config IDs). This gives you explicit control over the function's version and when its cache should be invalidated.
121
+
122
+ * **`when`** (Type: `bool`, Default: `True`)\
123
+ Enable or disable checkpointing dynamically, useful for environment-based toggling.
124
+
125
+ * **`verbosity`** (Type: `int` (`0`, `1`, or `2`), Default: `1`)\
126
+ Controls the level of logging output from `checkpointer`.
127
+ * `0`: No output.
128
+ * `1`: Shows when functions are computed and cached.
129
+ * `2`: Also shows when cached results are remembered (loaded from cache).
130
+
131
+ ## 🔬 Customize Argument Hashing
132
+
133
+ You can customize how arguments are hashed without modifying the actual argument values to improve cache hit rates or speed up hashing.
134
+
135
+ * **`Annotated[T, HashBy[fn]]`**:\
136
+ Transform the argument via `fn(argument)` before hashing. Useful for normalization (e.g., sorting lists) or optimized hashing for complex inputs.
137
+
138
+ * **`NoHash[T]`**:\
139
+ Exclude the argument from hashing completely, so changes to it won't trigger cache invalidation.
140
+
141
+ **Example:**
142
+
143
+ ```python
144
+ from typing import Annotated
145
+ from checkpointer import checkpoint, HashBy, NoHash
146
+ from pathlib import Path
147
+ import logging
148
+
149
+ def file_bytes(path: Path) -> bytes:
150
+ return path.read_bytes()
151
+
152
+ @checkpoint
153
+ def process(
154
+ numbers: Annotated[list[int], HashBy[sorted]], # Hash by sorted list
155
+ data_file: Annotated[Path, HashBy[file_bytes]], # Hash by file content
156
+ log: NoHash[logging.Logger], # Exclude logger from hashing
157
+ ):
158
+ ...
159
+ ```
160
+
161
+ In this example, the hash for `numbers` ignores order, `data_file` is hashed based on its contents rather than path, and changes to `log` don't affect caching.
162
+
163
+ ## 🎯 Capturing Global Variables
164
+
165
+ `checkpointer` can include **captured global variables** in call hashes - these are globals your function reads during execution that may affect results.
166
+
167
+ Use `capture=True` on `@checkpoint` to capture **all** referenced globals (except those explicitly excluded with `NoHash`).
168
+
169
+ Alternatively, you can **opt-in selectively** by annotating globals with:
170
+
171
+ * **`CaptureMe[T]`**:\
172
+ Capture the variable on every call (triggers invalidation on changes).
173
+
174
+ * **`CaptureMeOnce[T]`**:\
175
+ Capture once per Python session (for expensive, immutable globals).
176
+
177
+ You can also combine these with `HashBy` to customize how captured variables are hashed (e.g., hash by subset of attributes).
178
+
179
+ **Example:**
180
+
181
+ ```python
182
+ from typing import Annotated
183
+ from checkpointer import checkpoint, CaptureMe, CaptureMeOnce, HashBy
184
+ from pathlib import Path
185
+
186
+ def file_bytes(path: Path) -> bytes:
187
+ return path.read_bytes()
188
+
189
+ captured_data: CaptureMe[Annotated[Path, HashBy[file_bytes]]] = Path("data.txt")
190
+ session_config: CaptureMeOnce[dict] = {"mode": "prod"}
191
+
192
+ @checkpoint
193
+ def process():
194
+ # `captured_data` is included in the call hash on every call, hashed by file content
195
+ # `session_config` is hashed once per session
196
+ ...
197
+ ```
198
+
199
+ ## 🗄️ Custom Storage Backends
200
+
201
+ Implement your own storage backend by subclassing `checkpointer.Storage` and overriding required methods.
202
+
203
+ Within storage methods, `call_hash` identifies calls by arguments. Use `self.fn_id()` to get function identity (name + hash/version), important for organizing checkpoints.
204
+
205
+ **Example:**
206
+
207
+ ```python
208
+ from checkpointer import checkpoint, Storage
209
+ from datetime import datetime
210
+
211
+ class MyCustomStorage(Storage):
212
+ def exists(self, call_hash):
213
+ fn_dir = self.checkpointer.directory / self.fn_id()
214
+ return (fn_dir / call_hash).exists()
215
+
216
+ def store(self, call_hash, data):
217
+ ... # Store serialized data
218
+ return data # Must return data to checkpointer
219
+
220
+ def checkpoint_date(self, call_hash): ...
221
+ def load(self, call_hash): ...
222
+ def delete(self, call_hash): ...
223
+
224
+ @checkpoint(storage=MyCustomStorage)
225
+ def custom_cached_function(x: int):
226
+ return x ** 2
227
+ ```
@@ -1,5 +1,6 @@
1
1
  import gc
2
2
  import tempfile
3
+ from datetime import timedelta
3
4
  from typing import Callable
4
5
  from .checkpoint import CachedFunction, Checkpointer, CheckpointError, FunctionIdent
5
6
  from .object_hash import ObjectHash
@@ -8,8 +9,8 @@ from .types import AwaitableValue, Captured, CapturedOnce, CaptureMe, CaptureMeO
8
9
 
9
10
  checkpoint = Checkpointer()
10
11
  capture_checkpoint = Checkpointer(capture=True)
11
- memory_checkpoint = Checkpointer(format="memory", verbosity=0)
12
- tmp_checkpoint = Checkpointer(root_path=f"{tempfile.gettempdir()}/checkpoints")
12
+ memory_checkpoint = Checkpointer(storage="memory", verbosity=0)
13
+ tmp_checkpoint = Checkpointer(directory=f"{tempfile.gettempdir()}/checkpoints")
13
14
  static_checkpoint = Checkpointer(fn_hash_from=())
14
15
 
15
16
  def cleanup_all(invalidated=True, expired=True):