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.

Files changed (147) hide show
  1. modal/__main__.py +3 -4
  2. modal/_billing.py +80 -0
  3. modal/_clustered_functions.py +7 -3
  4. modal/_clustered_functions.pyi +4 -2
  5. modal/_container_entrypoint.py +41 -49
  6. modal/_functions.py +424 -195
  7. modal/_grpc_client.py +171 -0
  8. modal/_load_context.py +105 -0
  9. modal/_object.py +68 -20
  10. modal/_output.py +58 -45
  11. modal/_partial_function.py +36 -11
  12. modal/_pty.py +7 -3
  13. modal/_resolver.py +21 -35
  14. modal/_runtime/asgi.py +4 -3
  15. modal/_runtime/container_io_manager.py +301 -186
  16. modal/_runtime/container_io_manager.pyi +70 -61
  17. modal/_runtime/execution_context.py +18 -2
  18. modal/_runtime/execution_context.pyi +4 -1
  19. modal/_runtime/gpu_memory_snapshot.py +170 -63
  20. modal/_runtime/user_code_imports.py +28 -58
  21. modal/_serialization.py +57 -1
  22. modal/_utils/async_utils.py +33 -12
  23. modal/_utils/auth_token_manager.py +2 -5
  24. modal/_utils/blob_utils.py +110 -53
  25. modal/_utils/function_utils.py +49 -42
  26. modal/_utils/grpc_utils.py +80 -50
  27. modal/_utils/mount_utils.py +26 -1
  28. modal/_utils/name_utils.py +17 -3
  29. modal/_utils/task_command_router_client.py +536 -0
  30. modal/_utils/time_utils.py +34 -6
  31. modal/app.py +219 -83
  32. modal/app.pyi +229 -56
  33. modal/billing.py +5 -0
  34. modal/{requirements → builder}/2025.06.txt +1 -0
  35. modal/{requirements → builder}/PREVIEW.txt +1 -0
  36. modal/cli/_download.py +19 -3
  37. modal/cli/_traceback.py +3 -2
  38. modal/cli/app.py +4 -4
  39. modal/cli/cluster.py +15 -7
  40. modal/cli/config.py +5 -3
  41. modal/cli/container.py +7 -6
  42. modal/cli/dict.py +22 -16
  43. modal/cli/entry_point.py +12 -5
  44. modal/cli/environment.py +5 -4
  45. modal/cli/import_refs.py +3 -3
  46. modal/cli/launch.py +102 -5
  47. modal/cli/network_file_system.py +9 -13
  48. modal/cli/profile.py +3 -2
  49. modal/cli/programs/launch_instance_ssh.py +94 -0
  50. modal/cli/programs/run_jupyter.py +1 -1
  51. modal/cli/programs/run_marimo.py +95 -0
  52. modal/cli/programs/vscode.py +1 -1
  53. modal/cli/queues.py +57 -26
  54. modal/cli/run.py +58 -16
  55. modal/cli/secret.py +48 -22
  56. modal/cli/utils.py +3 -4
  57. modal/cli/volume.py +28 -25
  58. modal/client.py +13 -116
  59. modal/client.pyi +9 -91
  60. modal/cloud_bucket_mount.py +5 -3
  61. modal/cloud_bucket_mount.pyi +5 -1
  62. modal/cls.py +130 -102
  63. modal/cls.pyi +45 -85
  64. modal/config.py +29 -10
  65. modal/container_process.py +291 -13
  66. modal/container_process.pyi +95 -32
  67. modal/dict.py +282 -63
  68. modal/dict.pyi +423 -73
  69. modal/environments.py +15 -27
  70. modal/environments.pyi +5 -15
  71. modal/exception.py +8 -0
  72. modal/experimental/__init__.py +143 -38
  73. modal/experimental/flash.py +247 -78
  74. modal/experimental/flash.pyi +137 -9
  75. modal/file_io.py +14 -28
  76. modal/file_io.pyi +2 -2
  77. modal/file_pattern_matcher.py +25 -16
  78. modal/functions.pyi +134 -61
  79. modal/image.py +255 -86
  80. modal/image.pyi +300 -62
  81. modal/io_streams.py +436 -126
  82. modal/io_streams.pyi +236 -171
  83. modal/mount.py +62 -157
  84. modal/mount.pyi +45 -172
  85. modal/network_file_system.py +30 -53
  86. modal/network_file_system.pyi +16 -76
  87. modal/object.pyi +42 -8
  88. modal/parallel_map.py +821 -113
  89. modal/parallel_map.pyi +134 -0
  90. modal/partial_function.pyi +4 -1
  91. modal/proxy.py +16 -7
  92. modal/proxy.pyi +10 -2
  93. modal/queue.py +263 -61
  94. modal/queue.pyi +409 -66
  95. modal/runner.py +112 -92
  96. modal/runner.pyi +45 -27
  97. modal/sandbox.py +451 -124
  98. modal/sandbox.pyi +513 -67
  99. modal/secret.py +291 -67
  100. modal/secret.pyi +425 -19
  101. modal/serving.py +7 -11
  102. modal/serving.pyi +7 -8
  103. modal/snapshot.py +11 -8
  104. modal/token_flow.py +4 -4
  105. modal/volume.py +344 -98
  106. modal/volume.pyi +464 -68
  107. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +9 -8
  108. modal-1.2.3.dev7.dist-info/RECORD +195 -0
  109. modal_docs/mdmd/mdmd.py +11 -1
  110. modal_proto/api.proto +399 -67
  111. modal_proto/api_grpc.py +241 -1
  112. modal_proto/api_pb2.py +1395 -1000
  113. modal_proto/api_pb2.pyi +1239 -79
  114. modal_proto/api_pb2_grpc.py +499 -4
  115. modal_proto/api_pb2_grpc.pyi +162 -14
  116. modal_proto/modal_api_grpc.py +175 -160
  117. modal_proto/sandbox_router.proto +145 -0
  118. modal_proto/sandbox_router_grpc.py +105 -0
  119. modal_proto/sandbox_router_pb2.py +149 -0
  120. modal_proto/sandbox_router_pb2.pyi +333 -0
  121. modal_proto/sandbox_router_pb2_grpc.py +203 -0
  122. modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
  123. modal_proto/task_command_router.proto +144 -0
  124. modal_proto/task_command_router_grpc.py +105 -0
  125. modal_proto/task_command_router_pb2.py +149 -0
  126. modal_proto/task_command_router_pb2.pyi +333 -0
  127. modal_proto/task_command_router_pb2_grpc.py +203 -0
  128. modal_proto/task_command_router_pb2_grpc.pyi +75 -0
  129. modal_version/__init__.py +1 -1
  130. modal-1.0.6.dev58.dist-info/RECORD +0 -183
  131. modal_proto/modal_options_grpc.py +0 -3
  132. modal_proto/options.proto +0 -19
  133. modal_proto/options_grpc.py +0 -3
  134. modal_proto/options_pb2.py +0 -35
  135. modal_proto/options_pb2.pyi +0 -20
  136. modal_proto/options_pb2_grpc.py +0 -4
  137. modal_proto/options_pb2_grpc.pyi +0 -7
  138. /modal/{requirements → builder}/2023.12.312.txt +0 -0
  139. /modal/{requirements → builder}/2023.12.txt +0 -0
  140. /modal/{requirements → builder}/2024.04.txt +0 -0
  141. /modal/{requirements → builder}/2024.10.txt +0 -0
  142. /modal/{requirements → builder}/README.md +0 -0
  143. /modal/{requirements → builder}/base-images.json +0 -0
  144. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
  145. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
  146. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
  147. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/dict.pyi CHANGED
