beaver-db 0.19.3__tar.gz → 0.20.2__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.

Potentially problematic release.


This version of beaver-db might be problematic. Click here for more details.

Files changed (140) hide show
  1. {beaver_db-0.19.3 → beaver_db-0.20.2}/PKG-INFO +42 -4
  2. {beaver_db-0.19.3 → beaver_db-0.20.2}/README.md +41 -3
  3. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/__init__.py +1 -1
  4. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/blobs.py +60 -1
  5. beaver_db-0.20.2/beaver/client.py +360 -0
  6. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/collections.py +43 -1
  7. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/core.py +4 -4
  8. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/dicts.py +51 -1
  9. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/lists.py +49 -2
  10. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/logs.py +72 -7
  11. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/queues.py +82 -1
  12. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/types.py +16 -2
  13. beaver_db-0.20.2/docs/.gitignore +2 -0
  14. beaver_db-0.20.2/docs/.quarto/cites/index.json +1 -0
  15. beaver_db-0.20.2/docs/.quarto/idx/dev-architecture.qmd.json +1 -0
  16. beaver_db-0.20.2/docs/.quarto/idx/dev-concurrency.qmd.json +1 -0
  17. beaver_db-0.20.2/docs/.quarto/idx/dev-contributing.qmd.json +1 -0
  18. beaver_db-0.20.2/docs/.quarto/idx/dev-search.qmd.json +1 -0
  19. beaver_db-0.20.2/docs/.quarto/idx/guide-collections.qmd.json +1 -0
  20. beaver_db-0.20.2/docs/.quarto/idx/guide-concurrency.qmd.json +1 -0
  21. beaver_db-0.20.2/docs/.quarto/idx/guide-deployment.qmd.json +1 -0
  22. beaver_db-0.20.2/docs/.quarto/idx/guide-dicts-blobs.qmd.json +1 -0
  23. beaver_db-0.20.2/docs/.quarto/idx/guide-lists-queues.qmd.json +1 -0
  24. beaver_db-0.20.2/docs/.quarto/idx/guide-realtime.qmd.json +1 -0
  25. beaver_db-0.20.2/docs/.quarto/idx/index.qmd.json +1 -0
  26. beaver_db-0.20.2/docs/.quarto/idx/quickstart.qmd.json +1 -0
  27. beaver_db-0.20.2/docs/.quarto/project-cache/08c652dc55400ed7625e092b14557eff +399 -0
  28. beaver_db-0.20.2/docs/.quarto/project-cache/78e7cce51608b23bc20ef50628d80ee9 +461 -0
  29. beaver_db-0.20.2/docs/.quarto/project-cache/deno-kv-file +0 -0
  30. beaver_db-0.20.2/docs/.quarto/project-cache/efb0c620c38ca7c31075f463c90f647b +461 -0
  31. beaver_db-0.20.2/docs/.quarto/xref/1e84074e +1 -0
  32. beaver_db-0.20.2/docs/.quarto/xref/41b07c8e +1 -0
  33. beaver_db-0.20.2/docs/.quarto/xref/4a9bbebe +1 -0
  34. beaver_db-0.20.2/docs/.quarto/xref/57337df9 +1 -0
  35. beaver_db-0.20.2/docs/.quarto/xref/674e8fe6 +1 -0
  36. beaver_db-0.20.2/docs/.quarto/xref/6a721ef9 +1 -0
  37. beaver_db-0.20.2/docs/.quarto/xref/807a23ea +1 -0
  38. beaver_db-0.20.2/docs/.quarto/xref/9c486562 +1 -0
  39. beaver_db-0.20.2/docs/.quarto/xref/INDEX +47 -0
  40. beaver_db-0.20.2/docs/.quarto/xref/b2f009dd +1 -0
  41. beaver_db-0.20.2/docs/.quarto/xref/c40f73a8 +1 -0
  42. beaver_db-0.20.2/docs/.quarto/xref/cf0735bd +1 -0
  43. beaver_db-0.20.2/docs/.quarto/xref/d7f66f00 +1 -0
  44. beaver_db-0.20.2/docs/.quarto/xref/f22e8d4c +1 -0
  45. beaver_db-0.20.2/docs/.quarto/xref/f9d58622 +1 -0
  46. beaver_db-0.20.2/docs/.quarto/xref/fa9f9ca6 +1 -0
  47. beaver_db-0.20.2/docs/_book/BeaverDB--The-Complete-Guide.pdf +0 -0
  48. beaver_db-0.20.2/docs/_book/dev-architecture.html +676 -0
  49. beaver_db-0.20.2/docs/_book/dev-concurrency.html +676 -0
  50. beaver_db-0.20.2/docs/_book/dev-contributing.html +664 -0
  51. beaver_db-0.20.2/docs/_book/dev-search.html +670 -0
  52. beaver_db-0.20.2/docs/_book/guide-collections.html +683 -0
  53. beaver_db-0.20.2/docs/_book/guide-concurrency.html +669 -0
  54. beaver_db-0.20.2/docs/_book/guide-deployment.html +675 -0
  55. beaver_db-0.20.2/docs/_book/guide-dicts-blobs.html +671 -0
  56. beaver_db-0.20.2/docs/_book/guide-lists-queues.html +671 -0
  57. beaver_db-0.20.2/docs/_book/guide-realtime.html +671 -0
  58. beaver_db-0.20.2/docs/_book/index.html +734 -0
  59. beaver_db-0.20.2/docs/_book/quickstart.html +784 -0
  60. beaver_db-0.20.2/docs/_book/search.json +192 -0
  61. beaver_db-0.20.2/docs/_book/site_libs/bootstrap/bootstrap-5d653dd4d7566aed16f42eb06e547978.min.css +12 -0
  62. beaver_db-0.20.2/docs/_book/site_libs/bootstrap/bootstrap-icons.css +2078 -0
  63. beaver_db-0.20.2/docs/_book/site_libs/bootstrap/bootstrap-icons.woff +0 -0
  64. beaver_db-0.20.2/docs/_book/site_libs/bootstrap/bootstrap.min.js +7 -0
  65. beaver_db-0.20.2/docs/_book/site_libs/clipboard/clipboard.min.js +7 -0
  66. beaver_db-0.20.2/docs/_book/site_libs/quarto-html/anchor.min.js +9 -0
  67. beaver_db-0.20.2/docs/_book/site_libs/quarto-html/popper.min.js +6 -0
  68. beaver_db-0.20.2/docs/_book/site_libs/quarto-html/quarto-syntax-highlighting-de070a7b0ab54f8780927367ac907214.css +236 -0
  69. beaver_db-0.20.2/docs/_book/site_libs/quarto-html/quarto.js +845 -0
  70. beaver_db-0.20.2/docs/_book/site_libs/quarto-html/tabsets/tabsets.js +95 -0
  71. beaver_db-0.20.2/docs/_book/site_libs/quarto-html/tippy.css +1 -0
  72. beaver_db-0.20.2/docs/_book/site_libs/quarto-html/tippy.umd.min.js +2 -0
  73. beaver_db-0.20.2/docs/_book/site_libs/quarto-nav/quarto-nav.js +325 -0
  74. beaver_db-0.20.2/docs/_book/site_libs/quarto-search/autocomplete.umd.js +3 -0
  75. beaver_db-0.20.2/docs/_book/site_libs/quarto-search/fuse.min.js +9 -0
  76. beaver_db-0.20.2/docs/_book/site_libs/quarto-search/quarto-search.js +1290 -0
  77. beaver_db-0.20.2/docs/_quarto.yml +62 -0
  78. beaver_db-0.20.2/docs/cover.png +0 -0
  79. beaver_db-0.20.2/docs/dev-architecture.qmd +15 -0
  80. beaver_db-0.20.2/docs/dev-concurrency.qmd +15 -0
  81. beaver_db-0.20.2/docs/dev-contributing.qmd +9 -0
  82. beaver_db-0.20.2/docs/dev-search.qmd +11 -0
  83. beaver_db-0.20.2/docs/guide-collections.qmd +20 -0
  84. beaver_db-0.20.2/docs/guide-concurrency.qmd +10 -0
  85. beaver_db-0.20.2/docs/guide-deployment.qmd +14 -0
  86. beaver_db-0.20.2/docs/guide-dicts-blobs.qmd +12 -0
  87. beaver_db-0.20.2/docs/guide-lists-queues.qmd +12 -0
  88. beaver_db-0.20.2/docs/guide-realtime.qmd +12 -0
  89. beaver_db-0.20.2/docs/index.qmd +89 -0
  90. beaver_db-0.20.2/docs/logo.png +0 -0
  91. beaver_db-0.20.2/docs/quickstart.qmd +120 -0
  92. beaver_db-0.20.2/issues/13-refactor-adopt-pydantic-deprecate-beavermodel-and-refactor-document-to-be-generic.md +83 -0
  93. {beaver_db-0.19.3/issues → beaver_db-0.20.2/issues/closed}/10-expose-dblock-functionality-on-all-high-level-data-managers.md +1 -1
  94. beaver_db-0.20.2/issues/closed/12-add-dump-method-for-json-export-to-all-data-managers.md +195 -0
  95. {beaver_db-0.19.3/issues → beaver_db-0.20.2/issues/closed}/9-type-safe-wrappers-based-on-pydantic-compatible-models.md +1 -1
  96. beaver_db-0.20.2/logo.png +0 -0
  97. {beaver_db-0.19.3 → beaver_db-0.20.2}/pyproject.toml +1 -1
  98. {beaver_db-0.19.3 → beaver_db-0.20.2}/.dockerignore +0 -0
  99. {beaver_db-0.19.3 → beaver_db-0.20.2}/.gitignore +0 -0
  100. {beaver_db-0.19.3 → beaver_db-0.20.2}/.python-version +0 -0
  101. {beaver_db-0.19.3 → beaver_db-0.20.2}/LICENSE +0 -0
  102. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/channels.py +0 -0
  103. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/cli.py +0 -0
  104. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/locks.py +0 -0
  105. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/server.py +0 -0
  106. {beaver_db-0.19.3 → beaver_db-0.20.2}/beaver/vectors.py +0 -0
  107. {beaver_db-0.19.3 → beaver_db-0.20.2}/design.md +0 -0
  108. {beaver_db-0.19.3 → beaver_db-0.20.2}/dockerfile +0 -0
  109. {beaver_db-0.19.3 → beaver_db-0.20.2/docs/_book}/logo.png +0 -0
  110. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/async_pubsub.py +0 -0
  111. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/blobs.py +0 -0
  112. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/cache.py +0 -0
  113. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/fts.py +0 -0
  114. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/fuzzy.py +0 -0
  115. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/general_test.py +0 -0
  116. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/graph.py +0 -0
  117. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/kvstore.py +0 -0
  118. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/list.py +0 -0
  119. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/locks.py +0 -0
  120. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/logs.py +0 -0
  121. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/pqueue.py +0 -0
  122. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/producer_consumer.py +0 -0
  123. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/publisher.py +0 -0
  124. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/pubsub.py +0 -0
  125. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/rerank.py +0 -0
  126. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/stress_vectors.py +0 -0
  127. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/subscriber.py +0 -0
  128. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/textual_chat.css +0 -0
  129. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/textual_chat.py +0 -0
  130. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/type_hints.py +0 -0
  131. {beaver_db-0.19.3 → beaver_db-0.20.2}/examples/vector.py +0 -0
  132. {beaver_db-0.19.3 → beaver_db-0.20.2}/issues/2-comprehensive-async-wrappers.md +0 -0
  133. {beaver_db-0.19.3 → beaver_db-0.20.2}/issues/6-drop-in-replacement-for-beaver-rest-server-client.md +0 -0
  134. {beaver_db-0.19.3 → beaver_db-0.20.2}/issues/7-replace-faiss-with-simpler-linear-numpy-vectorial-search.md +0 -0
  135. {beaver_db-0.19.3 → beaver_db-0.20.2}/issues/closed/1-refactor-vector-store-to-use-faiss.md +0 -0
  136. {beaver_db-0.19.3 → beaver_db-0.20.2}/issues/closed/11-a-new-issue-for-gabriel.md +0 -0
  137. {beaver_db-0.19.3 → beaver_db-0.20.2}/issues/closed/5-add-dblock-for-inter-process-synchronization.md +0 -0
  138. {beaver_db-0.19.3 → beaver_db-0.20.2}/issues/closed/8-first-class-synchronization-primitive.md +0 -0
  139. {beaver_db-0.19.3 → beaver_db-0.20.2}/makefile +0 -0
  140. {beaver_db-0.19.3 → beaver_db-0.20.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beaver-db
3
- Version: 0.19.3
3
+ Version: 0.20.2
4
4
  Summary: Fast, embedded, and multi-modal DB based on SQLite for AI-powered applications.
5
5
  License-File: LICENSE
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -71,6 +71,7 @@ Description-Content-Type: text/markdown
71
71
  - **Built-in REST API Server (Optional)**: Instantly serve your database over a RESTful API with automatic OpenAPI documentation using FastAPI.
72
72
  - **Full-Featured CLI Client (Optional)**: Interact with your database directly from the command line for administrative tasks and data exploration.
73
73
  - **Optional Type-Safety:** Although the database is schemaless, you can use a minimalistic typing system for automatic serialization and deserialization that is Pydantic-compatible out of the box.
74
+ - **Data Export & Backups:** Dump any dictionary, list, collection, queue, blob, or log structure to a portable JSON file with a single `.dump()` command.
74
75
 
75
76
  ## How Beaver is Implemented
76
77
 
@@ -203,6 +204,43 @@ beaver client --database data.db dict app_config get theme
203
204
  beaver client --database data.db list daily_tasks push "Review PRs"
204
205
  ```
205
206
 
207
+ ## Data Export for Backups
208
+
209
+ All data structures (`dict`, `list`, `collection`, `queue`, `log`, and `blobs`) support a `.dump()` method for easy backups and migration. You can either write the data directly to a JSON file or get it as a Python dictionary.
210
+
211
+ ```python
212
+ import json
213
+ from beaver import BeaverDB
214
+
215
+ db = BeaverDB("my_app.db")
216
+ config = db.dict("app_config")
217
+
218
+ # Add some data
219
+ config["theme"] = "dark"
220
+ config["user_id"] = 456
221
+
222
+ # Dump the dictionary's contents to a JSON file
223
+ with open("config_backup.json", "w") as f:
224
+ config.dump(f)
225
+
226
+ # 'config_backup.json' now contains:
227
+ # {
228
+ # "metadata": {
229
+ # "type": "Dict",
230
+ # "name": "app_config",
231
+ # "count": 2,
232
+ # "dump_date": "2025-11-02T09:05:10.123456Z"
233
+ # },
234
+ # "items": [
235
+ # {"key": "theme", "value": "dark"},
236
+ # {"key": "user_id", "value": 456}
237
+ # ]
238
+ # }
239
+
240
+ # You can also get the dump as a Python object
241
+ dump_data = config.dump()
242
+ ```
243
+
206
244
  ## Things You Can Build with Beaver
207
245
 
208
246
  Here are a few ideas to inspire your next project, showcasing how to combine Beaver's features to build powerful local applications.
@@ -446,9 +484,9 @@ For more in-depth examples, check out the scripts in the `examples/` directory:
446
484
  These are some of the features and improvements planned for future releases:
447
485
 
448
486
  - **[Issue #2](https://github.com/syalia-srl/beaver/issues/2) Comprehensive async wrappers**: Extend the async support with on-demand wrappers for all data structures, not just channels.
449
- - **[Issue #9](https://github.com/syalia-srl/beaver/issues/2) Type-safe wrappers based on Pydantic-compatible models**: Enhance the built-in `Model` to handle recursive and embedded types and provide Pydantic compatibility.
450
- - **[Issue #6](https://github.com/syalia-srl/beaver/issues/2) Drop-in replacement for Beaver REST server client**: Implement a `BeaverClient` class that acts as a drop-in replacement for `BeaverDB` but works against the REST API server.
451
- - **[Issue #7](https://github.com/syalia-srl/beaver/issues/2) Replace `faiss` with simpler, linear `numpy` vectorial search**: Investigate removing the heavy `faiss` dependency in favor of a pure `numpy` implementation to improve installation simplicity, accepting a trade-off in search performance for O(1) installation.
487
+ - **[Issue #6](https://github.com/syalia-srl/beaver/issues/6) Drop-in replacement for Beaver REST server client**: Implement a `BeaverClient` class that acts as a drop-in replacement for `BeaverDB` but works against the REST API server.
488
+ - **[Issue #7](https://github.com/syalia-srl/beaver/issues/7) Replace `faiss` with simpler, linear `numpy` vectorial search**: Investigate removing the heavy `faiss` dependency in favor of a pure `numpy` implementation to improve installation simplicity, accepting a trade-off in search performance for O(1) installation.
489
+ - **[Issue #9](https://github.com/syalia-srl/beaver/issues/9) Type-safe wrappers based on Pydantic-compatible models**: Enhance the built-in `Model` to handle recursive and embedded types and provide Pydantic compatibility.
452
490
 
453
491
 
454
492
  If you think of something that would make `beaver` more useful for your use case, please open an issue and/or submit a pull request.
@@ -46,6 +46,7 @@
46
46
  - **Built-in REST API Server (Optional)**: Instantly serve your database over a RESTful API with automatic OpenAPI documentation using FastAPI.
47
47
  - **Full-Featured CLI Client (Optional)**: Interact with your database directly from the command line for administrative tasks and data exploration.
48
48
  - **Optional Type-Safety:** Although the database is schemaless, you can use a minimalistic typing system for automatic serialization and deserialization that is Pydantic-compatible out of the box.
49
+ - **Data Export & Backups:** Dump any dictionary, list, collection, queue, blob, or log structure to a portable JSON file with a single `.dump()` command.
49
50
 
50
51
  ## How Beaver is Implemented
51
52
 
@@ -178,6 +179,43 @@ beaver client --database data.db dict app_config get theme
178
179
  beaver client --database data.db list daily_tasks push "Review PRs"
179
180
  ```
180
181
 
182
+ ## Data Export for Backups
183
+
184
+ All data structures (`dict`, `list`, `collection`, `queue`, `log`, and `blobs`) support a `.dump()` method for easy backups and migration. You can either write the data directly to a JSON file or get it as a Python dictionary.
185
+
186
+ ```python
187
+ import json
188
+ from beaver import BeaverDB
189
+
190
+ db = BeaverDB("my_app.db")
191
+ config = db.dict("app_config")
192
+
193
+ # Add some data
194
+ config["theme"] = "dark"
195
+ config["user_id"] = 456
196
+
197
+ # Dump the dictionary's contents to a JSON file
198
+ with open("config_backup.json", "w") as f:
199
+ config.dump(f)
200
+
201
+ # 'config_backup.json' now contains:
202
+ # {
203
+ # "metadata": {
204
+ # "type": "Dict",
205
+ # "name": "app_config",
206
+ # "count": 2,
207
+ # "dump_date": "2025-11-02T09:05:10.123456Z"
208
+ # },
209
+ # "items": [
210
+ # {"key": "theme", "value": "dark"},
211
+ # {"key": "user_id", "value": 456}
212
+ # ]
213
+ # }
214
+
215
+ # You can also get the dump as a Python object
216
+ dump_data = config.dump()
217
+ ```
218
+
181
219
  ## Things You Can Build with Beaver
182
220
 
183
221
  Here are a few ideas to inspire your next project, showcasing how to combine Beaver's features to build powerful local applications.
@@ -421,9 +459,9 @@ For more in-depth examples, check out the scripts in the `examples/` directory:
421
459
  These are some of the features and improvements planned for future releases:
422
460
 
423
461
  - **[Issue #2](https://github.com/syalia-srl/beaver/issues/2) Comprehensive async wrappers**: Extend the async support with on-demand wrappers for all data structures, not just channels.
424
- - **[Issue #9](https://github.com/syalia-srl/beaver/issues/2) Type-safe wrappers based on Pydantic-compatible models**: Enhance the built-in `Model` to handle recursive and embedded types and provide Pydantic compatibility.
425
- - **[Issue #6](https://github.com/syalia-srl/beaver/issues/2) Drop-in replacement for Beaver REST server client**: Implement a `BeaverClient` class that acts as a drop-in replacement for `BeaverDB` but works against the REST API server.
426
- - **[Issue #7](https://github.com/syalia-srl/beaver/issues/2) Replace `faiss` with simpler, linear `numpy` vectorial search**: Investigate removing the heavy `faiss` dependency in favor of a pure `numpy` implementation to improve installation simplicity, accepting a trade-off in search performance for O(1) installation.
462
+ - **[Issue #6](https://github.com/syalia-srl/beaver/issues/6) Drop-in replacement for Beaver REST server client**: Implement a `BeaverClient` class that acts as a drop-in replacement for `BeaverDB` but works against the REST API server.
463
+ - **[Issue #7](https://github.com/syalia-srl/beaver/issues/7) Replace `faiss` with simpler, linear `numpy` vectorial search**: Investigate removing the heavy `faiss` dependency in favor of a pure `numpy` implementation to improve installation simplicity, accepting a trade-off in search performance for O(1) installation.
464
+ - **[Issue #9](https://github.com/syalia-srl/beaver/issues/9) Type-safe wrappers based on Pydantic-compatible models**: Enhance the built-in `Model` to handle recursive and embedded types and provide Pydantic compatibility.
427
465
 
428
466
 
429
467
  If you think of something that would make `beaver` more useful for your use case, please open an issue and/or submit a pull request.
@@ -2,4 +2,4 @@ from .core import BeaverDB
2
2
  from .types import Model
3
3
  from .collections import Document, WalkDirection
4
4
 
5
- __version__ = "0.19.3"
5
+ __version__ = "0.20.2"
@@ -1,6 +1,8 @@
1
+ import base64
2
+ from datetime import datetime, timezone
1
3
  import json
2
4
  import sqlite3
3
- from typing import Any, Dict, Iterator, NamedTuple, Optional, Type, TypeVar
5
+ from typing import IO, Any, Dict, Iterator, NamedTuple, Optional, Type, TypeVar
4
6
  from .types import JsonSerializable, IDatabase
5
7
  from .locks import LockManager
6
8
 
@@ -170,3 +172,60 @@ class BlobManager[M]:
170
172
  def __exit__(self, exc_type, exc_val, exc_tb):
171
173
  """Releases the lock when exiting a 'with' statement."""
172
174
  self.release()
175
+
176
+ def _get_dump_object(self) -> dict:
177
+ """Builds the JSON-compatible dump object."""
178
+
179
+ items_list = []
180
+ # __iter__ yields keys, so we get each blob
181
+ for key in self:
182
+ blob = self.get(key)
183
+ if blob:
184
+ metadata = blob.metadata
185
+
186
+ # Handle model instances in metadata
187
+ if self._model and isinstance(metadata, JsonSerializable):
188
+ metadata = json.loads(metadata.model_dump_json())
189
+
190
+ # Encode binary data to a base64 string
191
+ data_b64 = base64.b64encode(blob.data).decode('utf-8')
192
+
193
+ items_list.append({
194
+ "key": blob.key,
195
+ "metadata": metadata,
196
+ "data_b64": data_b64
197
+ })
198
+
199
+ metadata = {
200
+ "type": "BlobStore",
201
+ "name": self._name,
202
+ "count": len(items_list),
203
+ "dump_date": datetime.now(timezone.utc).isoformat()
204
+ }
205
+
206
+ return {
207
+ "metadata": metadata,
208
+ "items": items_list
209
+ }
210
+
211
+ def dump(self, fp: IO[str] | None = None) -> dict | None:
212
+ """
213
+ Dumps the entire contents of the blob store to a JSON-compatible
214
+ Python object or a file-like object.
215
+
216
+ Args:
217
+ fp: A file-like object opened in text mode (e.g., with 'w').
218
+ If provided, the JSON dump will be written to this file.
219
+ If None (default), the dump will be returned as a dictionary.
220
+
221
+ Returns:
222
+ A dictionary containing the dump if fp is None.
223
+ None if fp is provided.
224
+ """
225
+ dump_object = self._get_dump_object()
226
+
227
+ if fp:
228
+ json.dump(dump_object, fp, indent=2)
229
+ return None
230
+
231
+ return dump_object
@@ -0,0 +1,360 @@
1
+ """
2
+ This module implements the BeaverClient, a drop-in replacement for the
3
+ BeaverDB class that interacts with a remote BeaverDB server over HTTP.
4
+
5
+ This implementation relies on the 'httpx' library.
6
+ """
7
+
8
+ import threading
9
+ from typing import Generic, Type, TypeVar, Optional, Any
10
+ import httpx
11
+ from .types import JsonSerializable
12
+ from .collections import Document
13
+
14
+ # --- Base Remote Manager ---
15
+
16
+ class RemoteManager:
17
+ """Base class for all remote managers, holding the HTTP client."""
18
+ def __init__(
19
+ self,
20
+ client: httpx.Client,
21
+ name: str,
22
+ model: Type | None = None
23
+ ):
24
+ self._client = client
25
+ self._name = name
26
+ self._model = model
27
+ self._validate_model(model)
28
+
29
+ def _validate_model(self, model: Type | None):
30
+ """Helper to validate the model, mirroring BeaverDB.core"""
31
+ if model and not isinstance(model, JsonSerializable):
32
+ # This check might need to be refined if Pydantic isn't a direct dependency
33
+ pass
34
+
35
+ # --- Stub Remote Manager Implementations ---
36
+
37
+ class RemoteDictManager[T](RemoteManager):
38
+ """
39
+ Manages a remote dictionary. All methods will make HTTP requests.
40
+ (This is a skeleton implementation)
41
+ """
42
+ def __init__(
43
+ self,
44
+ client: httpx.Client,
45
+ name: str,
46
+ model: Type[T] | None = None
47
+ ):
48
+ super().__init__(client, name, model)
49
+ # Placeholder for manager-level locking, mirroring local implementation
50
+ self._lock = RemoteLockManager(client, f"__lock__dict__{name}", None)
51
+
52
+ def __setitem__(self, key: str, value: T):
53
+ raise NotImplementedError("This method will be implemented in a future milestone.")
54
+
55
+ def __getitem__(self, key: str) -> T:
56
+ raise NotImplementedError("This method will be implemented in a future milestone.")
57
+
58
+ # ... other dict methods (get, __delitem__, __len__, items, etc.) ...
59
+
60
+
61
+ class RemoteListManager[T](RemoteManager):
62
+ """
63
+ Manages a remote list. All methods will make HTTP requests.
64
+ (This is a skeleton implementation)
65
+ """
66
+ def __init__(
67
+ self,
68
+ client: httpx.Client,
69
+ name: str,
70
+ model: Type[T] | None = None
71
+ ):
72
+ super().__init__(client, name, model)
73
+ self._lock = RemoteLockManager(client, f"__lock__list__{name}", None)
74
+
75
+ def push(self, value: T):
76
+ raise NotImplementedError("This method will be implemented in a future milestone.")
77
+
78
+ # ... other list methods (pop, __getitem__, __setitem__, etc.) ...
79
+
80
+
81
+ class RemoteQueueManager[T](RemoteManager):
82
+ """
83
+ Manages a remote priority queue. All methods will make HTTP requests.
84
+ (This is a skeleton implementation)
85
+ """
86
+ def __init__(
87
+ self,
88
+ client: httpx.Client,
89
+ name: str,
90
+ model: Type[T] | None = None
91
+ ):
92
+ super().__init__(client, name, model)
93
+ self._lock = RemoteLockManager(client, f"__lock__queue__{name}", None)
94
+
95
+ def put(self, data: T, priority: float):
96
+ raise NotImplementedError("This method will be implemented in a future milestone.")
97
+
98
+ def get(self, block: bool = True, timeout: float | None = None):
99
+ raise NotImplementedError("This method will be implemented in a future milestone.")
100
+
101
+ # ... other queue methods (peek, __len__, etc.) ...
102
+
103
+
104
+ class RemoteCollectionManager[D](RemoteManager):
105
+ """
106
+ Manages a remote collection. All methods will make HTTP requests.
107
+ (This is a skeleton implementation)
108
+ """
109
+ def __init__(
110
+ self,
111
+ client: httpx.Client,
112
+ name: str,
113
+ model: Type[D] | None = None
114
+ ):
115
+ super().__init__(client, name, model)
116
+ self._lock = RemoteLockManager(client, f"__lock__collection__{name}", None)
117
+
118
+ def index(self, document: D, *, fts: bool | list[str] = True, fuzzy: bool = False):
119
+ raise NotImplementedError("This method will be implemented in a future milestone.")
120
+
121
+ def search(self, vector: list[float], top_k: int = 10):
122
+ raise NotImplementedError("This method will be implemented in a future milestone.")
123
+
124
+ # ... other collection methods (drop, match, connect, walk, etc.) ...
125
+
126
+
127
+ class RemoteChannelManager[T](RemoteManager):
128
+ """
129
+ Manages a remote pub/sub channel.
130
+ (This is a skeleton implementation)
131
+ """
132
+ def publish(self, payload: T):
133
+ raise NotImplementedError("This method will be implemented in a future milestone.")
134
+
135
+ def subscribe(self):
136
+ raise NotImplementedError("This method will be implemented in a future milestone.")
137
+
138
+
139
+ class RemoteBlobManager[M](RemoteManager):
140
+ """
141
+ Manages a remote blob store. All methods will make HTTP requests.
142
+ (This is a skeleton implementation)
143
+ """
144
+ def __init__(
145
+ self,
146
+ client: httpx.Client,
147
+ name: str,
148
+ model: Type[M] | None = None
149
+ ):
150
+ super().__init__(client, name, model)
151
+ self._lock = RemoteLockManager(client, f"__lock__blob__{name}", None)
152
+
153
+ def put(self, key: str, data: bytes, metadata: Optional[M] = None):
154
+ raise NotImplementedError("This method will be implemented in a future milestone.")
155
+
156
+ def get(self, key: str):
157
+ raise NotImplementedError("This method will be implemented in a future milestone.")
158
+
159
+ # ... other blob methods (delete, __len__, etc.) ...
160
+
161
+
162
+ class RemoteLogManager[T](RemoteManager):
163
+ """
164
+ Manages a remote time-indexed log.
165
+ (This is a skeleton implementation)
166
+ """
167
+ def log(self, data: T, timestamp: Any | None = None): # Using Any for datetime
168
+ raise NotImplementedError("This method will be implemented in a future milestone.")
169
+
170
+ def range(self, start: Any, end: Any): # Using Any for datetime
171
+ raise NotImplementedError("This method will be implemented in a future milestone.")
172
+
173
+ def live(self, window: Any, period: Any, aggregator: Any): # Using Any for timedelta/callable
174
+ raise NotImplementedError("This method will be implemented in a future milestone.")
175
+
176
+
177
+ class RemoteLockManager(RemoteManager):
178
+ """
179
+ Manages a remote inter-process lock.
180
+ (This is a skeleton implementation)
181
+ """
182
+ def __init__(
183
+ self,
184
+ client: httpx.Client,
185
+ name: str,
186
+ model: Type | None = None,
187
+ timeout: float | None = None,
188
+ lock_ttl: float = 60.0,
189
+ poll_interval: float = 0.1,
190
+ ):
191
+ super().__init__(client, name, model)
192
+ self._timeout = timeout
193
+ self._lock_ttl = lock_ttl
194
+ self._poll_interval = poll_interval
195
+
196
+ def acquire(self):
197
+ raise NotImplementedError("This method will be implemented in a future milestone.")
198
+
199
+ def release(self):
200
+ raise NotImplementedError("This method will be implemented in a future milestone.")
201
+
202
+ def __enter__(self):
203
+ self.acquire()
204
+ return self
205
+
206
+ def __exit__(self, exc_type, exc_val, exc_tb):
207
+ self.release()
208
+
209
+
210
+ # --- The Main Client Class ---
211
+
212
+ class BeaverClient:
213
+ """
214
+ A drop-in client for a remote BeaverDB server.
215
+
216
+ This class provides the same factory methods as the local BeaverDB class,
217
+ but all operations are performed over HTTP/WebSockets against a
218
+ server running 'beaver serve'.
219
+ """
220
+
221
+ def __init__(self, base_url: str, **httpx_args):
222
+ """
223
+ Initializes the client.
224
+
225
+ Args:
226
+ base_url: The base URL of the BeaverDB server (e.g., "http://127.0.0.1:8000").
227
+ **httpx_args: Additional keyword arguments to pass to the httpx.Client
228
+ (e.g., headers, timeouts).
229
+ """
230
+ self._client = httpx.Client(base_url=base_url, **httpx_args)
231
+
232
+ # Singleton managers for collections and channels, just like in BeaverDB.core
233
+ self._collections: dict[str, RemoteCollectionManager] = {}
234
+ self._collections_lock = threading.Lock()
235
+ self._channels: dict[str, RemoteChannelManager] = {}
236
+ self._channels_lock = threading.Lock()
237
+
238
+ def close(self):
239
+ """Closes the underlying HTTP client session."""
240
+ self._client.close()
241
+
242
+ def __enter__(self):
243
+ return self
244
+
245
+ def __exit__(self, exc_type, exc_val, exc_tb):
246
+ self.close()
247
+
248
+ def dict[T](self, name: str, model: type[T] | None = None) -> RemoteDictManager[T]:
249
+ """
250
+ Returns a wrapper for interacting with a remote named dictionary.
251
+ If model is defined, it should be a type used for automatic (de)serialization.
252
+ """
253
+ if not isinstance(name, str) or not name:
254
+ raise TypeError("Dictionary name must be a non-empty string.")
255
+
256
+ return RemoteDictManager(self._client, name, model)
257
+
258
+ def list[T](self, name: str, model: type[T] | None = None) -> RemoteListManager[T]:
259
+ """
260
+ Returns a wrapper for interacting with a remote named list.
261
+ If model is defined, it should be a type used for automatic (de)serialization.
262
+ """
263
+ if not isinstance(name, str) or not name:
264
+ raise TypeError("List name must be a non-empty string.")
265
+
266
+ return RemoteListManager(self._client, name, model)
267
+
268
+ def queue[T](self, name: str, model: type[T] | None = None) -> RemoteQueueManager[T]:
269
+ """
270
+ Returns a wrapper for interacting with a remote persistent priority queue.
271
+ If model is defined, it should be a type used for automatic (de)serialization.
272
+ """
273
+ if not isinstance(name, str) or not name:
274
+ raise TypeError("Queue name must be a non-empty string.")
275
+
276
+ return RemoteQueueManager(self._client, name, model)
277
+
278
+ def collection[D: Document](
279
+ self, name: str, model: Type[D] | None = None
280
+ ) -> RemoteCollectionManager[D]:
281
+ """
282
+ Returns a singleton wrapper for a remote document collection.
283
+ """
284
+ if not isinstance(name, str) or not name:
285
+ raise TypeError("Collection name must be a non-empty string.")
286
+
287
+ with self._collections_lock:
288
+ if name not in self._collections:
289
+ self._collections[name] = RemoteCollectionManager(self._client, name, model)
290
+ return self._collections[name] # type: ignore
291
+
292
+ def channel[T](self, name: str, model: type[T] | None = None) -> RemoteChannelManager[T]:
293
+ """
294
+ Returns a singleton wrapper for a remote pub/sub channel.
295
+ """
296
+ if not isinstance(name, str) or not name:
297
+ raise ValueError("Channel name must be a non-empty string.")
298
+
299
+ with self._channels_lock:
300
+ if name not in self._channels:
301
+ self._channels[name] = RemoteChannelManager(self._client, name, model)
302
+ return self._channels[name]
303
+
304
+ def blobs[M](self, name: str, model: type[M] | None = None) -> RemoteBlobManager[M]:
305
+ """Returns a wrapper for interacting with a remote blob store."""
306
+ if not isinstance(name, str) or not name:
307
+ raise TypeError("Blob store name must be a non-empty string.")
308
+
309
+ return RemoteBlobManager(self._client, name, model)
310
+
311
+ def log[T](self, name: str, model: type[T] | None = None) -> RemoteLogManager[T]:
312
+ """
313
+ Returns a wrapper for interacting with a remote, time-indexed log.
314
+ If model is defined, it should be a type used for automatic (de)serialization.
315
+ """
316
+ if not isinstance(name, str) or not name:
317
+ raise TypeError("Log name must be a non-empty string.")
318
+
319
+ return RemoteLogManager(self._client, name, model)
320
+
321
+ def lock(
322
+ self,
323
+ name: str,
324
+ timeout: float | None = None,
325
+ lock_ttl: float = 60.0,
326
+ poll_interval: float = 0.1,
327
+ ) -> RemoteLockManager:
328
+ """
329
+ Returns a wrapper for a remote inter-process lock.
330
+ """
331
+ return RemoteLockManager(
332
+ self._client,
333
+ name,
334
+ model=None,
335
+ timeout=timeout,
336
+ lock_ttl=lock_ttl,
337
+ poll_interval=poll_interval,
338
+ )
339
+
340
+ # --- Async API ---
341
+
342
+ def as_async(self) -> "AsyncBeaverClient":
343
+ """
344
+ Returns an async-compatible version of the client.
345
+ (This is a skeleton implementation)
346
+ """
347
+ raise NotImplementedError("AsyncBeaverClient will be implemented in a future milestone.")
348
+
349
+
350
+ class AsyncBeaverClient:
351
+ """
352
+ An async-compatible, drop-in client for a remote BeaverDB server.
353
+ (This is a skeleton implementation)
354
+ """
355
+ def __init__(self, base_url: str, **httpx_args):
356
+ self._client = httpx.AsyncClient(base_url=base_url, **httpx_args)
357
+ # ... async-compatible locks and manager caches ...
358
+
359
+ async def close(self):
360
+ await self._client.aclose()
@@ -1,9 +1,10 @@
1
+ from datetime import datetime, timezone
1
2
  import json
2
3
  import sqlite3
3
4
  import threading
4
5
  import uuid
5
6
  from enum import Enum
6
- from typing import Any, Iterator, List, Literal, Tuple, Type, TypeVar, Optional
7
+ from typing import IO, Any, Iterator, List, Literal, Tuple, Type, TypeVar, Optional
7
8
  from .types import Model, stub, IDatabase
8
9
  from .locks import LockManager
9
10
 
@@ -679,6 +680,47 @@ class CollectionManager[D: Document]:
679
680
  """Releases the lock when exiting a 'with' statement."""
680
681
  self.release()
681
682
 
683
+ def _get_dump_object(self) -> dict:
684
+ """Builds the JSON-compatible dump object."""
685
+
686
+ # The __iter__ method yields Document instances.
687
+ # doc.to_dict(metadata_only=False) correctly serializes
688
+ # the Document (including embedding) to a plain dictionary.
689
+ items_list = [doc.to_dict(metadata_only=False) for doc in self]
690
+
691
+ metadata = {
692
+ "type": "Collection",
693
+ "name": self._name,
694
+ "count": len(items_list),
695
+ "dump_date": datetime.now(timezone.utc).isoformat()
696
+ }
697
+
698
+ return {
699
+ "metadata": metadata,
700
+ "items": items_list
701
+ }
702
+
703
+ def dump(self, fp: IO[str] | None = None) -> dict | None:
704
+ """
705
+ Dumps the entire contents of the collection to a JSON-compatible
706
+ Python object or a file-like object.
707
+
708
+ Args:
709
+ fp: A file-like object opened in text mode (e.g., with 'w').
710
+ If provided, the JSON dump will be written to this file.
711
+ If None (default), the dump will be returned as a dictionary.
712
+
713
+ Returns:
714
+ A dictionary containing the dump if fp is None.
715
+ None if fp is provided.
716
+ """
717
+ dump_object = self._get_dump_object()
718
+
719
+ if fp:
720
+ json.dump(dump_object, fp, indent=2)
721
+ return None
722
+
723
+ return dump_object
682
724
 
683
725
  def rerank[D: Document](
684
726
  *results: list[D], weights: list[float] | None = None, k: int = 60
@@ -167,7 +167,7 @@ class BeaverDB:
167
167
  """Creates the table to store the serialized base ANN index."""
168
168
  self.connection.execute(
169
169
  """
170
- CREATE TABLE IF NOT EXISTS _beaver_ann_indexes (
170
+ CREATE TABLE IF NOT EXISTS beaver_ann_indexes (
171
171
  collection_name TEXT PRIMARY KEY,
172
172
  index_data BLOB,
173
173
  base_index_version INTEGER NOT NULL DEFAULT 0
@@ -179,7 +179,7 @@ class BeaverDB:
179
179
  """Creates the log for new vector additions."""
180
180
  self.connection.execute(
181
181
  """
182
- CREATE TABLE IF NOT EXISTS _beaver_ann_pending_log (
182
+ CREATE TABLE IF NOT EXISTS beaver_ann_pending_log (
183
183
  collection_name TEXT NOT NULL,
184
184
  str_id TEXT NOT NULL,
185
185
  PRIMARY KEY (collection_name, str_id)
@@ -191,7 +191,7 @@ class BeaverDB:
191
191
  """Creates the log for vector deletions (tombstones)."""
192
192
  self.connection.execute(
193
193
  """
194
- CREATE TABLE IF NOT EXISTS _beaver_ann_deletions_log (
194
+ CREATE TABLE IF NOT EXISTS beaver_ann_deletions_log (
195
195
  collection_name TEXT NOT NULL,
196
196
  int_id INTEGER NOT NULL,
197
197
  PRIMARY KEY (collection_name, int_id)
@@ -203,7 +203,7 @@ class BeaverDB:
203
203
  """Creates the table to map string IDs to integer IDs for Faiss."""
204
204
  self.connection.execute(
205
205
  """
206
- CREATE TABLE IF NOT EXISTS _beaver_ann_id_mapping (
206
+ CREATE TABLE IF NOT EXISTS beaver_ann_id_mapping (
207
207
  collection_name TEXT NOT NULL,
208
208
  str_id TEXT NOT NULL,
209
209
  int_id INTEGER PRIMARY KEY AUTOINCREMENT,