modal 1.0.3.dev10__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.

Files changed (160) hide show
  1. modal/__init__.py +0 -2
  2. modal/__main__.py +3 -4
  3. modal/_billing.py +80 -0
  4. modal/_clustered_functions.py +7 -3
  5. modal/_clustered_functions.pyi +15 -3
  6. modal/_container_entrypoint.py +51 -69
  7. modal/_functions.py +508 -240
  8. modal/_grpc_client.py +171 -0
  9. modal/_load_context.py +105 -0
  10. modal/_object.py +81 -21
  11. modal/_output.py +58 -45
  12. modal/_partial_function.py +48 -73
  13. modal/_pty.py +7 -3
  14. modal/_resolver.py +26 -46
  15. modal/_runtime/asgi.py +4 -3
  16. modal/_runtime/container_io_manager.py +358 -220
  17. modal/_runtime/container_io_manager.pyi +296 -101
  18. modal/_runtime/execution_context.py +18 -2
  19. modal/_runtime/execution_context.pyi +64 -7
  20. modal/_runtime/gpu_memory_snapshot.py +262 -57
  21. modal/_runtime/user_code_imports.py +28 -58
  22. modal/_serialization.py +90 -6
  23. modal/_traceback.py +42 -1
  24. modal/_tunnel.pyi +380 -12
  25. modal/_utils/async_utils.py +84 -29
  26. modal/_utils/auth_token_manager.py +111 -0
  27. modal/_utils/blob_utils.py +181 -58
  28. modal/_utils/deprecation.py +19 -0
  29. modal/_utils/function_utils.py +91 -47
  30. modal/_utils/grpc_utils.py +89 -66
  31. modal/_utils/mount_utils.py +26 -1
  32. modal/_utils/name_utils.py +17 -3
  33. modal/_utils/task_command_router_client.py +536 -0
  34. modal/_utils/time_utils.py +34 -6
  35. modal/app.py +256 -88
  36. modal/app.pyi +909 -92
  37. modal/billing.py +5 -0
  38. modal/builder/2025.06.txt +18 -0
  39. modal/builder/PREVIEW.txt +18 -0
  40. modal/builder/base-images.json +58 -0
  41. modal/cli/_download.py +19 -3
  42. modal/cli/_traceback.py +3 -2
  43. modal/cli/app.py +4 -4
  44. modal/cli/cluster.py +15 -7
  45. modal/cli/config.py +5 -3
  46. modal/cli/container.py +7 -6
  47. modal/cli/dict.py +22 -16
  48. modal/cli/entry_point.py +12 -5
  49. modal/cli/environment.py +5 -4
  50. modal/cli/import_refs.py +3 -3
  51. modal/cli/launch.py +102 -5
  52. modal/cli/network_file_system.py +11 -12
  53. modal/cli/profile.py +3 -2
  54. modal/cli/programs/launch_instance_ssh.py +94 -0
  55. modal/cli/programs/run_jupyter.py +1 -1
  56. modal/cli/programs/run_marimo.py +95 -0
  57. modal/cli/programs/vscode.py +1 -1
  58. modal/cli/queues.py +57 -26
  59. modal/cli/run.py +91 -23
  60. modal/cli/secret.py +48 -22
  61. modal/cli/token.py +7 -8
  62. modal/cli/utils.py +4 -7
  63. modal/cli/volume.py +31 -25
  64. modal/client.py +15 -85
  65. modal/client.pyi +183 -62
  66. modal/cloud_bucket_mount.py +5 -3
  67. modal/cloud_bucket_mount.pyi +197 -5
  68. modal/cls.py +200 -126
  69. modal/cls.pyi +446 -68
  70. modal/config.py +29 -11
  71. modal/container_process.py +319 -19
  72. modal/container_process.pyi +190 -20
  73. modal/dict.py +290 -71
  74. modal/dict.pyi +835 -83
  75. modal/environments.py +15 -27
  76. modal/environments.pyi +46 -24
  77. modal/exception.py +14 -2
  78. modal/experimental/__init__.py +194 -40
  79. modal/experimental/flash.py +618 -0
  80. modal/experimental/flash.pyi +380 -0
  81. modal/experimental/ipython.py +11 -7
  82. modal/file_io.py +29 -36
  83. modal/file_io.pyi +251 -53
  84. modal/file_pattern_matcher.py +56 -16
  85. modal/functions.pyi +673 -92
  86. modal/gpu.py +1 -1
  87. modal/image.py +528 -176
  88. modal/image.pyi +1572 -145
  89. modal/io_streams.py +458 -128
  90. modal/io_streams.pyi +433 -52
  91. modal/mount.py +216 -151
  92. modal/mount.pyi +225 -78
  93. modal/network_file_system.py +45 -62
  94. modal/network_file_system.pyi +277 -56
  95. modal/object.pyi +93 -17
  96. modal/parallel_map.py +942 -129
  97. modal/parallel_map.pyi +294 -15
  98. modal/partial_function.py +0 -2
  99. modal/partial_function.pyi +234 -19
  100. modal/proxy.py +17 -8
  101. modal/proxy.pyi +36 -3
  102. modal/queue.py +270 -65
  103. modal/queue.pyi +817 -57
  104. modal/runner.py +115 -101
  105. modal/runner.pyi +205 -49
  106. modal/sandbox.py +512 -136
  107. modal/sandbox.pyi +845 -111
  108. modal/schedule.py +1 -1
  109. modal/secret.py +300 -70
  110. modal/secret.pyi +589 -34
  111. modal/serving.py +7 -11
  112. modal/serving.pyi +7 -8
  113. modal/snapshot.py +11 -8
  114. modal/snapshot.pyi +25 -4
  115. modal/token_flow.py +4 -4
  116. modal/token_flow.pyi +28 -8
  117. modal/volume.py +416 -158
  118. modal/volume.pyi +1117 -121
  119. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +10 -9
  120. modal-1.2.3.dev7.dist-info/RECORD +195 -0
  121. modal_docs/mdmd/mdmd.py +17 -4
  122. modal_proto/api.proto +534 -79
  123. modal_proto/api_grpc.py +337 -1
  124. modal_proto/api_pb2.py +1522 -968
  125. modal_proto/api_pb2.pyi +1619 -134
  126. modal_proto/api_pb2_grpc.py +699 -4
  127. modal_proto/api_pb2_grpc.pyi +226 -14
  128. modal_proto/modal_api_grpc.py +175 -154
  129. modal_proto/sandbox_router.proto +145 -0
  130. modal_proto/sandbox_router_grpc.py +105 -0
  131. modal_proto/sandbox_router_pb2.py +149 -0
  132. modal_proto/sandbox_router_pb2.pyi +333 -0
  133. modal_proto/sandbox_router_pb2_grpc.py +203 -0
  134. modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
  135. modal_proto/task_command_router.proto +144 -0
  136. modal_proto/task_command_router_grpc.py +105 -0
  137. modal_proto/task_command_router_pb2.py +149 -0
  138. modal_proto/task_command_router_pb2.pyi +333 -0
  139. modal_proto/task_command_router_pb2_grpc.py +203 -0
  140. modal_proto/task_command_router_pb2_grpc.pyi +75 -0
  141. modal_version/__init__.py +1 -1
  142. modal/requirements/PREVIEW.txt +0 -16
  143. modal/requirements/base-images.json +0 -26
  144. modal-1.0.3.dev10.dist-info/RECORD +0 -179
  145. modal_proto/modal_options_grpc.py +0 -3
  146. modal_proto/options.proto +0 -19
  147. modal_proto/options_grpc.py +0 -3
  148. modal_proto/options_pb2.py +0 -35
  149. modal_proto/options_pb2.pyi +0 -20
  150. modal_proto/options_pb2_grpc.py +0 -4
  151. modal_proto/options_pb2_grpc.pyi +0 -7
  152. /modal/{requirements → builder}/2023.12.312.txt +0 -0
  153. /modal/{requirements → builder}/2023.12.txt +0 -0
  154. /modal/{requirements → builder}/2024.04.txt +0 -0
  155. /modal/{requirements → builder}/2024.10.txt +0 -0
  156. /modal/{requirements → builder}/README.md +0 -0
  157. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
  158. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
  159. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
  160. {modal-1.0.3.dev10.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 typing import Any, Optional
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 ._object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
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
- from ._utils.deprecation import deprecation_warning
18
- from ._utils.grpc_utils import retry_transient_errors
28
+ from ._utils.deprecation import deprecation_warning, warn_if_passing_namespace
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"):
@@ -64,7 +255,7 @@ class _Queue(_Object, type_prefix="qu"):
64
255
  assert queue.get() == 42
65
256
  ```
66
257
 
67
- For more examples, see the [guide](/docs/guide/dicts-and-queues#modal-queues).
258
+ For more examples, see the [guide](https://modal.com/docs/guide/dicts-and-queues#modal-queues).
68
259
 
69
260
  **Queue partitions (beta)**
70
261
 
@@ -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,21 +353,22 @@ 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, None, is_another_app=True)
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(
149
360
  name: str,
150
361
  *,
151
- namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
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
 
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.
369
+ This is a lazy method the defers hydrating the local
370
+ object with metadata from Modal servers until the first
371
+ time it is actually used.
160
372
 
161
373
  ```python
162
374
  q = modal.Queue.from_name("my-queue", create_if_missing=True)
@@ -164,60 +376,54 @@ class _Queue(_Object, type_prefix="qu"):
164
376
  ```
165
377
  """
166
378
  check_object_name(name, "Queue")
379
+ warn_if_passing_namespace(namespace, "modal.Queue.from_name")
167
380
 
168
- 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]):
169
382
  req = api_pb2.QueueGetOrCreateRequest(
170
383
  deployment_name=name,
171
- namespace=namespace,
172
- environment_name=_get_environment_name(environment_name, resolver),
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 resolver.client.stub.QueueGetOrCreate(req)
176
- self._hydrate(response.queue_id, resolver.client, None)
177
-
178
- return _Queue._from_loader(_load, "Queue()", is_another_app=True, hydrate_lazily=True)
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 lookup(
182
- name: str,
183
- namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
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
- Lookup a named Queue.
403
+ Delete a named Queue.
190
404
 
191
- DEPRECATED: This method is deprecated in favor of `modal.Queue.from_name`.
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
- In contrast to `modal.Queue.from_name`, this is an eager method
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, 1, 27),
203
- "`modal.Queue.lookup` is deprecated and will be removed in a future release."
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
- obj = _Queue.from_name(
208
- name, namespace=namespace, environment_name=environment_name, create_if_missing=create_if_missing
412
+ (2025, 8, 6),
413
+ "`modal.Queue.delete` is deprecated; we recommend using `modal.Queue.objects.delete` instead.",
209
414
  )
210
- if client is None:
211
- client = await _Client.from_env()
212
- resolver = Resolver(client=client)
213
- await resolver.load(obj)
214
- return obj
415
+ await _Queue.objects.delete(name, environment_name=environment_name, client=client)
215
416
 
216
- @staticmethod
217
- async def delete(name: str, *, client: Optional[_Client] = None, environment_name: Optional[str] = None):
218
- obj = await _Queue.from_name(name, environment_name=environment_name).hydrate(client)
219
- req = api_pb2.QueueDeleteRequest(queue_id=obj.object_id)
220
- await retry_transient_errors(obj._client.stub.QueueDelete, req)
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
+ )
221
427
 
222
428
  async def _get_nonblocking(self, partition: Optional[str], n_values: int) -> list[Any]:
223
429
  request = api_pb2.QueueGetRequest(
@@ -227,7 +433,7 @@ class _Queue(_Object, type_prefix="qu"):
227
433
  n_values=n_values,
228
434
  )
229
435
 
230
- response = await retry_transient_errors(self._client.stub.QueueGet, request)
436
+ response = await self._client.stub.QueueGet(request)
231
437
  if response.values:
232
438
  return [deserialize(value, self._client) for value in response.values]
233
439
  else:
@@ -252,7 +458,7 @@ class _Queue(_Object, type_prefix="qu"):
252
458
  n_values=n_values,
253
459
  )
254
460
 
255
- response = await retry_transient_errors(self._client.stub.QueueGet, request)
461
+ response = await self._client.stub.QueueGet(request)
256
462
 
257
463
  if response.values:
258
464
  return [deserialize(value, self._client) for value in response.values]
@@ -272,7 +478,7 @@ class _Queue(_Object, type_prefix="qu"):
272
478
  partition_key=self.validate_partition_key(partition),
273
479
  all_partitions=all,
274
480
  )
275
- await retry_transient_errors(self._client.stub.QueueClear, request)
481
+ await self._client.stub.QueueClear(request)
276
482
 
277
483
  @live_method
278
484
  async def get(
@@ -381,14 +587,15 @@ class _Queue(_Object, type_prefix="qu"):
381
587
  partition_ttl_seconds=partition_ttl,
382
588
  )
383
589
  try:
384
- await retry_transient_errors(
385
- self._client.stub.QueuePut,
590
+ await self._client.stub.QueuePut(
386
591
  request,
387
592
  # A full queue will return this status.
388
- additional_status_codes=[Status.RESOURCE_EXHAUSTED],
389
- max_delay=30.0,
390
- max_retries=None,
391
- total_timeout=timeout,
593
+ retry=Retry(
594
+ additional_status_codes=[Status.RESOURCE_EXHAUSTED],
595
+ max_delay=30.0,
596
+ max_retries=None,
597
+ total_timeout=timeout,
598
+ ),
392
599
  )
393
600
  except GRPCError as exc:
394
601
  if exc.status == Status.RESOURCE_EXHAUSTED:
@@ -408,7 +615,7 @@ class _Queue(_Object, type_prefix="qu"):
408
615
  partition_ttl_seconds=partition_ttl,
409
616
  )
410
617
  try:
411
- await retry_transient_errors(self._client.stub.QueuePut, request)
618
+ await self._client.stub.QueuePut(request)
412
619
  except GRPCError as exc:
413
620
  if exc.status == Status.RESOURCE_EXHAUSTED:
414
621
  raise queue.Full(exc.message)
@@ -428,7 +635,7 @@ class _Queue(_Object, type_prefix="qu"):
428
635
  partition_key=self.validate_partition_key(partition),
429
636
  total=total,
430
637
  )
431
- response = await retry_transient_errors(self._client.stub.QueueLen, request)
638
+ response = await self._client.stub.QueueLen(request)
432
639
  return response.len
433
640
 
434
641
  @warn_if_generator_is_not_consumed()
@@ -454,9 +661,7 @@ class _Queue(_Object, type_prefix="qu"):
454
661
  item_poll_timeout=poll_duration,
455
662
  )
456
663
 
457
- response: api_pb2.QueueNextItemsResponse = await retry_transient_errors(
458
- self._client.stub.QueueNextItems, request
459
- )
664
+ response: api_pb2.QueueNextItemsResponse = await self._client.stub.QueueNextItems(request)
460
665
  if response.items:
461
666
  for item in response.items:
462
667
  yield deserialize(item.value, self._client)