@@ -1,13 +1,365 @@
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
364
  """Distributed dictionary for storage in Modal apps.
13
365
 
@@ -49,10 +401,20 @@ class _Dict(modal._object._Object):
49
401
 
50
402
  For more examples, see the [guide](https://modal.com/docs/guide/dicts-and-queues#modal-dicts).
51
403
  """
404
+
405
+ _name: typing.Optional[str]
406
+ _metadata: typing.Optional[modal_proto.api_pb2.DictMetadata]
407
+
52
408
  def __init__(self, data={}):
53
409
  """mdmd:hidden"""
54
410
  ...
55
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: ...
56
418
  @classmethod
57
419
  def ephemeral(
58
420
  cls: type[_Dict],
@@ -86,6 +448,7 @@ class _Dict(modal._object._Object):
86
448
  namespace=None,
87
449
  environment_name: typing.Optional[str] = None,
88
450
  create_if_missing: bool = False,
451
+ client: typing.Optional[modal.client._Client] = None,
89
452
  ) -> _Dict:
90
453
  """Reference a named Dict, creating if necessary.
91
454
 
@@ -101,36 +464,26 @@ class _Dict(modal._object._Object):
101
464
  ...
102
465
 
103
466
  @staticmethod
104
- async def lookup(
467
+ async def delete(
105
468
  name: str,
106
- data: typing.Optional[dict] = None,
107
- namespace=None,
469
+ *,
108
470
  client: typing.Optional[modal.client._Client] = None,
109
471
  environment_name: typing.Optional[str] = None,
110
- create_if_missing: bool = False,
111
- ) -> _Dict:
472
+ ):
112
473
  """mdmd:hidden
