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.
- modal/__init__.py +0 -2
- modal/__main__.py +3 -4
- modal/_billing.py +80 -0
- modal/_clustered_functions.py +7 -3
- modal/_clustered_functions.pyi +15 -3
- modal/_container_entrypoint.py +51 -69
- modal/_functions.py +508 -240
- modal/_grpc_client.py +171 -0
- modal/_load_context.py +105 -0
- modal/_object.py +81 -21
- modal/_output.py +58 -45
- modal/_partial_function.py +48 -73
- modal/_pty.py +7 -3
- modal/_resolver.py +26 -46
- modal/_runtime/asgi.py +4 -3
- modal/_runtime/container_io_manager.py +358 -220
- modal/_runtime/container_io_manager.pyi +296 -101
- modal/_runtime/execution_context.py +18 -2
- modal/_runtime/execution_context.pyi +64 -7
- modal/_runtime/gpu_memory_snapshot.py +262 -57
- modal/_runtime/user_code_imports.py +28 -58
- modal/_serialization.py +90 -6
- modal/_traceback.py +42 -1
- modal/_tunnel.pyi +380 -12
- modal/_utils/async_utils.py +84 -29
- modal/_utils/auth_token_manager.py +111 -0
- modal/_utils/blob_utils.py +181 -58
- modal/_utils/deprecation.py +19 -0
- modal/_utils/function_utils.py +91 -47
- modal/_utils/grpc_utils.py +89 -66
- modal/_utils/mount_utils.py +26 -1
- modal/_utils/name_utils.py +17 -3
- modal/_utils/task_command_router_client.py +536 -0
- modal/_utils/time_utils.py +34 -6
- modal/app.py +256 -88
- modal/app.pyi +909 -92
- modal/billing.py +5 -0
- modal/builder/2025.06.txt +18 -0
- modal/builder/PREVIEW.txt +18 -0
- modal/builder/base-images.json +58 -0
- modal/cli/_download.py +19 -3
- modal/cli/_traceback.py +3 -2
- modal/cli/app.py +4 -4
- modal/cli/cluster.py +15 -7
- modal/cli/config.py +5 -3
- modal/cli/container.py +7 -6
- modal/cli/dict.py +22 -16
- modal/cli/entry_point.py +12 -5
- modal/cli/environment.py +5 -4
- modal/cli/import_refs.py +3 -3
- modal/cli/launch.py +102 -5
- modal/cli/network_file_system.py +11 -12
- modal/cli/profile.py +3 -2
- modal/cli/programs/launch_instance_ssh.py +94 -0
- modal/cli/programs/run_jupyter.py +1 -1
- modal/cli/programs/run_marimo.py +95 -0
- modal/cli/programs/vscode.py +1 -1
- modal/cli/queues.py +57 -26
- modal/cli/run.py +91 -23
- modal/cli/secret.py +48 -22
- modal/cli/token.py +7 -8
- modal/cli/utils.py +4 -7
- modal/cli/volume.py +31 -25
- modal/client.py +15 -85
- modal/client.pyi +183 -62
- modal/cloud_bucket_mount.py +5 -3
- modal/cloud_bucket_mount.pyi +197 -5
- modal/cls.py +200 -126
- modal/cls.pyi +446 -68
- modal/config.py +29 -11
- modal/container_process.py +319 -19
- modal/container_process.pyi +190 -20
- modal/dict.py +290 -71
- modal/dict.pyi +835 -83
- modal/environments.py +15 -27
- modal/environments.pyi +46 -24
- modal/exception.py +14 -2
- modal/experimental/__init__.py +194 -40
- modal/experimental/flash.py +618 -0
- modal/experimental/flash.pyi +380 -0
- modal/experimental/ipython.py +11 -7
- modal/file_io.py +29 -36
- modal/file_io.pyi +251 -53
- modal/file_pattern_matcher.py +56 -16
- modal/functions.pyi +673 -92
- modal/gpu.py +1 -1
- modal/image.py +528 -176
- modal/image.pyi +1572 -145
- modal/io_streams.py +458 -128
- modal/io_streams.pyi +433 -52
- modal/mount.py +216 -151
- modal/mount.pyi +225 -78
- modal/network_file_system.py +45 -62
- modal/network_file_system.pyi +277 -56
- modal/object.pyi +93 -17
- modal/parallel_map.py +942 -129
- modal/parallel_map.pyi +294 -15
- modal/partial_function.py +0 -2
- modal/partial_function.pyi +234 -19
- modal/proxy.py +17 -8
- modal/proxy.pyi +36 -3
- modal/queue.py +270 -65
- modal/queue.pyi +817 -57
- modal/runner.py +115 -101
- modal/runner.pyi +205 -49
- modal/sandbox.py +512 -136
- modal/sandbox.pyi +845 -111
- modal/schedule.py +1 -1
- modal/secret.py +300 -70
- modal/secret.pyi +589 -34
- modal/serving.py +7 -11
- modal/serving.pyi +7 -8
- modal/snapshot.py +11 -8
- modal/snapshot.pyi +25 -4
- modal/token_flow.py +4 -4
- modal/token_flow.pyi +28 -8
- modal/volume.py +416 -158
- modal/volume.pyi +1117 -121
- {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +10 -9
- modal-1.2.3.dev7.dist-info/RECORD +195 -0
- modal_docs/mdmd/mdmd.py +17 -4
- modal_proto/api.proto +534 -79
- modal_proto/api_grpc.py +337 -1
- modal_proto/api_pb2.py +1522 -968
- modal_proto/api_pb2.pyi +1619 -134
- modal_proto/api_pb2_grpc.py +699 -4
- modal_proto/api_pb2_grpc.pyi +226 -14
- modal_proto/modal_api_grpc.py +175 -154
- modal_proto/sandbox_router.proto +145 -0
- modal_proto/sandbox_router_grpc.py +105 -0
- modal_proto/sandbox_router_pb2.py +149 -0
- modal_proto/sandbox_router_pb2.pyi +333 -0
- modal_proto/sandbox_router_pb2_grpc.py +203 -0
- modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
- modal_proto/task_command_router.proto +144 -0
- modal_proto/task_command_router_grpc.py +105 -0
- modal_proto/task_command_router_pb2.py +149 -0
- modal_proto/task_command_router_pb2.pyi +333 -0
- modal_proto/task_command_router_pb2_grpc.py +203 -0
- modal_proto/task_command_router_pb2_grpc.pyi +75 -0
- modal_version/__init__.py +1 -1
- modal/requirements/PREVIEW.txt +0 -16
- modal/requirements/base-images.json +0 -26
- modal-1.0.3.dev10.dist-info/RECORD +0 -179
- modal_proto/modal_options_grpc.py +0 -3
- modal_proto/options.proto +0 -19
- modal_proto/options_grpc.py +0 -3
- modal_proto/options_pb2.py +0 -35
- modal_proto/options_pb2.pyi +0 -20
- modal_proto/options_pb2_grpc.py +0 -4
- modal_proto/options_pb2_grpc.pyi +0 -7
- /modal/{requirements → builder}/2023.12.312.txt +0 -0
- /modal/{requirements → builder}/2023.12.txt +0 -0
- /modal/{requirements → builder}/2024.04.txt +0 -0
- /modal/{requirements → builder}/2024.10.txt +0 -0
- /modal/{requirements → builder}/README.md +0 -0
- {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
- {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
- {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/volume.pyi
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import _io
|
|
2
2
|
import asyncio.locks
|
|
3
3
|
import collections.abc
|
|
4
|
+
import datetime
|
|
4
5
|
import enum
|
|
5
6
|
import google.protobuf.message
|
|
6
7
|
import modal._object
|
|
@@ -9,6 +10,7 @@ import modal.client
|
|
|
9
10
|
import modal.object
|
|
10
11
|
import modal_proto.api_pb2
|
|
11
12
|
import pathlib
|
|
13
|
+
import synchronicity
|
|
12
14
|
import synchronicity.combined_types
|
|
13
15
|
import typing
|
|
14
16
|
import typing_extensions
|
|
@@ -24,6 +26,8 @@ class FileEntryType(enum.IntEnum):
|
|
|
24
26
|
SOCKET = 5
|
|
25
27
|
|
|
26
28
|
class FileEntry:
|
|
29
|
+
"""A file or directory entry listed from a Modal volume."""
|
|
30
|
+
|
|
27
31
|
path: str
|
|
28
32
|
type: FileEntryType
|
|
29
33
|
mtime: int
|
|
@@ -31,31 +35,481 @@ class FileEntry:
|
|
|
31
35
|
|
|
32
36
|
@classmethod
|
|
33
37
|
def _from_proto(cls, proto: modal_proto.api_pb2.FileEntry) -> FileEntry: ...
|
|
34
|
-
def __init__(self, path: str, type: FileEntryType, mtime: int, size: int) -> None:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def
|
|
39
|
-
|
|
38
|
+
def __init__(self, path: str, type: FileEntryType, mtime: int, size: int) -> None:
|
|
39
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
def __repr__(self):
|
|
43
|
+
"""Return repr(self)."""
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
def __eq__(self, other):
|
|
47
|
+
"""Return self==value."""
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
def __setattr__(self, name, value):
|
|
51
|
+
"""Implement setattr(self, name, value)."""
|
|
52
|
+
...
|
|
53
|
+
|
|
54
|
+
def __delattr__(self, name):
|
|
55
|
+
"""Implement delattr(self, name)."""
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
def __hash__(self):
|
|
59
|
+
"""Return hash(self)."""
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
class VolumeInfo:
|
|
63
|
+
"""Information about the Volume object."""
|
|
64
|
+
|
|
65
|
+
name: typing.Optional[str]
|
|
66
|
+
created_at: datetime.datetime
|
|
67
|
+
created_by: typing.Optional[str]
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self, name: typing.Optional[str], created_at: datetime.datetime, created_by: typing.Optional[str]
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
def __repr__(self):
|
|
76
|
+
"""Return repr(self)."""
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
def __eq__(self, other):
|
|
80
|
+
"""Return self==value."""
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
class _VolumeManager:
|
|
84
|
+
"""Namespace with methods for managing named Volume objects."""
|
|
85
|
+
@staticmethod
|
|
86
|
+
async def create(
|
|
87
|
+
name: str,
|
|
88
|
+
*,
|
|
89
|
+
version: typing.Optional[int] = None,
|
|
90
|
+
allow_existing: bool = False,
|
|
91
|
+
environment_name: typing.Optional[str] = None,
|
|
92
|
+
client: typing.Optional[modal.client._Client] = None,
|
|
93
|
+
) -> None:
|
|
94
|
+
"""Create a new Volume object.
|
|
95
|
+
|
|
96
|
+
**Examples:**
|
|
97
|
+
|
|
98
|
+
```python notest
|
|
99
|
+
modal.Volume.objects.create("my-volume")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Volumes will be created in the active environment, or another one can be specified:
|
|
103
|
+
|
|
104
|
+
```python notest
|
|
105
|
+
modal.Volume.objects.create("my-volume", environment_name="dev")
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
By default, an error will be raised if the Volume already exists, but passing
|
|
109
|
+
`allow_existing=True` will make the creation attempt a no-op in this case.
|
|
110
|
+
|
|
111
|
+
```python notest
|
|
112
|
+
modal.Volume.objects.create("my-volume", allow_existing=True)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Note that this method does not return a local instance of the Volume. You can use
|
|
116
|
+
`modal.Volume.from_name` to perform a lookup after creation.
|
|
117
|
+
|
|
118
|
+
Added in v1.1.2.
|
|
119
|
+
"""
|
|
120
|
+
...
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
async def list(
|
|
124
|
+
*,
|
|
125
|
+
max_objects: typing.Optional[int] = None,
|
|
126
|
+
created_before: typing.Union[datetime.datetime, str, None] = None,
|
|
127
|
+
environment_name: str = "",
|
|
128
|
+
client: typing.Optional[modal.client._Client] = None,
|
|
129
|
+
) -> list[_Volume]:
|
|
130
|
+
"""Return a list of hydrated Volume objects.
|
|
131
|
+
|
|
132
|
+
**Examples:**
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
volumes = modal.Volume.objects.list()
|
|
136
|
+
print([v.name for v in volumes])
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Volumes will be retreived from the active environment, or another one can be specified:
|
|
140
|
+
|
|
141
|
+
```python notest
|
|
142
|
+
dev_volumes = modal.Volume.objects.list(environment_name="dev")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
By default, all named Volumes are returned, newest to oldest. It's also possible to limit the
|
|
146
|
+
number of results and to filter by creation date:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
volumes = modal.Volume.objects.list(max_objects=10, created_before="2025-01-01")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Added in v1.1.2.
|
|
153
|
+
"""
|
|
154
|
+
...
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
async def delete(
|
|
158
|
+
name: str,
|
|
159
|
+
*,
|
|
160
|
+
allow_missing: bool = False,
|
|
161
|
+
environment_name: typing.Optional[str] = None,
|
|
162
|
+
client: typing.Optional[modal.client._Client] = None,
|
|
163
|
+
):
|
|
164
|
+
"""Delete a named Volume.
|
|
165
|
+
|
|
166
|
+
Warning: This deletes an *entire Volume*, not just a specific file.
|
|
167
|
+
Deletion is irreversible and will affect any Apps currently using the Volume.
|
|
168
|
+
|
|
169
|
+
**Examples:**
|
|
170
|
+
|
|
171
|
+
```python notest
|
|
172
|
+
await modal.Volume.objects.delete("my-volume")
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Volumes will be deleted from the active environment, or another one can be specified:
|
|
176
|
+
|
|
177
|
+
```python notest
|
|
178
|
+
await modal.Volume.objects.delete("my-volume", environment_name="dev")
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Added in v1.1.2.
|
|
182
|
+
"""
|
|
183
|
+
...
|
|
184
|
+
|
|
185
|
+
class VolumeManager:
|
|
186
|
+
"""Namespace with methods for managing named Volume objects."""
|
|
187
|
+
def __init__(self, /, *args, **kwargs):
|
|
188
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
189
|
+
...
|
|
190
|
+
|
|
191
|
+
class __create_spec(typing_extensions.Protocol):
|
|
192
|
+
def __call__(
|
|
193
|
+
self,
|
|
194
|
+
/,
|
|
195
|
+
name: str,
|
|
196
|
+
*,
|
|
197
|
+
version: typing.Optional[int] = None,
|
|
198
|
+
allow_existing: bool = False,
|
|
199
|
+
environment_name: typing.Optional[str] = None,
|
|
200
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
201
|
+
) -> None:
|
|
202
|
+
"""Create a new Volume object.
|
|
203
|
+
|
|
204
|
+
**Examples:**
|
|
205
|
+
|
|
206
|
+
```python notest
|
|
207
|
+
modal.Volume.objects.create("my-volume")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Volumes will be created in the active environment, or another one can be specified:
|
|
211
|
+
|
|
212
|
+
```python notest
|
|
213
|
+
modal.Volume.objects.create("my-volume", environment_name="dev")
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
By default, an error will be raised if the Volume already exists, but passing
|
|
217
|
+
`allow_existing=True` will make the creation attempt a no-op in this case.
|
|
218
|
+
|
|
219
|
+
```python notest
|
|
220
|
+
modal.Volume.objects.create("my-volume", allow_existing=True)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Note that this method does not return a local instance of the Volume. You can use
|
|
224
|
+
`modal.Volume.from_name` to perform a lookup after creation.
|
|
225
|
+
|
|
226
|
+
Added in v1.1.2.
|
|
227
|
+
"""
|
|
228
|
+
...
|
|
229
|
+
|
|
230
|
+
async def aio(
|
|
231
|
+
self,
|
|
232
|
+
/,
|
|
233
|
+
name: str,
|
|
234
|
+
*,
|
|
235
|
+
version: typing.Optional[int] = None,
|
|
236
|
+
allow_existing: bool = False,
|
|
237
|
+
environment_name: typing.Optional[str] = None,
|
|
238
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
239
|
+
) -> None:
|
|
240
|
+
"""Create a new Volume object.
|
|
241
|
+
|
|
242
|
+
**Examples:**
|
|
243
|
+
|
|
244
|
+
```python notest
|
|
245
|
+
modal.Volume.objects.create("my-volume")
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Volumes will be created in the active environment, or another one can be specified:
|
|
249
|
+
|
|
250
|
+
```python notest
|
|
251
|
+
modal.Volume.objects.create("my-volume", environment_name="dev")
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
By default, an error will be raised if the Volume already exists, but passing
|
|
255
|
+
`allow_existing=True` will make the creation attempt a no-op in this case.
|
|
256
|
+
|
|
257
|
+
```python notest
|
|
258
|
+
modal.Volume.objects.create("my-volume", allow_existing=True)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Note that this method does not return a local instance of the Volume. You can use
|
|
262
|
+
`modal.Volume.from_name` to perform a lookup after creation.
|
|
263
|
+
|
|
264
|
+
Added in v1.1.2.
|
|
265
|
+
"""
|
|
266
|
+
...
|
|
267
|
+
|
|
268
|
+
create: __create_spec
|
|
269
|
+
|
|
270
|
+
class __list_spec(typing_extensions.Protocol):
|
|
271
|
+
def __call__(
|
|
272
|
+
self,
|
|
273
|
+
/,
|
|
274
|
+
*,
|
|
275
|
+
max_objects: typing.Optional[int] = None,
|
|
276
|
+
created_before: typing.Union[datetime.datetime, str, None] = None,
|
|
277
|
+
environment_name: str = "",
|
|
278
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
279
|
+
) -> list[Volume]:
|
|
280
|
+
"""Return a list of hydrated Volume objects.
|
|
281
|
+
|
|
282
|
+
**Examples:**
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
volumes = modal.Volume.objects.list()
|
|
286
|
+
print([v.name for v in volumes])
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Volumes will be retreived from the active environment, or another one can be specified:
|
|
290
|
+
|
|
291
|
+
```python notest
|
|
292
|
+
dev_volumes = modal.Volume.objects.list(environment_name="dev")
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
By default, all named Volumes are returned, newest to oldest. It's also possible to limit the
|
|
296
|
+
number of results and to filter by creation date:
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
volumes = modal.Volume.objects.list(max_objects=10, created_before="2025-01-01")
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Added in v1.1.2.
|
|
303
|
+
"""
|
|
304
|
+
...
|
|
305
|
+
|
|
306
|
+
async def aio(
|
|
307
|
+
self,
|
|
308
|
+
/,
|
|
309
|
+
*,
|
|
310
|
+
max_objects: typing.Optional[int] = None,
|
|
311
|
+
created_before: typing.Union[datetime.datetime, str, None] = None,
|
|
312
|
+
environment_name: str = "",
|
|
313
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
314
|
+
) -> list[Volume]:
|
|
315
|
+
"""Return a list of hydrated Volume objects.
|
|
316
|
+
|
|
317
|
+
**Examples:**
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
volumes = modal.Volume.objects.list()
|
|
321
|
+
print([v.name for v in volumes])
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Volumes will be retreived from the active environment, or another one can be specified:
|
|
325
|
+
|
|
326
|
+
```python notest
|
|
327
|
+
dev_volumes = modal.Volume.objects.list(environment_name="dev")
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
By default, all named Volumes are returned, newest to oldest. It's also possible to limit the
|
|
331
|
+
number of results and to filter by creation date:
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
volumes = modal.Volume.objects.list(max_objects=10, created_before="2025-01-01")
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Added in v1.1.2.
|
|
338
|
+
"""
|
|
339
|
+
...
|
|
340
|
+
|
|
341
|
+
list: __list_spec
|
|
342
|
+
|
|
343
|
+
class __delete_spec(typing_extensions.Protocol):
|
|
344
|
+
def __call__(
|
|
345
|
+
self,
|
|
346
|
+
/,
|
|
347
|
+
name: str,
|
|
348
|
+
*,
|
|
349
|
+
allow_missing: bool = False,
|
|
350
|
+
environment_name: typing.Optional[str] = None,
|
|
351
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
352
|
+
):
|
|
353
|
+
"""Delete a named Volume.
|
|
354
|
+
|
|
355
|
+
Warning: This deletes an *entire Volume*, not just a specific file.
|
|
356
|
+
Deletion is irreversible and will affect any Apps currently using the Volume.
|
|
357
|
+
|
|
358
|
+
**Examples:**
|
|
359
|
+
|
|
360
|
+
```python notest
|
|
361
|
+
await modal.Volume.objects.delete("my-volume")
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Volumes will be deleted from the active environment, or another one can be specified:
|
|
365
|
+
|
|
366
|
+
```python notest
|
|
367
|
+
await modal.Volume.objects.delete("my-volume", environment_name="dev")
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Added in v1.1.2.
|
|
371
|
+
"""
|
|
372
|
+
...
|
|
373
|
+
|
|
374
|
+
async def aio(
|
|
375
|
+
self,
|
|
376
|
+
/,
|
|
377
|
+
name: str,
|
|
378
|
+
*,
|
|
379
|
+
allow_missing: bool = False,
|
|
380
|
+
environment_name: typing.Optional[str] = None,
|
|
381
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
382
|
+
):
|
|
383
|
+
"""Delete a named Volume.
|
|
384
|
+
|
|
385
|
+
Warning: This deletes an *entire Volume*, not just a specific file.
|
|
386
|
+
Deletion is irreversible and will affect any Apps currently using the Volume.
|
|
387
|
+
|
|
388
|
+
**Examples:**
|
|
389
|
+
|
|
390
|
+
```python notest
|
|
391
|
+
await modal.Volume.objects.delete("my-volume")
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Volumes will be deleted from the active environment, or another one can be specified:
|
|
395
|
+
|
|
396
|
+
```python notest
|
|
397
|
+
await modal.Volume.objects.delete("my-volume", environment_name="dev")
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Added in v1.1.2.
|
|
401
|
+
"""
|
|
402
|
+
...
|
|
403
|
+
|
|
404
|
+
delete: __delete_spec
|
|
40
405
|
|
|
41
406
|
class _Volume(modal._object._Object):
|
|
407
|
+
"""A writeable volume that can be used to share files between one or more Modal functions.
|
|
408
|
+
|
|
409
|
+
The contents of a volume is exposed as a filesystem. You can use it to share data between different functions, or
|
|
410
|
+
to persist durable state across several instances of the same function.
|
|
411
|
+
|
|
412
|
+
Unlike a networked filesystem, you need to explicitly reload the volume to see changes made since it was mounted.
|
|
413
|
+
Similarly, you need to explicitly commit any changes you make to the volume for the changes to become visible
|
|
414
|
+
outside the current container.
|
|
415
|
+
|
|
416
|
+
Concurrent modification is supported, but concurrent modifications of the same files should be avoided! Last write
|
|
417
|
+
wins in case of concurrent modification of the same file - any data the last writer didn't have when committing
|
|
418
|
+
changes will be lost!
|
|
419
|
+
|
|
420
|
+
As a result, volumes are typically not a good fit for use cases where you need to make concurrent modifications to
|
|
421
|
+
the same file (nor is distributed file locking supported).
|
|
422
|
+
|
|
423
|
+
Volumes can only be reloaded if there are no open files for the volume - attempting to reload with open files
|
|
424
|
+
will result in an error.
|
|
425
|
+
|
|
426
|
+
**Usage**
|
|
427
|
+
|
|
428
|
+
```python
|
|
429
|
+
import modal
|
|
430
|
+
|
|
431
|
+
app = modal.App()
|
|
432
|
+
volume = modal.Volume.from_name("my-persisted-volume", create_if_missing=True)
|
|
433
|
+
|
|
434
|
+
@app.function(volumes={"/root/foo": volume})
|
|
435
|
+
def f():
|
|
436
|
+
with open("/root/foo/bar.txt", "w") as f:
|
|
437
|
+
f.write("hello")
|
|
438
|
+
volume.commit() # Persist changes
|
|
439
|
+
|
|
440
|
+
@app.function(volumes={"/root/foo": volume})
|
|
441
|
+
def g():
|
|
442
|
+
volume.reload() # Fetch latest changes
|
|
443
|
+
with open("/root/foo/bar.txt", "r") as f:
|
|
444
|
+
print(f.read())
|
|
445
|
+
```
|
|
446
|
+
"""
|
|
447
|
+
|
|
42
448
|
_lock: typing.Optional[asyncio.locks.Lock]
|
|
43
449
|
_metadata: typing.Optional[modal_proto.api_pb2.VolumeMetadata]
|
|
450
|
+
_read_only: bool
|
|
44
451
|
|
|
452
|
+
@synchronicity.classproperty
|
|
453
|
+
def objects(cls) -> _VolumeManager: ...
|
|
454
|
+
@property
|
|
455
|
+
def name(self) -> typing.Optional[str]: ...
|
|
456
|
+
def read_only(self) -> _Volume:
|
|
457
|
+
"""Configure Volume to mount as read-only.
|
|
458
|
+
|
|
459
|
+
**Example**
|
|
460
|
+
|
|
461
|
+
```python
|
|
462
|
+
import modal
|
|
463
|
+
|
|
464
|
+
volume = modal.Volume.from_name("my-volume", create_if_missing=True)
|
|
465
|
+
|
|
466
|
+
@app.function(volumes={"/mnt/items": volume.read_only()})
|
|
467
|
+
def f():
|
|
468
|
+
with open("/mnt/items/my-file.txt") as f:
|
|
469
|
+
return f.read()
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
The Volume is mounted as a read-only volume in a function. Any file system write operation into the
|
|
473
|
+
mounted volume will result in an error.
|
|
474
|
+
|
|
475
|
+
Added in v1.0.5.
|
|
476
|
+
"""
|
|
477
|
+
...
|
|
478
|
+
|
|
479
|
+
def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
|
|
480
|
+
def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
|
|
45
481
|
async def _get_lock(self): ...
|
|
482
|
+
@property
|
|
483
|
+
def _is_v1(self) -> bool: ...
|
|
46
484
|
@staticmethod
|
|
47
485
|
def from_name(
|
|
48
486
|
name: str,
|
|
49
487
|
*,
|
|
50
|
-
namespace=
|
|
488
|
+
namespace=None,
|
|
51
489
|
environment_name: typing.Optional[str] = None,
|
|
52
490
|
create_if_missing: bool = False,
|
|
53
491
|
version: typing.Optional[int] = None,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
492
|
+
client: typing.Optional[modal.client._Client] = None,
|
|
493
|
+
) -> _Volume:
|
|
494
|
+
"""Reference a Volume by name, creating if necessary.
|
|
495
|
+
|
|
496
|
+
This is a lazy method that defers hydrating the local
|
|
497
|
+
object with metadata from Modal servers until the first
|
|
498
|
+
time is is actually used.
|
|
499
|
+
|
|
500
|
+
```python
|
|
501
|
+
vol = modal.Volume.from_name("my-volume", create_if_missing=True)
|
|
502
|
+
|
|
503
|
+
app = modal.App()
|
|
504
|
+
|
|
505
|
+
# Volume refers to the same object, even across instances of `app`.
|
|
506
|
+
@app.function(volumes={"/data": vol})
|
|
507
|
+
def f():
|
|
508
|
+
pass
|
|
509
|
+
```
|
|
510
|
+
"""
|
|
511
|
+
...
|
|
512
|
+
|
|
59
513
|
@classmethod
|
|
60
514
|
def ephemeral(
|
|
61
515
|
cls: type[_Volume],
|
|
@@ -63,52 +517,189 @@ class _Volume(modal._object._Object):
|
|
|
63
517
|
environment_name: typing.Optional[str] = None,
|
|
64
518
|
version: typing.Optional[int] = None,
|
|
65
519
|
_heartbeat_sleep: float = 300,
|
|
66
|
-
) -> typing.AsyncContextManager[_Volume]:
|
|
520
|
+
) -> typing.AsyncContextManager[_Volume]:
|
|
521
|
+
"""Creates a new ephemeral volume within a context manager:
|
|
522
|
+
|
|
523
|
+
Usage:
|
|
524
|
+
```python
|
|
525
|
+
import modal
|
|
526
|
+
with modal.Volume.ephemeral() as vol:
|
|
527
|
+
assert vol.listdir("/") == []
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
```python notest
|
|
531
|
+
async with modal.Volume.ephemeral() as vol:
|
|
532
|
+
assert await vol.listdir("/") == []
|
|
533
|
+
```
|
|
534
|
+
"""
|
|
535
|
+
...
|
|
536
|
+
|
|
67
537
|
@staticmethod
|
|
68
|
-
async def
|
|
69
|
-
|
|
70
|
-
namespace=
|
|
538
|
+
async def create_deployed(
|
|
539
|
+
deployment_name: str,
|
|
540
|
+
namespace=None,
|
|
71
541
|
client: typing.Optional[modal.client._Client] = None,
|
|
72
542
|
environment_name: typing.Optional[str] = None,
|
|
73
|
-
create_if_missing: bool = False,
|
|
74
543
|
version: typing.Optional[int] = None,
|
|
75
|
-
) ->
|
|
544
|
+
) -> str:
|
|
545
|
+
"""mdmd:hidden"""
|
|
546
|
+
...
|
|
547
|
+
|
|
76
548
|
@staticmethod
|
|
77
|
-
async def
|
|
549
|
+
async def _create_deployed(
|
|
78
550
|
deployment_name: str,
|
|
79
|
-
namespace=
|
|
551
|
+
namespace=None,
|
|
80
552
|
client: typing.Optional[modal.client._Client] = None,
|
|
81
553
|
environment_name: typing.Optional[str] = None,
|
|
82
554
|
version: typing.Optional[int] = None,
|
|
83
|
-
) -> str:
|
|
555
|
+
) -> str:
|
|
556
|
+
"""mdmd:hidden"""
|
|
557
|
+
...
|
|
558
|
+
|
|
559
|
+
async def info(self) -> VolumeInfo:
|
|
560
|
+
"""Return information about the Volume object."""
|
|
561
|
+
...
|
|
562
|
+
|
|
84
563
|
async def _do_reload(self, lock=True): ...
|
|
85
|
-
async def commit(self):
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
564
|
+
async def commit(self):
|
|
565
|
+
"""Commit changes to a mounted volume.
|
|
566
|
+
|
|
567
|
+
If successful, the changes made are now persisted in durable storage and available to other containers accessing
|
|
568
|
+
the volume.
|
|
569
|
+
"""
|
|
570
|
+
...
|
|
571
|
+
|
|
572
|
+
async def reload(self):
|
|
573
|
+
"""Make latest committed state of volume available in the running container.
|
|
574
|
+
|
|
575
|
+
Any uncommitted changes to the volume, such as new or modified files, may implicitly be committed when
|
|
576
|
+
reloading.
|
|
577
|
+
|
|
578
|
+
Reloading will fail if there are open files for the volume.
|
|
579
|
+
"""
|
|
580
|
+
...
|
|
581
|
+
|
|
582
|
+
def iterdir(self, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]:
|
|
583
|
+
"""Iterate over all files in a directory in the volume.
|
|
584
|
+
|
|
585
|
+
Passing a directory path lists all files in the directory. For a file path, return only that
|
|
586
|
+
file's description. If `recursive` is set to True, list all files and folders under the path
|
|
587
|
+
recursively.
|
|
588
|
+
"""
|
|
589
|
+
...
|
|
590
|
+
|
|
591
|
+
async def listdir(self, path: str, *, recursive: bool = False) -> list[FileEntry]:
|
|
592
|
+
"""List all files under a path prefix in the modal.Volume.
|
|
593
|
+
|
|
594
|
+
Passing a directory path lists all files in the directory. For a file path, return only that
|
|
595
|
+
file's description. If `recursive` is set to True, list all files and folders under the path
|
|
596
|
+
recursively.
|
|
597
|
+
"""
|
|
598
|
+
...
|
|
599
|
+
|
|
600
|
+
def read_file(self, path: str) -> collections.abc.AsyncIterator[bytes]:
|
|
601
|
+
"""Read a file from the modal.Volume.
|
|
602
|
+
|
|
603
|
+
Note - this function is primarily intended to be used outside of a Modal App.
|
|
604
|
+
For more information on downloading files from a Modal Volume, see
|
|
605
|
+
[the guide](https://modal.com/docs/guide/volumes).
|
|
606
|
+
|
|
607
|
+
**Example:**
|
|
608
|
+
|
|
609
|
+
```python notest
|
|
610
|
+
vol = modal.Volume.from_name("my-modal-volume")
|
|
611
|
+
data = b""
|
|
612
|
+
for chunk in vol.read_file("1mb.csv"):
|
|
613
|
+
data += chunk
|
|
614
|
+
print(len(data)) # == 1024 * 1024
|
|
615
|
+
```
|
|
616
|
+
"""
|
|
617
|
+
...
|
|
618
|
+
|
|
92
619
|
async def read_file_into_fileobj(
|
|
93
620
|
self,
|
|
94
621
|
path: str,
|
|
95
622
|
fileobj: typing.IO[bytes],
|
|
96
623
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
|
624
|
+
) -> int:
|
|
625
|
+
"""mdmd:hidden
|
|
626
|
+
Read volume file into file-like IO object.
|
|
627
|
+
"""
|
|
628
|
+
...
|
|
629
|
+
|
|
630
|
+
async def _read_file_into_fileobj(
|
|
631
|
+
self,
|
|
632
|
+
path: str,
|
|
633
|
+
fileobj: typing.IO[bytes],
|
|
634
|
+
concurrency: typing.Optional[int] = None,
|
|
635
|
+
download_semaphore: typing.Optional[asyncio.locks.Semaphore] = None,
|
|
636
|
+
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
|
97
637
|
) -> int: ...
|
|
98
|
-
async def
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
638
|
+
async def remove_file(self, path: str, recursive: bool = False) -> None:
|
|
639
|
+
"""Remove a file or directory from a volume."""
|
|
640
|
+
...
|
|
641
|
+
|
|
642
|
+
async def copy_files(
|
|
643
|
+
self, src_paths: collections.abc.Sequence[str], dst_path: str, recursive: bool = False
|
|
644
|
+
) -> None:
|
|
645
|
+
"""Copy files within the volume from src_paths to dst_path.
|
|
646
|
+
The semantics of the copy operation follow those of the UNIX cp command.
|
|
647
|
+
|
|
648
|
+
The `src_paths` parameter is a list. If you want to copy a single file, you should pass a list with a
|
|
649
|
+
single element.
|
|
650
|
+
|
|
651
|
+
`src_paths` and `dst_path` should refer to the desired location *inside* the volume. You do not need to prepend
|
|
652
|
+
the volume mount path.
|
|
653
|
+
|
|
654
|
+
**Usage**
|
|
655
|
+
|
|
656
|
+
```python notest
|
|
657
|
+
vol = modal.Volume.from_name("my-modal-volume")
|
|
658
|
+
|
|
659
|
+
vol.copy_files(["bar/example.txt"], "bar2") # Copy files to another directory
|
|
660
|
+
vol.copy_files(["bar/example.txt"], "bar/example2.txt") # Rename a file by copying
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
Note that if the volume is already mounted on the Modal function, you should use normal filesystem operations
|
|
664
|
+
like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
|
|
665
|
+
the volume mounted as a filesystem, e.g. when running a script on your local computer.
|
|
666
|
+
"""
|
|
667
|
+
...
|
|
668
|
+
|
|
669
|
+
async def batch_upload(self, force: bool = False) -> _AbstractVolumeUploadContextManager:
|
|
670
|
+
"""Initiate a batched upload to a volume.
|
|
671
|
+
|
|
672
|
+
To allow overwriting existing files, set `force` to `True` (you cannot overwrite existing directories with
|
|
673
|
+
uploaded files regardless).
|
|
674
|
+
|
|
675
|
+
**Example:**
|
|
676
|
+
|
|
677
|
+
```python notest
|
|
678
|
+
vol = modal.Volume.from_name("my-modal-volume")
|
|
679
|
+
|
|
680
|
+
with vol.batch_upload() as batch:
|
|
681
|
+
batch.put_file("local-path.txt", "/remote-path.txt")
|
|
682
|
+
batch.put_directory("/local/directory/", "/remote/directory")
|
|
683
|
+
batch.put_file(io.BytesIO(b"some data"), "/foobar")
|
|
684
|
+
```
|
|
685
|
+
"""
|
|
686
|
+
...
|
|
687
|
+
|
|
107
688
|
async def _instance_delete(self): ...
|
|
108
689
|
@staticmethod
|
|
109
690
|
async def delete(
|
|
110
691
|
name: str, client: typing.Optional[modal.client._Client] = None, environment_name: typing.Optional[str] = None
|
|
111
|
-
):
|
|
692
|
+
):
|
|
693
|
+
"""mdmd:hidden
|
|
694
|
+
Delete a named Volume.
|
|
695
|
+
|
|
696
|
+
Warning: This deletes an *entire Volume*, not just a specific file.
|
|
697
|
+
Deletion is irreversible and will affect any Apps currently using the Volume.
|
|
698
|
+
|
|
699
|
+
DEPRECATED: This method is deprecated; we recommend using `modal.Volume.objects.delete` instead.
|
|
700
|
+
"""
|
|
701
|
+
...
|
|
702
|
+
|
|
112
703
|
@staticmethod
|
|
113
704
|
async def rename(
|
|
114
705
|
old_name: str,
|
|
@@ -121,10 +712,84 @@ class _Volume(modal._object._Object):
|
|
|
121
712
|
SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
|
|
122
713
|
|
|
123
714
|
class Volume(modal.object.Object):
|
|
715
|
+
"""A writeable volume that can be used to share files between one or more Modal functions.
|
|
716
|
+
|
|
717
|
+
The contents of a volume is exposed as a filesystem. You can use it to share data between different functions, or
|
|
718
|
+
to persist durable state across several instances of the same function.
|
|
719
|
+
|
|
720
|
+
Unlike a networked filesystem, you need to explicitly reload the volume to see changes made since it was mounted.
|
|
721
|
+
Similarly, you need to explicitly commit any changes you make to the volume for the changes to become visible
|
|
722
|
+
outside the current container.
|
|
723
|
+
|
|
724
|
+
Concurrent modification is supported, but concurrent modifications of the same files should be avoided! Last write
|
|
725
|
+
wins in case of concurrent modification of the same file - any data the last writer didn't have when committing
|
|
726
|
+
changes will be lost!
|
|
727
|
+
|
|
728
|
+
As a result, volumes are typically not a good fit for use cases where you need to make concurrent modifications to
|
|
729
|
+
the same file (nor is distributed file locking supported).
|
|
730
|
+
|
|
731
|
+
Volumes can only be reloaded if there are no open files for the volume - attempting to reload with open files
|
|
732
|
+
will result in an error.
|
|
733
|
+
|
|
734
|
+
**Usage**
|
|
735
|
+
|
|
736
|
+
```python
|
|
737
|
+
import modal
|
|
738
|
+
|
|
739
|
+
app = modal.App()
|
|
740
|
+
volume = modal.Volume.from_name("my-persisted-volume", create_if_missing=True)
|
|
741
|
+
|
|
742
|
+
@app.function(volumes={"/root/foo": volume})
|
|
743
|
+
def f():
|
|
744
|
+
with open("/root/foo/bar.txt", "w") as f:
|
|
745
|
+
f.write("hello")
|
|
746
|
+
volume.commit() # Persist changes
|
|
747
|
+
|
|
748
|
+
@app.function(volumes={"/root/foo": volume})
|
|
749
|
+
def g():
|
|
750
|
+
volume.reload() # Fetch latest changes
|
|
751
|
+
with open("/root/foo/bar.txt", "r") as f:
|
|
752
|
+
print(f.read())
|
|
753
|
+
```
|
|
754
|
+
"""
|
|
755
|
+
|
|
124
756
|
_lock: typing.Optional[asyncio.locks.Lock]
|
|
125
757
|
_metadata: typing.Optional[modal_proto.api_pb2.VolumeMetadata]
|
|
758
|
+
_read_only: bool
|
|
759
|
+
|
|
760
|
+
def __init__(self, *args, **kwargs):
|
|
761
|
+
"""mdmd:hidden"""
|
|
762
|
+
...
|
|
763
|
+
|
|
764
|
+
@synchronicity.classproperty
|
|
765
|
+
def objects(cls) -> VolumeManager: ...
|
|
766
|
+
@property
|
|
767
|
+
def name(self) -> typing.Optional[str]: ...
|
|
768
|
+
def read_only(self) -> Volume:
|
|
769
|
+
"""Configure Volume to mount as read-only.
|
|
770
|
+
|
|
771
|
+
**Example**
|
|
126
772
|
|
|
127
|
-
|
|
773
|
+
```python
|
|
774
|
+
import modal
|
|
775
|
+
|
|
776
|
+
volume = modal.Volume.from_name("my-volume", create_if_missing=True)
|
|
777
|
+
|
|
778
|
+
@app.function(volumes={"/mnt/items": volume.read_only()})
|
|
779
|
+
def f():
|
|
780
|
+
with open("/mnt/items/my-file.txt") as f:
|
|
781
|
+
return f.read()
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
The Volume is mounted as a read-only volume in a function. Any file system write operation into the
|
|
785
|
+
mounted volume will result in an error.
|
|
786
|
+
|
|
787
|
+
Added in v1.0.5.
|
|
788
|
+
"""
|
|
789
|
+
...
|
|
790
|
+
|
|
791
|
+
def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
|
|
792
|
+
def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
|
|
128
793
|
|
|
129
794
|
class ___get_lock_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
130
795
|
def __call__(self, /): ...
|
|
@@ -132,19 +797,37 @@ class Volume(modal.object.Object):
|
|
|
132
797
|
|
|
133
798
|
_get_lock: ___get_lock_spec[typing_extensions.Self]
|
|
134
799
|
|
|
800
|
+
@property
|
|
801
|
+
def _is_v1(self) -> bool: ...
|
|
135
802
|
@staticmethod
|
|
136
803
|
def from_name(
|
|
137
804
|
name: str,
|
|
138
805
|
*,
|
|
139
|
-
namespace=
|
|
806
|
+
namespace=None,
|
|
140
807
|
environment_name: typing.Optional[str] = None,
|
|
141
808
|
create_if_missing: bool = False,
|
|
142
809
|
version: typing.Optional[int] = None,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
810
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
811
|
+
) -> Volume:
|
|
812
|
+
"""Reference a Volume by name, creating if necessary.
|
|
813
|
+
|
|
814
|
+
This is a lazy method that defers hydrating the local
|
|
815
|
+
object with metadata from Modal servers until the first
|
|
816
|
+
time is is actually used.
|
|
817
|
+
|
|
818
|
+
```python
|
|
819
|
+
vol = modal.Volume.from_name("my-volume", create_if_missing=True)
|
|
820
|
+
|
|
821
|
+
app = modal.App()
|
|
822
|
+
|
|
823
|
+
# Volume refers to the same object, even across instances of `app`.
|
|
824
|
+
@app.function(volumes={"/data": vol})
|
|
825
|
+
def f():
|
|
826
|
+
pass
|
|
827
|
+
```
|
|
828
|
+
"""
|
|
829
|
+
...
|
|
830
|
+
|
|
148
831
|
@classmethod
|
|
149
832
|
def ephemeral(
|
|
150
833
|
cls: type[Volume],
|
|
@@ -152,53 +835,87 @@ class Volume(modal.object.Object):
|
|
|
152
835
|
environment_name: typing.Optional[str] = None,
|
|
153
836
|
version: typing.Optional[int] = None,
|
|
154
837
|
_heartbeat_sleep: float = 300,
|
|
155
|
-
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Volume]:
|
|
838
|
+
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Volume]:
|
|
839
|
+
"""Creates a new ephemeral volume within a context manager:
|
|
840
|
+
|
|
841
|
+
Usage:
|
|
842
|
+
```python
|
|
843
|
+
import modal
|
|
844
|
+
with modal.Volume.ephemeral() as vol:
|
|
845
|
+
assert vol.listdir("/") == []
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
```python notest
|
|
849
|
+
async with modal.Volume.ephemeral() as vol:
|
|
850
|
+
assert await vol.listdir("/") == []
|
|
851
|
+
```
|
|
852
|
+
"""
|
|
853
|
+
...
|
|
156
854
|
|
|
157
|
-
class
|
|
855
|
+
class __create_deployed_spec(typing_extensions.Protocol):
|
|
158
856
|
def __call__(
|
|
159
857
|
self,
|
|
160
858
|
/,
|
|
161
|
-
|
|
162
|
-
namespace=
|
|
859
|
+
deployment_name: str,
|
|
860
|
+
namespace=None,
|
|
163
861
|
client: typing.Optional[modal.client.Client] = None,
|
|
164
862
|
environment_name: typing.Optional[str] = None,
|
|
165
|
-
create_if_missing: bool = False,
|
|
166
863
|
version: typing.Optional[int] = None,
|
|
167
|
-
) ->
|
|
864
|
+
) -> str:
|
|
865
|
+
"""mdmd:hidden"""
|
|
866
|
+
...
|
|
867
|
+
|
|
168
868
|
async def aio(
|
|
169
869
|
self,
|
|
170
870
|
/,
|
|
171
|
-
|
|
172
|
-
namespace=
|
|
871
|
+
deployment_name: str,
|
|
872
|
+
namespace=None,
|
|
173
873
|
client: typing.Optional[modal.client.Client] = None,
|
|
174
874
|
environment_name: typing.Optional[str] = None,
|
|
175
|
-
create_if_missing: bool = False,
|
|
176
875
|
version: typing.Optional[int] = None,
|
|
177
|
-
) ->
|
|
876
|
+
) -> str:
|
|
877
|
+
"""mdmd:hidden"""
|
|
878
|
+
...
|
|
178
879
|
|
|
179
|
-
|
|
880
|
+
create_deployed: __create_deployed_spec
|
|
180
881
|
|
|
181
|
-
class
|
|
882
|
+
class ___create_deployed_spec(typing_extensions.Protocol):
|
|
182
883
|
def __call__(
|
|
183
884
|
self,
|
|
184
885
|
/,
|
|
185
886
|
deployment_name: str,
|
|
186
|
-
namespace=
|
|
887
|
+
namespace=None,
|
|
187
888
|
client: typing.Optional[modal.client.Client] = None,
|
|
188
889
|
environment_name: typing.Optional[str] = None,
|
|
189
890
|
version: typing.Optional[int] = None,
|
|
190
|
-
) -> str:
|
|
891
|
+
) -> str:
|
|
892
|
+
"""mdmd:hidden"""
|
|
893
|
+
...
|
|
894
|
+
|
|
191
895
|
async def aio(
|
|
192
896
|
self,
|
|
193
897
|
/,
|
|
194
898
|
deployment_name: str,
|
|
195
|
-
namespace=
|
|
899
|
+
namespace=None,
|
|
196
900
|
client: typing.Optional[modal.client.Client] = None,
|
|
197
901
|
environment_name: typing.Optional[str] = None,
|
|
198
902
|
version: typing.Optional[int] = None,
|
|
199
|
-
) -> str:
|
|
903
|
+
) -> str:
|
|
904
|
+
"""mdmd:hidden"""
|
|
905
|
+
...
|
|
200
906
|
|
|
201
|
-
|
|
907
|
+
_create_deployed: ___create_deployed_spec
|
|
908
|
+
|
|
909
|
+
class __info_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
910
|
+
def __call__(self, /) -> VolumeInfo:
|
|
911
|
+
"""Return information about the Volume object."""
|
|
912
|
+
...
|
|
913
|
+
|
|
914
|
+
async def aio(self, /) -> VolumeInfo:
|
|
915
|
+
"""Return information about the Volume object."""
|
|
916
|
+
...
|
|
917
|
+
|
|
918
|
+
info: __info_spec[typing_extensions.Self]
|
|
202
919
|
|
|
203
920
|
class ___do_reload_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
204
921
|
def __call__(self, /, lock=True): ...
|
|
@@ -207,46 +924,129 @@ class Volume(modal.object.Object):
|
|
|
207
924
|
_do_reload: ___do_reload_spec[typing_extensions.Self]
|
|
208
925
|
|
|
209
926
|
class __commit_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
210
|
-
def __call__(self, /):
|
|
211
|
-
|
|
927
|
+
def __call__(self, /):
|
|
928
|
+
"""Commit changes to a mounted volume.
|
|
929
|
+
|
|
930
|
+
If successful, the changes made are now persisted in durable storage and available to other containers accessing
|
|
931
|
+
the volume.
|
|
932
|
+
"""
|
|
933
|
+
...
|
|
934
|
+
|
|
935
|
+
async def aio(self, /):
|
|
936
|
+
"""Commit changes to a mounted volume.
|
|
937
|
+
|
|
938
|
+
If successful, the changes made are now persisted in durable storage and available to other containers accessing
|
|
939
|
+
the volume.
|
|
940
|
+
"""
|
|
941
|
+
...
|
|
212
942
|
|
|
213
943
|
commit: __commit_spec[typing_extensions.Self]
|
|
214
944
|
|
|
215
945
|
class __reload_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
216
|
-
def __call__(self, /):
|
|
217
|
-
|
|
946
|
+
def __call__(self, /):
|
|
947
|
+
"""Make latest committed state of volume available in the running container.
|
|
948
|
+
|
|
949
|
+
Any uncommitted changes to the volume, such as new or modified files, may implicitly be committed when
|
|
950
|
+
reloading.
|
|
951
|
+
|
|
952
|
+
Reloading will fail if there are open files for the volume.
|
|
953
|
+
"""
|
|
954
|
+
...
|
|
955
|
+
|
|
956
|
+
async def aio(self, /):
|
|
957
|
+
"""Make latest committed state of volume available in the running container.
|
|
958
|
+
|
|
959
|
+
Any uncommitted changes to the volume, such as new or modified files, may implicitly be committed when
|
|
960
|
+
reloading.
|
|
961
|
+
|
|
962
|
+
Reloading will fail if there are open files for the volume.
|
|
963
|
+
"""
|
|
964
|
+
...
|
|
218
965
|
|
|
219
966
|
reload: __reload_spec[typing_extensions.Self]
|
|
220
967
|
|
|
221
968
|
class __iterdir_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
222
|
-
def __call__(self, /, path: str, *, recursive: bool = True) -> typing.Iterator[FileEntry]:
|
|
223
|
-
|
|
969
|
+
def __call__(self, /, path: str, *, recursive: bool = True) -> typing.Iterator[FileEntry]:
|
|
970
|
+
"""Iterate over all files in a directory in the volume.
|
|
971
|
+
|
|
972
|
+
Passing a directory path lists all files in the directory. For a file path, return only that
|
|
973
|
+
file's description. If `recursive` is set to True, list all files and folders under the path
|
|
974
|
+
recursively.
|
|
975
|
+
"""
|
|
976
|
+
...
|
|
977
|
+
|
|
978
|
+
def aio(self, /, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]:
|
|
979
|
+
"""Iterate over all files in a directory in the volume.
|
|
980
|
+
|
|
981
|
+
Passing a directory path lists all files in the directory. For a file path, return only that
|
|
982
|
+
file's description. If `recursive` is set to True, list all files and folders under the path
|
|
983
|
+
recursively.
|
|
984
|
+
"""
|
|
985
|
+
...
|
|
224
986
|
|
|
225
987
|
iterdir: __iterdir_spec[typing_extensions.Self]
|
|
226
988
|
|
|
227
989
|
class __listdir_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
228
|
-
def __call__(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]:
|
|
229
|
-
|
|
990
|
+
def __call__(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]:
|
|
991
|
+
"""List all files under a path prefix in the modal.Volume.
|
|
230
992
|
|
|
231
|
-
|
|
993
|
+
Passing a directory path lists all files in the directory. For a file path, return only that
|
|
994
|
+
file's description. If `recursive` is set to True, list all files and folders under the path
|
|
995
|
+
recursively.
|
|
996
|
+
"""
|
|
997
|
+
...
|
|
232
998
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
|
236
|
-
|
|
237
|
-
read_file: __read_file_spec[typing_extensions.Self]
|
|
999
|
+
async def aio(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]:
|
|
1000
|
+
"""List all files under a path prefix in the modal.Volume.
|
|
238
1001
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
1002
|
+
Passing a directory path lists all files in the directory. For a file path, return only that
|
|
1003
|
+
file's description. If `recursive` is set to True, list all files and folders under the path
|
|
1004
|
+
recursively.
|
|
1005
|
+
"""
|
|
1006
|
+
...
|
|
242
1007
|
|
|
243
|
-
|
|
1008
|
+
listdir: __listdir_spec[typing_extensions.Self]
|
|
244
1009
|
|
|
245
|
-
class
|
|
246
|
-
def __call__(self, /, path: str) -> typing.Iterator[bytes]:
|
|
247
|
-
|
|
1010
|
+
class __read_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
1011
|
+
def __call__(self, /, path: str) -> typing.Iterator[bytes]:
|
|
1012
|
+
"""Read a file from the modal.Volume.
|
|
1013
|
+
|
|
1014
|
+
Note - this function is primarily intended to be used outside of a Modal App.
|
|
1015
|
+
For more information on downloading files from a Modal Volume, see
|
|
1016
|
+
[the guide](https://modal.com/docs/guide/volumes).
|
|
1017
|
+
|
|
1018
|
+
**Example:**
|
|
1019
|
+
|
|
1020
|
+
```python notest
|
|
1021
|
+
vol = modal.Volume.from_name("my-modal-volume")
|
|
1022
|
+
data = b""
|
|
1023
|
+
for chunk in vol.read_file("1mb.csv"):
|
|
1024
|
+
data += chunk
|
|
1025
|
+
print(len(data)) # == 1024 * 1024
|
|
1026
|
+
```
|
|
1027
|
+
"""
|
|
1028
|
+
...
|
|
1029
|
+
|
|
1030
|
+
def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]:
|
|
1031
|
+
"""Read a file from the modal.Volume.
|
|
1032
|
+
|
|
1033
|
+
Note - this function is primarily intended to be used outside of a Modal App.
|
|
1034
|
+
For more information on downloading files from a Modal Volume, see
|
|
1035
|
+
[the guide](https://modal.com/docs/guide/volumes).
|
|
1036
|
+
|
|
1037
|
+
**Example:**
|
|
1038
|
+
|
|
1039
|
+
```python notest
|
|
1040
|
+
vol = modal.Volume.from_name("my-modal-volume")
|
|
1041
|
+
data = b""
|
|
1042
|
+
for chunk in vol.read_file("1mb.csv"):
|
|
1043
|
+
data += chunk
|
|
1044
|
+
print(len(data)) # == 1024 * 1024
|
|
1045
|
+
```
|
|
1046
|
+
"""
|
|
1047
|
+
...
|
|
248
1048
|
|
|
249
|
-
|
|
1049
|
+
read_file: __read_file_spec[typing_extensions.Self]
|
|
250
1050
|
|
|
251
1051
|
class __read_file_into_fileobj_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
252
1052
|
def __call__(
|
|
@@ -255,52 +1055,152 @@ class Volume(modal.object.Object):
|
|
|
255
1055
|
path: str,
|
|
256
1056
|
fileobj: typing.IO[bytes],
|
|
257
1057
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
|
258
|
-
) -> int:
|
|
1058
|
+
) -> int:
|
|
1059
|
+
"""mdmd:hidden
|
|
1060
|
+
Read volume file into file-like IO object.
|
|
1061
|
+
"""
|
|
1062
|
+
...
|
|
1063
|
+
|
|
259
1064
|
async def aio(
|
|
260
1065
|
self,
|
|
261
1066
|
/,
|
|
262
1067
|
path: str,
|
|
263
1068
|
fileobj: typing.IO[bytes],
|
|
264
1069
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
|
265
|
-
) -> int:
|
|
1070
|
+
) -> int:
|
|
1071
|
+
"""mdmd:hidden
|
|
1072
|
+
Read volume file into file-like IO object.
|
|
1073
|
+
"""
|
|
1074
|
+
...
|
|
266
1075
|
|
|
267
1076
|
read_file_into_fileobj: __read_file_into_fileobj_spec[typing_extensions.Self]
|
|
268
1077
|
|
|
269
|
-
class
|
|
270
|
-
def __call__(
|
|
271
|
-
self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
|
272
|
-
) -> int: ...
|
|
273
|
-
async def aio(
|
|
274
|
-
self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
|
275
|
-
) -> int: ...
|
|
276
|
-
|
|
277
|
-
_read_file_into_fileobj1: ___read_file_into_fileobj1_spec[typing_extensions.Self]
|
|
278
|
-
|
|
279
|
-
class ___read_file_into_fileobj2_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
1078
|
+
class ___read_file_into_fileobj_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
280
1079
|
def __call__(
|
|
281
|
-
self,
|
|
1080
|
+
self,
|
|
1081
|
+
/,
|
|
1082
|
+
path: str,
|
|
1083
|
+
fileobj: typing.IO[bytes],
|
|
1084
|
+
concurrency: typing.Optional[int] = None,
|
|
1085
|
+
download_semaphore: typing.Optional[asyncio.locks.Semaphore] = None,
|
|
1086
|
+
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
|
282
1087
|
) -> int: ...
|
|
283
1088
|
async def aio(
|
|
284
|
-
self,
|
|
1089
|
+
self,
|
|
1090
|
+
/,
|
|
1091
|
+
path: str,
|
|
1092
|
+
fileobj: typing.IO[bytes],
|
|
1093
|
+
concurrency: typing.Optional[int] = None,
|
|
1094
|
+
download_semaphore: typing.Optional[asyncio.locks.Semaphore] = None,
|
|
1095
|
+
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
|
285
1096
|
) -> int: ...
|
|
286
1097
|
|
|
287
|
-
|
|
1098
|
+
_read_file_into_fileobj: ___read_file_into_fileobj_spec[typing_extensions.Self]
|
|
288
1099
|
|
|
289
1100
|
class __remove_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
290
|
-
def __call__(self, /, path: str, recursive: bool = False) -> None:
|
|
291
|
-
|
|
1101
|
+
def __call__(self, /, path: str, recursive: bool = False) -> None:
|
|
1102
|
+
"""Remove a file or directory from a volume."""
|
|
1103
|
+
...
|
|
1104
|
+
|
|
1105
|
+
async def aio(self, /, path: str, recursive: bool = False) -> None:
|
|
1106
|
+
"""Remove a file or directory from a volume."""
|
|
1107
|
+
...
|
|
292
1108
|
|
|
293
1109
|
remove_file: __remove_file_spec[typing_extensions.Self]
|
|
294
1110
|
|
|
295
1111
|
class __copy_files_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
296
|
-
def __call__(self, /, src_paths: collections.abc.Sequence[str], dst_path: str) -> None:
|
|
297
|
-
|
|
1112
|
+
def __call__(self, /, src_paths: collections.abc.Sequence[str], dst_path: str, recursive: bool = False) -> None:
|
|
1113
|
+
"""Copy files within the volume from src_paths to dst_path.
|
|
1114
|
+
The semantics of the copy operation follow those of the UNIX cp command.
|
|
1115
|
+
|
|
1116
|
+
The `src_paths` parameter is a list. If you want to copy a single file, you should pass a list with a
|
|
1117
|
+
single element.
|
|
1118
|
+
|
|
1119
|
+
`src_paths` and `dst_path` should refer to the desired location *inside* the volume. You do not need to prepend
|
|
1120
|
+
the volume mount path.
|
|
1121
|
+
|
|
1122
|
+
**Usage**
|
|
1123
|
+
|
|
1124
|
+
```python notest
|
|
1125
|
+
vol = modal.Volume.from_name("my-modal-volume")
|
|
1126
|
+
|
|
1127
|
+
vol.copy_files(["bar/example.txt"], "bar2") # Copy files to another directory
|
|
1128
|
+
vol.copy_files(["bar/example.txt"], "bar/example2.txt") # Rename a file by copying
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1131
|
+
Note that if the volume is already mounted on the Modal function, you should use normal filesystem operations
|
|
1132
|
+
like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
|
|
1133
|
+
the volume mounted as a filesystem, e.g. when running a script on your local computer.
|
|
1134
|
+
"""
|
|
1135
|
+
...
|
|
1136
|
+
|
|
1137
|
+
async def aio(
|
|
1138
|
+
self, /, src_paths: collections.abc.Sequence[str], dst_path: str, recursive: bool = False
|
|
1139
|
+
) -> None:
|
|
1140
|
+
"""Copy files within the volume from src_paths to dst_path.
|
|
1141
|
+
The semantics of the copy operation follow those of the UNIX cp command.
|
|
1142
|
+
|
|
1143
|
+
The `src_paths` parameter is a list. If you want to copy a single file, you should pass a list with a
|
|
1144
|
+
single element.
|
|
1145
|
+
|
|
1146
|
+
`src_paths` and `dst_path` should refer to the desired location *inside* the volume. You do not need to prepend
|
|
1147
|
+
the volume mount path.
|
|
1148
|
+
|
|
1149
|
+
**Usage**
|
|
1150
|
+
|
|
1151
|
+
```python notest
|
|
1152
|
+
vol = modal.Volume.from_name("my-modal-volume")
|
|
1153
|
+
|
|
1154
|
+
vol.copy_files(["bar/example.txt"], "bar2") # Copy files to another directory
|
|
1155
|
+
vol.copy_files(["bar/example.txt"], "bar/example2.txt") # Rename a file by copying
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
Note that if the volume is already mounted on the Modal function, you should use normal filesystem operations
|
|
1159
|
+
like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
|
|
1160
|
+
the volume mounted as a filesystem, e.g. when running a script on your local computer.
|
|
1161
|
+
"""
|
|
1162
|
+
...
|
|
298
1163
|
|
|
299
1164
|
copy_files: __copy_files_spec[typing_extensions.Self]
|
|
300
1165
|
|
|
301
1166
|
class __batch_upload_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
302
|
-
def __call__(self, /, force: bool = False) -> AbstractVolumeUploadContextManager:
|
|
303
|
-
|
|
1167
|
+
def __call__(self, /, force: bool = False) -> AbstractVolumeUploadContextManager:
|
|
1168
|
+
"""Initiate a batched upload to a volume.
|
|
1169
|
+
|
|
1170
|
+
To allow overwriting existing files, set `force` to `True` (you cannot overwrite existing directories with
|
|
1171
|
+
uploaded files regardless).
|
|
1172
|
+
|
|
1173
|
+
**Example:**
|
|
1174
|
+
|
|
1175
|
+
```python notest
|
|
1176
|
+
vol = modal.Volume.from_name("my-modal-volume")
|
|
1177
|
+
|
|
1178
|
+
with vol.batch_upload() as batch:
|
|
1179
|
+
batch.put_file("local-path.txt", "/remote-path.txt")
|
|
1180
|
+
batch.put_directory("/local/directory/", "/remote/directory")
|
|
1181
|
+
batch.put_file(io.BytesIO(b"some data"), "/foobar")
|
|
1182
|
+
```
|
|
1183
|
+
"""
|
|
1184
|
+
...
|
|
1185
|
+
|
|
1186
|
+
async def aio(self, /, force: bool = False) -> AbstractVolumeUploadContextManager:
|
|
1187
|
+
"""Initiate a batched upload to a volume.
|
|
1188
|
+
|
|
1189
|
+
To allow overwriting existing files, set `force` to `True` (you cannot overwrite existing directories with
|
|
1190
|
+
uploaded files regardless).
|
|
1191
|
+
|
|
1192
|
+
**Example:**
|
|
1193
|
+
|
|
1194
|
+
```python notest
|
|
1195
|
+
vol = modal.Volume.from_name("my-modal-volume")
|
|
1196
|
+
|
|
1197
|
+
with vol.batch_upload() as batch:
|
|
1198
|
+
batch.put_file("local-path.txt", "/remote-path.txt")
|
|
1199
|
+
batch.put_directory("/local/directory/", "/remote/directory")
|
|
1200
|
+
batch.put_file(io.BytesIO(b"some data"), "/foobar")
|
|
1201
|
+
```
|
|
1202
|
+
"""
|
|
1203
|
+
...
|
|
304
1204
|
|
|
305
1205
|
batch_upload: __batch_upload_spec[typing_extensions.Self]
|
|
306
1206
|
|
|
@@ -317,14 +1217,33 @@ class Volume(modal.object.Object):
|
|
|
317
1217
|
name: str,
|
|
318
1218
|
client: typing.Optional[modal.client.Client] = None,
|
|
319
1219
|
environment_name: typing.Optional[str] = None,
|
|
320
|
-
):
|
|
1220
|
+
):
|
|
1221
|
+
"""mdmd:hidden
|
|
1222
|
+
Delete a named Volume.
|
|
1223
|
+
|
|
1224
|
+
Warning: This deletes an *entire Volume*, not just a specific file.
|
|
1225
|
+
Deletion is irreversible and will affect any Apps currently using the Volume.
|
|
1226
|
+
|
|
1227
|
+
DEPRECATED: This method is deprecated; we recommend using `modal.Volume.objects.delete` instead.
|
|
1228
|
+
"""
|
|
1229
|
+
...
|
|
1230
|
+
|
|
321
1231
|
async def aio(
|
|
322
1232
|
self,
|
|
323
1233
|
/,
|
|
324
1234
|
name: str,
|
|
325
1235
|
client: typing.Optional[modal.client.Client] = None,
|
|
326
1236
|
environment_name: typing.Optional[str] = None,
|
|
327
|
-
):
|
|
1237
|
+
):
|
|
1238
|
+
"""mdmd:hidden
|
|
1239
|
+
Delete a named Volume.
|
|
1240
|
+
|
|
1241
|
+
Warning: This deletes an *entire Volume*, not just a specific file.
|
|
1242
|
+
Deletion is irreversible and will affect any Apps currently using the Volume.
|
|
1243
|
+
|
|
1244
|
+
DEPRECATED: This method is deprecated; we recommend using `modal.Volume.objects.delete` instead.
|
|
1245
|
+
"""
|
|
1246
|
+
...
|
|
328
1247
|
|
|
329
1248
|
delete: __delete_spec
|
|
330
1249
|
|
|
@@ -375,7 +1294,10 @@ class _AbstractVolumeUploadContextManager:
|
|
|
375
1294
|
) -> _AbstractVolumeUploadContextManager: ...
|
|
376
1295
|
|
|
377
1296
|
class AbstractVolumeUploadContextManager:
|
|
378
|
-
def __init__(self, /, *args, **kwargs):
|
|
1297
|
+
def __init__(self, /, *args, **kwargs):
|
|
1298
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
1299
|
+
...
|
|
1300
|
+
|
|
379
1301
|
def __enter__(self): ...
|
|
380
1302
|
async def __aenter__(self): ...
|
|
381
1303
|
def __exit__(self, exc_type, exc_val, exc_tb): ...
|
|
@@ -402,6 +1324,8 @@ class AbstractVolumeUploadContextManager:
|
|
|
402
1324
|
) -> AbstractVolumeUploadContextManager: ...
|
|
403
1325
|
|
|
404
1326
|
class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
|
|
1327
|
+
"""Context manager for batch-uploading files to a Volume."""
|
|
1328
|
+
|
|
405
1329
|
_volume_id: str
|
|
406
1330
|
_client: modal.client._Client
|
|
407
1331
|
_force: bool
|
|
@@ -416,7 +1340,10 @@ class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
|
|
|
416
1340
|
client: modal.client._Client,
|
|
417
1341
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
|
418
1342
|
force: bool = False,
|
|
419
|
-
):
|
|
1343
|
+
):
|
|
1344
|
+
"""mdmd:hidden"""
|
|
1345
|
+
...
|
|
1346
|
+
|
|
420
1347
|
async def __aenter__(self): ...
|
|
421
1348
|
async def __aexit__(self, exc_type, exc_val, exc_tb): ...
|
|
422
1349
|
def put_file(
|
|
@@ -424,18 +1351,34 @@ class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
|
|
|
424
1351
|
local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
|
|
425
1352
|
remote_path: typing.Union[pathlib.PurePosixPath, str],
|
|
426
1353
|
mode: typing.Optional[int] = None,
|
|
427
|
-
):
|
|
1354
|
+
):
|
|
1355
|
+
"""Upload a file from a local file or file-like object.
|
|
1356
|
+
|
|
1357
|
+
Will create any needed parent directories automatically.
|
|
1358
|
+
|
|
1359
|
+
If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
|
|
1360
|
+
"""
|
|
1361
|
+
...
|
|
1362
|
+
|
|
428
1363
|
def put_directory(
|
|
429
1364
|
self,
|
|
430
1365
|
local_path: typing.Union[pathlib.Path, str],
|
|
431
1366
|
remote_path: typing.Union[pathlib.PurePosixPath, str],
|
|
432
1367
|
recursive: bool = True,
|
|
433
|
-
):
|
|
1368
|
+
):
|
|
1369
|
+
"""Upload all files in a local directory.
|
|
1370
|
+
|
|
1371
|
+
Will create any needed parent directories automatically.
|
|
1372
|
+
"""
|
|
1373
|
+
...
|
|
1374
|
+
|
|
434
1375
|
async def _upload_file(
|
|
435
1376
|
self, file_spec: modal._utils.blob_utils.FileUploadSpec
|
|
436
1377
|
) -> modal_proto.api_pb2.MountFile: ...
|
|
437
1378
|
|
|
438
1379
|
class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
|
|
1380
|
+
"""Context manager for batch-uploading files to a Volume."""
|
|
1381
|
+
|
|
439
1382
|
_volume_id: str
|
|
440
1383
|
_client: modal.client.Client
|
|
441
1384
|
_force: bool
|
|
@@ -450,7 +1393,10 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
|
|
|
450
1393
|
client: modal.client.Client,
|
|
451
1394
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
|
452
1395
|
force: bool = False,
|
|
453
|
-
):
|
|
1396
|
+
):
|
|
1397
|
+
"""mdmd:hidden"""
|
|
1398
|
+
...
|
|
1399
|
+
|
|
454
1400
|
def __enter__(self): ...
|
|
455
1401
|
async def __aenter__(self): ...
|
|
456
1402
|
def __exit__(self, exc_type, exc_val, exc_tb): ...
|
|
@@ -460,13 +1406,26 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
|
|
|
460
1406
|
local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
|
|
461
1407
|
remote_path: typing.Union[pathlib.PurePosixPath, str],
|
|
462
1408
|
mode: typing.Optional[int] = None,
|
|
463
|
-
):
|
|
1409
|
+
):
|
|
1410
|
+
"""Upload a file from a local file or file-like object.
|
|
1411
|
+
|
|
1412
|
+
Will create any needed parent directories automatically.
|
|
1413
|
+
|
|
1414
|
+
If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
|
|
1415
|
+
"""
|
|
1416
|
+
...
|
|
1417
|
+
|
|
464
1418
|
def put_directory(
|
|
465
1419
|
self,
|
|
466
1420
|
local_path: typing.Union[pathlib.Path, str],
|
|
467
1421
|
remote_path: typing.Union[pathlib.PurePosixPath, str],
|
|
468
1422
|
recursive: bool = True,
|
|
469
|
-
):
|
|
1423
|
+
):
|
|
1424
|
+
"""Upload all files in a local directory.
|
|
1425
|
+
|
|
1426
|
+
Will create any needed parent directories automatically.
|
|
1427
|
+
"""
|
|
1428
|
+
...
|
|
470
1429
|
|
|
471
1430
|
class ___upload_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
472
1431
|
def __call__(self, /, file_spec: modal._utils.blob_utils.FileUploadSpec) -> modal_proto.api_pb2.MountFile: ...
|
|
@@ -475,6 +1434,8 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
|
|
|
475
1434
|
_upload_file: ___upload_file_spec[typing_extensions.Self]
|
|
476
1435
|
|
|
477
1436
|
class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
1437
|
+
"""Context manager for batch-uploading files to a Volume version 2."""
|
|
1438
|
+
|
|
478
1439
|
_volume_id: str
|
|
479
1440
|
_client: modal.client._Client
|
|
480
1441
|
_progress_cb: collections.abc.Callable[..., typing.Any]
|
|
@@ -497,7 +1458,10 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
|
497
1458
|
force: bool = False,
|
|
498
1459
|
hash_concurrency: int = 4,
|
|
499
1460
|
put_concurrency: int = 128,
|
|
500
|
-
):
|
|
1461
|
+
):
|
|
1462
|
+
"""mdmd:hidden"""
|
|
1463
|
+
...
|
|
1464
|
+
|
|
501
1465
|
async def __aenter__(self): ...
|
|
502
1466
|
async def __aexit__(self, exc_type, exc_val, exc_tb): ...
|
|
503
1467
|
def put_file(
|
|
@@ -505,16 +1469,32 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
|
505
1469
|
local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
|
|
506
1470
|
remote_path: typing.Union[pathlib.PurePosixPath, str],
|
|
507
1471
|
mode: typing.Optional[int] = None,
|
|
508
|
-
):
|
|
1472
|
+
):
|
|
1473
|
+
"""Upload a file from a local file or file-like object.
|
|
1474
|
+
|
|
1475
|
+
Will create any needed parent directories automatically.
|
|
1476
|
+
|
|
1477
|
+
If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
|
|
1478
|
+
"""
|
|
1479
|
+
...
|
|
1480
|
+
|
|
509
1481
|
def put_directory(
|
|
510
1482
|
self,
|
|
511
1483
|
local_path: typing.Union[pathlib.Path, str],
|
|
512
1484
|
remote_path: typing.Union[pathlib.PurePosixPath, str],
|
|
513
1485
|
recursive: bool = True,
|
|
514
|
-
):
|
|
1486
|
+
):
|
|
1487
|
+
"""Upload all files in a local directory.
|
|
1488
|
+
|
|
1489
|
+
Will create any needed parent directories automatically.
|
|
1490
|
+
"""
|
|
1491
|
+
...
|
|
1492
|
+
|
|
515
1493
|
async def _put_file_specs(self, file_specs: list[modal._utils.blob_utils.FileUploadSpec2]): ...
|
|
516
1494
|
|
|
517
1495
|
class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
|
|
1496
|
+
"""Context manager for batch-uploading files to a Volume version 2."""
|
|
1497
|
+
|
|
518
1498
|
_volume_id: str
|
|
519
1499
|
_client: modal.client.Client
|
|
520
1500
|
_progress_cb: collections.abc.Callable[..., typing.Any]
|
|
@@ -537,7 +1517,10 @@ class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
|
|
|
537
1517
|
force: bool = False,
|
|
538
1518
|
hash_concurrency: int = 4,
|
|
539
1519
|
put_concurrency: int = 128,
|
|
540
|
-
):
|
|
1520
|
+
):
|
|
1521
|
+
"""mdmd:hidden"""
|
|
1522
|
+
...
|
|
1523
|
+
|
|
541
1524
|
def __enter__(self): ...
|
|
542
1525
|
async def __aenter__(self): ...
|
|
543
1526
|
def __exit__(self, exc_type, exc_val, exc_tb): ...
|
|
@@ -547,13 +1530,26 @@ class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
|
|
|
547
1530
|
local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
|
|
548
1531
|
remote_path: typing.Union[pathlib.PurePosixPath, str],
|
|
549
1532
|
mode: typing.Optional[int] = None,
|
|
550
|
-
):
|
|
1533
|
+
):
|
|
1534
|
+
"""Upload a file from a local file or file-like object.
|
|
1535
|
+
|
|
1536
|
+
Will create any needed parent directories automatically.
|
|
1537
|
+
|
|
1538
|
+
If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
|
|
1539
|
+
"""
|
|
1540
|
+
...
|
|
1541
|
+
|
|
551
1542
|
def put_directory(
|
|
552
1543
|
self,
|
|
553
1544
|
local_path: typing.Union[pathlib.Path, str],
|
|
554
1545
|
remote_path: typing.Union[pathlib.PurePosixPath, str],
|
|
555
1546
|
recursive: bool = True,
|
|
556
|
-
):
|
|
1547
|
+
):
|
|
1548
|
+
"""Upload all files in a local directory.
|
|
1549
|
+
|
|
1550
|
+
Will create any needed parent directories automatically.
|
|
1551
|
+
"""
|
|
1552
|
+
...
|
|
557
1553
|
|
|
558
1554
|
class ___put_file_specs_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
559
1555
|
def __call__(self, /, file_specs: list[modal._utils.blob_utils.FileUploadSpec2]): ...
|