beaver-db 2.0rc2__py3-none-any.whl
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.
- beaver/__init__.py +16 -0
- beaver/blobs.py +223 -0
- beaver/bridge.py +167 -0
- beaver/cache.py +274 -0
- beaver/channels.py +249 -0
- beaver/cli/__init__.py +133 -0
- beaver/cli/blobs.py +225 -0
- beaver/cli/channels.py +166 -0
- beaver/cli/collections.py +500 -0
- beaver/cli/dicts.py +171 -0
- beaver/cli/lists.py +244 -0
- beaver/cli/locks.py +202 -0
- beaver/cli/logs.py +248 -0
- beaver/cli/queues.py +215 -0
- beaver/client.py +392 -0
- beaver/core.py +646 -0
- beaver/dicts.py +314 -0
- beaver/docs.py +459 -0
- beaver/events.py +155 -0
- beaver/graphs.py +212 -0
- beaver/lists.py +337 -0
- beaver/locks.py +186 -0
- beaver/logs.py +187 -0
- beaver/manager.py +203 -0
- beaver/queries.py +66 -0
- beaver/queues.py +215 -0
- beaver/security.py +144 -0
- beaver/server.py +452 -0
- beaver/sketches.py +307 -0
- beaver/types.py +32 -0
- beaver/vectors.py +198 -0
- beaver_db-2.0rc2.dist-info/METADATA +149 -0
- beaver_db-2.0rc2.dist-info/RECORD +36 -0
- beaver_db-2.0rc2.dist-info/WHEEL +4 -0
- beaver_db-2.0rc2.dist-info/entry_points.txt +2 -0
- beaver_db-2.0rc2.dist-info/licenses/LICENSE +21 -0
beaver/client.py
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
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
|
+
raise NotImplementedError("Remote client functionality is under development.")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
import threading
|
|
12
|
+
from typing import Generic, Type, TypeVar, Optional, Any
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel
|
|
15
|
+
import httpx
|
|
16
|
+
from .collections import Document
|
|
17
|
+
|
|
18
|
+
# --- Base Remote Manager ---
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RemoteManager:
|
|
22
|
+
"""Base class for all remote managers, holding the HTTP client."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, client: httpx.Client, name: str, model: Type | None = None):
|
|
25
|
+
self._client = client
|
|
26
|
+
self._name = name
|
|
27
|
+
self._model = model
|
|
28
|
+
self._validate_model(model)
|
|
29
|
+
|
|
30
|
+
def _validate_model(self, model: Type | None):
|
|
31
|
+
"""Helper to validate the model, mirroring BeaverDB.core"""
|
|
32
|
+
if model and not isinstance(model, BaseModel):
|
|
33
|
+
# This check might need to be refined if Pydantic isn't a direct dependency
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# --- Stub Remote Manager Implementations ---
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RemoteDictManager[T](RemoteManager):
|
|
41
|
+
"""
|
|
42
|
+
Manages a remote dictionary. All methods will make HTTP requests.
|
|
43
|
+
(This is a skeleton implementation)
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, client: httpx.Client, name: str, model: Type[T] | None = None):
|
|
47
|
+
super().__init__(client, name, model)
|
|
48
|
+
# Placeholder for manager-level locking, mirroring local implementation
|
|
49
|
+
self._lock = RemoteLockManager(client, f"__lock__dict__{name}", None)
|
|
50
|
+
|
|
51
|
+
def __setitem__(self, key: str, value: T):
|
|
52
|
+
raise NotImplementedError(
|
|
53
|
+
"This method will be implemented in a future milestone."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def __getitem__(self, key: str) -> T:
|
|
57
|
+
raise NotImplementedError(
|
|
58
|
+
"This method will be implemented in a future milestone."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# ... other dict methods (get, __delitem__, __len__, items, etc.) ...
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class RemoteListManager[T](RemoteManager):
|
|
65
|
+
"""
|
|
66
|
+
Manages a remote list. All methods will make HTTP requests.
|
|
67
|
+
(This is a skeleton implementation)
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, client: httpx.Client, name: str, model: Type[T] | None = None):
|
|
71
|
+
super().__init__(client, name, model)
|
|
72
|
+
self._lock = RemoteLockManager(client, f"__lock__list__{name}", None)
|
|
73
|
+
|
|
74
|
+
def push(self, value: T):
|
|
75
|
+
raise NotImplementedError(
|
|
76
|
+
"This method will be implemented in a future milestone."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# ... other list methods (pop, __getitem__, __setitem__, etc.) ...
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class RemoteQueueManager[T](RemoteManager):
|
|
83
|
+
"""
|
|
84
|
+
Manages a remote priority queue. All methods will make HTTP requests.
|
|
85
|
+
(This is a skeleton implementation)
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __init__(self, client: httpx.Client, name: str, model: Type[T] | None = None):
|
|
89
|
+
super().__init__(client, name, model)
|
|
90
|
+
self._lock = RemoteLockManager(client, f"__lock__queue__{name}", None)
|
|
91
|
+
|
|
92
|
+
def put(self, data: T, priority: float):
|
|
93
|
+
raise NotImplementedError(
|
|
94
|
+
"This method will be implemented in a future milestone."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def get(self, block: bool = True, timeout: float | None = None):
|
|
98
|
+
raise NotImplementedError(
|
|
99
|
+
"This method will be implemented in a future milestone."
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# ... other queue methods (peek, __len__, etc.) ...
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class RemoteCollectionManager[D](RemoteManager):
|
|
106
|
+
"""
|
|
107
|
+
Manages a remote collection. All methods will make HTTP requests.
|
|
108
|
+
(This is a skeleton implementation)
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(self, client: httpx.Client, name: str, model: Type[D] | None = None):
|
|
112
|
+
super().__init__(client, name, model)
|
|
113
|
+
self._lock = RemoteLockManager(client, f"__lock__collection__{name}", None)
|
|
114
|
+
|
|
115
|
+
def index(self, document: D, *, fts: bool | list[str] = True, fuzzy: bool = False):
|
|
116
|
+
raise NotImplementedError(
|
|
117
|
+
"This method will be implemented in a future milestone."
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def search(self, vector: list[float], top_k: int = 10):
|
|
121
|
+
raise NotImplementedError(
|
|
122
|
+
"This method will be implemented in a future milestone."
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# ... other collection methods (drop, match, connect, walk, etc.) ...
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class RemoteChannelManager[T](RemoteManager):
|
|
129
|
+
"""
|
|
130
|
+
Manages a remote pub/sub channel.
|
|
131
|
+
(This is a skeleton implementation)
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
def publish(self, payload: T):
|
|
135
|
+
raise NotImplementedError(
|
|
136
|
+
"This method will be implemented in a future milestone."
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def subscribe(self):
|
|
140
|
+
raise NotImplementedError(
|
|
141
|
+
"This method will be implemented in a future milestone."
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class RemoteBlobManager[M](RemoteManager):
|
|
146
|
+
"""
|
|
147
|
+
Manages a remote blob store. All methods will make HTTP requests.
|
|
148
|
+
(This is a skeleton implementation)
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
def __init__(self, client: httpx.Client, name: str, model: Type[M] | None = None):
|
|
152
|
+
super().__init__(client, name, model)
|
|
153
|
+
self._lock = RemoteLockManager(client, f"__lock__blob__{name}", None)
|
|
154
|
+
|
|
155
|
+
def put(self, key: str, data: bytes, metadata: Optional[M] = None):
|
|
156
|
+
raise NotImplementedError(
|
|
157
|
+
"This method will be implemented in a future milestone."
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def get(self, key: str):
|
|
161
|
+
raise NotImplementedError(
|
|
162
|
+
"This method will be implemented in a future milestone."
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# ... other blob methods (delete, __len__, etc.) ...
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class RemoteLogManager[T](RemoteManager):
|
|
169
|
+
"""
|
|
170
|
+
Manages a remote time-indexed log.
|
|
171
|
+
(This is a skeleton implementation)
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
def log(self, data: T, timestamp: Any | None = None): # Using Any for datetime
|
|
175
|
+
raise NotImplementedError(
|
|
176
|
+
"This method will be implemented in a future milestone."
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def range(self, start: Any, end: Any): # Using Any for datetime
|
|
180
|
+
raise NotImplementedError(
|
|
181
|
+
"This method will be implemented in a future milestone."
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def live(
|
|
185
|
+
self, window: Any, period: Any, aggregator: Any
|
|
186
|
+
): # Using Any for timedelta/callable
|
|
187
|
+
raise NotImplementedError(
|
|
188
|
+
"This method will be implemented in a future milestone."
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class RemoteLockManager(RemoteManager):
|
|
193
|
+
"""
|
|
194
|
+
Manages a remote inter-process lock.
|
|
195
|
+
(This is a skeleton implementation)
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
def __init__(
|
|
199
|
+
self,
|
|
200
|
+
client: httpx.Client,
|
|
201
|
+
name: str,
|
|
202
|
+
model: Type | None = None,
|
|
203
|
+
timeout: float | None = None,
|
|
204
|
+
lock_ttl: float = 60.0,
|
|
205
|
+
poll_interval: float = 0.1,
|
|
206
|
+
):
|
|
207
|
+
super().__init__(client, name, model)
|
|
208
|
+
self._timeout = timeout
|
|
209
|
+
self._lock_ttl = lock_ttl
|
|
210
|
+
self._poll_interval = poll_interval
|
|
211
|
+
|
|
212
|
+
def acquire(self):
|
|
213
|
+
raise NotImplementedError(
|
|
214
|
+
"This method will be implemented in a future milestone."
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
def release(self):
|
|
218
|
+
raise NotImplementedError(
|
|
219
|
+
"This method will be implemented in a future milestone."
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def __enter__(self):
|
|
223
|
+
self.acquire()
|
|
224
|
+
return self
|
|
225
|
+
|
|
226
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
227
|
+
self.release()
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# --- The Main Client Class ---
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class BeaverClient:
|
|
234
|
+
"""
|
|
235
|
+
A drop-in client for a remote BeaverDB server.
|
|
236
|
+
|
|
237
|
+
This class provides the same factory methods as the local BeaverDB class,
|
|
238
|
+
but all operations are performed over HTTP/WebSockets against a
|
|
239
|
+
server running 'beaver serve'.
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
def __init__(self, base_url: str, **httpx_args):
|
|
243
|
+
"""
|
|
244
|
+
Initializes the client.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
base_url: The base URL of the BeaverDB server (e.g., "http://127.0.0.1:8000").
|
|
248
|
+
**httpx_args: Additional keyword arguments to pass to the httpx.Client
|
|
249
|
+
(e.g., headers, timeouts).
|
|
250
|
+
"""
|
|
251
|
+
self._client = httpx.Client(base_url=base_url, **httpx_args)
|
|
252
|
+
|
|
253
|
+
# Singleton managers for collections and channels, just like in BeaverDB.core
|
|
254
|
+
self._collections: dict[str, RemoteCollectionManager] = {}
|
|
255
|
+
self._collections_lock = threading.Lock()
|
|
256
|
+
self._channels: dict[str, RemoteChannelManager] = {}
|
|
257
|
+
self._channels_lock = threading.Lock()
|
|
258
|
+
|
|
259
|
+
raise NotImplemented
|
|
260
|
+
|
|
261
|
+
def close(self):
|
|
262
|
+
"""Closes the underlying HTTP client session."""
|
|
263
|
+
self._client.close()
|
|
264
|
+
|
|
265
|
+
def __enter__(self):
|
|
266
|
+
return self
|
|
267
|
+
|
|
268
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
269
|
+
self.close()
|
|
270
|
+
|
|
271
|
+
def dict[T](self, name: str, model: type[T] | None = None) -> RemoteDictManager[T]:
|
|
272
|
+
"""
|
|
273
|
+
Returns a wrapper for interacting with a remote named dictionary.
|
|
274
|
+
If model is defined, it should be a type used for automatic (de)serialization.
|
|
275
|
+
"""
|
|
276
|
+
if not isinstance(name, str) or not name:
|
|
277
|
+
raise TypeError("Dictionary name must be a non-empty string.")
|
|
278
|
+
|
|
279
|
+
return RemoteDictManager(self._client, name, model)
|
|
280
|
+
|
|
281
|
+
def list[T](self, name: str, model: type[T] | None = None) -> RemoteListManager[T]:
|
|
282
|
+
"""
|
|
283
|
+
Returns a wrapper for interacting with a remote named list.
|
|
284
|
+
If model is defined, it should be a type used for automatic (de)serialization.
|
|
285
|
+
"""
|
|
286
|
+
if not isinstance(name, str) or not name:
|
|
287
|
+
raise TypeError("List name must be a non-empty string.")
|
|
288
|
+
|
|
289
|
+
return RemoteListManager(self._client, name, model)
|
|
290
|
+
|
|
291
|
+
def queue[T](
|
|
292
|
+
self, name: str, model: type[T] | None = None
|
|
293
|
+
) -> RemoteQueueManager[T]:
|
|
294
|
+
"""
|
|
295
|
+
Returns a wrapper for interacting with a remote persistent priority queue.
|
|
296
|
+
If model is defined, it should be a type used for automatic (de)serialization.
|
|
297
|
+
"""
|
|
298
|
+
if not isinstance(name, str) or not name:
|
|
299
|
+
raise TypeError("Queue name must be a non-empty string.")
|
|
300
|
+
|
|
301
|
+
return RemoteQueueManager(self._client, name, model)
|
|
302
|
+
|
|
303
|
+
def collection[D: Document](
|
|
304
|
+
self, name: str, model: Type[D] | None = None
|
|
305
|
+
) -> RemoteCollectionManager[D]:
|
|
306
|
+
"""
|
|
307
|
+
Returns a singleton wrapper for a remote document collection.
|
|
308
|
+
"""
|
|
309
|
+
if not isinstance(name, str) or not name:
|
|
310
|
+
raise TypeError("Collection name must be a non-empty string.")
|
|
311
|
+
|
|
312
|
+
with self._collections_lock:
|
|
313
|
+
if name not in self._collections:
|
|
314
|
+
self._collections[name] = RemoteCollectionManager(
|
|
315
|
+
self._client, name, model
|
|
316
|
+
)
|
|
317
|
+
return self._collections[name] # type: ignore
|
|
318
|
+
|
|
319
|
+
def channel[T](
|
|
320
|
+
self, name: str, model: type[T] | None = None
|
|
321
|
+
) -> RemoteChannelManager[T]:
|
|
322
|
+
"""
|
|
323
|
+
Returns a singleton wrapper for a remote pub/sub channel.
|
|
324
|
+
"""
|
|
325
|
+
if not isinstance(name, str) or not name:
|
|
326
|
+
raise ValueError("Channel name must be a non-empty string.")
|
|
327
|
+
|
|
328
|
+
with self._channels_lock:
|
|
329
|
+
if name not in self._channels:
|
|
330
|
+
self._channels[name] = RemoteChannelManager(self._client, name, model)
|
|
331
|
+
return self._channels[name]
|
|
332
|
+
|
|
333
|
+
def blobs[M](self, name: str, model: type[M] | None = None) -> RemoteBlobManager[M]:
|
|
334
|
+
"""Returns a wrapper for interacting with a remote blob store."""
|
|
335
|
+
if not isinstance(name, str) or not name:
|
|
336
|
+
raise TypeError("Blob store name must be a non-empty string.")
|
|
337
|
+
|
|
338
|
+
return RemoteBlobManager(self._client, name, model)
|
|
339
|
+
|
|
340
|
+
def log[T](self, name: str, model: type[T] | None = None) -> RemoteLogManager[T]:
|
|
341
|
+
"""
|
|
342
|
+
Returns a wrapper for interacting with a remote, time-indexed log.
|
|
343
|
+
If model is defined, it should be a type used for automatic (de)serialization.
|
|
344
|
+
"""
|
|
345
|
+
if not isinstance(name, str) or not name:
|
|
346
|
+
raise TypeError("Log name must be a non-empty string.")
|
|
347
|
+
|
|
348
|
+
return RemoteLogManager(self._client, name, model)
|
|
349
|
+
|
|
350
|
+
def lock(
|
|
351
|
+
self,
|
|
352
|
+
name: str,
|
|
353
|
+
timeout: float | None = None,
|
|
354
|
+
lock_ttl: float = 60.0,
|
|
355
|
+
poll_interval: float = 0.1,
|
|
356
|
+
) -> RemoteLockManager:
|
|
357
|
+
"""
|
|
358
|
+
Returns a wrapper for a remote inter-process lock.
|
|
359
|
+
"""
|
|
360
|
+
return RemoteLockManager(
|
|
361
|
+
self._client,
|
|
362
|
+
name,
|
|
363
|
+
model=None,
|
|
364
|
+
timeout=timeout,
|
|
365
|
+
lock_ttl=lock_ttl,
|
|
366
|
+
poll_interval=poll_interval,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
# --- Async API ---
|
|
370
|
+
|
|
371
|
+
def as_async(self) -> "AsyncBeaverClient":
|
|
372
|
+
"""
|
|
373
|
+
Returns an async-compatible version of the client.
|
|
374
|
+
(This is a skeleton implementation)
|
|
375
|
+
"""
|
|
376
|
+
raise NotImplementedError(
|
|
377
|
+
"AsyncBeaverClient will be implemented in a future milestone."
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class AsyncBeaverClient:
|
|
382
|
+
"""
|
|
383
|
+
An async-compatible, drop-in client for a remote BeaverDB server.
|
|
384
|
+
(This is a skeleton implementation)
|
|
385
|
+
"""
|
|
386
|
+
|
|
387
|
+
def __init__(self, base_url: str, **httpx_args):
|
|
388
|
+
self._client = httpx.AsyncClient(base_url=base_url, **httpx_args)
|
|
389
|
+
# ... async-compatible locks and manager caches ...
|
|
390
|
+
|
|
391
|
+
async def close(self):
|
|
392
|
+
await self._client.aclose()
|