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/dict.pyi CHANGED
@@ -1,15 +1,420 @@
1
1
  import collections.abc
2
+ import datetime
3
+ import google.protobuf.message
2
4
  import modal._object
3
5
  import modal.client
4
6
  import modal.object
7
+ import modal_proto.api_pb2
8
+ import synchronicity
5
9
  import synchronicity.combined_types
6
10
  import typing
7
11
  import typing_extensions
8
12
 
13
+ class _NoDefaultSentinel:
14
+ def __repr__(self) -> str:
15
+ """Return repr(self)."""
16
+ ...
17
+
18
+ _NO_DEFAULT: _NoDefaultSentinel
19
+
9
20
  def _serialize_dict(data): ...
10
21
 
22
+ class DictInfo:
23
+ """Information about a Dict object."""
24
+
25
+ name: typing.Optional[str]
26
+ created_at: datetime.datetime
27
+ created_by: typing.Optional[str]
28
+
29
+ def __init__(
30
+ self, name: typing.Optional[str], created_at: datetime.datetime, created_by: typing.Optional[str]
31
+ ) -> None:
32
+ """Initialize self. See help(type(self)) for accurate signature."""
33
+ ...
34
+
35
+ def __repr__(self):
36
+ """Return repr(self)."""
37
+ ...
38
+
39
+ def __eq__(self, other):
40
+ """Return self==value."""
41
+ ...
42
+
43
+ class _DictManager:
44
+ """Namespace with methods for managing named Dict objects."""
45
+ @staticmethod
46
+ async def create(
47
+ name: str,
48
+ *,
49
+ allow_existing: bool = False,
50
+ environment_name: typing.Optional[str] = None,
51
+ client: typing.Optional[modal.client._Client] = None,
52
+ ) -> None:
53
+ """Create a new Dict object.
54
+
55
+ **Examples:**
56
+
57
+ ```python notest
58
+ modal.Dict.objects.create("my-dict")
59
+ ```
60
+
61
+ Dicts will be created in the active environment, or another one can be specified:
62
+
63
+ ```python notest
64
+ modal.Dict.objects.create("my-dict", environment_name="dev")
65
+ ```
66
+
67
+ By default, an error will be raised if the Dict already exists, but passing
68
+ `allow_existing=True` will make the creation attempt a no-op in this case.
69
+
70
+ ```python notest
71
+ modal.Dict.objects.create("my-dict", allow_existing=True)
72
+ ```
73
+
74
+ Note that this method does not return a local instance of the Dict. You can use
75
+ `modal.Dict.from_name` to perform a lookup after creation.
76
+
77
+ Added in v1.1.2.
78
+ """
79
+ ...
80
+
81
+ @staticmethod
82
+ async def list(
83
+ *,
84
+ max_objects: typing.Optional[int] = None,
85
+ created_before: typing.Union[datetime.datetime, str, None] = None,
86
+ environment_name: str = "",
87
+ client: typing.Optional[modal.client._Client] = None,
88
+ ) -> list[_Dict]:
89
+ """Return a list of hydrated Dict objects.
90
+
91
+ **Examples:**
92
+
93
+ ```python
94
+ dicts = modal.Dict.objects.list()
95
+ print([d.name for d in dicts])
96
+ ```
97
+
98
+ Dicts will be retreived from the active environment, or another one can be specified:
99
+
100
+ ```python notest
101
+ dev_dicts = modal.Dict.objects.list(environment_name="dev")
102
+ ```
103
+
104
+ By default, all named Dict are returned, newest to oldest. It's also possible to limit the
105
+ number of results and to filter by creation date:
106
+
107
+ ```python
108
+ dicts = modal.Dict.objects.list(max_objects=10, created_before="2025-01-01")
109
+ ```
110
+
111
+ Added in v1.1.2.
112
+ """
113
+ ...
114
+
115
+ @staticmethod
116
+ async def delete(
117
+ name: str,
118
+ *,
119
+ allow_missing: bool = False,
120
+ environment_name: typing.Optional[str] = None,
121
+ client: typing.Optional[modal.client._Client] = None,
122
+ ):
123
+ """Delete a named Dict.
124
+
125
+ Warning: This deletes an *entire Dict*, not just a specific key.
126
+ Deletion is irreversible and will affect any Apps currently using the Dict.
127
+
128
+ **Examples:**
129
+
130
+ ```python notest
131
+ await modal.Dict.objects.delete("my-dict")
132
+ ```
133
+
134
+ Dicts will be deleted from the active environment, or another one can be specified:
135
+
136
+ ```python notest
137
+ await modal.Dict.objects.delete("my-dict", environment_name="dev")
138
+ ```
139
+
140
+ Added in v1.1.2.
141
+ """
142
+ ...
143
+
144
+ class DictManager:
145
+ """Namespace with methods for managing named Dict objects."""
146
+ def __init__(self, /, *args, **kwargs):
147
+ """Initialize self. See help(type(self)) for accurate signature."""
148
+ ...
149
+
150
+ class __create_spec(typing_extensions.Protocol):
151
+ def __call__(
152
+ self,
153
+ /,
154
+ name: str,
155
+ *,
156
+ allow_existing: bool = False,
157
+ environment_name: typing.Optional[str] = None,
158
+ client: typing.Optional[modal.client.Client] = None,
159
+ ) -> None:
160
+ """Create a new Dict object.
161
+
162
+ **Examples:**
163
+
164
+ ```python notest
165
+ modal.Dict.objects.create("my-dict")
166
+ ```
167
+
168
+ Dicts will be created in the active environment, or another one can be specified:
169
+
170
+ ```python notest
171
+ modal.Dict.objects.create("my-dict", environment_name="dev")
172
+ ```
173
+
174
+ By default, an error will be raised if the Dict already exists, but passing
175
+ `allow_existing=True` will make the creation attempt a no-op in this case.
176
+
177
+ ```python notest
178
+ modal.Dict.objects.create("my-dict", allow_existing=True)
179
+ ```
180
+
181
+ Note that this method does not return a local instance of the Dict. You can use
182
+ `modal.Dict.from_name` to perform a lookup after creation.
183
+
184
+ Added in v1.1.2.
185
+ """
186
+ ...
187
+
188
+ async def aio(
189
+ self,
190
+ /,
191
+ name: str,
192
+ *,
193
+ allow_existing: bool = False,
194
+ environment_name: typing.Optional[str] = None,
195
+ client: typing.Optional[modal.client.Client] = None,
196
+ ) -> None:
197
+ """Create a new Dict object.
198
+
199
+ **Examples:**
200
+
201
+ ```python notest
202
+ modal.Dict.objects.create("my-dict")
203
+ ```
204
+
205
+ Dicts will be created in the active environment, or another one can be specified:
206
+
207
+ ```python notest
208
+ modal.Dict.objects.create("my-dict", environment_name="dev")
209
+ ```
210
+
211
+ By default, an error will be raised if the Dict already exists, but passing
212
+ `allow_existing=True` will make the creation attempt a no-op in this case.
213
+
214
+ ```python notest
215
+ modal.Dict.objects.create("my-dict", allow_existing=True)
216
+ ```
217
+
218
+ Note that this method does not return a local instance of the Dict. You can use
219
+ `modal.Dict.from_name` to perform a lookup after creation.
220
+
221
+ Added in v1.1.2.
222
+ """
223
+ ...
224
+
225
+ create: __create_spec
226
+
227
+ class __list_spec(typing_extensions.Protocol):
228
+ def __call__(
229
+ self,
230
+ /,
231
+ *,
232
+ max_objects: typing.Optional[int] = None,
233
+ created_before: typing.Union[datetime.datetime, str, None] = None,
234
+ environment_name: str = "",
235
+ client: typing.Optional[modal.client.Client] = None,
236
+ ) -> list[Dict]:
237
+ """Return a list of hydrated Dict objects.
238
+
239
+ **Examples:**
240
+
241
+ ```python
242
+ dicts = modal.Dict.objects.list()
243
+ print([d.name for d in dicts])
244
+ ```
245
+
246
+ Dicts will be retreived from the active environment, or another one can be specified:
247
+
248
+ ```python notest
249
+ dev_dicts = modal.Dict.objects.list(environment_name="dev")
250
+ ```
251
+
252
+ By default, all named Dict are returned, newest to oldest. It's also possible to limit the
253
+ number of results and to filter by creation date:
254
+
255
+ ```python
256
+ dicts = modal.Dict.objects.list(max_objects=10, created_before="2025-01-01")
257
+ ```
258
+
259
+ Added in v1.1.2.
260
+ """
261
+ ...
262
+
263
+ async def aio(
264
+ self,
265
+ /,
266
+ *,
267
+ max_objects: typing.Optional[int] = None,
268
+ created_before: typing.Union[datetime.datetime, str, None] = None,
269
+ environment_name: str = "",
270
+ client: typing.Optional[modal.client.Client] = None,
271
+ ) -> list[Dict]:
272
+ """Return a list of hydrated Dict objects.
273
+
274
+ **Examples:**
275
+
276
+ ```python
277
+ dicts = modal.Dict.objects.list()
278
+ print([d.name for d in dicts])
279
+ ```
280
+
281
+ Dicts will be retreived from the active environment, or another one can be specified:
282
+
283
+ ```python notest
284
+ dev_dicts = modal.Dict.objects.list(environment_name="dev")
285
+ ```
286
+
287
+ By default, all named Dict are returned, newest to oldest. It's also possible to limit the
288
+ number of results and to filter by creation date:
289
+
290
+ ```python
291
+ dicts = modal.Dict.objects.list(max_objects=10, created_before="2025-01-01")
292
+ ```
293
+
294
+ Added in v1.1.2.
295
+ """
296
+ ...
297
+
298
+ list: __list_spec
299
+
300
+ class __delete_spec(typing_extensions.Protocol):
301
+ def __call__(
302
+ self,
303
+ /,
304
+ name: str,
305
+ *,
306
+ allow_missing: bool = False,
307
+ environment_name: typing.Optional[str] = None,
308
+ client: typing.Optional[modal.client.Client] = None,
309
+ ):
310
+ """Delete a named Dict.
311
+
312
+ Warning: This deletes an *entire Dict*, not just a specific key.
313
+ Deletion is irreversible and will affect any Apps currently using the Dict.
314
+
315
+ **Examples:**
316
+
317
+ ```python notest
318
+ await modal.Dict.objects.delete("my-dict")
319
+ ```
320
+
321
+ Dicts will be deleted from the active environment, or another one can be specified:
322
+
323
+ ```python notest
324
+ await modal.Dict.objects.delete("my-dict", environment_name="dev")
325
+ ```
326
+
327
+ Added in v1.1.2.
328
+ """
329
+ ...
330
+
331
+ async def aio(
332
+ self,
333
+ /,
334
+ name: str,
335
+ *,
336
+ allow_missing: bool = False,
337
+ environment_name: typing.Optional[str] = None,
338
+ client: typing.Optional[modal.client.Client] = None,
339
+ ):
340
+ """Delete a named Dict.
341
+
342
+ Warning: This deletes an *entire Dict*, not just a specific key.
343
+ Deletion is irreversible and will affect any Apps currently using the Dict.
344
+
345
+ **Examples:**
346
+
347
+ ```python notest
348
+ await modal.Dict.objects.delete("my-dict")
349
+ ```
350
+
351
+ Dicts will be deleted from the active environment, or another one can be specified:
352
+
353
+ ```python notest
354
+ await modal.Dict.objects.delete("my-dict", environment_name="dev")
355
+ ```
356
+
357
+ Added in v1.1.2.
358
+ """
359
+ ...
360
+
361
+ delete: __delete_spec
362
+
11
363
  class _Dict(modal._object._Object):
