beaver-db 0.20.1__tar.gz → 0.21.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.

Potentially problematic release.


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

Files changed (150) hide show
  1. {beaver_db-0.20.1 → beaver_db-0.21.1}/PKG-INFO +42 -14
  2. {beaver_db-0.20.1 → beaver_db-0.21.1}/README.md +39 -8
  3. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/__init__.py +1 -1
  4. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/blobs.py +60 -1
  5. beaver_db-0.21.1/beaver/client.py +360 -0
  6. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/collections.py +43 -1
  7. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/dicts.py +51 -1
  8. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/lists.py +49 -2
  9. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/logs.py +72 -7
  10. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/queues.py +82 -1
  11. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/types.py +16 -2
  12. beaver_db-0.21.1/docs/_book/BeaverDB--The-Complete-Guide.pdf +0 -0
  13. beaver_db-0.21.1/docs/_book/dev-architecture.html +676 -0
  14. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/dev-concurrency.html +12 -23
  15. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/dev-contributing.html +12 -23
  16. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/dev-search.html +12 -23
  17. beaver_db-0.21.1/docs/_book/guide-collections.html +683 -0
  18. beaver_db-0.21.1/docs/_book/guide-concurrency.html +669 -0
  19. beaver_db-0.21.1/docs/_book/guide-deployment.html +675 -0
  20. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/guide-dicts-blobs.html +12 -23
  21. beaver_db-0.21.1/docs/_book/guide-lists-queues.html +671 -0
  22. beaver_db-0.21.1/docs/_book/guide-realtime.html +671 -0
  23. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/index.html +16 -210
  24. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/quickstart.html +12 -23
  25. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/search.json +72 -80
  26. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_quarto.yml +4 -6
  27. beaver_db-0.21.1/issues/13-refactor-adopt-pydantic-deprecate-beavermodel-and-refactor-document-to-be-generic.md +83 -0
  28. beaver_db-0.21.1/issues/14-deprecate-fire-based-cli-and-build-a-feature-rich-typer-and-rich-cli.md +114 -0
  29. beaver_db-0.21.1/issues/closed/12-add-dump-method-for-json-export-to-all-data-managers.md +195 -0
  30. {beaver_db-0.20.1/issues → beaver_db-0.21.1/issues/closed}/9-type-safe-wrappers-based-on-pydantic-compatible-models.md +1 -1
  31. {beaver_db-0.20.1 → beaver_db-0.21.1}/pyproject.toml +6 -3
  32. beaver_db-0.21.1/uv.lock +929 -0
  33. beaver_db-0.20.1/docs/_book/dev-architecture.html +0 -870
  34. beaver_db-0.20.1/docs/_book/guide-collections.html +0 -877
  35. beaver_db-0.20.1/docs/_book/guide-concurrency.html +0 -863
  36. beaver_db-0.20.1/docs/_book/guide-deployment.html +0 -869
  37. beaver_db-0.20.1/docs/_book/guide-lists-queues.html +0 -865
  38. beaver_db-0.20.1/docs/_book/guide-realtime.html +0 -865
  39. beaver_db-0.20.1/docs/_book/site_libs/bootstrap/bootstrap-dark-5cc7fd2e95cc32b4ed915ed1d7e2d366.min.css +0 -12
  40. beaver_db-0.20.1/docs/_book/site_libs/quarto-html/quarto-syntax-highlighting-dark-2b3e328b71be8d25427581baeb23079b.css +0 -219
  41. beaver_db-0.20.1/uv.lock +0 -881
  42. {beaver_db-0.20.1 → beaver_db-0.21.1}/.dockerignore +0 -0
  43. {beaver_db-0.20.1 → beaver_db-0.21.1}/.gitignore +0 -0
  44. {beaver_db-0.20.1 → beaver_db-0.21.1}/.python-version +0 -0
  45. {beaver_db-0.20.1 → beaver_db-0.21.1}/LICENSE +0 -0
  46. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/channels.py +0 -0
  47. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/cli.py +0 -0
  48. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/core.py +0 -0
  49. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/locks.py +0 -0
  50. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/server.py +0 -0
  51. {beaver_db-0.20.1 → beaver_db-0.21.1}/beaver/vectors.py +0 -0
  52. {beaver_db-0.20.1 → beaver_db-0.21.1}/design.md +0 -0
  53. {beaver_db-0.20.1 → beaver_db-0.21.1}/dockerfile +0 -0
  54. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.gitignore +0 -0
  55. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/cites/index.json +0 -0
  56. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/dev-architecture.qmd.json +0 -0
  57. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/dev-concurrency.qmd.json +0 -0
  58. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/dev-contributing.qmd.json +0 -0
  59. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/dev-search.qmd.json +0 -0
  60. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/guide-collections.qmd.json +0 -0
  61. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/guide-concurrency.qmd.json +0 -0
  62. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/guide-deployment.qmd.json +0 -0
  63. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/guide-dicts-blobs.qmd.json +0 -0
  64. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/guide-lists-queues.qmd.json +0 -0
  65. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/guide-realtime.qmd.json +0 -0
  66. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/index.qmd.json +0 -0
  67. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/idx/quickstart.qmd.json +0 -0
  68. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/project-cache/08c652dc55400ed7625e092b14557eff +0 -0
  69. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/project-cache/78e7cce51608b23bc20ef50628d80ee9 +0 -0
  70. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/project-cache/deno-kv-file +0 -0
  71. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/project-cache/efb0c620c38ca7c31075f463c90f647b +0 -0
  72. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/1e84074e +0 -0
  73. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/41b07c8e +0 -0
  74. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/4a9bbebe +0 -0
  75. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/57337df9 +0 -0
  76. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/674e8fe6 +0 -0
  77. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/6a721ef9 +0 -0
  78. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/807a23ea +0 -0
  79. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/9c486562 +0 -0
  80. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/INDEX +0 -0
  81. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/b2f009dd +0 -0
  82. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/c40f73a8 +0 -0
  83. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/cf0735bd +0 -0
  84. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/d7f66f00 +0 -0
  85. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/f22e8d4c +0 -0
  86. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/f9d58622 +0 -0
  87. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/.quarto/xref/fa9f9ca6 +0 -0
  88. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/logo.png +0 -0
  89. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/bootstrap/bootstrap-5d653dd4d7566aed16f42eb06e547978.min.css +0 -0
  90. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/bootstrap/bootstrap-icons.css +0 -0
  91. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/bootstrap/bootstrap-icons.woff +0 -0
  92. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/bootstrap/bootstrap.min.js +0 -0
  93. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/clipboard/clipboard.min.js +0 -0
  94. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-html/anchor.min.js +0 -0
  95. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-html/popper.min.js +0 -0
  96. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-html/quarto-syntax-highlighting-de070a7b0ab54f8780927367ac907214.css +0 -0
  97. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-html/quarto.js +0 -0
  98. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-html/tabsets/tabsets.js +0 -0
  99. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-html/tippy.css +0 -0
  100. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-html/tippy.umd.min.js +0 -0
  101. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-nav/quarto-nav.js +0 -0
  102. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-search/autocomplete.umd.js +0 -0
  103. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-search/fuse.min.js +0 -0
  104. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/_book/site_libs/quarto-search/quarto-search.js +0 -0
  105. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/cover.png +0 -0
  106. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/dev-architecture.qmd +0 -0
  107. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/dev-concurrency.qmd +0 -0
  108. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/dev-contributing.qmd +0 -0
  109. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/dev-search.qmd +0 -0
  110. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/guide-collections.qmd +0 -0
  111. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/guide-concurrency.qmd +0 -0
  112. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/guide-deployment.qmd +0 -0
  113. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/guide-dicts-blobs.qmd +0 -0
  114. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/guide-lists-queues.qmd +0 -0
  115. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/guide-realtime.qmd +0 -0
  116. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/index.qmd +0 -0
  117. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/logo.png +0 -0
  118. {beaver_db-0.20.1 → beaver_db-0.21.1}/docs/quickstart.qmd +0 -0
  119. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/async_pubsub.py +0 -0
  120. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/blobs.py +0 -0
  121. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/cache.py +0 -0
  122. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/fts.py +0 -0
  123. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/fuzzy.py +0 -0
  124. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/general_test.py +0 -0
  125. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/graph.py +0 -0
  126. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/kvstore.py +0 -0
  127. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/list.py +0 -0
  128. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/locks.py +0 -0
  129. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/logs.py +0 -0
  130. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/pqueue.py +0 -0
  131. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/producer_consumer.py +0 -0
  132. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/publisher.py +0 -0
  133. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/pubsub.py +0 -0
  134. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/rerank.py +0 -0
  135. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/stress_vectors.py +0 -0
  136. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/subscriber.py +0 -0
  137. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/textual_chat.css +0 -0
  138. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/textual_chat.py +0 -0
  139. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/type_hints.py +0 -0
  140. {beaver_db-0.20.1 → beaver_db-0.21.1}/examples/vector.py +0 -0
  141. {beaver_db-0.20.1 → beaver_db-0.21.1}/issues/2-comprehensive-async-wrappers.md +0 -0
  142. {beaver_db-0.20.1 → beaver_db-0.21.1}/issues/6-drop-in-replacement-for-beaver-rest-server-client.md +0 -0
  143. {beaver_db-0.20.1 → beaver_db-0.21.1}/issues/7-replace-faiss-with-simpler-linear-numpy-vectorial-search.md +0 -0
  144. {beaver_db-0.20.1 → beaver_db-0.21.1}/issues/closed/1-refactor-vector-store-to-use-faiss.md +0 -0
  145. {beaver_db-0.20.1 → beaver_db-0.21.1}/issues/closed/10-expose-dblock-functionality-on-all-high-level-data-managers.md +0 -0
  146. {beaver_db-0.20.1 → beaver_db-0.21.1}/issues/closed/11-a-new-issue-for-gabriel.md +0 -0
  147. {beaver_db-0.20.1 → beaver_db-0.21.1}/issues/closed/5-add-dblock-for-inter-process-synchronization.md +0 -0
  148. {beaver_db-0.20.1 → beaver_db-0.21.1}/issues/closed/8-first-class-synchronization-primitive.md +0 -0
  149. {beaver_db-0.20.1 → beaver_db-0.21.1}/logo.png +0 -0
  150. {beaver_db-0.20.1 → beaver_db-0.21.1}/makefile +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beaver-db
