flo-python 0.1.1__py3-none-any.whl → 0.1.3__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.
flo/__init__.py CHANGED
@@ -34,6 +34,7 @@ Example:
34
34
  from .client import FloClient
35
35
  from .exceptions import (
36
36
  BadRequestError,
37
+ BlockTooLongError,
37
38
  ConflictError,
38
39
  ConnectionFailedError,
39
40
  FloError,
@@ -48,20 +49,25 @@ from .exceptions import (
48
49
  NonRetryableError,
49
50
  NotConnectedError,
50
51
  NotFoundError,
52
+ OutcomePendingError,
51
53
  OverloadedError,
52
54
  PayloadTooLargeError,
53
55
  ProtocolError,
54
56
  RateLimitedError,
57
+ RequestTimeoutError,
55
58
  ServerError,
59
+ StaleNotFoundError,
60
+ TableMismatchError,
56
61
  UnauthorizedError,
62
+ UnavailableError,
57
63
  UnexpectedEofError,
58
- UnsupportedVersionError,
59
64
  ValidationError,
60
65
  ValueTooLargeError,
61
66
  is_connection_error,
62
67
  )
63
68
  from .kv_txn import Transaction, TxnFinishedError, TxnUnsupportedOpError
64
69
  from .processing import ProcessingOperations
70
+ from .retry import DEFAULT_RETRY_POLICY, RetryPolicy
65
71
  from .types import (
66
72
  AckOptions,
67
73
  ActionDeleteOptions,
@@ -74,24 +80,27 @@ from .types import (
74
80
  ActionRunStatus,
75
81
  ActionStatusOptions,
76
82
  ActionType,
83
+ Condition,
77
84
  DeleteOptions,
78
85
  DequeueOptions,
79
86
  DequeueResult,
80
87
  DlqListOptions,
81
88
  DlqRequeueOptions,
82
89
  EnqueueOptions,
90
+ ExistsResult,
83
91
  GetJsonResult,
84
92
  GetOptions,
85
93
  GetResult,
86
94
  HistoryOptions,
95
+ HistoryResult,
96
+ IncrResult,
87
97
  KVBeginResult,
88
98
  KVCommitResult,
89
- KVEntry,
90
99
  KVExistsOptions,
100
+ KVExpireOptions,
91
101
  KVIncrOptions,
92
102
  KVJsonOptions,
93
103
  KVMGetOptions,
94
- KVTouchOptions,
95
104
  Message,
96
105
  MGetEntry,
97
106
  NackOptions,
@@ -102,17 +111,18 @@ from .types import (
102
111
  ProcessingCancelOptions,
103
112
  ProcessingListEntry,
104
113
  ProcessingListOptions,
105
- ProcessingRescaleOptions,
106
- ProcessingRestoreOptions,
107
- ProcessingSavepointOptions,
108
114
  ProcessingStatusOptions,
109
115
  ProcessingStatusResult,
110
116
  ProcessingStopOptions,
111
117
  ProcessingSubmitOptions,
112
118
  ProcessingSyncOptions,
113
119
  ProcessingSyncResult,
120
+ ProcessKind,
114
121
  PutOptions,
115
122
  PutResult,
123
+ Ran,
124
+ Reason,
125
+ RunStatus,
116
126
  ScanOptions,
117
127
  ScanResult,
118
128
  StatusCode,
@@ -131,19 +141,23 @@ from .types import (
131
141
  StreamReadResult,
132
142
  StreamRecord,
133
143
  StreamTrimOptions,
144
+ StreamTrimResult,
134
145
  TaskAssignment,
135
- TouchOptions,
136
146
  VersionEntry,
147
+ WaitFor,
137
148
  WorkerAwaitOptions,
138
149
  WorkerAwaitResult,
139
150
  WorkerCompleteOptions,
151
+ WorkerDetails,
140
152
  WorkerFailOptions,
141
153
  WorkerInfo,
142
154
  WorkerListOptions,
143
155
  WorkerListResult,
156
+ WorkerProcess,
144
157
  WorkerRegisterOptions,
145
- WorkerTask,
158
+ WorkerStatus,
146
159
  WorkerTouchOptions,
160
+ WorkerType,
147
161
  WorkflowCancelOptions,
148
162
  WorkflowCreateOptions,
149
163
  WorkflowDisableOptions,
@@ -170,7 +184,7 @@ from .worker import (
170
184
  )
171
185
  from .workflows import WorkflowOperations
172
186
 
173
- __version__ = "0.1.1"
187
+ __version__ = "0.1.3"
174
188
 
175
189
  __all__ = [
176
190
  "Transaction",
@@ -182,13 +196,18 @@ __all__ = [
182
196
  "KVIncrOptions",
183
197
  "KVJsonOptions",
184
198
  "KVMGetOptions",
185
- "KVTouchOptions",
199
+ "KVExpireOptions",
186
200
  "GetResult",
201
+ "HistoryResult",
202
+ "ExistsResult",
187
203
  "GetJsonResult",
188
204
  "MGetEntry",
189
205
  "PutResult",
206
+ "IncrResult",
190
207
  # Client
191
208
  "FloClient",
209
+ "RetryPolicy",
210
+ "DEFAULT_RETRY_POLICY",
192
211
  # High-level Worker API
193
212
  "ActionWorker",
194
213
  "ActionWorkerOptions",
@@ -207,9 +226,10 @@ __all__ = [
207
226
  "ConnectionFailedError",
208
227
  "InvalidEndpointError",
209
228
  "UnexpectedEofError",
229
+ "RequestTimeoutError",
210
230
  "ProtocolError",
211
231
  "InvalidMagicError",
212
- "UnsupportedVersionError",
232
+ "TableMismatchError",
213
233
  "InvalidChecksumError",
214
234
  "PayloadTooLargeError",
215
235
  "IncompleteResponseError",
@@ -217,28 +237,35 @@ __all__ = [
217
237
  "NamespaceTooLargeError",
218
238
  "KeyTooLargeError",
219
239
  "ValueTooLargeError",
240
+ "BlockTooLongError",
220
241
  "ServerError",
221
242
  "NotFoundError",
222
243
  "BadRequestError",
223
244
  "ConflictError",
224
245
  "UnauthorizedError",
225
246
  "OverloadedError",
247
+ "OutcomePendingError",
248
+ "StaleNotFoundError",
249
+ "UnavailableError",
226
250
  "RateLimitedError",
227
251
  "InternalServerError",
228
252
  "GenericServerError",
229
253
  "is_connection_error",
230
254
  # Types
231
255
  "OpCode",
256
+ "Ran",
257
+ "Reason",
232
258
  "StatusCode",
233
259
  "OptionTag",
234
260
  # Result types
235
- "KVEntry",
236
261
  "ScanResult",
237
262
  "VersionEntry",
238
263
  "Message",
239
264
  "DequeueResult",
240
265
  # Options
266
+ "Condition",
241
267
  "GetOptions",
268
+ "WaitFor",
242
269
  "PutOptions",
243
270
  "DeleteOptions",
244
271
  "ScanOptions",
@@ -250,7 +277,6 @@ __all__ = [
250
277
  "DlqListOptions",
251
278
  "DlqRequeueOptions",
252
279
  "PeekOptions",
253
- "TouchOptions",
254
280
  # Stream types
255
281
  "StreamID",
256
282
  "StorageTier",
@@ -264,6 +290,7 @@ __all__ = [
264
290
  "StreamAppendOptions",
265
291
  "StreamReadOptions",
266
292
  "StreamTrimOptions",
293
+ "StreamTrimResult",
267
294
  "StreamInfoOptions",
268
295
  "StreamGroupJoinOptions",
269
296
  "StreamGroupReadOptions",
@@ -271,6 +298,7 @@ __all__ = [
271
298
  "StreamGroupNackOptions",
272
299
  # Action types
273
300
  "ActionType",
301
+ "RunStatus",
274
302
  "ActionInfo",
275
303
  "ActionRunStatus",
276
304
  "ActionInvokeResult",
@@ -283,10 +311,14 @@ __all__ = [
283
311
  "ActionDeleteOptions",
284
312
  # Worker types
285
313
  "TaskAssignment",
286
- "WorkerTask",
287
314
  "WorkerAwaitResult",
315
+ "ProcessKind",
316
+ "WorkerDetails",
288
317
  "WorkerInfo",
318
+ "WorkerProcess",
289
319
  "WorkerListResult",
320
+ "WorkerStatus",
321
+ "WorkerType",
290
322
  # Worker options
291
323
  "WorkerRegisterOptions",
292
324
  "WorkerAwaitOptions",
@@ -316,9 +348,6 @@ __all__ = [
316
348
  "ProcessingListOptions",
317
349
  "ProcessingStopOptions",
318
350
  "ProcessingCancelOptions",
319
- "ProcessingSavepointOptions",
320
- "ProcessingRestoreOptions",
321
- "ProcessingRescaleOptions",
322
351
  "ProcessingSyncOptions",
323
352
  "ProcessingStatusResult",
324
353
  "ProcessingListEntry",
flo/actions.py CHANGED
@@ -9,6 +9,13 @@ For a higher-level API, see `Worker` in worker.py.
9
9
 
10
10
  from typing import TYPE_CHECKING
11
11
 
12
+ from .kv_body import (
13
+ decode_action_invoke_answer,
14
+ decode_action_status_answer,
15
+ decode_worker_info_answer,
16
+ decode_worker_list_answer,
17
+ encode_worker_list_body,
18
+ )
12
19
  from .types import (
13
20
  ActionDeleteOptions,
14
21
  ActionInvokeOptions,
@@ -24,10 +31,12 @@ from .types import (
24
31
  WorkerAwaitOptions,
25
32
  WorkerAwaitResult,
26
33
  WorkerCompleteOptions,
34
+ WorkerDetails,
27
35
  WorkerFailOptions,
28
36
  WorkerListOptions,
29
37
  WorkerListResult,
30
38
  WorkerRegisterOptions,
39
+ WorkerStatus,
31
40
  WorkerTouchOptions,
32
41
  )
33
42
  from .wire import (
@@ -39,7 +48,6 @@ from .wire import (
39
48
  serialize_worker_await_value,
40
49
  serialize_worker_complete_value,
41
50
  serialize_worker_fail_value,
42
- serialize_worker_list_value,
43
51
  serialize_worker_register_value,
44
52
  serialize_worker_touch_value,
45
53
  )
@@ -78,7 +86,7 @@ class ActionOperations:
78
86
  action_type=action_type,
79
87
  timeout_ms=opts.timeout_ms,
80
88
  max_retries=opts.max_retries,
81
- description=opts.description,
89
+ owner=opts.owner,
82
90
  )
83
91
 
84
92
  await self._client._send_and_check(
@@ -109,8 +117,7 @@ class ActionOperations:
109
117
 
110
118
  value = serialize_action_invoke_value(
111
119
  input_data=input_data,
112
- priority=opts.priority,
113
- idempotency_key=opts.idempotency_key,
120
+ labels=opts.labels,
114
121
  )
115
122
 
116
123
  response = await self._client._send_and_check(
@@ -120,9 +127,7 @@ class ActionOperations:
120
127
  value,
121
128
  )
122
129
 
123
- # Response contains run_id as string
124
- run_id = response.data.decode("utf-8") if response.data else ""
125
- return ActionInvokeResult(run_id=run_id)
130
+ return ActionInvokeResult(run_id=decode_action_invoke_answer(response.data))
126
131
 
127
132
  async def status(
128
133
  self,
@@ -148,13 +153,7 @@ class ActionOperations:
148
153
  b"",
149
154
  )
150
155
 
151
- # Parse status response - format TBD based on server implementation
152
- # For now return basic status
153
- return ActionRunStatus(
154
- run_id=run_id,
155
- status="unknown",
156
- result=response.data if response.data else None,
157
- )
156
+ return decode_action_status_answer(run_id, response.data)
158
157
 
159
158
  async def list(
160
159
  self,
@@ -233,6 +232,7 @@ class WorkerOperations:
233
232
 
234
233
  value = serialize_worker_register_value(
235
234
  task_types,
235
+ worker_type=int(opts.worker_type),
236
236
  max_concurrency=opts.concurrency,
237
237
  metadata=opts.metadata,
238
238
  machine_id=opts.machine_id,
@@ -250,24 +250,57 @@ class WorkerOperations:
250
250
  worker_id: str,
251
251
  current_load: int = 0,
252
252
  namespace: str | None = None,
253
- ) -> None:
253
+ ) -> WorkerStatus:
254
254
  """Send a worker heartbeat to keep the registration alive.
255
255
 
256
256
  Args:
257
257
  worker_id: Worker identifier.
258
258
  current_load: Current number of active tasks.
259
259
  namespace: Optional namespace override.
260
+
261
+ Returns:
262
+ The worker's status on the server: DRAINING once it is drained.
263
+
264
+ Raises:
265
+ NotFoundError: The server doesn't know the worker (it never
266
+ registered, or was removed after going silent).
260
267
  """
261
268
  import struct
262
269
 
263
270
  ns = self._client.get_namespace(namespace)
264
271
  value = struct.pack("<I", current_load)
265
- await self._client._send_and_check(
272
+ response = await self._client._send_and_check(
266
273
  OpCode.WORKER_HEARTBEAT,
267
274
  ns,
268
275
  worker_id.encode("utf-8"),
269
276
  value,
270
277
  )
278
+ if len(response.data) != 1:
279
+ raise ValueError(f"heartbeat answer is {len(response.data)} bytes, want 1")
280
+ return WorkerStatus(response.data[0])
281
+
282
+ async def drain(self, worker_id: str, namespace: str | None = None) -> None:
283
+ """Mark a worker draining: the server gives it no more work, and
284
+ its heartbeats answer DRAINING until it deregisters.
285
+
286
+ Raises:
287
+ NotFoundError: The server doesn't know the worker.
288
+ """
289
+ await self._client._send_and_check(
290
+ OpCode.WORKER_DRAIN,
291
+ self._client.get_namespace(namespace),
292
+ worker_id.encode("utf-8"),
293
+ b"",
294
+ )
295
+
296
+ async def deregister(self, worker_id: str, namespace: str | None = None) -> None:
297
+ """Remove a worker from the registry."""
298
+ await self._client._send_and_check(
299
+ OpCode.WORKER_DEREGISTER,
300
+ self._client.get_namespace(namespace),
301
+ worker_id.encode("utf-8"),
302
+ b"",
303
+ )
271
304
 
272
305
  async def await_task(
273
306
  self,
@@ -280,7 +313,7 @@ class WorkerOperations:
280
313
  Args:
281
314
  worker_id: Worker identifier.
282
315
  task_types: Task types to listen for.
283
- options: Optional await options (block_ms, timeout_ms).
316
+ options: Optional await options (block_ms).
284
317
 
285
318
  Returns:
286
319
  WorkerAwaitResult with task if available.
@@ -290,12 +323,9 @@ class WorkerOperations:
290
323
 
291
324
  value = serialize_worker_await_value(task_types)
292
325
 
293
- # Build options for block_ms and timeout_ms
294
326
  options_builder = OptionsBuilder()
295
327
  if opts.block_ms is not None:
296
328
  options_builder.add_u32(OptionTag.BLOCK_MS, opts.block_ms)
297
- if opts.timeout_ms is not None:
298
- options_builder.add_u32(OptionTag.TIMEOUT_MS, opts.timeout_ms)
299
329
 
300
330
  response = await self._client._send_and_check(
301
331
  OpCode.ACTION_AWAIT,
@@ -402,25 +432,40 @@ class WorkerOperations:
402
432
  self,
403
433
  options: WorkerListOptions | None = None,
404
434
  ) -> WorkerListResult:
405
- """List registered workers.
435
+ """List a page of the namespace's workers, in id order.
406
436
 
407
437
  Args:
408
- options: Optional list options.
438
+ options: The page's limit and cursor (the last page's ``cursor``;
439
+ None starts).
409
440
 
410
441
  Returns:
411
- WorkerListResult with list of workers.
442
+ WorkerListResult with the page's workers and the next page's
443
+ cursor, None on the last page.
412
444
  """
413
445
  opts = options or WorkerListOptions()
414
446
  namespace = self._client.get_namespace(opts.namespace)
415
447
 
416
- value = serialize_worker_list_value(opts.limit)
417
-
418
- await self._client._send_and_check(
448
+ response = await self._client._send_and_check(
419
449
  OpCode.WORKER_LIST,
420
450
  namespace,
421
451
  b"",
422
- value,
452
+ encode_worker_list_body(opts.limit, opts.cursor, opts.action),
423
453
  )
424
454
 
425
- # Parse list response - format TBD based on server implementation
426
- return WorkerListResult(workers=[])
455
+ workers, cursor = decode_worker_list_answer(response.data)
456
+ return WorkerListResult(workers=workers, cursor=cursor)
457
+
458
+ async def info(self, worker_id: str, namespace: str | None = None) -> WorkerDetails:
459
+ """A worker's record: its row, as :meth:`list` shows it, the metadata
460
+ it registered with, and its processes with their run and fail counts.
461
+
462
+ Raises:
463
+ NotFoundError: The server doesn't know the worker.
464
+ """
465
+ response = await self._client._send_and_check(
466
+ OpCode.WORKER_INFO,
467
+ self._client.get_namespace(namespace),
468
+ worker_id.encode("utf-8"),
469
+ b"",
470
+ )
471
+ return decode_worker_info_answer(response.data)