|
6 | 6 | from crawlee._utils.docs import docs_group
|
7 | 7 |
|
8 | 8 | if TYPE_CHECKING:
|
9 |
| - from contextlib import AbstractAsyncContextManager |
10 |
| - |
11 |
| - from httpx import Response |
12 |
| - |
13 |
| - from crawlee.storage_clients.models import KeyValueStoreListKeysPage, KeyValueStoreMetadata, KeyValueStoreRecord |
| 9 | + from collections.abc import AsyncIterator |
| 10 | + from datetime import datetime |
| 11 | + from pathlib import Path |
| 12 | + |
| 13 | + from crawlee.storage_clients.models import ( |
| 14 | + KeyValueStoreRecord, |
| 15 | + KeyValueStoreRecordMetadata, |
| 16 | + ) |
| 17 | + |
| 18 | +# Properties: |
| 19 | +# - id |
| 20 | +# - name |
| 21 | +# - created_at |
| 22 | +# - accessed_at |
| 23 | +# - modified_at |
| 24 | + |
| 25 | +# Methods: |
| 26 | +# - open |
| 27 | +# - drop |
| 28 | +# - get_value |
| 29 | +# - set_value |
| 30 | +# - delete_value |
| 31 | +# - iterate_keys |
| 32 | +# - get_public_url |
14 | 33 |
|
15 | 34 |
|
16 | 35 | @docs_group('Abstract classes')
|
17 | 36 | class KeyValueStoreClient(ABC):
|
18 |
| - """An abstract class for key-value store resource clients. |
| 37 | + """An abstract class for key-value store (KVS) resource clients. |
19 | 38 |
|
20 | 39 | These clients are specific to the type of resource they manage and operate under a designated storage
|
21 | 40 | client, like a memory storage client.
|
22 | 41 | """
|
23 | 42 |
|
| 43 | + @property |
24 | 44 | @abstractmethod
|
25 |
| - async def get(self) -> KeyValueStoreMetadata | None: |
26 |
| - """Get metadata about the key-value store being managed by this client. |
| 45 | + def id(self) -> str: |
| 46 | + """The ID of the key-value store.""" |
27 | 47 |
|
28 |
| - Returns: |
29 |
| - An object containing the key-value store's details, or None if the key-value store does not exist. |
30 |
| - """ |
| 48 | + @property |
| 49 | + @abstractmethod |
| 50 | + def name(self) -> str | None: |
| 51 | + """The name of the key-value store.""" |
31 | 52 |
|
| 53 | + @property |
32 | 54 | @abstractmethod
|
33 |
| - async def delete(self) -> None: |
34 |
| - """Permanently delete the key-value store managed by this client.""" |
| 55 | + def created_at(self) -> datetime: |
| 56 | + """The time at which the key-value store was created.""" |
35 | 57 |
|
| 58 | + @property |
36 | 59 | @abstractmethod
|
37 |
| - async def list_keys( |
38 |
| - self, |
| 60 | + def accessed_at(self) -> datetime: |
| 61 | + """The time at which the key-value store was last accessed.""" |
| 62 | + |
| 63 | + @property |
| 64 | + @abstractmethod |
| 65 | + def modified_at(self) -> datetime: |
| 66 | + """The time at which the key-value store was last modified.""" |
| 67 | + |
| 68 | + @classmethod |
| 69 | + @abstractmethod |
| 70 | + async def open( |
| 71 | + cls, |
39 | 72 | *,
|
40 |
| - limit: int = 1000, |
41 |
| - exclusive_start_key: str | None = None, |
42 |
| - ) -> KeyValueStoreListKeysPage: |
43 |
| - """List the keys in the key-value store. |
| 73 | + id: str | None, |
| 74 | + name: str | None, |
| 75 | + storage_dir: Path, |
| 76 | + ) -> KeyValueStoreClient: |
| 77 | + """Open existing or create a new key-value store client. |
| 78 | +
|
| 79 | + If a key-value store with the given name already exists, the appropriate key-value store client is returned. |
| 80 | + Otherwise, a new key-value store is created and client for it is returned. |
44 | 81 |
|
45 | 82 | Args:
|
46 |
| - limit: Number of keys to be returned. Maximum value is 1000. |
47 |
| - exclusive_start_key: All keys up to this one (including) are skipped from the result. |
| 83 | + id: The ID of the key-value store. |
| 84 | + name: The name of the key-value store. |
| 85 | + storage_dir: The path to the storage directory. If the client persists data, it should use this directory. |
48 | 86 |
|
49 | 87 | Returns:
|
50 |
| - The list of keys in the key-value store matching the given arguments. |
| 88 | + A key-value store client. |
51 | 89 | """
|
52 | 90 |
|
53 | 91 | @abstractmethod
|
54 |
| - async def get_record(self, key: str) -> KeyValueStoreRecord | None: |
55 |
| - """Retrieve the given record from the key-value store. |
56 |
| -
|
57 |
| - Args: |
58 |
| - key: Key of the record to retrieve. |
| 92 | + async def drop(self) -> None: |
| 93 | + """Drop the whole key-value store and remove all its values. |
59 | 94 |
|
60 |
| - Returns: |
61 |
| - The requested record, or None, if the record does not exist |
| 95 | + The backend method for the `KeyValueStore.drop` call. |
62 | 96 | """
|
63 | 97 |
|
64 | 98 | @abstractmethod
|
65 |
| - async def get_record_as_bytes(self, key: str) -> KeyValueStoreRecord[bytes] | None: |
66 |
| - """Retrieve the given record from the key-value store, without parsing it. |
67 |
| -
|
68 |
| - Args: |
69 |
| - key: Key of the record to retrieve. |
| 99 | + async def get_value(self, *, key: str) -> KeyValueStoreRecord | None: |
| 100 | + """Retrieve the given record from the key-value store. |
70 | 101 |
|
71 |
| - Returns: |
72 |
| - The requested record, or None, if the record does not exist |
| 102 | + The backend method for the `KeyValueStore.get_value` call. |
73 | 103 | """
|
74 | 104 |
|
75 | 105 | @abstractmethod
|
76 |
| - async def stream_record(self, key: str) -> AbstractAsyncContextManager[KeyValueStoreRecord[Response] | None]: |
77 |
| - """Retrieve the given record from the key-value store, as a stream. |
| 106 | + async def set_value(self, *, key: str, value: Any, content_type: str | None = None) -> None: |
| 107 | + """Set a value in the key-value store by its key. |
78 | 108 |
|
79 |
| - Args: |
80 |
| - key: Key of the record to retrieve. |
81 |
| -
|
82 |
| - Returns: |
83 |
| - The requested record as a context-managed streaming Response, or None, if the record does not exist |
| 109 | + The backend method for the `KeyValueStore.set_value` call. |
84 | 110 | """
|
85 | 111 |
|
86 | 112 | @abstractmethod
|
87 |
| - async def set_record(self, key: str, value: Any, content_type: str | None = None) -> None: |
88 |
| - """Set a value to the given record in the key-value store. |
| 113 | + async def delete_value(self, *, key: str) -> None: |
| 114 | + """Delete a value from the key-value store by its key. |
89 | 115 |
|
90 |
| - Args: |
91 |
| - key: The key of the record to save the value to. |
92 |
| - value: The value to save into the record. |
93 |
| - content_type: The content type of the saved value. |
| 116 | + The backend method for the `KeyValueStore.delete_value` call. |
94 | 117 | """
|
95 | 118 |
|
96 | 119 | @abstractmethod
|
97 |
| - async def delete_record(self, key: str) -> None: |
98 |
| - """Delete the specified record from the key-value store. |
| 120 | + async def iterate_keys( |
| 121 | + self, |
| 122 | + *, |
| 123 | + exclusive_start_key: str | None = None, |
| 124 | + limit: int | None = None, |
| 125 | + ) -> AsyncIterator[KeyValueStoreRecordMetadata]: |
| 126 | + """Iterate over all the existing keys in the key-value store. |
99 | 127 |
|
100 |
| - Args: |
101 |
| - key: The key of the record which to delete. |
| 128 | + The backend method for the `KeyValueStore.iterate_keys` call. |
102 | 129 | """
|
| 130 | + # This syntax is to make mypy properly work with abstract AsyncIterator. |
| 131 | + # https://mypy.readthedocs.io/en/stable/more_types.html#asynchronous-iterators |
| 132 | + raise NotImplementedError |
| 133 | + if False: # type: ignore[unreachable] |
| 134 | + yield 0 |
103 | 135 |
|
104 | 136 | @abstractmethod
|
105 |
| - async def get_public_url(self, key: str) -> str: |
| 137 | + async def get_public_url(self, *, key: str) -> str: |
106 | 138 | """Get the public URL for the given key.
|
107 | 139 |
|
108 |
| - Args: |
109 |
| - key: Key of the record for which URL is required. |
110 |
| -
|
111 |
| - Returns: |
112 |
| - The public URL for the given key. |
113 |
| -
|
114 |
| - Raises: |
115 |
| - ValueError: If the key does not exist. |
| 140 | + The backend method for the `KeyValueStore.get_public_url` call. |
116 | 141 | """
|
0 commit comments