shelfdb 3.0.2__tar.gz → 3.1.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.
- {shelfdb-3.0.2 → shelfdb-3.1.0}/PKG-INFO +32 -8
- {shelfdb-3.0.2 → shelfdb-3.1.0}/README.md +28 -6
- {shelfdb-3.0.2 → shelfdb-3.1.0}/pyproject.toml +8 -6
- {shelfdb-3.0.2 → shelfdb-3.1.0}/pyproject.toml.orig +8 -4
- shelfdb-3.1.0/src/shelfdb/cli.py +53 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/client/client.py +1 -1
- shelfdb-3.1.0/src/shelfdb/protocol/__init__.py +59 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/protocol.py +4 -5
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/shelf/__init__.py +2 -1
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/shelf/db.py +2 -2
- {shelfdb-3.0.2/src/shelfdb/shelf → shelfdb-3.1.0/src/shelfdb}/shelf/query.py +1 -1
- shelfdb-3.0.2/ai-skill/shelfdb-usage/SKILL.md +0 -56
- shelfdb-3.0.2/src/shelfdb/cli.py +0 -109
- shelfdb-3.0.2/src/shelfdb/protocol/__init__.py +0 -35
- shelfdb-3.0.2/src/shelfdb/shelf/shelf/__init__.py +0 -4
- {shelfdb-3.0.2 → shelfdb-3.1.0}/LICENSE +0 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/__init__.py +0 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/__main__.py +0 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/client/__init__.py +0 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/demo_poc.py +0 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/query_result.py +0 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/server.py +0 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/session.py +0 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/write_admission.py +0 -0
- {shelfdb-3.0.2/src/shelfdb/shelf → shelfdb-3.1.0/src/shelfdb}/shelf/schema.py +0 -0
- /shelfdb-3.0.2/src/shelfdb/shelf/shelf/shelf.py → /shelfdb-3.1.0/src/shelfdb/shelf/storage.py +0 -0
- {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/target.py +0 -0
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shelfdb
|
|
3
|
-
Version: 3.0
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: A tiny database for Python
|
|
5
5
|
Author: Nitipit Nontasuwan
|
|
6
6
|
Author-email: Nitipit Nontasuwan <nitipit@gmail.com>
|
|
7
7
|
License-Expression: MIT
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
10
12
|
Classifier: Operating System :: OS Independent
|
|
11
13
|
Requires-Dist: cyclopts>=4.10.2,<5
|
|
12
14
|
Requires-Dist: dill>=0.4.0,<0.5
|
|
13
15
|
Requires-Dist: lmdb>=1.7.3,<2
|
|
14
16
|
Requires-Dist: msgpack>=1.2.1,<2
|
|
15
|
-
Requires-Python: >=3.12
|
|
17
|
+
Requires-Python: >=3.12, <3.15
|
|
16
18
|
Project-URL: Documentation, https://keenlycode.github.io/shelfdb/
|
|
17
19
|
Project-URL: Repository, https://github.com/keenlycode/shelfdb
|
|
18
20
|
Description-Content-Type: text/markdown
|
|
@@ -34,16 +36,29 @@ pip install shelfdb
|
|
|
34
36
|
|
|
35
37
|
## Development
|
|
36
38
|
|
|
39
|
+
Python 3.12–3.14 is supported. Development uses Python 3.14, selected by
|
|
40
|
+
`.python-version`; compatibility checks cover Python 3.12 and 3.14. Python 3.15
|
|
41
|
+
is temporarily excluded because the released `dill` cannot serialize query
|
|
42
|
+
callbacks on it; support will be reassessed after an upstream fix is released.
|
|
43
|
+
|
|
37
44
|
Install development dependencies:
|
|
38
45
|
|
|
39
46
|
```bash
|
|
40
47
|
uv sync --dev
|
|
41
48
|
```
|
|
42
49
|
|
|
50
|
+
Development tooling lives in `src/dev/`, outside the published `shelfdb` package.
|
|
51
|
+
Use `--directory src` to run its module; workflow commands resolve paths from the
|
|
52
|
+
repository root. Documentation macros live in `src/macros.py`.
|
|
53
|
+
|
|
54
|
+
Unit tests mirror `src/` under `tests/unit/`: `shelfdb/`, `dev/`, and
|
|
55
|
+
`macros_test.py`. Integration tests cover cross-layer behavior; `tests/usage/`
|
|
56
|
+
contains executable usage examples.
|
|
57
|
+
|
|
43
58
|
Run the complete non-publishing release gate:
|
|
44
59
|
|
|
45
60
|
```bash
|
|
46
|
-
uv run python -m dev release-check
|
|
61
|
+
uv run --directory src python -m dev release-check
|
|
47
62
|
```
|
|
48
63
|
|
|
49
64
|
The gate audits locked dependencies, checks formatting, linting, types, supported
|
|
@@ -53,13 +68,13 @@ and version tags. Before tagging, authenticated release maintainers also check
|
|
|
53
68
|
GitHub's repository alerts:
|
|
54
69
|
|
|
55
70
|
```bash
|
|
56
|
-
uv run python -m dev release-check --github
|
|
71
|
+
uv run --directory src python -m dev release-check --github
|
|
57
72
|
```
|
|
58
73
|
|
|
59
74
|
Serve the docs locally (Zensical dev server; live reload is built in):
|
|
60
75
|
|
|
61
76
|
```bash
|
|
62
|
-
uv run python -m dev docs serve --port 9001 --livereload
|
|
77
|
+
uv run --directory src python -m dev docs serve --port 9001 --livereload
|
|
63
78
|
```
|
|
64
79
|
|
|
65
80
|
`--livereload` is a legacy compatibility flag and is intentionally ignored by Zensical
|
|
@@ -68,19 +83,19 @@ uv run python -m dev docs serve --port 9001 --livereload
|
|
|
68
83
|
Build docs for verification (Zensical build):
|
|
69
84
|
|
|
70
85
|
```bash
|
|
71
|
-
uv run python -m dev docs build
|
|
86
|
+
uv run --directory src python -m dev docs build
|
|
72
87
|
```
|
|
73
88
|
|
|
74
89
|
Publish the docs with mike to the `docs` branch:
|
|
75
90
|
|
|
76
91
|
```bash
|
|
77
|
-
uv run python -m dev docs publish
|
|
92
|
+
uv run --directory src python -m dev docs publish
|
|
78
93
|
```
|
|
79
94
|
|
|
80
95
|
Override the publish target when needed:
|
|
81
96
|
|
|
82
97
|
```bash
|
|
83
|
-
uv run python -m dev docs publish --publish-version 3.0.1 --alias latest --branch docs --remote origin
|
|
98
|
+
uv run --directory src python -m dev docs publish --publish-version 3.0.1 --alias latest --branch docs --remote origin
|
|
84
99
|
```
|
|
85
100
|
|
|
86
101
|
## Server
|
|
@@ -130,6 +145,15 @@ client = await Client.connect("unix:///tmp/shelfdb.sock")
|
|
|
130
145
|
See [remote transaction usage](docs-src/usage/remote.md#concurrent-transactions)
|
|
131
146
|
for coordination limits and details on partial updates and error handling.
|
|
132
147
|
|
|
148
|
+
## Large results
|
|
149
|
+
|
|
150
|
+
Remote messages are buffered, not streamed. The former 1 MiB cap is removed;
|
|
151
|
+
only the unsigned 32-bit payload length remains (4 GiB minus 1 byte). Large
|
|
152
|
+
queries can still exhaust memory, so paginate bulk reads with `.slice(...)`.
|
|
153
|
+
Store large assets externally and keep references in ShelfDB.
|
|
154
|
+
|
|
155
|
+
See [pagination and its limits](docs-src/usage/remote.md#large-results-and-pagination).
|
|
156
|
+
|
|
133
157
|
## Example
|
|
134
158
|
|
|
135
159
|
```python
|
|
@@ -15,16 +15,29 @@ pip install shelfdb
|
|
|
15
15
|
|
|
16
16
|
## Development
|
|
17
17
|
|
|
18
|
+
Python 3.12–3.14 is supported. Development uses Python 3.14, selected by
|
|
19
|
+
`.python-version`; compatibility checks cover Python 3.12 and 3.14. Python 3.15
|
|
20
|
+
is temporarily excluded because the released `dill` cannot serialize query
|
|
21
|
+
callbacks on it; support will be reassessed after an upstream fix is released.
|
|
22
|
+
|
|
18
23
|
Install development dependencies:
|
|
19
24
|
|
|
20
25
|
```bash
|
|
21
26
|
uv sync --dev
|
|
22
27
|
```
|
|
23
28
|
|
|
29
|
+
Development tooling lives in `src/dev/`, outside the published `shelfdb` package.
|
|
30
|
+
Use `--directory src` to run its module; workflow commands resolve paths from the
|
|
31
|
+
repository root. Documentation macros live in `src/macros.py`.
|
|
32
|
+
|
|
33
|
+
Unit tests mirror `src/` under `tests/unit/`: `shelfdb/`, `dev/`, and
|
|
34
|
+
`macros_test.py`. Integration tests cover cross-layer behavior; `tests/usage/`
|
|
35
|
+
contains executable usage examples.
|
|
36
|
+
|
|
24
37
|
Run the complete non-publishing release gate:
|
|
25
38
|
|
|
26
39
|
```bash
|
|
27
|
-
uv run python -m dev release-check
|
|
40
|
+
uv run --directory src python -m dev release-check
|
|
28
41
|
```
|
|
29
42
|
|
|
30
43
|
The gate audits locked dependencies, checks formatting, linting, types, supported
|
|
@@ -34,13 +47,13 @@ and version tags. Before tagging, authenticated release maintainers also check
|
|
|
34
47
|
GitHub's repository alerts:
|
|
35
48
|
|
|
36
49
|
```bash
|
|
37
|
-
uv run python -m dev release-check --github
|
|
50
|
+
uv run --directory src python -m dev release-check --github
|
|
38
51
|
```
|
|
39
52
|
|
|
40
53
|
Serve the docs locally (Zensical dev server; live reload is built in):
|
|
41
54
|
|
|
42
55
|
```bash
|
|
43
|
-
uv run python -m dev docs serve --port 9001 --livereload
|
|
56
|
+
uv run --directory src python -m dev docs serve --port 9001 --livereload
|
|
44
57
|
```
|
|
45
58
|
|
|
46
59
|
`--livereload` is a legacy compatibility flag and is intentionally ignored by Zensical
|
|
@@ -49,19 +62,19 @@ uv run python -m dev docs serve --port 9001 --livereload
|
|
|
49
62
|
Build docs for verification (Zensical build):
|
|
50
63
|
|
|
51
64
|
```bash
|
|
52
|
-
uv run python -m dev docs build
|
|
65
|
+
uv run --directory src python -m dev docs build
|
|
53
66
|
```
|
|
54
67
|
|
|
55
68
|
Publish the docs with mike to the `docs` branch:
|
|
56
69
|
|
|
57
70
|
```bash
|
|
58
|
-
uv run python -m dev docs publish
|
|
71
|
+
uv run --directory src python -m dev docs publish
|
|
59
72
|
```
|
|
60
73
|
|
|
61
74
|
Override the publish target when needed:
|
|
62
75
|
|
|
63
76
|
```bash
|
|
64
|
-
uv run python -m dev docs publish --publish-version 3.0.1 --alias latest --branch docs --remote origin
|
|
77
|
+
uv run --directory src python -m dev docs publish --publish-version 3.0.1 --alias latest --branch docs --remote origin
|
|
65
78
|
```
|
|
66
79
|
|
|
67
80
|
## Server
|
|
@@ -111,6 +124,15 @@ client = await Client.connect("unix:///tmp/shelfdb.sock")
|
|
|
111
124
|
See [remote transaction usage](docs-src/usage/remote.md#concurrent-transactions)
|
|
112
125
|
for coordination limits and details on partial updates and error handling.
|
|
113
126
|
|
|
127
|
+
## Large results
|
|
128
|
+
|
|
129
|
+
Remote messages are buffered, not streamed. The former 1 MiB cap is removed;
|
|
130
|
+
only the unsigned 32-bit payload length remains (4 GiB minus 1 byte). Large
|
|
131
|
+
queries can still exhaust memory, so paginate bulk reads with `.slice(...)`.
|
|
132
|
+
Store large assets externally and keep references in ShelfDB.
|
|
133
|
+
|
|
134
|
+
See [pagination and its limits](docs-src/usage/remote.md#large-results-and-pagination).
|
|
135
|
+
|
|
114
136
|
## Example
|
|
115
137
|
|
|
116
138
|
```python
|
|
@@ -4,14 +4,16 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "shelfdb"
|
|
7
|
-
version = "3.0
|
|
7
|
+
version = "3.1.0"
|
|
8
8
|
description = "A tiny database for Python"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.12"
|
|
10
|
+
requires-python = ">=3.12,<3.15"
|
|
11
11
|
license = "MIT"
|
|
12
12
|
license-files = ["LICENSE"]
|
|
13
13
|
classifiers = [
|
|
14
14
|
"Programming Language :: Python :: 3.12",
|
|
15
|
+
"Programming Language :: Python :: 3.13",
|
|
16
|
+
"Programming Language :: Python :: 3.14",
|
|
15
17
|
"Operating System :: OS Independent",
|
|
16
18
|
]
|
|
17
19
|
dependencies = [
|
|
@@ -43,10 +45,10 @@ dev = [
|
|
|
43
45
|
"ty>=0.0.1a17,<0.0.2",
|
|
44
46
|
]
|
|
45
47
|
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
addopts = ["--import-mode=importlib"]
|
|
50
|
+
pythonpath = ["src"]
|
|
51
|
+
|
|
46
52
|
[tool.uv.build-backend]
|
|
47
53
|
module-name = "shelfdb"
|
|
48
54
|
module-root = "src"
|
|
49
|
-
source-include = ["ai-skill/**"]
|
|
50
|
-
|
|
51
|
-
[tool.uv.build-backend.data]
|
|
52
|
-
data = "ai-skill"
|
|
@@ -4,10 +4,10 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "shelfdb"
|
|
7
|
-
version = "3.0
|
|
7
|
+
version = "3.1.0"
|
|
8
8
|
description = "A tiny database for Python"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.12"
|
|
10
|
+
requires-python = ">=3.12,<3.15"
|
|
11
11
|
license = "MIT"
|
|
12
12
|
license-files = ["LICENSE"]
|
|
13
13
|
authors = [
|
|
@@ -15,6 +15,8 @@ authors = [
|
|
|
15
15
|
]
|
|
16
16
|
classifiers = [
|
|
17
17
|
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Programming Language :: Python :: 3.14",
|
|
18
20
|
"Operating System :: OS Independent",
|
|
19
21
|
]
|
|
20
22
|
dependencies = [
|
|
@@ -42,8 +44,10 @@ dev = [
|
|
|
42
44
|
"ty>=0.0.1a17,<0.0.2",
|
|
43
45
|
]
|
|
44
46
|
|
|
47
|
+
[tool.pytest.ini_options]
|
|
48
|
+
addopts = ["--import-mode=importlib"]
|
|
49
|
+
pythonpath = ["src"]
|
|
50
|
+
|
|
45
51
|
[tool.uv.build-backend]
|
|
46
52
|
module-name = "shelfdb"
|
|
47
53
|
module-root = "src"
|
|
48
|
-
data = { data = "ai-skill" }
|
|
49
|
-
source-include = ["ai-skill/**"]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Command-line interface for ShelfDB."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from contextlib import suppress
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from cyclopts import App
|
|
9
|
+
|
|
10
|
+
from shelfdb.protocol import serve, serve_unix
|
|
11
|
+
from shelfdb.shelf import DB
|
|
12
|
+
from shelfdb.target import parse_target, parse_tcp_location, parse_unix_location
|
|
13
|
+
|
|
14
|
+
app = App("shelfdb")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def run_server(
|
|
18
|
+
*,
|
|
19
|
+
db_path: str,
|
|
20
|
+
url: str = "tcp://127.0.0.1:31337",
|
|
21
|
+
) -> None:
|
|
22
|
+
scheme, location = parse_target(url)
|
|
23
|
+
socket_path = None
|
|
24
|
+
|
|
25
|
+
with DB(db_path) as db:
|
|
26
|
+
if scheme == "tcp":
|
|
27
|
+
host, port = parse_tcp_location(location)
|
|
28
|
+
server = await serve(db, host=host, port=port)
|
|
29
|
+
else:
|
|
30
|
+
socket_path = str(Path(parse_unix_location(location)))
|
|
31
|
+
server = await serve_unix(db, path=socket_path)
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
await server.serve_forever()
|
|
35
|
+
finally:
|
|
36
|
+
server.close()
|
|
37
|
+
await server.wait_closed()
|
|
38
|
+
if socket_path is not None:
|
|
39
|
+
with suppress(FileNotFoundError):
|
|
40
|
+
Path(socket_path).unlink()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@app.command
|
|
44
|
+
async def server(
|
|
45
|
+
db_path: str = "db",
|
|
46
|
+
url: str = "tcp://127.0.0.1:31337",
|
|
47
|
+
) -> None:
|
|
48
|
+
"""Run the ShelfDB protocol server."""
|
|
49
|
+
await run_server(db_path=db_path, url=url)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main(argv: list[str] | None = None) -> None:
|
|
53
|
+
app(argv, backend="asyncio", result_action="return_none")
|
|
@@ -6,7 +6,7 @@ from asyncio import StreamReader, StreamWriter, open_connection, open_unix_conne
|
|
|
6
6
|
from contextlib import suppress
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
|
-
from shelfdb.protocol import read_response, write_request
|
|
9
|
+
from shelfdb.protocol.protocol import read_response, write_request
|
|
10
10
|
from shelfdb.protocol.query_result import denormalize_query_result
|
|
11
11
|
from shelfdb.shelf import Item
|
|
12
12
|
from shelfdb.target import parse_target, parse_tcp_location, parse_unix_location
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Wire helpers, with backward-compatible lazy server-side exports."""
|
|
2
|
+
|
|
3
|
+
from importlib import import_module
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from .protocol import (
|
|
7
|
+
MAX_FRAME_SIZE,
|
|
8
|
+
decode_request,
|
|
9
|
+
decode_response,
|
|
10
|
+
encode_request,
|
|
11
|
+
encode_response,
|
|
12
|
+
frame_payload,
|
|
13
|
+
read_payload,
|
|
14
|
+
read_request,
|
|
15
|
+
read_response,
|
|
16
|
+
write_payload,
|
|
17
|
+
write_request,
|
|
18
|
+
write_response,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from .server import handle_client, serve, serve_unix
|
|
23
|
+
from .session import Session
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def __getattr__(name: str):
|
|
27
|
+
if name in {"handle_client", "serve", "serve_unix"}:
|
|
28
|
+
module = import_module(".server", __name__)
|
|
29
|
+
elif name == "Session":
|
|
30
|
+
module = import_module(".session", __name__)
|
|
31
|
+
else:
|
|
32
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
33
|
+
value = getattr(module, name)
|
|
34
|
+
globals()[name] = value
|
|
35
|
+
return value
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def __dir__() -> list[str]:
|
|
39
|
+
return sorted(set(globals()) | set(__all__))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
"MAX_FRAME_SIZE",
|
|
44
|
+
"decode_request",
|
|
45
|
+
"decode_response",
|
|
46
|
+
"encode_request",
|
|
47
|
+
"encode_response",
|
|
48
|
+
"frame_payload",
|
|
49
|
+
"handle_client",
|
|
50
|
+
"read_payload",
|
|
51
|
+
"read_request",
|
|
52
|
+
"read_response",
|
|
53
|
+
"Session",
|
|
54
|
+
"serve",
|
|
55
|
+
"serve_unix",
|
|
56
|
+
"write_payload",
|
|
57
|
+
"write_request",
|
|
58
|
+
"write_response",
|
|
59
|
+
]
|
|
@@ -10,7 +10,8 @@ import dill
|
|
|
10
10
|
import msgpack
|
|
11
11
|
|
|
12
12
|
_FRAME_HEADER: Final = struct.Struct(">I")
|
|
13
|
-
|
|
13
|
+
# The unsigned 32-bit length header is the only frame-size limit.
|
|
14
|
+
MAX_FRAME_SIZE: Final = (1 << 32) - 1
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class WritableStream(Protocol):
|
|
@@ -20,9 +21,9 @@ class WritableStream(Protocol):
|
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def frame_payload(payload: bytes) -> bytes:
|
|
23
|
-
"""Prefix payload
|
|
24
|
+
"""Prefix a buffered payload with its unsigned 32-bit big-endian length."""
|
|
24
25
|
if len(payload) > MAX_FRAME_SIZE:
|
|
25
|
-
raise ValueError("payload exceeds
|
|
26
|
+
raise ValueError("payload exceeds 32-bit frame length (4294967295 bytes)")
|
|
26
27
|
return _FRAME_HEADER.pack(len(payload)) + payload
|
|
27
28
|
|
|
28
29
|
|
|
@@ -50,8 +51,6 @@ async def read_payload(reader: StreamReader) -> bytes:
|
|
|
50
51
|
"""Read one length-prefixed payload from a stream."""
|
|
51
52
|
size_data = await reader.readexactly(_FRAME_HEADER.size)
|
|
52
53
|
(size,) = _FRAME_HEADER.unpack(size_data)
|
|
53
|
-
if size > MAX_FRAME_SIZE:
|
|
54
|
-
raise ValueError("frame exceeds max frame size")
|
|
55
54
|
return await reader.readexactly(size)
|
|
56
55
|
|
|
57
56
|
|
|
@@ -9,8 +9,8 @@ from typing import Any
|
|
|
9
9
|
import lmdb
|
|
10
10
|
|
|
11
11
|
# lib: local
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
12
|
+
from .query import ShelfQuery
|
|
13
|
+
from .storage import ShelfCursor, ShelfStore
|
|
14
14
|
|
|
15
15
|
LmdbEnvironment = Any
|
|
16
16
|
LmdbTransaction = Any
|
|
@@ -20,7 +20,7 @@ from itertools import islice
|
|
|
20
20
|
from typing import Any
|
|
21
21
|
|
|
22
22
|
from .schema import UNDEF, Item, MutationResult
|
|
23
|
-
from .
|
|
23
|
+
from .storage import ShelfCursor, ShelfStore
|
|
24
24
|
|
|
25
25
|
type Transform = Callable[[Iterator[Item]], Iterator[Item]]
|
|
26
26
|
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: shelfdb-usage
|
|
3
|
-
description: Use when writing, reviewing, or explaining ShelfDB local database code, async client/server code, transactions, or fluent queries.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# ShelfDB usage
|
|
7
|
-
|
|
8
|
-
Choose access mode from the deployment:
|
|
9
|
-
|
|
10
|
-
- Use `shelfdb.shelf.DB` when the process can open the LMDB database directly.
|
|
11
|
-
- Use `shelfdb.client.Client` when a separate trusted process runs `shelfdb server`.
|
|
12
|
-
|
|
13
|
-
## Safety boundary
|
|
14
|
-
|
|
15
|
-
The remote protocol uses `dill` so queries can contain Python callables. Deserializing a
|
|
16
|
-
request can execute arbitrary code in the server process. Never expose the server to
|
|
17
|
-
untrusted clients or public networks.
|
|
18
|
-
|
|
19
|
-
## Local API
|
|
20
|
-
|
|
21
|
-
Use synchronous transactions. Operations execute directly; do not add `.query()`.
|
|
22
|
-
|
|
23
|
-
```python
|
|
24
|
-
from shelfdb.shelf import DB
|
|
25
|
-
|
|
26
|
-
with DB("db") as db:
|
|
27
|
-
with db.transaction(write=False) as tx:
|
|
28
|
-
users = list(tx.shelf("users").items())
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Remote API
|
|
32
|
-
|
|
33
|
-
Connect asynchronously, use read transactions by default, and pass `write=True` for
|
|
34
|
-
mutations. Fluent operations only build a remote query; `await ...query()` sends it.
|
|
35
|
-
|
|
36
|
-
```python
|
|
37
|
-
from shelfdb.client import Client
|
|
38
|
-
|
|
39
|
-
client = await Client.connect("tcp://127.0.0.1:31337")
|
|
40
|
-
try:
|
|
41
|
-
async with client.transaction() as tx:
|
|
42
|
-
users = await tx.shelf("users").items().query()
|
|
43
|
-
finally:
|
|
44
|
-
await client.close()
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Rules
|
|
48
|
-
|
|
49
|
-
- Use `client.transaction()` for remote reads.
|
|
50
|
-
- Use `client.transaction(write=True)` for remote mutations.
|
|
51
|
-
- Always close a remote client with `await client.close()`.
|
|
52
|
-
- Keep local and remote terminal behavior distinct.
|
|
53
|
-
- In a ShelfDB source checkout, use `tests/usage/` as executable examples.
|
|
54
|
-
- Consult the current documentation before assuming unsupported query behavior.
|
|
55
|
-
|
|
56
|
-
Documentation: https://keenlycode.github.io/shelfdb/
|
shelfdb-3.0.2/src/shelfdb/cli.py
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
"""Command-line interface for ShelfDB."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import shutil
|
|
6
|
-
import sysconfig
|
|
7
|
-
from contextlib import suppress
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
|
|
10
|
-
from cyclopts import App
|
|
11
|
-
|
|
12
|
-
from shelfdb.protocol import serve, serve_unix
|
|
13
|
-
from shelfdb.shelf import DB
|
|
14
|
-
from shelfdb.target import parse_target, parse_tcp_location, parse_unix_location
|
|
15
|
-
|
|
16
|
-
app = App("shelfdb")
|
|
17
|
-
DEFAULT_AI_SKILL_INSTALL_PATH = ".agents/skills/shelfdb-usage"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def repo_root() -> Path:
|
|
21
|
-
return Path(__file__).resolve().parents[2]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def bundled_ai_skill_path() -> Path:
|
|
25
|
-
data_root = Path(sysconfig.get_path("data"))
|
|
26
|
-
installed_path = data_root / "shelfdb-usage"
|
|
27
|
-
if installed_path.exists():
|
|
28
|
-
return installed_path
|
|
29
|
-
|
|
30
|
-
repo_path = repo_root() / "ai-skill" / "shelfdb-usage"
|
|
31
|
-
if repo_path.exists():
|
|
32
|
-
return repo_path
|
|
33
|
-
|
|
34
|
-
raise FileNotFoundError("Bundled ShelfDB AI skill not found.")
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def install_ai_skill(destination: Path, *, force: bool = False) -> Path:
|
|
38
|
-
source_file = bundled_ai_skill_path() / "SKILL.md"
|
|
39
|
-
if not source_file.is_file():
|
|
40
|
-
raise FileNotFoundError("Bundled ShelfDB SKILL.md not found.")
|
|
41
|
-
|
|
42
|
-
if destination.exists():
|
|
43
|
-
if not destination.is_dir():
|
|
44
|
-
raise NotADirectoryError(
|
|
45
|
-
f"AI skill destination is not a directory: {destination}"
|
|
46
|
-
)
|
|
47
|
-
if not force:
|
|
48
|
-
raise FileExistsError(
|
|
49
|
-
f"AI skill destination already exists: {destination}. Use --force to replace SKILL.md."
|
|
50
|
-
)
|
|
51
|
-
else:
|
|
52
|
-
destination.mkdir(parents=True)
|
|
53
|
-
|
|
54
|
-
shutil.copy2(source_file, destination / "SKILL.md")
|
|
55
|
-
return destination
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def prompt_ai_skill_install_path(
|
|
59
|
-
default_path: str = DEFAULT_AI_SKILL_INSTALL_PATH,
|
|
60
|
-
) -> Path:
|
|
61
|
-
response = input(f"Install ShelfDB AI skill to [{default_path}]: ").strip()
|
|
62
|
-
return Path(response or default_path)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
async def run_server(
|
|
66
|
-
*,
|
|
67
|
-
db_path: str,
|
|
68
|
-
url: str = "tcp://127.0.0.1:31337",
|
|
69
|
-
) -> None:
|
|
70
|
-
scheme, location = parse_target(url)
|
|
71
|
-
socket_path = None
|
|
72
|
-
|
|
73
|
-
with DB(db_path) as db:
|
|
74
|
-
if scheme == "tcp":
|
|
75
|
-
host, port = parse_tcp_location(location)
|
|
76
|
-
server = await serve(db, host=host, port=port)
|
|
77
|
-
else:
|
|
78
|
-
socket_path = str(Path(parse_unix_location(location)))
|
|
79
|
-
server = await serve_unix(db, path=socket_path)
|
|
80
|
-
|
|
81
|
-
try:
|
|
82
|
-
await server.serve_forever()
|
|
83
|
-
finally:
|
|
84
|
-
server.close()
|
|
85
|
-
await server.wait_closed()
|
|
86
|
-
if socket_path is not None:
|
|
87
|
-
with suppress(FileNotFoundError):
|
|
88
|
-
Path(socket_path).unlink()
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@app.command
|
|
92
|
-
async def server(
|
|
93
|
-
db_path: str = "db",
|
|
94
|
-
url: str = "tcp://127.0.0.1:31337",
|
|
95
|
-
) -> None:
|
|
96
|
-
"""Run the ShelfDB protocol server."""
|
|
97
|
-
await run_server(db_path=db_path, url=url)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
@app.command(name="ai-skill-install")
|
|
101
|
-
def ai_skill_install(path: str | None = None, force: bool = False) -> None:
|
|
102
|
-
"""Install the bundled ShelfDB AI skill to a local path."""
|
|
103
|
-
destination = Path(path) if path is not None else prompt_ai_skill_install_path()
|
|
104
|
-
installed = install_ai_skill(destination, force=force)
|
|
105
|
-
print(f"Installed ShelfDB AI skill to {installed}")
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def main(argv: list[str] | None = None) -> None:
|
|
109
|
-
app(argv, backend="asyncio", result_action="return_none")
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
from .protocol import (
|
|
2
|
-
MAX_FRAME_SIZE,
|
|
3
|
-
decode_request,
|
|
4
|
-
decode_response,
|
|
5
|
-
encode_request,
|
|
6
|
-
encode_response,
|
|
7
|
-
frame_payload,
|
|
8
|
-
read_payload,
|
|
9
|
-
read_request,
|
|
10
|
-
read_response,
|
|
11
|
-
write_payload,
|
|
12
|
-
write_request,
|
|
13
|
-
write_response,
|
|
14
|
-
)
|
|
15
|
-
from .server import handle_client, serve, serve_unix
|
|
16
|
-
from .session import Session
|
|
17
|
-
|
|
18
|
-
__all__ = [
|
|
19
|
-
"MAX_FRAME_SIZE",
|
|
20
|
-
"decode_request",
|
|
21
|
-
"decode_response",
|
|
22
|
-
"encode_request",
|
|
23
|
-
"encode_response",
|
|
24
|
-
"frame_payload",
|
|
25
|
-
"handle_client",
|
|
26
|
-
"read_payload",
|
|
27
|
-
"read_request",
|
|
28
|
-
"read_response",
|
|
29
|
-
"Session",
|
|
30
|
-
"serve",
|
|
31
|
-
"serve_unix",
|
|
32
|
-
"write_payload",
|
|
33
|
-
"write_request",
|
|
34
|
-
"write_response",
|
|
35
|
-
]
|
|
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
|
/shelfdb-3.0.2/src/shelfdb/shelf/shelf/shelf.py → /shelfdb-3.1.0/src/shelfdb/shelf/storage.py
RENAMED
|
File without changes
|
|
File without changes
|