12
- def __init__(self, data={}): ...
364
+ """Distributed dictionary for storage in Modal apps.
365
+
366
+ Dict contents can be essentially any object so long as they can be serialized by
367
+ `cloudpickle`. This includes other Modal objects. If writing and reading in different
368
+ environments (eg., writing locally and reading remotely), it's necessary to have the
369
+ library defining the data type installed, with compatible versions, on both sides.
370
+ Additionally, cloudpickle serialization is not guaranteed to be deterministic, so it is
371
+ generally recommended to use primitive types for keys.
372
+
373
+ **Lifetime of a Dict and its items**
374
+
375
+ An individual Dict entry will expire after 7 days of inactivity (no reads or writes). The
376
+ Dict entries are written to durable storage.
377
+
378
+ Legacy Dicts (created before 2025-05-20) will still have entries expire 30 days after being
379
+ last added. Additionally, contents are stored in memory on the Modal server and could be lost
380
+ due to unexpected server restarts. Eventually, these Dicts will be fully sunset.
381
+
382
+ **Usage**
383
+
384
+ ```python
385
+ from modal import Dict
386
+
387
+ my_dict = Dict.from_name("my-persisted_dict", create_if_missing=True)
388
+
389
+ my_dict["some key"] = "some value"
390
+ my_dict[123] = 456
391
+
392
+ assert my_dict["some key"] == "some value"
393
+ assert my_dict[123] == 456
394
+ ```
395
+
396
+ The `Dict` class offers a few methods for operations that are usually accomplished
397
+ in Python with operators, such as `Dict.put` and `Dict.contains`. The advantage of
398
+ these methods is that they can be safely called in an asynchronous context by using
399
+ the `.aio` suffix on the method, whereas their operator-based analogues will always
400
+ run synchronously and block the event loop.
401
+
402
+ For more examples, see the [guide](https://modal.com/docs/guide/dicts-and-queues#modal-dicts).
403
+ """
404
+
405
+ _name: typing.Optional[str]
406
+ _metadata: typing.Optional[modal_proto.api_pb2.DictMetadata]
407
+
408
+ def __init__(self, data={}):
409
+ """mdmd:hidden"""
410
+ ...
411
+
412
+ @synchronicity.classproperty
413
+ def objects(cls) -> _DictManager: ...
414
+ @property
415
+ def name(self) -> typing.Optional[str]: ...
416
+ def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
417
+ def _get_metadata(self) -> modal_proto.api_pb2.DictMetadata: ...
13
418
  @classmethod
