avtomatika 1.0b4__py3-none-any.whl → 1.0b5__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.
@@ -1,5 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Any, Dict, Optional
2
+ from typing import Any
3
3
 
4
4
 
5
5
  class StorageBackend(ABC):
@@ -8,7 +8,7 @@ class StorageBackend(ABC):
8
8
  """
9
9
 
10
10
  @abstractmethod
11
- async def get_job_state(self, job_id: str) -> Optional[Dict[str, Any]]:
11
+ async def get_job_state(self, job_id: str) -> dict[str, Any] | None:
12
12
  """Get the full state of a job by its ID.
13
13
 
14
14
  :param job_id: Unique identifier for the job.
@@ -20,8 +20,8 @@ class StorageBackend(ABC):
20
20
  async def update_worker_data(
21
21
  self,
22
22
  worker_id: str,
23
- update_data: Dict[str, Any],
24
- ) -> Optional[Dict[str, Any]]:
23
+ update_data: dict[str, Any],
24
+ ) -> dict[str, Any] | None:
25
25
  """Partially update worker information without affecting its TTL.
26
26
  Used for background processes like the reputation calculator.
27
27
 
@@ -54,9 +54,9 @@ class StorageBackend(ABC):
54
54
  async def update_worker_status(
55
55
  self,
56
56
  worker_id: str,
57
- status_update: Dict[str, Any],
57
+ status_update: dict[str, Any],
58
58
  ttl: int,
59
- ) -> Optional[Dict[str, Any]]:
59
+ ) -> dict[str, Any] | None:
60
60
  """Partially update worker information and extend its TTL.
61
61
  Used for heartbeat messages.
62
62
 
@@ -68,7 +68,7 @@ class StorageBackend(ABC):
68
68
  raise NotImplementedError
69
69
 
70
70
  @abstractmethod
71
- async def save_job_state(self, job_id: str, state: Dict[str, Any]) -> None:
71
+ async def save_job_state(self, job_id: str, state: dict[str, Any]) -> None:
72
72
  """Save the full state of a job.
73
73
 
74
74
  :param job_id: Unique identifier for the job.
@@ -80,8 +80,8 @@ class StorageBackend(ABC):
80
80
  async def update_job_state(
81
81
  self,
82
82
  job_id: str,
83
- update_data: Dict[str, Any],
84
- ) -> Dict[str, Any]:
83
+ update_data: dict[str, Any],
84
+ ) -> dict[str, Any]:
85
85
  """Partially update the state of a job.
86
86
 
87
87
  :param job_id: Unique identifier for the job.
@@ -94,7 +94,7 @@ class StorageBackend(ABC):
94
94
  async def register_worker(
95
95
  self,
96
96
  worker_id: str,
97
- worker_info: Dict[str, Any],
97
+ worker_info: dict[str, Any],
98
98
  ttl: int,
99
99
  ) -> None:
100
100
  """Registers a new worker or updates information about an existing one.
@@ -109,7 +109,7 @@ class StorageBackend(ABC):
109
109
  async def enqueue_task_for_worker(
110
110
  self,
111
111
  worker_id: str,
112
- task_payload: Dict[str, Any],
112
+ task_payload: dict[str, Any],
113
113
  priority: float,
114
114
  ) -> None:
115
115
  """Adds a task to the priority queue for a specific worker.
@@ -125,7 +125,7 @@ class StorageBackend(ABC):
125
125
  self,
126
126
  worker_id: str,
127
127
  timeout: int,
128
- ) -> Optional[Dict[str, Any]]:
128
+ ) -> dict[str, Any] | None:
129
129
  """Retrieves the highest priority task from the queue for a worker (blocking operation).
130
130
 
131
131
  :param worker_id: The ID of the worker for whom to retrieve the task.
@@ -135,7 +135,7 @@ class StorageBackend(ABC):
135
135
  raise NotImplementedError
136
136
 
137
137
  @abstractmethod
138
- async def get_available_workers(self) -> list[Dict[str, Any]]:
138
+ async def get_available_workers(self) -> list[dict[str, Any]]:
139
139
  """Get a list of all active (not expired) workers.
140
140
 
141
141
  :return: A list of dictionaries, where each dictionary represents information about a worker.
@@ -165,8 +165,19 @@ class StorageBackend(ABC):
165
165
  raise NotImplementedError
166
166
 
167
167
  @abstractmethod
