modal 1.0.6.dev58__py3-none-any.whl → 1.2.3.dev7__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.
Potentially problematic release.
This version of modal might be problematic. Click here for more details.
- modal/__main__.py +3 -4
- modal/_billing.py +80 -0
- modal/_clustered_functions.py +7 -3
- modal/_clustered_functions.pyi +4 -2
- modal/_container_entrypoint.py +41 -49
- modal/_functions.py +424 -195
- modal/_grpc_client.py +171 -0
- modal/_load_context.py +105 -0
- modal/_object.py +68 -20
- modal/_output.py +58 -45
- modal/_partial_function.py +36 -11
- modal/_pty.py +7 -3
- modal/_resolver.py +21 -35
- modal/_runtime/asgi.py +4 -3
- modal/_runtime/container_io_manager.py +301 -186
- modal/_runtime/container_io_manager.pyi +70 -61
- modal/_runtime/execution_context.py +18 -2
- modal/_runtime/execution_context.pyi +4 -1
- modal/_runtime/gpu_memory_snapshot.py +170 -63
- modal/_runtime/user_code_imports.py +28 -58
- modal/_serialization.py +57 -1
- modal/_utils/async_utils.py +33 -12
- modal/_utils/auth_token_manager.py +2 -5
- modal/_utils/blob_utils.py +110 -53
- modal/_utils/function_utils.py +49 -42
- modal/_utils/grpc_utils.py +80 -50
- modal/_utils/mount_utils.py +26 -1
- modal/_utils/name_utils.py +17 -3
- modal/_utils/task_command_router_client.py +536 -0
- modal/_utils/time_utils.py +34 -6
- modal/app.py +219 -83
- modal/app.pyi +229 -56
- modal/billing.py +5 -0
- modal/{requirements → builder}/2025.06.txt +1 -0
- modal/{requirements → builder}/PREVIEW.txt +1 -0
- modal/cli/_download.py +19 -3
- modal/cli/_traceback.py +3 -2
- modal/cli/app.py +4 -4
- modal/cli/cluster.py +15 -7
- modal/cli/config.py +5 -3
- modal/cli/container.py +7 -6
- modal/cli/dict.py +22 -16
- modal/cli/entry_point.py +12 -5
- modal/cli/environment.py +5 -4
- modal/cli/import_refs.py +3 -3
- modal/cli/launch.py +102 -5
- modal/cli/network_file_system.py +9 -13
- modal/cli/profile.py +3 -2
- modal/cli/programs/launch_instance_ssh.py +94 -0
- modal/cli/programs/run_jupyter.py +1 -1
- modal/cli/programs/run_marimo.py +95 -0
- modal/cli/programs/vscode.py +1 -1
- modal/cli/queues.py +57 -26
- modal/cli/run.py +58 -16
- modal/cli/secret.py +48 -22
- modal/cli/utils.py +3 -4
- modal/cli/volume.py +28 -25
- modal/client.py +13 -116
- modal/client.pyi +9 -91
- modal/cloud_bucket_mount.py +5 -3
- modal/cloud_bucket_mount.pyi +5 -1
- modal/cls.py +130 -102
- modal/cls.pyi +45 -85
- modal/config.py +29 -10
- modal/container_process.py +291 -13
- modal/container_process.pyi +95 -32
- modal/dict.py +282 -63
- modal/dict.pyi +423 -73
- modal/environments.py +15 -27
- modal/environments.pyi +5 -15
- modal/exception.py +8 -0
- modal/experimental/__init__.py +143 -38
- modal/experimental/flash.py +247 -78
- modal/experimental/flash.pyi +137 -9
- modal/file_io.py +14 -28
- modal/file_io.pyi +2 -2
- modal/file_pattern_matcher.py +25 -16
- modal/functions.pyi +134 -61
- modal/image.py +255 -86
- modal/image.pyi +300 -62
- modal/io_streams.py +436 -126
- modal/io_streams.pyi +236 -171
- modal/mount.py +62 -157
- modal/mount.pyi +45 -172
- modal/network_file_system.py +30 -53
- modal/network_file_system.pyi +16 -76
- modal/object.pyi +42 -8
- modal/parallel_map.py +821 -113
- modal/parallel_map.pyi +134 -0
- modal/partial_function.pyi +4 -1
- modal/proxy.py +16 -7
- modal/proxy.pyi +10 -2
- modal/queue.py +263 -61
- modal/queue.pyi +409 -66
- modal/runner.py +112 -92
- modal/runner.pyi +45 -27
- modal/sandbox.py +451 -124
- modal/sandbox.pyi +513 -67
- modal/secret.py +291 -67
- modal/secret.pyi +425 -19
- modal/serving.py +7 -11
- modal/serving.pyi +7 -8
- modal/snapshot.py +11 -8
- modal/token_flow.py +4 -4
- modal/volume.py +344 -98
- modal/volume.pyi +464 -68
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +9 -8
- modal-1.2.3.dev7.dist-info/RECORD +195 -0
- modal_docs/mdmd/mdmd.py +11 -1
- modal_proto/api.proto +399 -67
- modal_proto/api_grpc.py +241 -1
- modal_proto/api_pb2.py +1395 -1000
- modal_proto/api_pb2.pyi +1239 -79
- modal_proto/api_pb2_grpc.py +499 -4
- modal_proto/api_pb2_grpc.pyi +162 -14
- modal_proto/modal_api_grpc.py +175 -160
- modal_proto/sandbox_router.proto +145 -0
- modal_proto/sandbox_router_grpc.py +105 -0
- modal_proto/sandbox_router_pb2.py +149 -0
- modal_proto/sandbox_router_pb2.pyi +333 -0
- modal_proto/sandbox_router_pb2_grpc.py +203 -0
- modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
- modal_proto/task_command_router.proto +144 -0
- modal_proto/task_command_router_grpc.py +105 -0
- modal_proto/task_command_router_pb2.py +149 -0
- modal_proto/task_command_router_pb2.pyi +333 -0
- modal_proto/task_command_router_pb2_grpc.py +203 -0
- modal_proto/task_command_router_pb2_grpc.pyi +75 -0
- modal_version/__init__.py +1 -1
- modal-1.0.6.dev58.dist-info/RECORD +0 -183
- modal_proto/modal_options_grpc.py +0 -3
- modal_proto/options.proto +0 -19
- modal_proto/options_grpc.py +0 -3
- modal_proto/options_pb2.py +0 -35
- modal_proto/options_pb2.pyi +0 -20
- modal_proto/options_pb2_grpc.py +0 -4
- modal_proto/options_pb2_grpc.pyi +0 -7
- /modal/{requirements → builder}/2023.12.312.txt +0 -0
- /modal/{requirements → builder}/2023.12.txt +0 -0
- /modal/{requirements → builder}/2024.04.txt +0 -0
- /modal/{requirements → builder}/2024.10.txt +0 -0
- /modal/{requirements → builder}/README.md +0 -0
- /modal/{requirements → builder}/base-images.json +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/queue.py
CHANGED
|
@@ -3,22 +3,213 @@ import queue # The system library
|
|
|
3
3
|
import time
|
|
4
4
|
import warnings
|
|
5
5
|
from collections.abc import AsyncGenerator, AsyncIterator
|
|
6
|
-
from
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Any, Optional, Union
|
|
7
9
|
|
|
10
|
+
from google.protobuf.message import Message
|
|
8
11
|
from grpclib import GRPCError, Status
|
|
12
|
+
from synchronicity import classproperty
|
|
9
13
|
from synchronicity.async_wrap import asynccontextmanager
|
|
10
14
|
|
|
11
15
|
from modal_proto import api_pb2
|
|
12
16
|
|
|
13
|
-
from .
|
|
17
|
+
from ._load_context import LoadContext
|
|
18
|
+
from ._object import (
|
|
19
|
+
EPHEMERAL_OBJECT_HEARTBEAT_SLEEP,
|
|
20
|
+
_get_environment_name,
|
|
21
|
+
_Object,
|
|
22
|
+
live_method,
|
|
23
|
+
live_method_gen,
|
|
24
|
+
)
|
|
14
25
|
from ._resolver import Resolver
|
|
15
26
|
from ._serialization import deserialize, serialize
|
|
16
27
|
from ._utils.async_utils import TaskContext, synchronize_api, warn_if_generator_is_not_consumed
|
|
17
28
|
from ._utils.deprecation import deprecation_warning, warn_if_passing_namespace
|
|
18
|
-
from ._utils.grpc_utils import
|
|
29
|
+
from ._utils.grpc_utils import Retry
|
|
19
30
|
from ._utils.name_utils import check_object_name
|
|
31
|
+
from ._utils.time_utils import as_timestamp, timestamp_to_localized_dt
|
|
20
32
|
from .client import _Client
|
|
21
|
-
from .exception import InvalidError, RequestSizeError
|
|
33
|
+
from .exception import AlreadyExistsError, InvalidError, NotFoundError, RequestSizeError
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class QueueInfo:
|
|
38
|
+
"""Information about the Queue object."""
|
|
39
|
+
|
|
40
|
+
# This dataclass should be limited to information that is unchanging over the lifetime of the Queue,
|
|
41
|
+
# since it is transmitted from the server when the object is hydrated and could be stale when accessed.
|
|
42
|
+
|
|
43
|
+
name: Optional[str]
|
|
44
|
+
created_at: datetime
|
|
45
|
+
created_by: Optional[str]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class _QueueManager:
|
|
49
|
+
"""Namespace with methods for managing named Queue objects."""
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
async def create(
|
|
53
|
+
name: str, # Name to use for the new Queue
|
|
54
|
+
*,
|
|
55
|
+
allow_existing: bool = False, # If True, no-op when the Queue already exists
|
|
56
|
+
environment_name: Optional[str] = None, # Uses active environment if not specified
|
|
57
|
+
client: Optional[_Client] = None, # Optional client with Modal credentials
|
|
58
|
+
) -> None:
|
|
59
|
+
"""Create a new Queue object.
|
|
60
|
+
|
|
61
|
+
**Examples:**
|
|
62
|
+
|
|
63
|
+
```python notest
|
|
64
|
+
modal.Queue.objects.create("my-queue")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Queues will be created in the active environment, or another one can be specified:
|
|
68
|
+
|
|
69
|
+
```python notest
|
|
70
|
+
modal.Queue.objects.create("my-queue", environment_name="dev")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
By default, an error will be raised if the Queue already exists, but passing
|
|
74
|
+
`allow_existing=True` will make the creation attempt a no-op in this case.
|
|
75
|
+
|
|
76
|
+
```python notest
|
|
77
|
+
modal.Queue.objects.create("my-queue", allow_existing=True)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Note that this method does not return a local instance of the Queue. You can use
|
|
81
|
+
`modal.Queue.from_name` to perform a lookup after creation.
|
|
82
|
+
|
|
83
|
+
Added in v1.1.2.
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
check_object_name(name, "Queue")
|
|
87
|
+
client = await _Client.from_env() if client is None else client
|
|
88
|
+
object_creation_type = (
|
|
89
|
+
api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING
|
|
90
|
+
if allow_existing
|
|
91
|
+
else api_pb2.OBJECT_CREATION_TYPE_CREATE_FAIL_IF_EXISTS
|
|
92
|
+
)
|
|
93
|
+
req = api_pb2.QueueGetOrCreateRequest(
|
|
94
|
+
deployment_name=name,
|
|
95
|
+
environment_name=_get_environment_name(environment_name),
|
|
96
|
+
object_creation_type=object_creation_type,
|
|
97
|
+
)
|
|
98
|
+
try:
|
|
99
|
+
await client.stub.QueueGetOrCreate(req)
|
|
100
|
+
except GRPCError as exc:
|
|
101
|
+
if exc.status == Status.ALREADY_EXISTS and not allow_existing:
|
|
102
|
+
raise AlreadyExistsError(exc.message)
|
|
103
|
+
else:
|
|
104
|
+
raise
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
async def list(
|
|
108
|
+
*,
|
|
109
|
+
max_objects: Optional[int] = None, # Limit results to this size
|
|
110
|
+
created_before: Optional[Union[datetime, str]] = None, # Limit based on creation date
|
|
111
|
+
environment_name: str = "", # Uses active environment if not specified
|
|
112
|
+
client: Optional[_Client] = None, # Optional client with Modal credentials
|
|
113
|
+
) -> list["_Queue"]:
|
|
114
|
+
"""Return a list of hydrated Queue objects.
|
|
115
|
+
|
|
116
|
+
**Examples:**
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
queues = modal.Queue.objects.list()
|
|
120
|
+
print([q.name for q in queues])
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Queues will be retreived from the active environment, or another one can be specified:
|
|
124
|
+
|
|
125
|
+
```python notest
|
|
126
|
+
dev_queues = modal.Queue.objects.list(environment_name="dev")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
By default, all named Queues are returned, newest to oldest. It's also possible to limit the
|
|
130
|
+
number of results and to filter by creation date:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
queues = modal.Queue.objects.list(max_objects=10, created_before="2025-01-01")
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Added in v1.1.2.
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
client = await _Client.from_env() if client is None else client
|
|
140
|
+
if max_objects is not None and max_objects < 0:
|
|
141
|
+
raise InvalidError("max_objects cannot be negative")
|
|
142
|
+
|
|
143
|
+
items: list[api_pb2.QueueListResponse.QueueInfo] = []
|
|
144
|
+
|
|
145
|
+
async def retrieve_page(created_before: float) -> bool:
|
|
146
|
+
max_page_size = 100 if max_objects is None else min(100, max_objects - len(items))
|
|
147
|
+
pagination = api_pb2.ListPagination(max_objects=max_page_size, created_before=created_before)
|
|
148
|
+
req = api_pb2.QueueListRequest(
|
|
149
|
+
environment_name=_get_environment_name(environment_name), pagination=pagination
|
|
150
|
+
)
|
|
151
|
+
resp = await client.stub.QueueList(req)
|
|
152
|
+
items.extend(resp.queues)
|
|
153
|
+
finished = (len(resp.queues) < max_page_size) or (max_objects is not None and len(items) >= max_objects)
|
|
154
|
+
return finished
|
|
155
|
+
|
|
156
|
+
finished = await retrieve_page(as_timestamp(created_before))
|
|
157
|
+
while True:
|
|
158
|
+
if finished:
|
|
159
|
+
break
|
|
160
|
+
finished = await retrieve_page(items[-1].metadata.creation_info.created_at)
|
|
161
|
+
|
|
162
|
+
queues = [
|
|
163
|
+
_Queue._new_hydrated(
|
|
164
|
+
item.queue_id,
|
|
165
|
+
client,
|
|
166
|
+
item.metadata,
|
|
167
|
+
is_another_app=True,
|
|
168
|
+
rep=_Queue._repr(item.name, environment_name),
|
|
169
|
+
)
|
|
170
|
+
for item in items
|
|
171
|
+
]
|
|
172
|
+
return queues[:max_objects] if max_objects is not None else queues
|
|
173
|
+
|
|
174
|
+
@staticmethod
|
|
175
|
+
async def delete(
|
|
176
|
+
name: str, # Name of the Queue to delete
|
|
177
|
+
*,
|
|
178
|
+
allow_missing: bool = False, # If True, don't raise an error if the Queue doesn't exist
|
|
179
|
+
environment_name: Optional[str] = None, # Uses active environment if not specified
|
|
180
|
+
client: Optional[_Client] = None, # Optional client with Modal credentials
|
|
181
|
+
):
|
|
182
|
+
"""Delete a named Queue.
|
|
183
|
+
|
|
184
|
+
Warning: This deletes an *entire Queue*, not just a specific entry or partition.
|
|
185
|
+
Deletion is irreversible and will affect any Apps currently using the Queue.
|
|
186
|
+
|
|
187
|
+
**Examples:**
|
|
188
|
+
|
|
189
|
+
```python notest
|
|
190
|
+
await modal.Queue.objects.delete("my-queue")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Queues will be deleted from the active environment, or another one can be specified:
|
|
194
|
+
|
|
195
|
+
```python notest
|
|
196
|
+
await modal.Queue.objects.delete("my-queue", environment_name="dev")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Added in v1.1.2.
|
|
200
|
+
|
|
201
|
+
"""
|
|
202
|
+
try:
|
|
203
|
+
obj = await _Queue.from_name(name, environment_name=environment_name).hydrate(client)
|
|
204
|
+
except NotFoundError:
|
|
205
|
+
if not allow_missing:
|
|
206
|
+
raise
|
|
207
|
+
else:
|
|
208
|
+
req = api_pb2.QueueDeleteRequest(queue_id=obj.object_id)
|
|
209
|
+
await obj._client.stub.QueueDelete(req)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
QueueManager = synchronize_api(_QueueManager)
|
|
22
213
|
|
|
23
214
|
|
|
24
215
|
class _Queue(_Object, type_prefix="qu"):
|
|
@@ -94,10 +285,30 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
94
285
|
Partition keys must be non-empty and must not exceed 64 bytes.
|
|
95
286
|
"""
|
|
96
287
|
|
|
288
|
+
_metadata: Optional[api_pb2.QueueMetadata] = None
|
|
289
|
+
|
|
97
290
|
def __init__(self):
|
|
98
291
|
"""mdmd:hidden"""
|
|
99
292
|
raise RuntimeError("Queue() is not allowed. Please use `Queue.from_name(...)` or `Queue.ephemeral()` instead.")
|
|
100
293
|
|
|
294
|
+
@classproperty
|
|
295
|
+
def objects(cls) -> _QueueManager:
|
|
296
|
+
return _QueueManager
|
|
297
|
+
|
|
298
|
+
@property
|
|
299
|
+
def name(self) -> Optional[str]:
|
|
300
|
+
return self._name
|
|
301
|
+
|
|
302
|
+
def _hydrate_metadata(self, metadata: Optional[Message]):
|
|
303
|
+
if metadata:
|
|
304
|
+
assert isinstance(metadata, api_pb2.QueueMetadata)
|
|
305
|
+
self._metadata = metadata
|
|
306
|
+
self._name = metadata.name
|
|
307
|
+
|
|
308
|
+
def _get_metadata(self) -> api_pb2.QueueMetadata:
|
|
309
|
+
assert self._metadata
|
|
310
|
+
return self._metadata
|
|
311
|
+
|
|
101
312
|
@staticmethod
|
|
102
313
|
def validate_partition_key(partition: Optional[str]) -> bytes:
|
|
103
314
|
if partition is not None:
|
|
@@ -115,7 +326,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
115
326
|
cls: type["_Queue"],
|
|
116
327
|
client: Optional[_Client] = None,
|
|
117
328
|
environment_name: Optional[str] = None,
|
|
118
|
-
_heartbeat_sleep: float = EPHEMERAL_OBJECT_HEARTBEAT_SLEEP,
|
|
329
|
+
_heartbeat_sleep: float = EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, # mdmd:line-hidden
|
|
119
330
|
) -> AsyncIterator["_Queue"]:
|
|
120
331
|
"""Creates a new ephemeral queue within a context manager:
|
|
121
332
|
|
|
@@ -142,7 +353,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
142
353
|
async with TaskContext() as tc:
|
|
143
354
|
request = api_pb2.QueueHeartbeatRequest(queue_id=response.queue_id)
|
|
144
355
|
tc.infinite_loop(lambda: client.stub.QueueHeartbeat(request), sleep=_heartbeat_sleep)
|
|
145
|
-
yield cls._new_hydrated(response.queue_id, client,
|
|
356
|
+
yield cls._new_hydrated(response.queue_id, client, response.metadata, is_another_app=True)
|
|
146
357
|
|
|
147
358
|
@staticmethod
|
|
148
359
|
def from_name(
|
|
@@ -151,6 +362,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
151
362
|
namespace=None, # mdmd:line-hidden
|
|
152
363
|
environment_name: Optional[str] = None,
|
|
153
364
|
create_if_missing: bool = False,
|
|
365
|
+
client: Optional[_Client] = None,
|
|
154
366
|
) -> "_Queue":
|
|
155
367
|
"""Reference a named Queue, creating if necessary.
|
|
156
368
|
|
|
@@ -166,61 +378,52 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
166
378
|
check_object_name(name, "Queue")
|
|
167
379
|
warn_if_passing_namespace(namespace, "modal.Queue.from_name")
|
|
168
380
|
|
|
169
|
-
async def _load(self: _Queue, resolver: Resolver, existing_object_id: Optional[str]):
|
|
381
|
+
async def _load(self: _Queue, resolver: Resolver, load_context: LoadContext, existing_object_id: Optional[str]):
|
|
170
382
|
req = api_pb2.QueueGetOrCreateRequest(
|
|
171
383
|
deployment_name=name,
|
|
172
|
-
environment_name=
|
|
384
|
+
environment_name=load_context.environment_name,
|
|
173
385
|
object_creation_type=(api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING if create_if_missing else None),
|
|
174
386
|
)
|
|
175
|
-
response = await
|
|
176
|
-
self._hydrate(response.queue_id,
|
|
177
|
-
|
|
178
|
-
|
|
387
|
+
response = await load_context.client.stub.QueueGetOrCreate(req)
|
|
388
|
+
self._hydrate(response.queue_id, load_context.client, response.metadata)
|
|
389
|
+
|
|
390
|
+
rep = _Queue._repr(name, environment_name)
|
|
391
|
+
return _Queue._from_loader(
|
|
392
|
+
_load,
|
|
393
|
+
rep,
|
|
394
|
+
is_another_app=True,
|
|
395
|
+
hydrate_lazily=True,
|
|
396
|
+
name=name,
|
|
397
|
+
load_context_overrides=LoadContext(environment_name=environment_name, client=client),
|
|
398
|
+
)
|
|
179
399
|
|
|
180
400
|
@staticmethod
|
|
181
|
-
async def
|
|
182
|
-
name: str,
|
|
183
|
-
namespace=None, # mdmd:line-hidden
|
|
184
|
-
client: Optional[_Client] = None,
|
|
185
|
-
environment_name: Optional[str] = None,
|
|
186
|
-
create_if_missing: bool = False,
|
|
187
|
-
) -> "_Queue":
|
|
401
|
+
async def delete(name: str, *, client: Optional[_Client] = None, environment_name: Optional[str] = None):
|
|
188
402
|
"""mdmd:hidden
|
|
189
|
-
|
|
403
|
+
Delete a named Queue.
|
|
190
404
|
|
|
191
|
-
|
|
405
|
+
Warning: This deletes an *entire Queue*, not just a specific entry or partition.
|
|
406
|
+
Deletion is irreversible and will affect any Apps currently using the Queue.
|
|
192
407
|
|
|
193
|
-
|
|
194
|
-
that will hydrate the local object with metadata from Modal servers.
|
|
408
|
+
DEPRECATED: This method is deprecated; we recommend using `modal.Queue.objects.delete` instead.
|
|
195
409
|
|
|
196
|
-
```python notest
|
|
197
|
-
q = modal.Queue.lookup("my-queue")
|
|
198
|
-
q.put(123)
|
|
199
|
-
```
|
|
200
410
|
"""
|
|
201
411
|
deprecation_warning(
|
|
202
|
-
(2025,
|
|
203
|
-
"`modal.Queue.
|
|
204
|
-
" It can be replaced with `modal.Queue.from_name`."
|
|
205
|
-
"\n\nSee https://modal.com/docs/guide/modal-1-0-migration for more information.",
|
|
206
|
-
)
|
|
207
|
-
warn_if_passing_namespace(namespace, "modal.Queue.lookup")
|
|
208
|
-
obj = _Queue.from_name(
|
|
209
|
-
name,
|
|
210
|
-
environment_name=environment_name,
|
|
211
|
-
create_if_missing=create_if_missing,
|
|
412
|
+
(2025, 8, 6),
|
|
413
|
+
"`modal.Queue.delete` is deprecated; we recommend using `modal.Queue.objects.delete` instead.",
|
|
212
414
|
)
|
|
213
|
-
|
|
214
|
-
client = await _Client.from_env()
|
|
215
|
-
resolver = Resolver(client=client)
|
|
216
|
-
await resolver.load(obj)
|
|
217
|
-
return obj
|
|
415
|
+
await _Queue.objects.delete(name, environment_name=environment_name, client=client)
|
|
218
416
|
|
|
219
|
-
@
|
|
220
|
-
async def
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
417
|
+
@live_method
|
|
418
|
+
async def info(self) -> QueueInfo:
|
|
419
|
+
"""Return information about the Queue object."""
|
|
420
|
+
metadata = self._get_metadata()
|
|
421
|
+
creation_info = metadata.creation_info
|
|
422
|
+
return QueueInfo(
|
|
423
|
+
name=metadata.name or None,
|
|
424
|
+
created_at=timestamp_to_localized_dt(creation_info.created_at),
|
|
425
|
+
created_by=creation_info.created_by or None,
|
|
426
|
+
)
|
|
224
427
|
|
|
225
428
|
async def _get_nonblocking(self, partition: Optional[str], n_values: int) -> list[Any]:
|
|
226
429
|
request = api_pb2.QueueGetRequest(
|
|
@@ -230,7 +433,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
230
433
|
n_values=n_values,
|
|
231
434
|
)
|
|
232
435
|
|
|
233
|
-
response = await
|
|
436
|
+
response = await self._client.stub.QueueGet(request)
|
|
234
437
|
if response.values:
|
|
235
438
|
return [deserialize(value, self._client) for value in response.values]
|
|
236
439
|
else:
|
|
@@ -255,7 +458,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
255
458
|
n_values=n_values,
|
|
256
459
|
)
|
|
257
460
|
|
|
258
|
-
response = await
|
|
461
|
+
response = await self._client.stub.QueueGet(request)
|
|
259
462
|
|
|
260
463
|
if response.values:
|
|
261
464
|
return [deserialize(value, self._client) for value in response.values]
|
|
@@ -275,7 +478,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
275
478
|
partition_key=self.validate_partition_key(partition),
|
|
276
479
|
all_partitions=all,
|
|
277
480
|
)
|
|
278
|
-
await
|
|
481
|
+
await self._client.stub.QueueClear(request)
|
|
279
482
|
|
|
280
483
|
@live_method
|
|
281
484
|
async def get(
|
|
@@ -384,14 +587,15 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
384
587
|
partition_ttl_seconds=partition_ttl,
|
|
385
588
|
)
|
|
386
589
|
try:
|
|
387
|
-
await
|
|
388
|
-
self._client.stub.QueuePut,
|
|
590
|
+
await self._client.stub.QueuePut(
|
|
389
591
|
request,
|
|
390
592
|
# A full queue will return this status.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
593
|
+
retry=Retry(
|
|
594
|
+
additional_status_codes=[Status.RESOURCE_EXHAUSTED],
|
|
595
|
+
max_delay=30.0,
|
|
596
|
+
max_retries=None,
|
|
597
|
+
total_timeout=timeout,
|
|
598
|
+
),
|
|
395
599
|
)
|
|
396
600
|
except GRPCError as exc:
|
|
397
601
|
if exc.status == Status.RESOURCE_EXHAUSTED:
|
|
@@ -411,7 +615,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
411
615
|
partition_ttl_seconds=partition_ttl,
|
|
412
616
|
)
|
|
413
617
|
try:
|
|
414
|
-
await
|
|
618
|
+
await self._client.stub.QueuePut(request)
|
|
415
619
|
except GRPCError as exc:
|
|
416
620
|
if exc.status == Status.RESOURCE_EXHAUSTED:
|
|
417
621
|
raise queue.Full(exc.message)
|
|
@@ -431,7 +635,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
431
635
|
partition_key=self.validate_partition_key(partition),
|
|
432
636
|
total=total,
|
|
433
637
|
)
|
|
434
|
-
response = await
|
|
638
|
+
response = await self._client.stub.QueueLen(request)
|
|
435
639
|
return response.len
|
|
436
640
|
|
|
437
641
|
@warn_if_generator_is_not_consumed()
|
|
@@ -457,9 +661,7 @@ class _Queue(_Object, type_prefix="qu"):
|
|
|
457
661
|
item_poll_timeout=poll_duration,
|
|
458
662
|
)
|
|
459
663
|
|
|
460
|
-
response: api_pb2.QueueNextItemsResponse = await
|
|
461
|
-
self._client.stub.QueueNextItems, request
|
|
462
|
-
)
|
|
664
|
+
response: api_pb2.QueueNextItemsResponse = await self._client.stub.QueueNextItems(request)
|
|
463
665
|
if response.items:
|
|
464
666
|
for item in response.items:
|
|
465
667
|
yield deserialize(item.value, self._client)
|