14
419
  def ephemeral(
15
420
  cls: type[_Dict],
@@ -17,51 +422,218 @@ class _Dict(modal._object._Object):
17
422
  client: typing.Optional[modal.client._Client] = None,
18
423
  environment_name: typing.Optional[str] = None,
19
424
  _heartbeat_sleep: float = 300,
20
- ) -> typing.AsyncContextManager[_Dict]: ...
425
+ ) -> typing.AsyncContextManager[_Dict]:
426
+ """Creates a new ephemeral Dict within a context manager:
427
+
428
+ Usage:
429
+ ```python
430
+ from modal import Dict
431
+
432
+ with Dict.ephemeral() as d:
433
+ d["foo"] = "bar"
434
+ ```
435
+
436
+ ```python notest
437
+ async with Dict.ephemeral() as d:
438
+ await d.put.aio("foo", "bar")
439
+ ```
440
+ """
441
+ ...
442
+
21
443
  @staticmethod
22
444
  def from_name(
23
445
  name: str,
24
446
  data: typing.Optional[dict] = None,
25
447
  *,
26
- namespace=1,
448
+ namespace=None,
27
449
  environment_name: typing.Optional[str] = None,
28
450
  create_if_missing: bool = False,
29
- ) -> _Dict: ...
30
- @staticmethod
31
- async def lookup(
32
- name: str,
33
- data: typing.Optional[dict] = None,
34
- namespace=1,
35
451
  client: typing.Optional[modal.client._Client] = None,
36
- environment_name: typing.Optional[str] = None,
37
- create_if_missing: bool = False,
38
- ) -> _Dict: ...
452
+ ) -> _Dict:
453
+ """Reference a named Dict, creating if necessary.
454
+
455
+ This is a lazy method that defers hydrating the local
456
+ object with metadata from Modal servers until the first
457
+ time it is actually used.
458
+
459
+ ```python
460
+ d = modal.Dict.from_name("my-dict", create_if_missing=True)
461
+ d[123] = 456
462
+ ```
463
+ """
464
+ ...
465
+
39
466
  @staticmethod