168
- async def dequeue_job(self) -> Optional[str]:
169
- """Retrieve a job ID from the execution queue (blocking operation)."""
168
+ async def dequeue_job(self) -> tuple[str, str] | None:
169
+ """Retrieve a job ID and its message ID from the execution queue.
170
+
171
+ :return: A tuple of (job_id, message_id) or None if the timeout has expired.
172
+ """
173
+ raise NotImplementedError
174
+
175
+ @abstractmethod
176
+ async def ack_job(self, message_id: str) -> None:
177
+ """Acknowledge successful processing of a job from the queue.
178
+
179
+ :param message_id: The identifier of the message to acknowledge.
180
+ """
170
181
  raise NotImplementedError
171
182
 
172
183
  @abstractmethod
@@ -196,12 +207,12 @@ class StorageBackend(ABC):
196
207
  raise NotImplementedError
197
208
 
198
209
  @abstractmethod
199
- async def save_client_config(self, token: str, config: Dict[str, Any]) -> None:
210
+ async def save_client_config(self, token: str, config: dict[str, Any]) -> None:
200
211
  """Saves the static configuration of a client."""
201
212
  raise NotImplementedError
202
213
 
203
214
  @abstractmethod
204
- async def get_client_config(self, token: str) -> Optional[Dict[str, Any]]:
215
+ async def get_client_config(self, token: str) -> dict[str, Any] | None:
205
216
  """Gets the static configuration of a client."""
206
217
  raise NotImplementedError
207
218
 
@@ -225,7 +236,7 @@ class StorageBackend(ABC):
225
236
  raise NotImplementedError
226
237
 
227
238
  @abstractmethod
228
- async def get_priority_queue_stats(self, task_type: str) -> Dict[str, Any]:
239
+ async def get_priority_queue_stats(self, task_type: str) -> dict[str, Any]:
229
240
  """Get statistics on the priority queue for a given task type.
230
241
 
231
242
  :param task_type: The type of task (used as part of the queue key).
@@ -244,12 +255,12 @@ class StorageBackend(ABC):
244
255
  raise NotImplementedError
245
256
 
246
257
  @abstractmethod
247
- async def get_worker_token(self, worker_id: str) -> Optional[str]:
258
+ async def get_worker_token(self, worker_id: str) -> str | None:
248
259
  """Retrieves an individual token for a specific worker."""
249
260
  raise NotImplementedError
250
261
 
251
262
  @abstractmethod
252
- async def get_worker_info(self, worker_id: str) -> Optional[Dict[str, Any]]:
263
+ async def get_worker_info(self, worker_id: str) -> dict[str, Any] | None:
253
264
  """Get complete information about a worker by its ID."""
254
265
  raise NotImplementedError
255
266
 
@@ -1,7 +1,7 @@
1
1
  from asyncio import Lock, PriorityQueue, Queue, QueueEmpty, wait_for
2
2
  from asyncio import TimeoutError as AsyncTimeoutError
3
3
  from time import monotonic
4
- from typing import Any, Dict, List, Optional
4
+ from typing import Any
5
5
 
6
6
  from .base import StorageBackend
7
7
 
@@ -13,35 +13,35 @@ class MemoryStorage(StorageBackend):
13
13
  """
14
14
 
15
15
  def __init__(self):
16
- self._jobs: Dict[str, Dict[str, Any]] = {}
17
- self._workers: Dict[str, Dict[str, Any]] = {}
18
- self._worker_ttls: Dict[str, float] = {}
19
- self._worker_task_queues: Dict[str, PriorityQueue] = {}
16
+ self._jobs: dict[str, dict[str, Any]] = {}
17
+ self._workers: dict[str, dict[str, Any]] = {}
18
+ self._worker_ttls: dict[str, float] = {}
19
+ self._worker_task_queues: dict[str, PriorityQueue] = {}
20
20
  self._job_queue = Queue()
21
- self._quarantine_queue: List[str] = []
22
- self._watched_jobs: Dict[str, float] = {}
23
- self._client_configs: Dict[str, Dict[str, Any]] = {}
24
- self._quotas: Dict[str, int] = {}
25
- self._worker_tokens: Dict[str, str] = {}
26
- self._generic_keys: Dict[str, Any] = {}
27
- self._generic_key_ttls: Dict[str, float] = {}
28
- self._locks: Dict[str, tuple[str, float]] = {} # key -> (holder_id, expiry_time)
21
+ self._quarantine_queue: list[str] = []
22
+ self._watched_jobs: dict[str, float] = {}
23
+ self._client_configs: dict[str, dict[str, Any]] = {}
24
+ self._quotas: dict[str, int] = {}
25
+ self._worker_tokens: dict[str, str] = {}
26
+ self._generic_keys: dict[str, Any] = {}
27
+ self._generic_key_ttls: dict[str, float] = {}
28
+ self._locks: dict[str, tuple[str, float]] = {}
29
29
 
