checkpointer 2.11.0__tar.gz → 2.11.1__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.
- {checkpointer-2.11.0 → checkpointer-2.11.1}/PKG-INFO +9 -9
- {checkpointer-2.11.0 → checkpointer-2.11.1}/README.md +8 -8
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/checkpoint.py +27 -20
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/storages/memory_storage.py +12 -12
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/storages/pickle_storage.py +12 -12
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/storages/storage.py +5 -5
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/types.py +2 -2
- {checkpointer-2.11.0 → checkpointer-2.11.1}/pyproject.toml +2 -2
- {checkpointer-2.11.0 → checkpointer-2.11.1}/uv.lock +5 -5
- {checkpointer-2.11.0 → checkpointer-2.11.1}/.gitignore +0 -0
- {checkpointer-2.11.0 → checkpointer-2.11.1}/.python-version +0 -0
- {checkpointer-2.11.0 → checkpointer-2.11.1}/LICENSE +0 -0
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/__init__.py +0 -0
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/fn_ident.py +0 -0
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/object_hash.py +0 -0
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/print_checkpoint.py +0 -0
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/storages/__init__.py +0 -0
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/test_checkpointer.py +0 -0
- {checkpointer-2.11.0 → checkpointer-2.11.1}/checkpointer/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: checkpointer
|
3
|
-
Version: 2.11.
|
3
|
+
Version: 2.11.1
|
4
4
|
Summary: checkpointer adds code-aware caching to Python functions, maintaining correctness and speeding up execution as your code changes.
|
5
5
|
Project-URL: Repository, https://github.com/Reddan/checkpointer.git
|
6
6
|
Author: Hampus Hallman
|
@@ -166,7 +166,7 @@ In this example, the cache key for `numbers` ignores order, `data_file` is hashe
|
|
166
166
|
|
167
167
|
For integration with databases, cloud storage, or custom serialization, implement your own storage backend by inheriting from `checkpointer.Storage` and implementing its abstract methods.
|
168
168
|
|
169
|
-
Within custom storage methods, `
|
169
|
+
Within custom storage methods, `call_hash` identifies calls by arguments. Use `self.fn_id()` to get the function's unique identity (name + hash/version), crucial for organizing stored checkpoints (e.g., by function version). Access global `Checkpointer` config via `self.checkpointer`.
|
170
170
|
|
171
171
|
**Example: Custom Storage Backend**
|
172
172
|
|
@@ -175,18 +175,18 @@ from checkpointer import checkpoint, Storage
|
|
175
175
|
from datetime import datetime
|
176
176
|
|
177
177
|
class MyCustomStorage(Storage):
|
178
|
-
def exists(self,
|
178
|
+
def exists(self, call_hash):
|
179
179
|
# Example: Constructing a path based on function ID and call ID
|
180
180
|
fn_dir = self.checkpointer.root_path / self.fn_id()
|
181
|
-
return (fn_dir /
|
181
|
+
return (fn_dir / call_hash).exists()
|
182
182
|
|
183
|
-
def store(self,
|
184
|
-
... # Store the serialized data for `
|
183
|
+
def store(self, call_hash, data):
|
184
|
+
... # Store the serialized data for `call_hash`
|
185
185
|
return data # This method must return the data back to checkpointer
|
186
186
|
|
187
|
-
def checkpoint_date(self,
|
188
|
-
def load(self,
|
189
|
-
def delete(self,
|
187
|
+
def checkpoint_date(self, call_hash): ...
|
188
|
+
def load(self, call_hash): ...
|
189
|
+
def delete(self, call_hash): ...
|
190
190
|
|
191
191
|
@checkpoint(format=MyCustomStorage)
|
192
192
|
def custom_cached_function(x: int):
|
@@ -146,7 +146,7 @@ In this example, the cache key for `numbers` ignores order, `data_file` is hashe
|
|
146
146
|
|
147
147
|
For integration with databases, cloud storage, or custom serialization, implement your own storage backend by inheriting from `checkpointer.Storage` and implementing its abstract methods.
|
148
148
|
|
149
|
-
Within custom storage methods, `
|
149
|
+
Within custom storage methods, `call_hash` identifies calls by arguments. Use `self.fn_id()` to get the function's unique identity (name + hash/version), crucial for organizing stored checkpoints (e.g., by function version). Access global `Checkpointer` config via `self.checkpointer`.
|
150
150
|
|
151
151
|
**Example: Custom Storage Backend**
|
152
152
|
|
@@ -155,18 +155,18 @@ from checkpointer import checkpoint, Storage
|
|
155
155
|
from datetime import datetime
|
156
156
|
|
157
157
|
class MyCustomStorage(Storage):
|
158
|
-
def exists(self,
|
158
|
+
def exists(self, call_hash):
|
159
159
|
# Example: Constructing a path based on function ID and call ID
|
160
160
|
fn_dir = self.checkpointer.root_path / self.fn_id()
|
161
|
-
return (fn_dir /
|
161
|
+
return (fn_dir / call_hash).exists()
|
162
162
|
|
163
|
-
def store(self,
|
164
|
-
... # Store the serialized data for `
|
163
|
+
def store(self, call_hash, data):
|
164
|
+
... # Store the serialized data for `call_hash`
|
165
165
|
return data # This method must return the data back to checkpointer
|
166
166
|
|
167
|
-
def checkpoint_date(self,
|
168
|
-
def load(self,
|
169
|
-
def delete(self,
|
167
|
+
def checkpoint_date(self, call_hash): ...
|
168
|
+
def load(self, call_hash): ...
|
169
|
+
def delete(self, call_hash): ...
|
170
170
|
|
171
171
|
@checkpoint(format=MyCustomStorage)
|
172
172
|
def custom_cached_function(x: int):
|
@@ -129,7 +129,7 @@ class CachedFunction(Generic[Fn]):
|
|
129
129
|
for ident in depend_idents: ident.fn_hash
|
130
130
|
return self
|
131
131
|
|
132
|
-
def
|
132
|
+
def get_call_hash(self, args: tuple, kw: dict[str, object]) -> str:
|
133
133
|
args = self.bound + args
|
134
134
|
pos_args = args[len(self.arg_names):]
|
135
135
|
named_pos_args = dict(zip(self.arg_names, args))
|
@@ -143,8 +143,8 @@ class CachedFunction(Generic[Fn]):
|
|
143
143
|
pos_args = tuple(map(pos_hash_by, pos_args))
|
144
144
|
return str(ObjectHash(named_args, pos_args, self.ident.captured_hash, digest_size=16))
|
145
145
|
|
146
|
-
async def _resolve_coroutine(self,
|
147
|
-
return self.storage.store(
|
146
|
+
async def _resolve_coroutine(self, call_hash: str, coroutine: Coroutine):
|
147
|
+
return self.storage.store(call_hash, AwaitableValue(await coroutine)).value
|
148
148
|
|
149
149
|
def _call(self: CachedFunction[Callable[P, R]], args: tuple, kw: dict, rerun=False) -> R:
|
150
150
|
full_args = self.bound + args
|
@@ -152,27 +152,27 @@ class CachedFunction(Generic[Fn]):
|
|
152
152
|
if not params.when:
|
153
153
|
return self.fn(*full_args, **kw)
|
154
154
|
|
155
|
-
|
156
|
-
|
155
|
+
call_hash = self.get_call_hash(args, kw)
|
156
|
+
call_hash_long = f"{self.fn_dir}/{self.ident.fn_hash}/{call_hash}"
|
157
157
|
|
158
158
|
refresh = rerun \
|
159
|
-
or not self.storage.exists(
|
160
|
-
or (params.should_expire and params.should_expire(self.storage.checkpoint_date(
|
159
|
+
or not self.storage.exists(call_hash) \
|
160
|
+
or (params.should_expire and params.should_expire(self.storage.checkpoint_date(call_hash)))
|
161
161
|
|
162
162
|
if refresh:
|
163
|
-
print_checkpoint(params.verbosity >= 1, "MEMORIZING",
|
163
|
+
print_checkpoint(params.verbosity >= 1, "MEMORIZING", call_hash_long, "blue")
|
164
164
|
data = self.fn(*full_args, **kw)
|
165
165
|
if iscoroutine(data):
|
166
|
-
return self._resolve_coroutine(
|
167
|
-
return self.storage.store(
|
166
|
+
return self._resolve_coroutine(call_hash, data)
|
167
|
+
return self.storage.store(call_hash, data)
|
168
168
|
|
169
169
|
try:
|
170
|
-
data = self.storage.load(
|
171
|
-
print_checkpoint(params.verbosity >= 2, "REMEMBERED",
|
170
|
+
data = self.storage.load(call_hash)
|
171
|
+
print_checkpoint(params.verbosity >= 2, "REMEMBERED", call_hash_long, "green")
|
172
172
|
return data
|
173
173
|
except (EOFError, FileNotFoundError):
|
174
174
|
pass
|
175
|
-
print_checkpoint(params.verbosity >= 1, "CORRUPTED",
|
175
|
+
print_checkpoint(params.verbosity >= 1, "CORRUPTED", call_hash_long, "yellow")
|
176
176
|
return self._call(args, kw, True)
|
177
177
|
|
178
178
|
def __call__(self: CachedFunction[Callable[P, R]], *args: P.args, **kw: P.kwargs) -> R:
|
@@ -181,23 +181,30 @@ class CachedFunction(Generic[Fn]):
|
|
181
181
|
def rerun(self: CachedFunction[Callable[P, R]], *args: P.args, **kw: P.kwargs) -> R:
|
182
182
|
return self._call(args, kw, True)
|
183
183
|
|
184
|
+
def exists(self: CachedFunction[Callable[P, R]], *args: P.args, **kw: P.kwargs) -> bool:
|
185
|
+
return self.storage.exists(self.get_call_hash(args, kw))
|
186
|
+
|
187
|
+
def delete(self: CachedFunction[Callable[P, R]], *args: P.args, **kw: P.kwargs):
|
188
|
+
self.storage.delete(self.get_call_hash(args, kw))
|
189
|
+
|
184
190
|
@overload
|
185
191
|
def get(self: Callable[P, Coroutine[object, object, R]], *args: P.args, **kw: P.kwargs) -> R: ...
|
186
192
|
@overload
|
187
193
|
def get(self: Callable[P, R], *args: P.args, **kw: P.kwargs) -> R: ...
|
188
194
|
def get(self, *args, **kw):
|
189
|
-
|
195
|
+
call_hash = self.get_call_hash(args, kw)
|
190
196
|
try:
|
191
|
-
data = self.storage.load(
|
197
|
+
data = self.storage.load(call_hash)
|
192
198
|
return data.value if isinstance(data, AwaitableValue) else data
|
193
199
|
except Exception as ex:
|
194
200
|
raise CheckpointError("Could not load checkpoint") from ex
|
195
201
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
def
|
200
|
-
|
202
|
+
@overload
|
203
|
+
def _set(self: Callable[P, Coroutine[object, object, R]], value: AwaitableValue[R], *args: P.args, **kw: P.kwargs): ...
|
204
|
+
@overload
|
205
|
+
def _set(self: Callable[P, R], value: R, *args: P.args, **kw: P.kwargs): ...
|
206
|
+
def _set(self, value, *args, **kw):
|
207
|
+
self.storage.store(self.get_call_hash(args, kw), value)
|
201
208
|
|
202
209
|
def __repr__(self) -> str:
|
203
210
|
return f"<CachedFunction {self.fn.__name__} {self.ident.fn_hash[:6]}>"
|
@@ -9,21 +9,21 @@ class MemoryStorage(Storage):
|
|
9
9
|
def get_dict(self):
|
10
10
|
return item_map.setdefault(self.fn_dir(), {})
|
11
11
|
|
12
|
-
def store(self,
|
13
|
-
self.get_dict()[
|
12
|
+
def store(self, call_hash, data):
|
13
|
+
self.get_dict()[call_hash] = (datetime.now(), data)
|
14
14
|
return data
|
15
15
|
|
16
|
-
def exists(self,
|
17
|
-
return
|
16
|
+
def exists(self, call_hash):
|
17
|
+
return call_hash in self.get_dict()
|
18
18
|
|
19
|
-
def checkpoint_date(self,
|
20
|
-
return self.get_dict()[
|
19
|
+
def checkpoint_date(self, call_hash):
|
20
|
+
return self.get_dict()[call_hash][0]
|
21
21
|
|
22
|
-
def load(self,
|
23
|
-
return self.get_dict()[
|
22
|
+
def load(self, call_hash):
|
23
|
+
return self.get_dict()[call_hash][1]
|
24
24
|
|
25
|
-
def delete(self,
|
26
|
-
self.get_dict().pop(
|
25
|
+
def delete(self, call_hash):
|
26
|
+
self.get_dict().pop(call_hash, None)
|
27
27
|
|
28
28
|
def cleanup(self, invalidated=True, expired=True):
|
29
29
|
curr_key = self.fn_dir()
|
@@ -32,6 +32,6 @@ class MemoryStorage(Storage):
|
|
32
32
|
if invalidated and key != curr_key:
|
33
33
|
del item_map[key]
|
34
34
|
elif expired and self.checkpointer.should_expire:
|
35
|
-
for
|
35
|
+
for call_hash, (date, _) in list(calldict.items()):
|
36
36
|
if self.checkpointer.should_expire(date):
|
37
|
-
del calldict[
|
37
|
+
del calldict[call_hash]
|
@@ -8,29 +8,29 @@ def filedate(path: Path) -> datetime:
|
|
8
8
|
return datetime.fromtimestamp(path.stat().st_mtime)
|
9
9
|
|
10
10
|
class PickleStorage(Storage):
|
11
|
-
def get_path(self,
|
12
|
-
return self.fn_dir() / f"{
|
11
|
+
def get_path(self, call_hash: str):
|
12
|
+
return self.fn_dir() / f"{call_hash}.pkl"
|
13
13
|
|
14
|
-
def store(self,
|
15
|
-
path = self.get_path(
|
14
|
+
def store(self, call_hash, data):
|
15
|
+
path = self.get_path(call_hash)
|
16
16
|
path.parent.mkdir(parents=True, exist_ok=True)
|
17
17
|
with path.open("wb") as file:
|
18
18
|
pickle.dump(data, file, -1)
|
19
19
|
return data
|
20
20
|
|
21
|
-
def exists(self,
|
22
|
-
return self.get_path(
|
21
|
+
def exists(self, call_hash):
|
22
|
+
return self.get_path(call_hash).exists()
|
23
23
|
|
24
|
-
def checkpoint_date(self,
|
24
|
+
def checkpoint_date(self, call_hash):
|
25
25
|
# Should use st_atime/access time?
|
26
|
-
return filedate(self.get_path(
|
26
|
+
return filedate(self.get_path(call_hash))
|
27
27
|
|
28
|
-
def load(self,
|
29
|
-
with self.get_path(
|
28
|
+
def load(self, call_hash):
|
29
|
+
with self.get_path(call_hash).open("rb") as file:
|
30
30
|
return pickle.load(file)
|
31
31
|
|
32
|
-
def delete(self,
|
33
|
-
self.get_path(
|
32
|
+
def delete(self, call_hash):
|
33
|
+
self.get_path(call_hash).unlink(missing_ok=True)
|
34
34
|
|
35
35
|
def cleanup(self, invalidated=True, expired=True):
|
36
36
|
version_path = self.fn_dir()
|
@@ -20,14 +20,14 @@ class Storage:
|
|
20
20
|
def fn_dir(self) -> Path:
|
21
21
|
return self.checkpointer.root_path / self.fn_id()
|
22
22
|
|
23
|
-
def store(self,
|
23
|
+
def store(self, call_hash: str, data: Any) -> Any: ...
|
24
24
|
|
25
|
-
def exists(self,
|
25
|
+
def exists(self, call_hash: str) -> bool: ...
|
26
26
|
|
27
|
-
def checkpoint_date(self,
|
27
|
+
def checkpoint_date(self, call_hash: str) -> datetime: ...
|
28
28
|
|
29
|
-
def load(self,
|
29
|
+
def load(self, call_hash: str) -> Any: ...
|
30
30
|
|
31
|
-
def delete(self,
|
31
|
+
def delete(self, call_hash: str) -> None: ...
|
32
32
|
|
33
33
|
def cleanup(self, invalidated=True, expired=True): ...
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "checkpointer"
|
3
|
-
version = "2.11.
|
3
|
+
version = "2.11.1"
|
4
4
|
requires-python = ">=3.11"
|
5
5
|
dependencies = []
|
6
6
|
authors = [
|
@@ -21,7 +21,7 @@ Repository = "https://github.com/Reddan/checkpointer.git"
|
|
21
21
|
[dependency-groups]
|
22
22
|
dev = [
|
23
23
|
"numpy>=2.2.1",
|
24
|
-
"omg>=1.3.
|
24
|
+
"omg>=1.3.6",
|
25
25
|
"poethepoet>=0.30.0",
|
26
26
|
"pytest>=8.3.5",
|
27
27
|
"pytest-asyncio>=0.26.0",
|
@@ -8,7 +8,7 @@ resolution-markers = [
|
|
8
8
|
|
9
9
|
[[package]]
|
10
10
|
name = "checkpointer"
|
11
|
-
version = "2.11.
|
11
|
+
version = "2.11.1"
|
12
12
|
source = { editable = "." }
|
13
13
|
|
14
14
|
[package.dev-dependencies]
|
@@ -27,7 +27,7 @@ dev = [
|
|
27
27
|
[package.metadata.requires-dev]
|
28
28
|
dev = [
|
29
29
|
{ name = "numpy", specifier = ">=2.2.1" },
|
30
|
-
{ name = "omg", specifier = ">=1.3.
|
30
|
+
{ name = "omg", specifier = ">=1.3.6" },
|
31
31
|
{ name = "poethepoet", specifier = ">=0.30.0" },
|
32
32
|
{ name = "pytest", specifier = ">=8.3.5" },
|
33
33
|
{ name = "pytest-asyncio", specifier = ">=0.26.0" },
|
@@ -330,14 +330,14 @@ wheels = [
|
|
330
330
|
|
331
331
|
[[package]]
|
332
332
|
name = "omg"
|
333
|
-
version = "1.3.
|
333
|
+
version = "1.3.6"
|
334
334
|
source = { registry = "https://pypi.org/simple" }
|
335
335
|
dependencies = [
|
336
336
|
{ name = "watchdog" },
|
337
337
|
]
|
338
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
338
|
+
sdist = { url = "https://files.pythonhosted.org/packages/65/06/da0a3778b7ff8f1333ed7ddc0931ffff3c86ab5cb8bc4a96a1d0edb8671b/omg-1.3.6.tar.gz", hash = "sha256:465a51b7576fa31ef313e2b9a77d57f5d4816fb0a14dca0fc5c09ff471074fe6", size = 14268 }
|
339
339
|
wheels = [
|
340
|
-
{ url = "https://files.pythonhosted.org/packages/
|
340
|
+
{ url = "https://files.pythonhosted.org/packages/dd/d2/87346e94dbecd3a65a09e2156c1adf30c162f31e69d0936343c3eff53e7a/omg-1.3.6-py3-none-any.whl", hash = "sha256:8e3ac99a18d5284ceef2ed98492d288d5f22ee2bb417591654a7d2433e196607", size = 7988 },
|
341
341
|
]
|
342
342
|
|
343
343
|
[[package]]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|