40
467
  async def delete(
41
468
  name: str,
42
469
  *,
43
470
  client: typing.Optional[modal.client._Client] = None,
44
471
  environment_name: typing.Optional[str] = None,
45
- ): ...
46
- async def clear(self) -> None: ...
47
- async def get(self, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any: ...
48
- async def contains(self, key: typing.Any) -> bool: ...
49
- async def len(self) -> int: ...
50
- async def __getitem__(self, key: typing.Any) -> typing.Any: ...
51
- async def update(self, other: typing.Optional[collections.abc.Mapping] = None, /, **kwargs) -> None: ...
52
- async def put(self, key: typing.Any, value: typing.Any, *, skip_if_exists: bool = False) -> bool: ...
53
- async def __setitem__(self, key: typing.Any, value: typing.Any) -> None: ...
54
- async def pop(self, key: typing.Any) -> typing.Any: ...
55
- async def __delitem__(self, key: typing.Any) -> typing.Any: ...
56
- async def __contains__(self, key: typing.Any) -> bool: ...
57
- def keys(self) -> collections.abc.AsyncIterator[typing.Any]: ...
58
- def values(self) -> collections.abc.AsyncIterator[typing.Any]: ...
59
- def items(self) -> collections.abc.AsyncIterator[tuple[typing.Any, typing.Any]]: ...
472
+ ):
473
+ """mdmd:hidden
474
+ Delete a named Dict object.
475
+
476
+ Warning: This deletes an *entire Dict*, not just a specific key.
477
+ Deletion is irreversible and will affect any Apps currently using the Dict.
478
+
479
+ DEPRECATED: This method is deprecated; we recommend using `modal.Dict.objects.delete` instead.
480
+ """
481
+ ...
482
+
483
+ async def info(self) -> DictInfo:
484
+ """Return information about the Dict object."""
485
+ ...
486
+
487
+ async def clear(self) -> None:
488
+ """Remove all items from the Dict."""
489
+ ...
490
+
491
+ async def get(self, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any:
492
+ """Get the value associated with a key.
493
+
494
+ Returns `default` if key does not exist.
495
+ """
496
+ ...
497
+
498
+ async def contains(self, key: typing.Any) -> bool:
499
+ """Return if a key is present."""
500
+ ...
501
+
502
+ async def len(self) -> int:
503
+ """Return the length of the Dict.
504
+
505
+ Note: This is an expensive operation and will return at most 100,000.
506
+ """
507
+ ...
508
+
509
+ async def __getitem__(self, key: typing.Any) -> typing.Any:
510
+ """Get the value associated with a key.
511
+
512
+ Note: this function will block the event loop when called in an async context.
513
+ """
514
+ ...
515
+
516
+ async def update(self, other: typing.Optional[collections.abc.Mapping] = None, /, **kwargs) -> None:
517
+ """Update the Dict with additional items."""
518
+ ...
519
+
520
+ async def put(self, key: typing.Any, value: typing.Any, *, skip_if_exists: bool = False) -> bool:
521
+ """Add a specific key-value pair to the Dict.
522
+
523
+ Returns True if the key-value pair was added and False if it wasn't because the key already existed and
524
+ `skip_if_exists` was set.
525
+ """
526
+ ...
527
+
528
+ async def __setitem__(self, key: typing.Any, value: typing.Any) -> None:
529
+ """Set a specific key-value pair to the Dict.
530
+
531
+ Note: this function will block the event loop when called in an async context.
532
+ """
533
+ ...
534
+
535
+ async def pop(self, key: typing.Any, default: typing.Any = ...) -> typing.Any:
536
+ """Remove a key from the Dict, returning the value if it exists.
537
+
538
+ If key is not found, return default if provided, otherwise raise KeyError.
539
+ """
540
+ ...
541
+
542
+ async def __delitem__(self, key: typing.Any) -> typing.Any:
543
+ """Delete a key from the Dict.
544
+
545
+ Note: this function will block the event loop when called in an async context.
546
+ """
547
+ ...
548
+
549
+ async def __contains__(self, key: typing.Any) -> bool:
550
+ """Return if a key is present.
551
+
552
+ Note: this function will block the event loop when called in an async context.
553
+ """
554
+ ...
555
+
556
+ def keys(self) -> collections.abc.AsyncIterator[typing.Any]:
557
+ """Return an iterator over the keys in this Dict.
558
+
559
+ Note that (unlike with Python dicts) the return value is a simple iterator,
560
+ and results are unordered.
561
+ """
562
+ ...
563
+
564
+ def values(self) -> collections.abc.AsyncIterator[typing.Any]:
565
+ """Return an iterator over the values in this Dict.
566
+
567
+ Note that (unlike with Python dicts) the return value is a simple iterator,
568
+ and results are unordered.
569
+ """
570
+ ...
571
+
572
+ def items(self) -> collections.abc.AsyncIterator[tuple[typing.Any, typing.Any]]:
573
+ """Return an iterator over the (key, value) tuples in this Dict.
574
+
575
+ Note that (unlike with Python dicts) the return value is a simple iterator,
576
+ and results are unordered.
577
+ """
578
+ ...
60
579
 
61
580
  SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
62
581
 
63
582
  class Dict(modal.object.Object):
64
- def __init__(self, data={}): ...
583
+ """Distributed dictionary for storage in Modal apps.
584
+
585
+ Dict contents can be essentially any object so long as they can be serialized by
586
+ `cloudpickle`. This includes other Modal objects. If writing and reading in different
587
+ environments (eg., writing locally and reading remotely), it's necessary to have the
588
+ library defining the data type installed, with compatible versions, on both sides.
589
+ Additionally, cloudpickle serialization is not guaranteed to be deterministic, so it is
590
+ generally recommended to use primitive types for keys.
591
+
592
+ **Lifetime of a Dict and its items**
593
+
594
+ An individual Dict entry will expire after 7 days of inactivity (no reads or writes). The
595
+ Dict entries are written to durable storage.
596
+
597
+ Legacy Dicts (created before 2025-05-20) will still have entries expire 30 days after being
598
+ last added. Additionally, contents are stored in memory on the Modal server and could be lost
599
+ due to unexpected server restarts. Eventually, these Dicts will be fully sunset.
600
+
601
+ **Usage**
602
+
603
+ ```python
604
+ from modal import Dict
605
+
606
+ my_dict = Dict.from_name("my-persisted_dict", create_if_missing=True)
607
+
608
+ my_dict["some key"] = "some value"
609
+ my_dict[123] = 456
610
+
611
+ assert my_dict["some key"] == "some value"
612
+ assert my_dict[123] == 456
613
+ ```
614
+
615
+ The `Dict` class offers a few methods for operations that are usually accomplished
616
+ in Python with operators, such as `Dict.put` and `Dict.contains`. The advantage of
617
+ these methods is that they can be safely called in an asynchronous context by using
618
+ the `.aio` suffix on the method, whereas their operator-based analogues will always
619
+ run synchronously and block the event loop.
620
+
621
+ For more examples, see the [guide](https://modal.com/docs/guide/dicts-and-queues#modal-dicts).
622
+ """
623
+
624
+ _name: typing.Optional[str]
625
+ _metadata: typing.Optional[modal_proto.api_pb2.DictMetadata]
626
+
627
+ def __init__(self, data={}):
628
+ """mdmd:hidden"""
629
+ ...
630
+
631
+ @synchronicity.classproperty
632
+ def objects(cls) -> DictManager: ...
633
+ @property
634
+ def name(self) -> typing.Optional[str]: ...
635
+ def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
636
+ def _get_metadata(self) -> modal_proto.api_pb2.DictMetadata: ...
65
637
  @classmethod
66
638
  def ephemeral(
67
639
  cls: type[Dict],
@@ -69,40 +641,46 @@ class Dict(modal.object.Object):
69
641
  client: typing.Optional[modal.client.Client] = None,
70
642
  environment_name: typing.Optional[str] = None,
71
643
  _heartbeat_sleep: float = 300,
72
- ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Dict]: ...
644
+ ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Dict]:
645
+ """Creates a new ephemeral Dict within a context manager:
646
+
647
+ Usage:
648
+ ```python
649
+ from modal import Dict
650
+
651
+ with Dict.ephemeral() as d:
652
+ d["foo"] = "bar"
653
+ ```
654
+
655
+ ```python notest
656
+ async with Dict.ephemeral() as d:
657
+ await d.put.aio("foo", "bar")
658
+ ```
659
+ """
660
+ ...
661
+
73
662
  @staticmethod
