kvgit 0.3.2__tar.gz → 0.3.4__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.
- {kvgit-0.3.2 → kvgit-0.3.4}/PKG-INFO +2 -2
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/codecs/numpy.py +2 -2
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/codecs/pickler.py +2 -1
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/content_types.py +2 -1
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/encoding.py +2 -1
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/kv/indexeddb.py +4 -3
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/namespaced.py +2 -2
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/staged.py +2 -2
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/store.py +31 -4
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/versioned/base.py +1 -1
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/versioned/helpers.py +1 -1
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/versioned/kv.py +491 -68
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/versioned/merge.py +3 -3
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/versioned/protocol.py +2 -2
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit.egg-info/PKG-INFO +2 -2
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit.egg-info/requires.txt +1 -1
- kvgit-0.3.4/pyproject.toml +152 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/tests/test_hamt.py +4 -5
- kvgit-0.3.2/pyproject.toml +0 -69
- {kvgit-0.3.2 → kvgit-0.3.4}/LICENSE +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/README.md +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/__init__.py +1 -1
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/codecs/__init__.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/codecs/_hash.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/codecs/base.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/codecs/pandas.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/errors.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/hamt.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/kv/__init__.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/kv/base.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/kv/composite.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/kv/disk.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/kv/memory.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/py.typed +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/versioned/__init__.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit/versioned/keyset.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit.egg-info/SOURCES.txt +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit.egg-info/dependency_links.txt +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/kvgit.egg-info/top_level.txt +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/setup.cfg +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/tests/test_content_types.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/tests/test_delete_branches.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/tests/test_namespaced.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/tests/test_staged.py +0 -0
- {kvgit-0.3.2 → kvgit-0.3.4}/tests/test_store_factory.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kvgit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Versioned key-value store with git-like commit, branch, and merge semantics.
|
|
5
5
|
Author: ashenfad
|
|
6
6
|
License: MIT
|
|
@@ -42,7 +42,7 @@ Requires-Dist: pandas>=2.0; extra == "all"
|
|
|
42
42
|
Provides-Extra: dev
|
|
43
43
|
Requires-Dist: pytest; extra == "dev"
|
|
44
44
|
Requires-Dist: diskcache; extra == "dev"
|
|
45
|
-
Requires-Dist: ruff; extra == "dev"
|
|
45
|
+
Requires-Dist: ruff<0.17,>=0.16; extra == "dev"
|
|
46
46
|
Requires-Dist: numpy>=1.24; extra == "dev"
|
|
47
47
|
Requires-Dist: pandas>=2.0; extra == "dev"
|
|
48
48
|
Dynamic: license-file
|
|
@@ -33,7 +33,7 @@ storing each unique buffer once instead of N times.)
|
|
|
33
33
|
|
|
34
34
|
from __future__ import annotations
|
|
35
35
|
|
|
36
|
-
from typing import
|
|
36
|
+
from typing import TYPE_CHECKING, Any
|
|
37
37
|
|
|
38
38
|
if TYPE_CHECKING:
|
|
39
39
|
import numpy as np
|
|
@@ -66,7 +66,7 @@ def _descr_to_dtype(descr):
|
|
|
66
66
|
return npf.descr_to_dtype(descr)
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
def _root_and_offset(arr:
|
|
69
|
+
def _root_and_offset(arr: np.ndarray) -> tuple[np.ndarray, int]:
|
|
70
70
|
"""Walk ``.base`` to the root buffer; return (root, byte_offset).
|
|
71
71
|
|
|
72
72
|
Returns ``(arr, 0)`` if the .base chain leads somewhere we can't
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Merge functions for typed values."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
MergeFn = Callable[[Any | None, Any, Any], Any]
|
|
6
7
|
"""Merge function: (old_value | None, our_value, their_value) -> merged_value.
|
|
@@ -47,8 +47,8 @@ the handler is set, causing a deadlock.
|
|
|
47
47
|
|
|
48
48
|
from collections.abc import Iterable, Mapping
|
|
49
49
|
|
|
50
|
-
from pyodide.ffi import create_proxy, run_sync, to_js # type: ignore[import-not-found]
|
|
51
50
|
from js import Promise, indexedDB, undefined # type: ignore[import-not-found]
|
|
51
|
+
from pyodide.ffi import create_proxy, run_sync, to_js # type: ignore[import-not-found]
|
|
52
52
|
|
|
53
53
|
from .base import KVStore
|
|
54
54
|
|
|
@@ -155,8 +155,9 @@ def _to_bytes(js_value) -> bytes | None:
|
|
|
155
155
|
return None
|
|
156
156
|
try:
|
|
157
157
|
return js_value.to_py().tobytes()
|
|
158
|
-
except Exception:
|
|
159
|
-
#
|
|
158
|
+
except Exception: # noqa: BLE001 — a JS->Python conversion can fail
|
|
159
|
+
# with anything the FFI decides to raise; there is no narrower
|
|
160
|
+
# type to name. Corrupted or unexpected JS value — treat as missing.
|
|
160
161
|
return None
|
|
161
162
|
|
|
162
163
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from collections.abc import Iterator, MutableMapping
|
|
6
|
-
from typing import Any
|
|
5
|
+
from collections.abc import Iterable, Iterator, MutableMapping
|
|
6
|
+
from typing import Any
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class Namespaced(MutableMapping[str, Any]):
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
4
|
import pickle
|
|
5
|
-
from collections.abc import Iterable, Iterator, MutableMapping
|
|
6
|
-
from typing import Any
|
|
5
|
+
from collections.abc import Callable, Iterable, Iterator, MutableMapping
|
|
6
|
+
from typing import Any
|
|
7
7
|
|
|
8
8
|
from .codecs._hash import hash_bytes
|
|
9
9
|
from .content_types import MergeFn
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
"""Store factory function."""
|
|
2
2
|
|
|
3
3
|
import pickle
|
|
4
|
-
from collections.abc import Iterable
|
|
5
|
-
from typing import Any,
|
|
4
|
+
from collections.abc import Callable, Iterable
|
|
5
|
+
from typing import Any, Literal
|
|
6
6
|
|
|
7
7
|
from .kv.base import KVStore
|
|
8
8
|
from .kv.memory import Memory
|
|
9
9
|
from .staged import Staged
|
|
10
|
-
from .versioned.kv import
|
|
10
|
+
from .versioned.kv import (
|
|
11
|
+
BRANCH_HEAD,
|
|
12
|
+
BRANCH_HEAD_PREV,
|
|
13
|
+
CorruptHeadRecoverer,
|
|
14
|
+
VersionedKV,
|
|
15
|
+
clean_orphans,
|
|
16
|
+
)
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
def _make_backend(
|
|
@@ -46,6 +52,7 @@ def store(
|
|
|
46
52
|
encoder: Callable[..., bytes] = pickle.dumps,
|
|
47
53
|
decoder: Callable[..., Any] = pickle.loads,
|
|
48
54
|
codecs: str | None = None,
|
|
55
|
+
recover_from_corrupt_head: CorruptHeadRecoverer | None = None,
|
|
49
56
|
) -> Staged:
|
|
50
57
|
"""Create a Staged store with sensible defaults.
|
|
51
58
|
|
|
@@ -62,6 +69,13 @@ def store(
|
|
|
62
69
|
``"scientific"`` — numpy/pandas chunked codecs (requires
|
|
63
70
|
numpy; install with ``pip install kvgit[scientific]``).
|
|
64
71
|
Mutually exclusive with explicit ``encoder`` / ``decoder``.
|
|
72
|
+
recover_from_corrupt_head: Optional last-resort recovery for a
|
|
73
|
+
branch whose HEAD is unresolvable *and* whose prev-HEAD
|
|
74
|
+
backup is gone. Off by default: with both gone the store
|
|
75
|
+
does not know what the branch pointed at, so the honest
|
|
76
|
+
answer is to fail rather than guess. Pass
|
|
77
|
+
``kvgit.versioned.kv.recover_by_commit_scan`` to opt into
|
|
78
|
+
the scan, having read what it can return.
|
|
65
79
|
|
|
66
80
|
Returns:
|
|
67
81
|
A ``Staged`` store instance.
|
|
@@ -85,7 +99,15 @@ def store(
|
|
|
85
99
|
|
|
86
100
|
backend = _make_backend(kind, path=path, db_name=db_name)
|
|
87
101
|
|
|
88
|
-
return Staged(
|
|
102
|
+
return Staged(
|
|
103
|
+
VersionedKV(
|
|
104
|
+
backend,
|
|
105
|
+
branch=branch,
|
|
106
|
+
recover_from_corrupt_head=recover_from_corrupt_head,
|
|
107
|
+
),
|
|
108
|
+
encoder=encoder,
|
|
109
|
+
decoder=decoder,
|
|
110
|
+
)
|
|
89
111
|
|
|
90
112
|
|
|
91
113
|
def delete_branches(
|
|
@@ -123,6 +145,11 @@ def delete_branches(
|
|
|
123
145
|
not swept out from under it; an admin who knows the store is quiet
|
|
124
146
|
can pass ``0`` to reclaim young commits immediately.
|
|
125
147
|
|
|
148
|
+
That sweep does not reclaim chunks — the content-addressed bytes a
|
|
149
|
+
chunked codec writes — because a chunk key is not the deleted
|
|
150
|
+
branch's to give away. On a store using chunked codecs, follow up
|
|
151
|
+
with ``deep_clean`` on a quiescent store to get that space back.
|
|
152
|
+
|
|
126
153
|
Args:
|
|
127
154
|
names: Branch names to delete — one name or an iterable of
|
|
128
155
|
them. Missing names are no-ops. (A bare string is treated
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Shared commit/merge orchestration for versioned stores."""
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from
|
|
4
|
+
from collections.abc import Iterable
|
|
5
5
|
|
|
6
6
|
from ..errors import ConcurrencyError, MergeConflict
|
|
7
7
|
from .helpers import diff_keysets, walk_history
|