113
- Lookup a named Dict.
114
-
115
- DEPRECATED: This method is deprecated in favor of `modal.Dict.from_name`.
474
+ Delete a named Dict object.
116
475
 
117
- In contrast to `modal.Dict.from_name`, this is an eager method
118
- that will hydrate the local object with metadata from Modal servers.
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.
119
478
 
120
- ```python
121
- d = modal.Dict.from_name("my-dict")
122
- d["xyz"] = 123
123
- ```
479
+ DEPRECATED: This method is deprecated; we recommend using `modal.Dict.objects.delete` instead.
124
480
  """
125
481
  ...
126
482
 
127
- @staticmethod
128
- async def delete(
129
- name: str,
130
- *,
131
- client: typing.Optional[modal.client._Client] = None,
132
- environment_name: typing.Optional[str] = None,
133
- ): ...
483
+ async def info(self) -> DictInfo:
484
+ """Return information about the Dict object."""
485
+ ...
486
+
134
487
  async def clear(self) -> None:
135
488
  """Remove all items from the Dict."""
136
489
  ...
@@ -179,8 +532,11 @@ class _Dict(modal._object._Object):
179
532
  """
180
533
  ...
181
534
 
182
- async def pop(self, key: typing.Any) -> typing.Any:
183
- """Remove a key from the Dict, returning the value if it exists."""
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
+ """
184
540
  ...
185
541
 
186
542
  async def __delitem__(self, key: typing.Any) -> typing.Any:
@@ -264,10 +620,20 @@ class Dict(modal.object.Object):
264
620
 
265
621
  For more examples, see the [guide](https://modal.com/docs/guide/dicts-and-queues#modal-dicts).
266
622
  """
623
+
624
+ _name: typing.Optional[str]
625
+ _metadata: typing.Optional[modal_proto.api_pb2.DictMetadata]
626
+
267
627
  def __init__(self, data={}):
268
628
  """mdmd:hidden"""
269
629
  ...
270
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: ...
271
637
  @classmethod
272
638
  def ephemeral(
273
639
  cls: type[Dict],
@@ -301,6 +667,7 @@ class Dict(modal.object.Object):
301
667
  namespace=None,
302
668
  environment_name: typing.Optional[str] = None,
303
669
  create_if_missing: bool = False,
670
+ client: typing.Optional[modal.client.Client] = None,
304
671
  ) -> Dict:
305
672
  """Reference a named Dict, creating if necessary.
306
673
 
@@ -315,29 +682,22 @@ class Dict(modal.object.Object):
315
682
  """
316
683
  ...
317
684
 