74
663
  def from_name(
75
664
  name: str,
76
665
  data: typing.Optional[dict] = None,
77
666
  *,
78
- namespace=1,
667
+ namespace=None,
79
668
  environment_name: typing.Optional[str] = None,
80
669
  create_if_missing: bool = False,
81
- ) -> Dict: ...
670
+ client: typing.Optional[modal.client.Client] = None,
671
+ ) -> Dict:
672
+ """Reference a named Dict, creating if necessary.
82
673
 
83
- class __lookup_spec(typing_extensions.Protocol):
84
- def __call__(
85
- self,
86
- /,
87
- name: str,
88
- data: typing.Optional[dict] = None,
89
- namespace=1,
90
- client: typing.Optional[modal.client.Client] = None,
91
- environment_name: typing.Optional[str] = None,
92
- create_if_missing: bool = False,
93
- ) -> Dict: ...
94
- async def aio(
95
- self,
96
- /,
97
- name: str,
98
- data: typing.Optional[dict] = None,
99
- namespace=1,
100
- client: typing.Optional[modal.client.Client] = None,
101
- environment_name: typing.Optional[str] = None,
102
- create_if_missing: bool = False,
103
- ) -> Dict: ...
674
+ This is a lazy method that defers hydrating the local
675
+ object with metadata from Modal servers until the first
676
+ time it is actually used.
104
677
 
105
- lookup: __lookup_spec
678
+ ```python
679
+ d = modal.Dict.from_name("my-dict", create_if_missing=True)
680
+ d[123] = 456
681
+ ```
682
+ """
683
+ ...
106
684
 