30
30
  self._lock = Lock()
31
31
 
32
- async def get_job_state(self, job_id: str) -> Optional[Dict[str, Any]]:
32
+ async def get_job_state(self, job_id: str) -> dict[str, Any] | None:
33
33
  async with self._lock:
34
34
  return self._jobs.get(job_id)
35
35
 
36
- async def save_job_state(self, job_id: str, state: Dict[str, Any]) -> None:
36
+ async def save_job_state(self, job_id: str, state: dict[str, Any]) -> None:
37
37
  async with self._lock:
38
38
  self._jobs[job_id] = state
39
39
 
40
40
  async def update_job_state(
41
41
  self,
42
42
  job_id: str,
43
- update_data: Dict[str, Any],
44
- ) -> Dict[str, Any]:
43
+ update_data: dict[str, Any],
44
+ ) -> dict[str, Any]:
45
45
  async with self._lock:
46
46
  if job_id not in self._jobs:
47
47
  self._jobs[job_id] = {}
@@ -51,7 +51,7 @@ class MemoryStorage(StorageBackend):
51
51
  async def register_worker(
52
52
  self,
53
53
  worker_id: str,
54
- worker_info: Dict[str, Any],
54
+ worker_info: dict[str, Any],
55
55
  ttl: int,
56
56
  ) -> None:
57
57
  """Registers a worker and creates a task queue for it."""
@@ -66,21 +66,20 @@ class MemoryStorage(StorageBackend):
66
66
  async def enqueue_task_for_worker(
67
67
  self,
68
68
  worker_id: str,
69
- task_payload: Dict[str, Any],
69
+ task_payload: dict[str, Any],
70
70
  priority: float,
71
71
  ) -> None:
72
72
  """Puts a task on the priority queue for a worker."""
73
73
  async with self._lock:
74
74
  if worker_id not in self._worker_task_queues:
75
75
  self._worker_task_queues[worker_id] = PriorityQueue()
76
- # asyncio.PriorityQueue is a min-heap, so we invert the priority
77
76
  await self._worker_task_queues[worker_id].put((-priority, task_payload))
78
77
 
79
78
  async def dequeue_task_for_worker(
80
79
  self,
81
80
  worker_id: str,
82
81
  timeout: int,
83
- ) -> Optional[Dict[str, Any]]:
82
+ ) -> dict[str, Any] | None:
84
83
  """Retrieves a task from the worker's priority queue with a timeout."""
85
84
  queue = None
86
85
  async with self._lock:
@@ -104,9 +103,9 @@ class MemoryStorage(StorageBackend):
104
103
  async def update_worker_status(
105
104
  self,
106
105
  worker_id: str,
107
- status_update: Dict[str, Any],
106
+ status_update: dict[str, Any],
108
107
  ttl: int,
109
- ) -> Optional[Dict[str, Any]]:
108
+ ) -> dict[str, Any] | None:
110
109
  async with self._lock:
111
110
  if worker_id in self._workers:
112
111
  self._workers[worker_id].update(status_update)
@@ -117,8 +116,8 @@ class MemoryStorage(StorageBackend):
117
116
  async def update_worker_data(
118
117
  self,
119
118
  worker_id: str,
120
- update_data: Dict[str, Any],
121
- ) -> Optional[Dict[str, Any]]:
119
+ update_data: dict[str, Any],
120
+ ) -> dict[str, Any] | None:
122
121
  async with self._lock:
123
122
  if worker_id in self._workers:
124
123
  self._workers[worker_id].update(update_data)
@@ -155,13 +154,17 @@ class MemoryStorage(StorageBackend):
155
154
  async def enqueue_job(self, job_id: str) -> None:
156
155
  await self._job_queue.put(job_id)
157
156
 
158
- async def dequeue_job(self) -> str | None:
157
+ async def dequeue_job(self) -> tuple[str, str] | None:
159
158
  """Waits indefinitely for a job ID from the queue and returns it.
160
- This simulates the blocking behavior of Redis's BLPOP.
159
+ Returns a tuple of (job_id, message_id). In MemoryStorage, message_id is dummy.
161
160
  """