3
- Version: 0.20.1
3
+ Version: 0.21.1
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
@@ -9,14 +9,11 @@ Classifier: Programming Language :: Python :: 3.13
9
9
  Classifier: Topic :: Database
10
10
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
11
11
  Requires-Python: >=3.13
12
- Provides-Extra: cli
13
- Requires-Dist: fire[cli]>=0.7.1; extra == 'cli'
14
- Requires-Dist: typer>=0.19.2; extra == 'cli'
12
+ Requires-Dist: rich>=14.2.0
13
+ Requires-Dist: typer>=0.20.0
15
14
  Provides-Extra: full
16
15
  Requires-Dist: faiss-cpu>=1.12.0; extra == 'full'
17
16
  Requires-Dist: fastapi[standard]>=0.118.0; extra == 'full'
18
- Requires-Dist: fire[cli]>=0.7.1; extra == 'full'
19
- Requires-Dist: typer>=0.19.2; extra == 'full'
20
17
  Provides-Extra: server
21
18
  Requires-Dist: fastapi[standard]>=0.118.0; extra == 'server'
22
19
  Provides-Extra: vector
@@ -71,6 +68,7 @@ Description-Content-Type: text/markdown
71
68
  - **Built-in REST API Server (Optional)**: Instantly serve your database over a RESTful API with automatic OpenAPI documentation using FastAPI.
