modal 0.62.115__py3-none-any.whl → 0.72.13__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.
- modal/__init__.py +13 -9
- modal/__main__.py +41 -3
- modal/_clustered_functions.py +80 -0
- modal/_clustered_functions.pyi +22 -0
- modal/_container_entrypoint.py +402 -398
- modal/_ipython.py +3 -13
- modal/_location.py +17 -10
- modal/_output.py +243 -99
- modal/_pty.py +2 -2
- modal/_resolver.py +55 -60
- modal/_resources.py +26 -7
- modal/_runtime/__init__.py +1 -0
- modal/_runtime/asgi.py +519 -0
- modal/_runtime/container_io_manager.py +1025 -0
- modal/{execution_context.py → _runtime/execution_context.py} +11 -2
- modal/_runtime/telemetry.py +169 -0
- modal/_runtime/user_code_imports.py +356 -0
- modal/_serialization.py +123 -6
- modal/_traceback.py +47 -187
- modal/_tunnel.py +50 -14
- modal/_tunnel.pyi +19 -36
- modal/_utils/app_utils.py +3 -17
- modal/_utils/async_utils.py +386 -104
- modal/_utils/blob_utils.py +157 -186
- modal/_utils/bytes_io_segment_payload.py +97 -0
- modal/_utils/deprecation.py +89 -0
- modal/_utils/docker_utils.py +98 -0
- modal/_utils/function_utils.py +299 -98
- modal/_utils/grpc_testing.py +47 -34
- modal/_utils/grpc_utils.py +54 -21
- modal/_utils/hash_utils.py +51 -10
- modal/_utils/http_utils.py +39 -9
- modal/_utils/logger.py +2 -1
- modal/_utils/mount_utils.py +34 -16
- modal/_utils/name_utils.py +58 -0
- modal/_utils/package_utils.py +14 -1
- modal/_utils/pattern_utils.py +205 -0
- modal/_utils/rand_pb_testing.py +3 -3
- modal/_utils/shell_utils.py +15 -49
- modal/_vendor/a2wsgi_wsgi.py +62 -72
- modal/_vendor/cloudpickle.py +1 -1
- modal/_watcher.py +12 -10
- modal/app.py +561 -323
- modal/app.pyi +474 -262
- modal/call_graph.py +7 -6
- modal/cli/_download.py +22 -6
- modal/cli/_traceback.py +200 -0
- modal/cli/app.py +203 -42
- modal/cli/config.py +12 -5
- modal/cli/container.py +61 -13
- modal/cli/dict.py +128 -0
- modal/cli/entry_point.py +26 -13
- modal/cli/environment.py +40 -9
- modal/cli/import_refs.py +21 -48
- modal/cli/launch.py +28 -14
- modal/cli/network_file_system.py +57 -21
- modal/cli/profile.py +1 -1
- modal/cli/programs/run_jupyter.py +34 -9
- modal/cli/programs/vscode.py +58 -8
- modal/cli/queues.py +131 -0
- modal/cli/run.py +199 -96
- modal/cli/secret.py +5 -4
- modal/cli/token.py +7 -2
- modal/cli/utils.py +74 -8
- modal/cli/volume.py +97 -56
- modal/client.py +248 -144
- modal/client.pyi +156 -124
- modal/cloud_bucket_mount.py +43 -30
- modal/cloud_bucket_mount.pyi +32 -25
- modal/cls.py +528 -141
- modal/cls.pyi +189 -145
- modal/config.py +32 -15
- modal/container_process.py +177 -0
- modal/container_process.pyi +82 -0
- modal/dict.py +50 -54
- modal/dict.pyi +120 -164
- modal/environments.py +106 -5
- modal/environments.pyi +77 -25
- modal/exception.py +30 -43
- modal/experimental.py +62 -2
- modal/file_io.py +537 -0
- modal/file_io.pyi +235 -0
- modal/file_pattern_matcher.py +196 -0
- modal/functions.py +846 -428
- modal/functions.pyi +446 -387
- modal/gpu.py +57 -44
- modal/image.py +943 -417
- modal/image.pyi +584 -245
- modal/io_streams.py +434 -0
- modal/io_streams.pyi +122 -0
- modal/mount.py +223 -90
- modal/mount.pyi +241 -243
- modal/network_file_system.py +85 -86
- modal/network_file_system.pyi +151 -110
- modal/object.py +66 -36
- modal/object.pyi +166 -143
- modal/output.py +63 -0
- modal/parallel_map.py +73 -47
- modal/parallel_map.pyi +51 -63
- modal/partial_function.py +272 -107
- modal/partial_function.pyi +219 -120
- modal/proxy.py +15 -12
- modal/proxy.pyi +3 -8
- modal/queue.py +96 -72
- modal/queue.pyi +210 -135
- modal/requirements/2024.04.txt +2 -1
- modal/requirements/2024.10.txt +16 -0
- modal/requirements/README.md +21 -0
- modal/requirements/base-images.json +22 -0
- modal/retries.py +45 -4
- modal/runner.py +325 -203
- modal/runner.pyi +124 -110
- modal/running_app.py +27 -4
- modal/sandbox.py +509 -231
- modal/sandbox.pyi +396 -169
- modal/schedule.py +2 -2
- modal/scheduler_placement.py +20 -3
- modal/secret.py +41 -25
- modal/secret.pyi +62 -42
- modal/serving.py +39 -49
- modal/serving.pyi +37 -43
- modal/stream_type.py +15 -0
- modal/token_flow.py +5 -3
- modal/token_flow.pyi +37 -32
- modal/volume.py +123 -137
- modal/volume.pyi +228 -221
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/METADATA +5 -5
- modal-0.72.13.dist-info/RECORD +174 -0
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/top_level.txt +0 -1
- modal_docs/gen_reference_docs.py +3 -1
- modal_docs/mdmd/mdmd.py +0 -1
- modal_docs/mdmd/signatures.py +1 -2
- modal_global_objects/images/base_images.py +28 -0
- modal_global_objects/mounts/python_standalone.py +2 -2
- modal_proto/__init__.py +1 -1
- modal_proto/api.proto +1231 -531
- modal_proto/api_grpc.py +750 -430
- modal_proto/api_pb2.py +2102 -1176
- modal_proto/api_pb2.pyi +8859 -0
- modal_proto/api_pb2_grpc.py +1329 -675
- modal_proto/api_pb2_grpc.pyi +1416 -0
- modal_proto/modal_api_grpc.py +149 -0
- modal_proto/modal_options_grpc.py +3 -0
- modal_proto/options_pb2.pyi +20 -0
- modal_proto/options_pb2_grpc.pyi +7 -0
- modal_proto/py.typed +0 -0
- modal_version/__init__.py +1 -1
- modal_version/_version_generated.py +2 -2
- modal/_asgi.py +0 -370
- modal/_container_exec.py +0 -128
- modal/_container_io_manager.py +0 -646
- modal/_container_io_manager.pyi +0 -412
- modal/_sandbox_shell.py +0 -49
- modal/app_utils.py +0 -20
- modal/app_utils.pyi +0 -17
- modal/execution_context.pyi +0 -37
- modal/shared_volume.py +0 -23
- modal/shared_volume.pyi +0 -24
- modal-0.62.115.dist-info/RECORD +0 -207
- modal_global_objects/images/conda.py +0 -15
- modal_global_objects/images/debian_slim.py +0 -15
- modal_global_objects/images/micromamba.py +0 -15
- test/__init__.py +0 -1
- test/aio_test.py +0 -12
- test/async_utils_test.py +0 -279
- test/blob_test.py +0 -67
- test/cli_imports_test.py +0 -149
- test/cli_test.py +0 -674
- test/client_test.py +0 -203
- test/cloud_bucket_mount_test.py +0 -22
- test/cls_test.py +0 -636
- test/config_test.py +0 -149
- test/conftest.py +0 -1485
- test/container_app_test.py +0 -50
- test/container_test.py +0 -1405
- test/cpu_test.py +0 -23
- test/decorator_test.py +0 -85
- test/deprecation_test.py +0 -34
- test/dict_test.py +0 -51
- test/e2e_test.py +0 -68
- test/error_test.py +0 -7
- test/function_serialization_test.py +0 -32
- test/function_test.py +0 -791
- test/function_utils_test.py +0 -101
- test/gpu_test.py +0 -159
- test/grpc_utils_test.py +0 -82
- test/helpers.py +0 -47
- test/image_test.py +0 -814
- test/live_reload_test.py +0 -80
- test/lookup_test.py +0 -70
- test/mdmd_test.py +0 -329
- test/mount_test.py +0 -162
- test/mounted_files_test.py +0 -327
- test/network_file_system_test.py +0 -188
- test/notebook_test.py +0 -66
- test/object_test.py +0 -41
- test/package_utils_test.py +0 -25
- test/queue_test.py +0 -115
- test/resolver_test.py +0 -59
- test/retries_test.py +0 -67
- test/runner_test.py +0 -85
- test/sandbox_test.py +0 -191
- test/schedule_test.py +0 -15
- test/scheduler_placement_test.py +0 -57
- test/secret_test.py +0 -89
- test/serialization_test.py +0 -50
- test/stub_composition_test.py +0 -10
- test/stub_test.py +0 -361
- test/test_asgi_wrapper.py +0 -234
- test/token_flow_test.py +0 -18
- test/traceback_test.py +0 -135
- test/tunnel_test.py +0 -29
- test/utils_test.py +0 -88
- test/version_test.py +0 -14
- test/volume_test.py +0 -397
- test/watcher_test.py +0 -58
- test/webhook_test.py +0 -145
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/LICENSE +0 -0
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/WHEEL +0 -0
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/entry_points.txt +0 -0
modal/queue.py
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
import queue # The system library
|
3
3
|
import time
|
4
4
|
import warnings
|
5
|
-
from
|
5
|
+
from collections.abc import AsyncGenerator, AsyncIterator
|
6
|
+
from typing import Any, Optional
|
6
7
|
|
7
8
|
from grpclib import GRPCError, Status
|
8
9
|
from synchronicity.async_wrap import asynccontextmanager
|
@@ -12,9 +13,11 @@ from modal_proto import api_pb2
|
|
12
13
|
from ._resolver import Resolver
|
13
14
|
from ._serialization import deserialize, serialize
|
14
15
|
from ._utils.async_utils import TaskContext, synchronize_api, warn_if_generator_is_not_consumed
|
16
|
+
from ._utils.deprecation import renamed_parameter
|
15
17
|
from ._utils.grpc_utils import retry_transient_errors
|
18
|
+
from ._utils.name_utils import check_object_name
|
16
19
|
from .client import _Client
|
17
|
-
from .exception import InvalidError,
|
20
|
+
from .exception import InvalidError, RequestSizeError
|
18
21
|
from .object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
|
19
22
|
|
20
23
|
|
@@ -25,35 +28,12 @@ class _Queue(_Object, type_prefix="qu"):
|
|
25
28
|
|
26
29
|
By default, the `Queue` object acts as a single FIFO queue which supports puts and gets (blocking and non-blocking).
|
27
30
|
|
28
|
-
**Queue partitions (beta)**
|
29
|
-
|
30
|
-
Specifying partition keys gives access to other independent FIFO partitions within the same `Queue` object.
|
31
|
-
Across any two partitions, puts and gets are completely independent. For example, a put in one partition does not affect
|
32
|
-
a get in any other partition.
|
33
|
-
|
34
|
-
When no partition key is specified (by default), puts and gets will operate on a default partition. This default partition
|
35
|
-
is also isolated from all other partitions. Please see the Usage section below for an example using partitions.
|
36
|
-
|
37
|
-
**Lifetime of a queue and its partitions**
|
38
|
-
|
39
|
-
By default, each partition is cleared 24 hours after the last `put` operation. A lower TTL can be specified by the `partition_ttl`
|
40
|
-
argument in the `put` or `put_many` methods. Each partition's expiry is handled independently.
|
41
|
-
|
42
|
-
As such, `Queue`s are best used for communication between active functions and not relied on for persistent storage.
|
43
|
-
|
44
|
-
On app completion or after stopping an app any associated `Queue` objects are cleaned up. All its partitions will be cleared.
|
45
|
-
|
46
|
-
**Limits**
|
47
|
-
|
48
|
-
A single `Queue` can contain up to 100,000 partitions, each with up to 5,000 items. Each item can be up to 256 KiB.
|
49
|
-
|
50
|
-
Partition keys must be non-empty and must not exceed 64 bytes.
|
51
|
-
|
52
31
|
**Usage**
|
53
32
|
|
54
33
|
```python
|
55
34
|
from modal import Queue
|
56
35
|
|
36
|
+
# Create an ephemeral queue which is anonymous and garbage collected
|
57
37
|
with Queue.ephemeral() as my_queue:
|
58
38
|
# Putting values
|
59
39
|
my_queue.put("some value")
|
@@ -77,25 +57,42 @@ class _Queue(_Object, type_prefix="qu"):
|
|
77
57
|
# (beta feature) Iterate through items in place (read immutably)
|
78
58
|
my_queue.put(1)
|
79
59
|
assert [v for v in my_queue.iterate()] == [0, 1]
|
60
|
+
|
61
|
+
# You can also create persistent queues that can be used across apps
|
62
|
+
queue = Queue.from_name("my-persisted-queue", create_if_missing=True)
|
63
|
+
queue.put(42)
|
64
|
+
assert queue.get() == 42
|
80
65
|
```
|
81
66
|
|
82
67
|
For more examples, see the [guide](/docs/guide/dicts-and-queues#modal-queues).
|
83
|
-
"""
|
84
68
|
|
85
|
-
|
86
|
-
def new():
|
87
|
-
"""`Queue.new` is deprecated.
|
69
|
+
**Queue partitions (beta)**
|
88
70
|
|
89
|
-
|
90
|
-
|
91
|
-
|
71
|
+
Specifying partition keys gives access to other independent FIFO partitions within the same `Queue` object.
|
72
|
+
Across any two partitions, puts and gets are completely independent.
|
73
|
+
For example, a put in one partition does not affect a get in any other partition.
|
92
74
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
75
|
+
When no partition key is specified (by default), puts and gets will operate on a default partition.
|
76
|
+
This default partition is also isolated from all other partitions.
|
77
|
+
Please see the Usage section below for an example using partitions.
|
78
|
+
|
79
|
+
**Lifetime of a queue and its partitions**
|
97
80
|
|
98
|
-
|
81
|
+
By default, each partition is cleared 24 hours after the last `put` operation.
|
82
|
+
A lower TTL can be specified by the `partition_ttl` argument in the `put` or `put_many` methods.
|
83
|
+
Each partition's expiry is handled independently.
|
84
|
+
|
85
|
+
As such, `Queue`s are best used for communication between active functions and not relied on for persistent storage.
|
86
|
+
|
87
|
+
On app completion or after stopping an app any associated `Queue` objects are cleaned up.
|
88
|
+
All its partitions will be cleared.
|
89
|
+
|
90
|
+
**Limits**
|
91
|
+
|
92
|
+
A single `Queue` can contain up to 100,000 partitions, each with up to 5,000 items. Each item can be up to 256 KiB.
|
93
|
+
|
94
|
+
Partition keys must be non-empty and must not exceed 64 bytes.
|
95
|
+
"""
|
99
96
|
|
100
97
|
def __init__(self):
|
101
98
|
"""mdmd:hidden"""
|
@@ -115,7 +112,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
115
112
|
@classmethod
|
116
113
|
@asynccontextmanager
|
117
114
|
async def ephemeral(
|
118
|
-
cls:
|
115
|
+
cls: type["_Queue"],
|
119
116
|
client: Optional[_Client] = None,
|
120
117
|
environment_name: Optional[str] = None,
|
121
118
|
_heartbeat_sleep: float = EPHEMERAL_OBJECT_HEARTBEAT_SLEEP,
|
@@ -128,7 +125,9 @@ class _Queue(_Object, type_prefix="qu"):
|
|
128
125
|
|
129
126
|
with Queue.ephemeral() as q:
|
130
127
|
q.put(123)
|
128
|
+
```
|
131
129
|
|
130
|
+
```python notest
|
132
131
|
async with Queue.ephemeral() as q:
|
133
132
|
await q.put.aio(123)
|
134
133
|
```
|
@@ -146,27 +145,29 @@ class _Queue(_Object, type_prefix="qu"):
|
|
146
145
|
yield cls._new_hydrated(response.queue_id, client, None, is_another_app=True)
|
147
146
|
|
148
147
|
@staticmethod
|
148
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
149
149
|
def from_name(
|
150
|
-
|
150
|
+
name: str,
|
151
151
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
152
152
|
environment_name: Optional[str] = None,
|
153
153
|
create_if_missing: bool = False,
|
154
154
|
) -> "_Queue":
|
155
|
-
"""
|
155
|
+
"""Reference a named Queue, creating if necessary.
|
156
156
|
|
157
|
-
|
157
|
+
In contrast to `modal.Queue.lookup`, this is a lazy method
|
158
|
+
the defers hydrating the local object with metadata from
|
159
|
+
Modal servers until the first time it is actually used.
|
158
160
|
|
159
161
|
```python
|
160
|
-
|
161
|
-
|
162
|
-
queue = Queue.from_name("my-queue", create_if_missing=True)
|
163
|
-
queue.put(123)
|
162
|
+
q = modal.Queue.from_name("my-queue", create_if_missing=True)
|
163
|
+
q.put(123)
|
164
164
|
```
|
165
165
|
"""
|
166
|
+
check_object_name(name, "Queue")
|
166
167
|
|
167
168
|
async def _load(self: _Queue, resolver: Resolver, existing_object_id: Optional[str]):
|
168
169
|
req = api_pb2.QueueGetOrCreateRequest(
|
169
|
-
deployment_name=
|
170
|
+
deployment_name=name,
|
170
171
|
namespace=namespace,
|
171
172
|
environment_name=_get_environment_name(environment_name, resolver),
|
172
173
|
object_creation_type=(api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING if create_if_missing else None),
|
@@ -177,32 +178,26 @@ class _Queue(_Object, type_prefix="qu"):
|
|
177
178
|
return _Queue._from_loader(_load, "Queue()", is_another_app=True, hydrate_lazily=True)
|
178
179
|
|
179
180
|
@staticmethod
|
180
|
-
|
181
|
-
label: str, namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE, environment_name: Optional[str] = None
|
182
|
-
) -> "_Queue":
|
183
|
-
"""Deprecated! Use `Queue.from_name(name, create_if_missing=True)`."""
|
184
|
-
deprecation_warning((2024, 3, 1), _Queue.persisted.__doc__)
|
185
|
-
return _Queue.from_name(label, namespace, environment_name, create_if_missing=True)
|
186
|
-
|
187
|
-
@staticmethod
|
181
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
188
182
|
async def lookup(
|
189
|
-
|
183
|
+
name: str,
|
190
184
|
namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
191
185
|
client: Optional[_Client] = None,
|
192
186
|
environment_name: Optional[str] = None,
|
193
187
|
create_if_missing: bool = False,
|
194
188
|
) -> "_Queue":
|
195
|
-
"""Lookup a
|
189
|
+
"""Lookup a named Queue.
|
196
190
|
|
197
|
-
|
198
|
-
from
|
191
|
+
In contrast to `modal.Queue.from_name`, this is an eager method
|
192
|
+
that will hydrate the local object with metadata from Modal servers.
|
199
193
|
|
194
|
+
```python
|
200
195
|
q = modal.Queue.lookup("my-queue")
|
201
196
|
q.put(123)
|
202
197
|
```
|
203
198
|
"""
|
204
199
|
obj = _Queue.from_name(
|
205
|
-
|
200
|
+
name, namespace=namespace, environment_name=environment_name, create_if_missing=create_if_missing
|
206
201
|
)
|
207
202
|
if client is None:
|
208
203
|
client = await _Client.from_env()
|
@@ -211,12 +206,13 @@ class _Queue(_Object, type_prefix="qu"):
|
|
211
206
|
return obj
|
212
207
|
|
213
208
|
@staticmethod
|
214
|
-
|
215
|
-
|
209
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
210
|
+
async def delete(name: str, *, client: Optional[_Client] = None, environment_name: Optional[str] = None):
|
211
|
+
obj = await _Queue.lookup(name, client=client, environment_name=environment_name)
|
216
212
|
req = api_pb2.QueueDeleteRequest(queue_id=obj.object_id)
|
217
213
|
await retry_transient_errors(obj._client.stub.QueueDelete, req)
|
218
214
|
|
219
|
-
async def _get_nonblocking(self, partition: Optional[str], n_values: int) ->
|
215
|
+
async def _get_nonblocking(self, partition: Optional[str], n_values: int) -> list[Any]:
|
220
216
|
request = api_pb2.QueueGetRequest(
|
221
217
|
queue_id=self.object_id,
|
222
218
|
partition_key=self.validate_partition_key(partition),
|
@@ -230,7 +226,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
230
226
|
else:
|
231
227
|
return []
|
232
228
|
|
233
|
-
async def _get_blocking(self, partition: Optional[str], timeout: Optional[float], n_values: int) ->
|
229
|
+
async def _get_blocking(self, partition: Optional[str], timeout: Optional[float], n_values: int) -> list[Any]:
|
234
230
|
if timeout is not None:
|
235
231
|
deadline = time.time() + timeout
|
236
232
|
else:
|
@@ -259,6 +255,18 @@ class _Queue(_Object, type_prefix="qu"):
|
|
259
255
|
|
260
256
|
raise queue.Empty()
|
261
257
|
|
258
|
+
@live_method
|
259
|
+
async def clear(self, *, partition: Optional[str] = None, all: bool = False) -> None:
|
260
|
+
"""Clear the contents of a single partition or all partitions."""
|
261
|
+
if partition and all:
|
262
|
+
raise InvalidError("Partition must be null when requesting to clear all.")
|
263
|
+
request = api_pb2.QueueClearRequest(
|
264
|
+
queue_id=self.object_id,
|
265
|
+
partition_key=self.validate_partition_key(partition),
|
266
|
+
all_partitions=all,
|
267
|
+
)
|
268
|
+
await retry_transient_errors(self._client.stub.QueueClear, request)
|
269
|
+
|
262
270
|
@live_method
|
263
271
|
async def get(
|
264
272
|
self, block: bool = True, timeout: Optional[float] = None, *, partition: Optional[str] = None
|
@@ -288,7 +296,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
288
296
|
@live_method
|
289
297
|
async def get_many(
|
290
298
|
self, n_values: int, block: bool = True, timeout: Optional[float] = None, *, partition: Optional[str] = None
|
291
|
-
) ->
|
299
|
+
) -> list[Any]:
|
292
300
|
"""Remove and return up to `n_values` objects from the queue.
|
293
301
|
|
294
302
|
If there are fewer than `n_values` items in the queue, return all of them.
|
@@ -331,7 +339,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
331
339
|
@live_method
|
332
340
|
async def put_many(
|
333
341
|
self,
|
334
|
-
vs:
|
342
|
+
vs: list[Any],
|
335
343
|
block: bool = True,
|
336
344
|
timeout: Optional[float] = None,
|
337
345
|
*,
|
@@ -355,7 +363,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
355
363
|
await self._put_many_nonblocking(partition, partition_ttl, vs)
|
356
364
|
|
357
365
|
async def _put_many_blocking(
|
358
|
-
self, partition: Optional[str], partition_ttl: int, vs:
|
366
|
+
self, partition: Optional[str], partition_ttl: int, vs: list[Any], timeout: Optional[float] = None
|
359
367
|
):
|
360
368
|
vs_encoded = [serialize(v) for v in vs]
|
361
369
|
|
@@ -372,12 +380,19 @@ class _Queue(_Object, type_prefix="qu"):
|
|
372
380
|
# A full queue will return this status.
|
373
381
|
additional_status_codes=[Status.RESOURCE_EXHAUSTED],
|
374
382
|
max_delay=30.0,
|
383
|
+
max_retries=None,
|
375
384
|
total_timeout=timeout,
|
376
385
|
)
|
377
386
|
except GRPCError as exc:
|
378
|
-
|
379
|
-
|
380
|
-
|
387
|
+
if exc.status == Status.RESOURCE_EXHAUSTED:
|
388
|
+
raise queue.Full(str(exc))
|
389
|
+
elif "status = '413'" in exc.message:
|
390
|
+
method = "put_many" if len(vs) > 1 else "put"
|
391
|
+
raise RequestSizeError(f"Queue.{method} request is too large") from exc
|
392
|
+
else:
|
393
|
+
raise exc
|
394
|
+
|
395
|
+
async def _put_many_nonblocking(self, partition: Optional[str], partition_ttl: int, vs: list[Any]):
|
381
396
|
vs_encoded = [serialize(v) for v in vs]
|
382
397
|
request = api_pb2.QueuePutRequest(
|
383
398
|
queue_id=self.object_id,
|
@@ -388,14 +403,23 @@ class _Queue(_Object, type_prefix="qu"):
|
|
388
403
|
try:
|
389
404
|
await retry_transient_errors(self._client.stub.QueuePut, request)
|
390
405
|
except GRPCError as exc:
|
391
|
-
|
406
|
+
if exc.status == Status.RESOURCE_EXHAUSTED:
|
407
|
+
raise queue.Full(exc.message)
|
408
|
+
elif "status = '413'" in exc.message:
|
409
|
+
method = "put_many" if len(vs) > 1 else "put"
|
410
|
+
raise RequestSizeError(f"Queue.{method} request is too large") from exc
|
411
|
+
else:
|
412
|
+
raise exc
|
392
413
|
|
393
414
|
@live_method
|
394
|
-
async def len(self, *, partition: Optional[str] = None) -> int:
|
415
|
+
async def len(self, *, partition: Optional[str] = None, total: bool = False) -> int:
|
395
416
|
"""Return the number of objects in the queue partition."""
|
417
|
+
if partition and total:
|
418
|
+
raise InvalidError("Partition must be null when requesting total length.")
|
396
419
|
request = api_pb2.QueueLenRequest(
|
397
420
|
queue_id=self.object_id,
|
398
421
|
partition_key=self.validate_partition_key(partition),
|
422
|
+
total=total,
|
399
423
|
)
|
400
424
|
response = await retry_transient_errors(self._client.stub.QueueLen, request)
|
401
425
|
return response.len
|