107
685
  class __delete_spec(typing_extensions.Protocol):
108
686
  def __call__(
@@ -112,7 +690,17 @@ class Dict(modal.object.Object):
112
690
  *,
113
691
  client: typing.Optional[modal.client.Client] = None,
114
692
  environment_name: typing.Optional[str] = None,
115
- ): ...
693
+ ):
694
+ """mdmd:hidden
695
+ Delete a named Dict object.
696
+
697
+ Warning: This deletes an *entire Dict*, not just a specific key.
698
+ Deletion is irreversible and will affect any Apps currently using the Dict.
699
+
700
+ DEPRECATED: This method is deprecated; we recommend using `modal.Dict.objects.delete` instead.
701
+ """
702
+ ...
703
+
116
704
  async def aio(
117
705
  self,
118
706
  /,
@@ -120,90 +708,254 @@ class Dict(modal.object.Object):
120
708
  *,
121
709
  client: typing.Optional[modal.client.Client] = None,
122
710
  environment_name: typing.Optional[str] = None,
123
- ): ...
711
+ ):
712
+ """mdmd:hidden
713
+ Delete a named Dict object.
714
+
715
+ Warning: This deletes an *entire Dict*, not just a specific key.
716
+ Deletion is irreversible and will affect any Apps currently using the Dict.
717
+
718
+ DEPRECATED: This method is deprecated; we recommend using `modal.Dict.objects.delete` instead.
719
+ """
720
+ ...
124
721
 
125
722
  delete: __delete_spec
126
723
 
724
+ class __info_spec(typing_extensions.Protocol[SUPERSELF]):
725
+ def __call__(self, /) -> DictInfo:
726
+ """Return information about the Dict object."""
727
+ ...
728
+
729
+ async def aio(self, /) -> DictInfo:
730
+ """Return information about the Dict object."""
731
+ ...
732
+
733
+ info: __info_spec[typing_extensions.Self]
734
+
127
735
  class __clear_spec(typing_extensions.Protocol[SUPERSELF]):
128
- def __call__(self, /) -> None: ...
129
- async def aio(self, /) -> None: ...
736
+ def __call__(self, /) -> None:
737
+ """Remove all items from the Dict."""
738
+ ...
739
+
740
+ async def aio(self, /) -> None:
741
+ """Remove all items from the Dict."""
742
+ ...
130
743
 
131
744
  clear: __clear_spec[typing_extensions.Self]
132
745
 
133
746
  class __get_spec(typing_extensions.Protocol[SUPERSELF]):