72
69
  - **Full-Featured CLI Client (Optional)**: Interact with your database directly from the command line for administrative tasks and data exploration.
73
70
  - **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.
71
+ - **Data Export & Backups:** Dump any dictionary, list, collection, queue, blob, or log structure to a portable JSON file with a single `.dump()` command.
74
72
 
75
73
  ## How Beaver is Implemented
76
74
 
@@ -203,6 +201,43 @@ beaver client --database data.db dict app_config get theme
203
201
  beaver client --database data.db list daily_tasks push "Review PRs"
204
202
  ```
205
203
 
204
+ ## Data Export for Backups
205
+
206
+ 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.
207
+
208
+ ```python
209
+ import json
210
+ from beaver import BeaverDB
211
+
212
+ db = BeaverDB("my_app.db")
213
+ config = db.dict("app_config")
214
+
215
+ # Add some data
216
+ config["theme"] = "dark"
217
+ config["user_id"] = 456
218
+
219
+ # Dump the dictionary's contents to a JSON file
220
+ with open("config_backup.json", "w") as f:
221
+ config.dump(f)
222
+
223
+ # 'config_backup.json' now contains:
224
+ # {
225
+ # "metadata": {
226
+ # "type": "Dict",
227
+ # "name": "app_config",
228
+ # "count": 2,
229
+ # "dump_date": "2025-11-02T09:05:10.123456Z"
230
+ # },
231
+ # "items": [
232
+ # {"key": "theme", "value": "dark"},
233
+ # {"key": "user_id", "value": 456}
234
+ # ]
235
+ # }
236
+
237
+ # You can also get the dump as a Python object
238
+ dump_data = config.dump()
239
+ ```
240
+
206
241
  ## Things You Can Build with Beaver
207
242
 
208
243
  Here are a few ideas to inspire your next project, showcasing how to combine Beaver's features to build powerful local applications.
@@ -443,15 +478,8 @@ For more in-depth examples, check out the scripts in the `examples/` directory:
443
478
 
444
479
  `beaver` is roughly feature-complete, but there are still some features and improvements planned for future releases, mostly directed to improving developer experience.
445
480
 
446
- These are some of the features and improvements planned for future releases:
447
-
448
- - **[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.
452
-
453
-
454
481
  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.
482
+
455
483
  ## License
456
484
 
457
485
  This project is licensed under the MIT License.
@@ -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.
@@ -418,15 +456,8 @@ For more in-depth examples, check out the scripts in the `examples/` directory:
418
456
 
419
457
  `beaver` is roughly feature-complete, but there are still some features and improvements planned for future releases, mostly directed to improving developer experience.
420
458
 
421
- These are some of the features and improvements planned for future releases:
422
-
423
- - **[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.
427
-
428
-
429
459
  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.
460
+
430
461
  ## License
431
462
 
432
463
  This project is licensed under the MIT License.
@@ -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.20.1"
5
+ __version__ = "0.21.1"
@@ -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