318
- class __lookup_spec(typing_extensions.Protocol):
685
+ class __delete_spec(typing_extensions.Protocol):
319
686
  def __call__(
320
687
  self,
321
688
  /,
322
689
  name: str,
323
- data: typing.Optional[dict] = None,
324
- namespace=None,
690
+ *,
325
691
  client: typing.Optional[modal.client.Client] = None,
326
692
  environment_name: typing.Optional[str] = None,
327
- create_if_missing: bool = False,
328
- ) -> Dict:
693
+ ):
329
694
  """mdmd:hidden
330
- Lookup a named Dict.
331
-
332
- DEPRECATED: This method is deprecated in favor of `modal.Dict.from_name`.
695
+ Delete a named Dict object.
333
696
 
334
- In contrast to `modal.Dict.from_name`, this is an eager method
335
- that will hydrate the local object with metadata from Modal servers.
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.
336
699
 
337
- ```python
338
- d = modal.Dict.from_name("my-dict")
339
- d["xyz"] = 123
340
- ```
700
+ DEPRECATED: This method is deprecated; we recommend using `modal.Dict.objects.delete` instead.
341
701
  """
342
702
  ...
343
703
 
@@ -345,48 +705,32 @@ class Dict(modal.object.Object):
345
705
  self,
346
706
  /,
347
707
  name: str,
348
- data: typing.Optional[dict] = None,
349
- namespace=None,
708
+ *,
350
709
  client: typing.Optional[modal.client.Client] = None,
351
710
  environment_name: typing.Optional[str] = None,
352
- create_if_missing: bool = False,
353
- ) -> Dict:
711
+ ):
354
712
  """mdmd:hidden
355
- Lookup a named Dict.
356
-
357
- DEPRECATED: This method is deprecated in favor of `modal.Dict.from_name`.
713
+ Delete a named Dict object.
358
714
 
359
- In contrast to `modal.Dict.from_name`, this is an eager method
360
- that will hydrate the local object with metadata from Modal servers.
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.
361
717
 
362
- ```python
363
- d = modal.Dict.from_name("my-dict")
364
- d["xyz"] = 123
365
- ```
718
+ DEPRECATED: This method is deprecated; we recommend using `modal.Dict.objects.delete` instead.
366
719
  """
367
720
  ...
368
721
 
369
- lookup: __lookup_spec
722
+ delete: __delete_spec
370
723
 
371
- class __delete_spec(typing_extensions.Protocol):
372
- def __call__(
373
- self,
374
- /,
375
- name: str,
376
- *,
377
- client: typing.Optional[modal.client.Client] = None,
378
- environment_name: typing.Optional[str] = None,
379
- ): ...
380
- async def aio(
381
- self,
382
- /,
383
- name: str,
384
- *,
385
- client: typing.Optional[modal.client.Client] = None,
386
- environment_name: typing.Optional[str] = None,
387
- ): ...
724
+ class __info_spec(typing_extensions.Protocol[SUPERSELF]):
725
+ def __call__(self, /) -> DictInfo:
726
+ """Return information about the Dict object."""
727
+ ...
388
728
 
389
- delete: __delete_spec
729
+ async def aio(self, /) -> DictInfo:
730
+ """Return information about the Dict object."""
731
+ ...
732
+
733
+ info: __info_spec[typing_extensions.Self]
390
734
 
391
735
  class __clear_spec(typing_extensions.Protocol[SUPERSELF]):
392
736
  def __call__(self, /) -> None:
@@ -509,12 +853,18 @@ class Dict(modal.object.Object):
509
853
  __setitem__: ____setitem___spec[typing_extensions.Self]
510
854
 
511
855
  class __pop_spec(typing_extensions.Protocol[SUPERSELF]):
512
- def __call__(self, /, key: typing.Any) -> typing.Any:
513
- """Remove a key from the Dict, returning the value if it exists."""
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
+ """
514
861
  ...
515
862
 
516
- async def aio(self, /, key: typing.Any) -> typing.Any:
517
- """Remove a key from the Dict, returning the value if it exists."""
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
+ """
518
868
  ...
519
869
 
520
870
  pop: __pop_spec[typing_extensions.Self]