134
- def __call__(self, /, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any: ...
135
- async def aio(self, /, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any: ...
747
+ def __call__(self, /, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any:
748
+ """Get the value associated with a key.
749
+
750
+ Returns `default` if key does not exist.
751
+ """
752
+ ...
753
+
754
+ async def aio(self, /, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any:
755
+ """Get the value associated with a key.
756
+
757
+ Returns `default` if key does not exist.
758
+ """
759
+ ...
136
760
 
137
761
  get: __get_spec[typing_extensions.Self]
138
762
 
139
763
  class __contains_spec(typing_extensions.Protocol[SUPERSELF]):
140
- def __call__(self, /, key: typing.Any) -> bool: ...
141
- async def aio(self, /, key: typing.Any) -> bool: ...
764
+ def __call__(self, /, key: typing.Any) -> bool:
765
+ """Return if a key is present."""
766
+ ...
767
+
768
+ async def aio(self, /, key: typing.Any) -> bool:
769
+ """Return if a key is present."""
770
+ ...
142
771
 
143
772
  contains: __contains_spec[typing_extensions.Self]
144
773
 
145
774
  class __len_spec(typing_extensions.Protocol[SUPERSELF]):
146
- def __call__(self, /) -> int: ...
147
- async def aio(self, /) -> int: ...
775
+ def __call__(self, /) -> int:
776
+ """Return the length of the Dict.
777
+
778
+ Note: This is an expensive operation and will return at most 100,000.
779
+ """
780
+ ...
781
+
782
+ async def aio(self, /) -> int:
783
+ """Return the length of the Dict.
784
+
785
+ Note: This is an expensive operation and will return at most 100,000.
786
+ """
787
+ ...
148
788
 
149
789
  len: __len_spec[typing_extensions.Self]
150
790
 
151
791
  class ____getitem___spec(typing_extensions.Protocol[SUPERSELF]):
152
- def __call__(self, /, key: typing.Any) -> typing.Any: ...
153
- async def aio(self, /, key: typing.Any) -> typing.Any: ...
792
+ def __call__(self, /, key: typing.Any) -> typing.Any:
793
+ """Get the value associated with a key.
794
+
795
+ Note: this function will block the event loop when called in an async context.
796
+ """
797
+ ...
798
+
799
+ async def aio(self, /, key: typing.Any) -> typing.Any:
800
+ """Get the value associated with a key.
801
+
802
+ Note: this function will block the event loop when called in an async context.
803
+ """
804
+ ...
154
805
 
155
806
  __getitem__: ____getitem___spec[typing_extensions.Self]
156
807
 
157
808
  class __update_spec(typing_extensions.Protocol[SUPERSELF]):
158
- def __call__(self, other: typing.Optional[collections.abc.Mapping] = None, /, **kwargs) -> None: ...
159
- async def aio(self, other: typing.Optional[collections.abc.Mapping] = None, /, **kwargs) -> None: ...
809
+ def __call__(self, other: typing.Optional[collections.abc.Mapping] = None, /, **kwargs) -> None:
810
+ """Update the Dict with additional items."""
811
+ ...
812
+
813
+ async def aio(self, other: typing.Optional[collections.abc.Mapping] = None, /, **kwargs) -> None:
814
+ """Update the Dict with additional items."""
815
+ ...
160
816
 
161
817
  update: __update_spec[typing_extensions.Self]
162
818
 
163
819
  class __put_spec(typing_extensions.Protocol[SUPERSELF]):
164
- def __call__(self, /, key: typing.Any, value: typing.Any, *, skip_if_exists: bool = False) -> bool: ...
165
- async def aio(self, /, key: typing.Any, value: typing.Any, *, skip_if_exists: bool = False) -> bool: ...
820
+ def __call__(self, /, key: typing.Any, value: typing.Any, *, skip_if_exists: bool = False) -> bool:
821
+ """Add a specific key-value pair to the Dict.
822
+
823
+ Returns True if the key-value pair was added and False if it wasn't because the key already existed and
824
+ `skip_if_exists` was set.
825
+ """
826
+ ...
827
+
828
+ async def aio(self, /, key: typing.Any, value: typing.Any, *, skip_if_exists: bool = False) -> bool:
829
+ """Add a specific key-value pair to the Dict.
830
+
831
+ Returns True if the key-value pair was added and False if it wasn't because the key already existed and
832
+ `skip_if_exists` was set.
833
+ """
834
+ ...
166
835
 
167
836
  put: __put_spec[typing_extensions.Self]
168
837
 
169
838
  class ____setitem___spec(typing_extensions.Protocol[SUPERSELF]):
170
- def __call__(self, /, key: typing.Any, value: typing.Any) -> None: ...
171
- async def aio(self, /, key: typing.Any, value: typing.Any) -> None: ...
839
+ def __call__(self, /, key: typing.Any, value: typing.Any) -> None:
840
+ """Set a specific key-value pair to the Dict.
841
+
842
+ Note: this function will block the event loop when called in an async context.
843
+ """
844
+ ...
845
+
846
+ async def aio(self, /, key: typing.Any, value: typing.Any) -> None:
847
+ """Set a specific key-value pair to the Dict.
848
+
849
+ Note: this function will block the event loop when called in an async context.
850
+ """
851
+ ...
172
852
 
173
853
  __setitem__: ____setitem___spec[typing_extensions.Self]
174
854
 
175
855
  class __pop_spec(typing_extensions.Protocol[SUPERSELF]):
176
- def __call__(self, /, key: typing.Any) -> typing.Any: ...
177
- async def aio(self, /, key: typing.Any) -> typing.Any: ...
856
+ def __call__(self, /, key: typing.Any, default: typing.Any = ...) -> typing.Any:
857
+ """Remove a key from the Dict, returning the value if it exists.
858
+
859
+ If key is not found, return default if provided, otherwise raise KeyError.
860
+ """
861
+ ...
862
+
863
+ async def aio(self, /, key: typing.Any, default: typing.Any = ...) -> typing.Any:
864
+ """Remove a key from the Dict, returning the value if it exists.
865
+
866
+ If key is not found, return default if provided, otherwise raise KeyError.
867
+ """
868
+ ...
178
869
 
179
870
  pop: __pop_spec[typing_extensions.Self]
180
871
 
181
872
  class ____delitem___spec(typing_extensions.Protocol[SUPERSELF]):
182
- def __call__(self, /, key: typing.Any) -> typing.Any: ...
183
- async def aio(self, /, key: typing.Any) -> typing.Any: ...
873
+ def __call__(self, /, key: typing.Any) -> typing.Any:
874
+ """Delete a key from the Dict.
875
+
876
+ Note: this function will block the event loop when called in an async context.
877
+ """
878
+ ...
879
+
880
+ async def aio(self, /, key: typing.Any) -> typing.Any:
881
+ """Delete a key from the Dict.
882
+
883
+ Note: this function will block the event loop when called in an async context.
884
+ """
885
+ ...
184
886
 
185
887
  __delitem__: ____delitem___spec[typing_extensions.Self]
186
888
 
187
889
  class ____contains___spec(typing_extensions.Protocol[SUPERSELF]):
188
- def __call__(self, /, key: typing.Any) -> bool: ...
189
- async def aio(self, /, key: typing.Any) -> bool: ...
890
+ def __call__(self, /, key: typing.Any) -> bool:
891
+ """Return if a key is present.
892
+
893
+ Note: this function will block the event loop when called in an async context.
894
+ """
895
+ ...
896
+
897
+ async def aio(self, /, key: typing.Any) -> bool:
898
+ """Return if a key is present.
899
+
900
+ Note: this function will block the event loop when called in an async context.
901
+ """
902
+ ...
190
903
 
191
904
  __contains__: ____contains___spec[typing_extensions.Self]
192
905
 
193
906
  class __keys_spec(typing_extensions.Protocol[SUPERSELF]):
194
- def __call__(self, /) -> typing.Iterator[typing.Any]: ...
195
- def aio(self, /) -> collections.abc.AsyncIterator[typing.Any]: ...
907
+ def __call__(self, /) -> typing.Iterator[typing.Any]:
908
+ """Return an iterator over the keys in this Dict.
909
+
910
+ Note that (unlike with Python dicts) the return value is a simple iterator,
911
+ and results are unordered.
912
+ """
913
+ ...
914
+
915
+ def aio(self, /) -> collections.abc.AsyncIterator[typing.Any]:
916
+ """Return an iterator over the keys in this Dict.
917
+
918
+ Note that (unlike with Python dicts) the return value is a simple iterator,
919
+ and results are unordered.
920
+ """
921
+ ...
196
922
 
197
923
  keys: __keys_spec[typing_extensions.Self]
198
924
 
199
925
  class __values_spec(typing_extensions.Protocol[SUPERSELF]):
200
- def __call__(self, /) -> typing.Iterator[typing.Any]: ...
201
- def aio(self, /) -> collections.abc.AsyncIterator[typing.Any]: ...
926
+ def __call__(self, /) -> typing.Iterator[typing.Any]:
927
+ """Return an iterator over the values in this Dict.
928
+
929
+ Note that (unlike with Python dicts) the return value is a simple iterator,
930
+ and results are unordered.
931
+ """
932
+ ...
933
+
934
+ def aio(self, /) -> collections.abc.AsyncIterator[typing.Any]:
935
+ """Return an iterator over the values in this Dict.
936
+
937
+ Note that (unlike with Python dicts) the return value is a simple iterator,
938
+ and results are unordered.
939
+ """
940
+ ...
202
941
 
203
942
  values: __values_spec[typing_extensions.Self]
204
943
 
205
944
  class __items_spec(typing_extensions.Protocol[SUPERSELF]):
206
- def __call__(self, /) -> typing.Iterator[tuple[typing.Any, typing.Any]]: ...
207
- def aio(self, /) -> collections.abc.AsyncIterator[tuple[typing.Any, typing.Any]]: ...
945
+ def __call__(self, /) -> typing.Iterator[tuple[typing.Any, typing.Any]]:
946
+ """Return an iterator over the (key, value) tuples in this Dict.
947
+
948
+ Note that (unlike with Python dicts) the return value is a simple iterator,
949
+ and results are unordered.
950
+ """
951
+ ...
952
+
953
+ def aio(self, /) -> collections.abc.AsyncIterator[tuple[typing.Any, typing.Any]]:
954
+ """Return an iterator over the (key, value) tuples in this Dict.
955
+
956
+ Note that (unlike with Python dicts) the return value is a simple iterator,
957
+ and results are unordered.
958
+ """
959
+ ...
208
960
 
209
961
  items: __items_spec[typing_extensions.Self]