162
161
  job_id = await self._job_queue.get()
163
162
  self._job_queue.task_done()
164
- return job_id
163
+ return job_id, "memory-msg-id"
164
+
165
+ async def ack_job(self, message_id: str) -> None:
166
+ """No-op for MemoryStorage as it doesn't support persistent streams."""
167
+ pass
165
168
 
166
169
  async def quarantine_job(self, job_id: str) -> None:
167
170
  async with self._lock:
@@ -187,11 +190,11 @@ class MemoryStorage(StorageBackend):
187
190
  self._generic_key_ttls[key] = now + ttl
188
191
  return self._generic_keys[key]
189
192
 
190
- async def save_client_config(self, token: str, config: Dict[str, Any]) -> None:
193
+ async def save_client_config(self, token: str, config: dict[str, Any]) -> None:
191
194
  async with self._lock:
192
195
  self._client_configs[token] = config
193
196
 
194
- async def get_client_config(self, token: str) -> Optional[Dict[str, Any]]:
197
+ async def get_client_config(self, token: str) -> dict[str, Any] | None:
195
198
  async with self._lock:
196
199
  return self._client_configs.get(token)
197
200
 
@@ -217,7 +220,6 @@ class MemoryStorage(StorageBackend):
217
220
  self._workers.clear()
218
221
  self._worker_ttls.clear()
219
222
  self._worker_task_queues.clear()
220
- # Empty the queue
221
223
  while not self._job_queue.empty():
222
224
  try:
223
225
  self._job_queue.get_nowait()
@@ -232,17 +234,15 @@ class MemoryStorage(StorageBackend):
232
234
  self._locks.clear()
233
235
 
234
236
  async def get_job_queue_length(self) -> int:
235
- # No lock needed for asyncio.Queue.qsize()
236
237
  return self._job_queue.qsize()
237
238
 
238
239
  async def get_active_worker_count(self) -> int:
239
240
  async with self._lock:
240
241
  now = monotonic()
241
- # Create a copy of keys to avoid issues with concurrent modifications
242
242
  worker_ids = list(self._workers.keys())
243
243
  return sum(self._worker_ttls.get(worker_id, 0) > now for worker_id in worker_ids)
244
244
 
245
- async def get_worker_info(self, worker_id: str) -> Optional[Dict[str, Any]]:
245
+ async def get_worker_info(self, worker_id: str) -> dict[str, Any] | None:
246
246
  async with self._lock:
247
247
  return self._workers.get(worker_id)
248
248
 
@@ -250,7 +250,7 @@ class MemoryStorage(StorageBackend):
250
250
  async with self._lock:
251
251
  self._worker_tokens[worker_id] = token
252
252
 
253
- async def get_worker_token(self, worker_id: str) -> Optional[str]:
253
+ async def get_worker_token(self, worker_id: str) -> str | None:
254
254
  async with self._lock:
255
255
  return self._worker_tokens.get(worker_id)
256
256
 
@@ -258,7 +258,7 @@ class MemoryStorage(StorageBackend):
258
258
  key = f"task_cancel:{task_id}"
259
259
  await self.increment_key_with_ttl(key, 3600)
260
260
 
261
- async def get_priority_queue_stats(self, task_type: str) -> Dict[str, Any]:
261
+ async def get_priority_queue_stats(self, task_type: str) -> dict[str, Any]:
262
262
  """
263
263
  Returns empty data, as `asyncio.PriorityQueue` does not
264
264
  support introspection to get statistics.
@@ -278,14 +278,8 @@ class MemoryStorage(StorageBackend):
278
278
  async with self._lock:
279
279
  now = monotonic()
280
280
  current_lock = self._locks.get(key)
281
-
282
- # If lock exists and hasn't expired
283
281
  if current_lock and current_lock[1] > now:
284
- # If explicitly owned by us, we can extend/re-enter (optional behavior)
285
- # But for strict locking, if it's held, return False (unless it's us? let's simpler: just False if held)
286
282
  return False
287
-
288
- # Acquire lock
289
283
  self._locks[key] = (holder_id, now + ttl)
290
284
  return True
291
285
 
@@ -294,7 +288,6 @@ class MemoryStorage(StorageBackend):
294
288
  current_lock = self._locks.get(key)
295
289
  if current_lock:
296
290
  owner, expiry = current_lock
297
- # Only release if we are the owner
298
291
  if owner == holder_id:
299
292
  del self._locks[key]
300
293
  return True