modal/environments.py CHANGED
@@ -8,11 +8,10 @@ from google.protobuf.wrappers_pb2 import StringValue
8
8
 
9
9
  from modal_proto import api_pb2
10
10
 
11
+ from ._load_context import LoadContext
11
12
  from ._object import _Object
12
13
  from ._resolver import Resolver
13
14
  from ._utils.async_utils import synchronize_api, synchronizer
14
- from ._utils.deprecation import deprecation_warning
15
- from ._utils.grpc_utils import retry_transient_errors
16
15
  from ._utils.name_utils import check_object_name
17
16
  from .client import _Client
18
17
  from .config import config, logger
@@ -54,6 +53,7 @@ class _Environment(_Object, type_prefix="en"):
54
53
  name: str,
55
54
  *,
56
55
  create_if_missing: bool = False,
56
+ client: Optional[_Client] = None,
57
57
  ):
58
58
  if name:
59
59
  # Allow null names for the case where we want to look up the "default" environment,
@@ -63,7 +63,9 @@ class _Environment(_Object, type_prefix="en"):
63
63
  # environments as part of public API when we make this class more useful.
64
64
  check_object_name(name, "Environment")
65
65
 
66
- async def _load(self: _Environment, resolver: Resolver, existing_object_id: Optional[str]):
66
+ async def _load(
67
+ self: _Environment, resolver: Resolver, load_context: LoadContext, existing_object_id: Optional[str]
68
+ ):
67
69
  request = api_pb2.EnvironmentGetOrCreateRequest(
68
70
  deployment_name=name,
69
71
  object_creation_type=(
@@ -72,31 +74,17 @@ class _Environment(_Object, type_prefix="en"):
72
74
  else api_pb2.OBJECT_CREATION_TYPE_UNSPECIFIED
73
75
  ),
74
76
  )
75
- response = await retry_transient_errors(resolver.client.stub.EnvironmentGetOrCreate, request)
77
+ response = await load_context.client.stub.EnvironmentGetOrCreate(request)
76
78
  logger.debug(f"Created environment with id {response.environment_id}")
77
- self._hydrate(response.environment_id, resolver.client, response.metadata)
78
-
79
- # TODO environment name (and id?) in the repr? (We should make reprs consistently more useful)
80
- return _Environment._from_loader(_load, "Environment()", is_another_app=True, hydrate_lazily=True)
81
-
82
- @staticmethod
83
- async def lookup(
84
- name: str,
85
- client: Optional[_Client] = None,
86
- create_if_missing: bool = False,
87
- ):
88
- deprecation_warning(
89
- (2025, 1, 27),
90
- "`modal.Environment.lookup` is deprecated and will be removed in a future release."
91
- " It can be replaced with `modal.Environment.from_name`."
92
- "\n\nSee https://modal.com/docs/guide/modal-1-0-migration for more information.",
79
+ self._hydrate(response.environment_id, load_context.client, response.metadata)
80
+
81
+ return _Environment._from_loader(
82
+ _load,
83
+ f"Environment.from_name({name!r})",
84
+ is_another_app=True,
85
+ hydrate_lazily=True,
86
+ load_context_overrides=LoadContext(client=client),
93
87
  )
94
- obj = _Environment.from_name(name, create_if_missing=create_if_missing)
95
- if client is None:
96
- client = await _Client.from_env()
97
- resolver = Resolver(client=client)
98
- await resolver.load(obj)
99
- return obj
100
88
 
101
89
 
102
90
  Environment = synchronize_api(_Environment)
@@ -109,7 +97,7 @@ ENVIRONMENT_CACHE: dict[str, _Environment] = {}
109
97
  async def _get_environment_cached(name: str, client: _Client) -> _Environment:
110
98
  if name in ENVIRONMENT_CACHE:
111
99
  return ENVIRONMENT_CACHE[name]
112
- environment = await _Environment.from_name(name).hydrate(client)
100
+ environment = await _Environment.from_name(name, client=client).hydrate()
113
101
  ENVIRONMENT_CACHE[name] = environment
114
102
  return environment
115
103