modal 1.0.5.dev2__py3-none-any.whl → 1.0.5.dev3__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.
- modal/_clustered_functions.pyi +13 -3
- modal/_runtime/container_io_manager.pyi +222 -40
- modal/_runtime/execution_context.pyi +60 -6
- modal/_tunnel.pyi +380 -12
- modal/app.pyi +658 -48
- modal/client.pyi +224 -28
- modal/cloud_bucket_mount.pyi +192 -4
- modal/cls.pyi +442 -35
- modal/container_process.pyi +103 -14
- modal/dict.pyi +453 -51
- modal/environments.pyi +41 -9
- modal/file_io.pyi +236 -45
- modal/functions.pyi +543 -56
- modal/image.pyi +1256 -74
- modal/io_streams.pyi +342 -39
- modal/mount.pyi +261 -31
- modal/network_file_system.pyi +307 -26
- modal/object.pyi +48 -9
- modal/parallel_map.pyi +144 -14
- modal/partial_function.pyi +255 -14
- modal/proxy.pyi +28 -3
- modal/queue.pyi +447 -30
- modal/runner.pyi +160 -22
- modal/sandbox.pyi +310 -50
- modal/secret.pyi +164 -15
- modal/snapshot.pyi +25 -4
- modal/token_flow.pyi +28 -8
- modal/volume.pyi +649 -59
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev3.dist-info}/METADATA +1 -1
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev3.dist-info}/RECORD +35 -35
- modal_version/__init__.py +1 -1
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev3.dist-info}/WHEEL +0 -0
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev3.dist-info}/entry_points.txt +0 -0
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev3.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev3.dist-info}/top_level.txt +0 -0
modal/client.pyi
CHANGED
@@ -31,28 +31,94 @@ class _Client:
|
|
31
31
|
server_url: str,
|
32
32
|
client_type: int,
|
33
33
|
credentials: typing.Optional[tuple[str, str]],
|
34
|
-
version: str = "1.0.5.
|
35
|
-
):
|
34
|
+
version: str = "1.0.5.dev3",
|
35
|
+
):
|
36
|
+
"""mdmd:hidden
|
37
|
+
The Modal client object is not intended to be instantiated directly by users.
|
38
|
+
"""
|
39
|
+
...
|
40
|
+
|
36
41
|
def is_closed(self) -> bool: ...
|
37
42
|
@property
|
38
|
-
def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal:
|
39
|
-
|
43
|
+
def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal:
|
44
|
+
"""mdmd:hidden
|
45
|
+
The default stub. Stubs can safely be used across forks / client snapshots.
|
46
|
+
|
47
|
+
This is useful if you want to make requests to the default Modal server in us-east, for example
|
48
|
+
control plane requests.
|
49
|
+
|
50
|
+
This is equivalent to client.get_stub(default_server_url), but it's cached, so it's a bit faster.
|
51
|
+
"""
|
52
|
+
...
|
53
|
+
|
54
|
+
async def get_stub(self, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
|
55
|
+
"""mdmd:hidden
|
56
|
+
Get a stub for a specific server URL. Stubs can safely be used across forks / client snapshots.
|
57
|
+
|
58
|
+
This is useful if you want to make requests to a regional Modal server, for example low-latency
|
59
|
+
function calls in us-west.
|
60
|
+
|
61
|
+
This function is O(n) where n is the number of RPCs in ModalClient.
|
62
|
+
"""
|
63
|
+
...
|
64
|
+
|
40
65
|
async def _open(self): ...
|
41
66
|
async def _close(self, prep_for_restore: bool = False): ...
|
42
|
-
async def hello(self):
|
67
|
+
async def hello(self):
|
68
|
+
"""Connect to server and retrieve version information; raise appropriate error for various failures."""
|
69
|
+
...
|
70
|
+
|
43
71
|
async def __aenter__(self): ...
|
44
72
|
async def __aexit__(self, exc_type, exc, tb): ...
|
45
73
|
@classmethod
|
46
|
-
def anonymous(cls, server_url: str) -> typing.AsyncContextManager[_Client]:
|
74
|
+
def anonymous(cls, server_url: str) -> typing.AsyncContextManager[_Client]:
|
75
|
+
"""mdmd:hidden
|
76
|
+
Create a connection with no credentials; to be used for token creation.
|
77
|
+
"""
|
78
|
+
...
|
79
|
+
|
47
80
|
@classmethod
|
48
|
-
async def from_env(cls, _override_config=None) -> _Client:
|
81
|
+
async def from_env(cls, _override_config=None) -> _Client:
|
82
|
+
"""mdmd:hidden
|
83
|
+
Singleton that is instantiated from the Modal config and reused on subsequent calls.
|
84
|
+
"""
|
85
|
+
...
|
86
|
+
|
49
87
|
@classmethod
|
50
|
-
async def from_credentials(cls, token_id: str, token_secret: str) -> _Client:
|
88
|
+
async def from_credentials(cls, token_id: str, token_secret: str) -> _Client:
|
89
|
+
"""Constructor based on token credentials; useful for managing Modal on behalf of third-party users.
|
90
|
+
|
91
|
+
**Usage:**
|
92
|
+
|
93
|
+
```python notest
|
94
|
+
client = modal.Client.from_credentials("my_token_id", "my_token_secret")
|
95
|
+
|
96
|
+
modal.Sandbox.create("echo", "hi", client=client, app=app)
|
97
|
+
```
|
98
|
+
"""
|
99
|
+
...
|
100
|
+
|
51
101
|
@classmethod
|
52
|
-
async def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
|
102
|
+
async def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
|
103
|
+
"""mdmd:hidden
|
104
|
+
Check whether can the client can connect to this server with these credentials; raise if not.
|
105
|
+
"""
|
106
|
+
...
|
107
|
+
|
53
108
|
@classmethod
|
54
|
-
def set_env_client(cls, client: typing.Optional[_Client]):
|
55
|
-
|
109
|
+
def set_env_client(cls, client: typing.Optional[_Client]):
|
110
|
+
"""mdmd:hidden"""
|
111
|
+
...
|
112
|
+
|
113
|
+
async def _call_safely(self, coro, readable_method: str):
|
114
|
+
"""Runs coroutine wrapped in a task that's part of the client's task context
|
115
|
+
|
116
|
+
* Raises ClientClosed in case the client is closed while the coroutine is executed
|
117
|
+
* Logs warning if call is made outside of the event loop that the client is running in,
|
118
|
+
and execute without the cancellation context in that case
|
119
|
+
"""
|
120
|
+
...
|
121
|
+
|
56
122
|
async def _reset_on_pid_change(self): ...
|
57
123
|
async def _get_channel(self, server_url: str) -> grpclib.client.Channel: ...
|
58
124
|
async def _call_unary(
|
@@ -94,15 +160,48 @@ class Client:
|
|
94
160
|
server_url: str,
|
95
161
|
client_type: int,
|
96
162
|
credentials: typing.Optional[tuple[str, str]],
|
97
|
-
version: str = "1.0.5.
|
98
|
-
):
|
163
|
+
version: str = "1.0.5.dev3",
|
164
|
+
):
|
165
|
+
"""mdmd:hidden
|
166
|
+
The Modal client object is not intended to be instantiated directly by users.
|
167
|
+
"""
|
168
|
+
...
|
169
|
+
|
99
170
|
def is_closed(self) -> bool: ...
|
100
171
|
@property
|
101
|
-
def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal:
|
172
|
+
def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal:
|
173
|
+
"""mdmd:hidden
|
174
|
+
The default stub. Stubs can safely be used across forks / client snapshots.
|
175
|
+
|
176
|
+
This is useful if you want to make requests to the default Modal server in us-east, for example
|
177
|
+
control plane requests.
|
178
|
+
|
179
|
+
This is equivalent to client.get_stub(default_server_url), but it's cached, so it's a bit faster.
|
180
|
+
"""
|
181
|
+
...
|
102
182
|
|
103
183
|
class __get_stub_spec(typing_extensions.Protocol[SUPERSELF]):
|
104
|
-
def __call__(self, /, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
|
105
|
-
|
184
|
+
def __call__(self, /, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
|
185
|
+
"""mdmd:hidden
|
186
|
+
Get a stub for a specific server URL. Stubs can safely be used across forks / client snapshots.
|
187
|
+
|
188
|
+
This is useful if you want to make requests to a regional Modal server, for example low-latency
|
189
|
+
function calls in us-west.
|
190
|
+
|
191
|
+
This function is O(n) where n is the number of RPCs in ModalClient.
|
192
|
+
"""
|
193
|
+
...
|
194
|
+
|
195
|
+
async def aio(self, /, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
|
196
|
+
"""mdmd:hidden
|
197
|
+
Get a stub for a specific server URL. Stubs can safely be used across forks / client snapshots.
|
198
|
+
|
199
|
+
This is useful if you want to make requests to a regional Modal server, for example low-latency
|
200
|
+
function calls in us-west.
|
201
|
+
|
202
|
+
This function is O(n) where n is the number of RPCs in ModalClient.
|
203
|
+
"""
|
204
|
+
...
|
106
205
|
|
107
206
|
get_stub: __get_stub_spec[typing_extensions.Self]
|
108
207
|
|
@@ -119,8 +218,13 @@ class Client:
|
|
119
218
|
_close: ___close_spec[typing_extensions.Self]
|
120
219
|
|
121
220
|
class __hello_spec(typing_extensions.Protocol[SUPERSELF]):
|
122
|
-
def __call__(self, /):
|
123
|
-
|
221
|
+
def __call__(self, /):
|
222
|
+
"""Connect to server and retrieve version information; raise appropriate error for various failures."""
|
223
|
+
...
|
224
|
+
|
225
|
+
async def aio(self, /):
|
226
|
+
"""Connect to server and retrieve version information; raise appropriate error for various failures."""
|
227
|
+
...
|
124
228
|
|
125
229
|
hello: __hello_spec[typing_extensions.Self]
|
126
230
|
|
@@ -129,19 +233,63 @@ class Client:
|
|
129
233
|
def __exit__(self, exc_type, exc, tb): ...
|
130
234
|
async def __aexit__(self, exc_type, exc, tb): ...
|
131
235
|
@classmethod
|
132
|
-
def anonymous(cls, server_url: str) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Client]:
|
236
|
+
def anonymous(cls, server_url: str) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Client]:
|
237
|
+
"""mdmd:hidden
|
238
|
+
Create a connection with no credentials; to be used for token creation.
|
239
|
+
"""
|
240
|
+
...
|
241
|
+
|
133
242
|
@classmethod
|
134
|
-
def from_env(cls, _override_config=None) -> Client:
|
243
|
+
def from_env(cls, _override_config=None) -> Client:
|
244
|
+
"""mdmd:hidden
|
245
|
+
Singleton that is instantiated from the Modal config and reused on subsequent calls.
|
246
|
+
"""
|
247
|
+
...
|
248
|
+
|
135
249
|
@classmethod
|
136
|
-
def from_credentials(cls, token_id: str, token_secret: str) -> Client:
|
250
|
+
def from_credentials(cls, token_id: str, token_secret: str) -> Client:
|
251
|
+
"""Constructor based on token credentials; useful for managing Modal on behalf of third-party users.
|
252
|
+
|
253
|
+
**Usage:**
|
254
|
+
|
255
|
+
```python notest
|
256
|
+
client = modal.Client.from_credentials("my_token_id", "my_token_secret")
|
257
|
+
|
258
|
+
modal.Sandbox.create("echo", "hi", client=client, app=app)
|
259
|
+
```
|
260
|
+
"""
|
261
|
+
...
|
262
|
+
|
137
263
|
@classmethod
|
138
|
-
def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
|
264
|
+
def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
|
265
|
+
"""mdmd:hidden
|
266
|
+
Check whether can the client can connect to this server with these credentials; raise if not.
|
267
|
+
"""
|
268
|
+
...
|
269
|
+
|
139
270
|
@classmethod
|
140
|
-
def set_env_client(cls, client: typing.Optional[Client]):
|
271
|
+
def set_env_client(cls, client: typing.Optional[Client]):
|
272
|
+
"""mdmd:hidden"""
|
273
|
+
...
|
141
274
|
|
142
275
|
class ___call_safely_spec(typing_extensions.Protocol[SUPERSELF]):
|
143
|
-
def __call__(self, /, coro, readable_method: str):
|
144
|
-
|
276
|
+
def __call__(self, /, coro, readable_method: str):
|
277
|
+
"""Runs coroutine wrapped in a task that's part of the client's task context
|
278
|
+
|
279
|
+
* Raises ClientClosed in case the client is closed while the coroutine is executed
|
280
|
+
* Logs warning if call is made outside of the event loop that the client is running in,
|
281
|
+
and execute without the cancellation context in that case
|
282
|
+
"""
|
283
|
+
...
|
284
|
+
|
285
|
+
async def aio(self, /, coro, readable_method: str):
|
286
|
+
"""Runs coroutine wrapped in a task that's part of the client's task context
|
287
|
+
|
288
|
+
* Raises ClientClosed in case the client is closed while the coroutine is executed
|
289
|
+
* Logs warning if call is made outside of the event loop that the client is running in,
|
290
|
+
and execute without the cancellation context in that case
|
291
|
+
"""
|
292
|
+
...
|
145
293
|
|
146
294
|
_call_safely: ___call_safely_spec[typing_extensions.Self]
|
147
295
|
|
@@ -182,6 +330,26 @@ class Client:
|
|
182
330
|
) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
|
183
331
|
|
184
332
|
class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
|
333
|
+
"""Abstract base class for generic types.
|
334
|
+
|
335
|
+
A generic type is typically declared by inheriting from
|
336
|
+
this class parameterized with one or more type variables.
|
337
|
+
For example, a generic mapping type might be defined as::
|
338
|
+
|
339
|
+
class Mapping(Generic[KT, VT]):
|
340
|
+
def __getitem__(self, key: KT) -> VT:
|
341
|
+
...
|
342
|
+
# Etc.
|
343
|
+
|
344
|
+
This class can then be used as follows::
|
345
|
+
|
346
|
+
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
|
347
|
+
try:
|
348
|
+
return mapping[key]
|
349
|
+
except KeyError:
|
350
|
+
return default
|
351
|
+
"""
|
352
|
+
|
185
353
|
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
|
186
354
|
client: _Client
|
187
355
|
|
@@ -190,7 +358,10 @@ class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
|
|
190
358
|
wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
|
191
359
|
client: _Client,
|
192
360
|
server_url: str,
|
193
|
-
):
|
361
|
+
):
|
362
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
363
|
+
...
|
364
|
+
|
194
365
|
@property
|
195
366
|
def name(self) -> str: ...
|
196
367
|
async def __call__(
|
@@ -203,9 +374,31 @@ class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
|
|
203
374
|
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
204
375
|
None,
|
205
376
|
] = None,
|
206
|
-
) -> ResponseType:
|
377
|
+
) -> ResponseType:
|
378
|
+
"""Call self as a function."""
|
379
|
+
...
|
207
380
|
|
208
381
|
class UnaryStreamWrapper(typing.Generic[RequestType, ResponseType]):
|
382
|
+
"""Abstract base class for generic types.
|
383
|
+
|
384
|
+
A generic type is typically declared by inheriting from
|
385
|
+
this class parameterized with one or more type variables.
|
386
|
+
For example, a generic mapping type might be defined as::
|
387
|
+
|
388
|
+
class Mapping(Generic[KT, VT]):
|
389
|
+
def __getitem__(self, key: KT) -> VT:
|
390
|
+
...
|
391
|
+
# Etc.
|
392
|
+
|
393
|
+
This class can then be used as follows::
|
394
|
+
|
395
|
+
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
|
396
|
+
try:
|
397
|
+
return mapping[key]
|
398
|
+
except KeyError:
|
399
|
+
return default
|
400
|
+
"""
|
401
|
+
|
209
402
|
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType]
|
210
403
|
|
211
404
|
def __init__(
|
@@ -213,7 +406,10 @@ class UnaryStreamWrapper(typing.Generic[RequestType, ResponseType]):
|
|
213
406
|
wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
|
214
407
|
client: _Client,
|
215
408
|
server_url: str,
|
216
|
-
):
|
409
|
+
):
|
410
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
411
|
+
...
|
412
|
+
|
217
413
|
@property
|
218
414
|
def name(self) -> str: ...
|
219
415
|
def unary_stream(self, request, metadata: typing.Optional[typing.Any] = None): ...
|
modal/cloud_bucket_mount.pyi
CHANGED
@@ -3,6 +3,95 @@ import modal_proto.api_pb2
|
|
3
3
|
import typing
|
4
4
|
|
5
5
|
class _CloudBucketMount:
|
6
|
+
"""Mounts a cloud bucket to your container. Currently supports AWS S3 buckets.
|
7
|
+
|
8
|
+
S3 buckets are mounted using [AWS S3 Mountpoint](https://github.com/awslabs/mountpoint-s3).
|
9
|
+
S3 mounts are optimized for reading large files sequentially. It does not support every file operation; consult
|
10
|
+
[the AWS S3 Mountpoint documentation](https://github.com/awslabs/mountpoint-s3/blob/main/doc/SEMANTICS.md)
|
11
|
+
for more information.
|
12
|
+
|
13
|
+
**AWS S3 Usage**
|
14
|
+
|
15
|
+
```python
|
16
|
+
import subprocess
|
17
|
+
|
18
|
+
app = modal.App()
|
19
|
+
secret = modal.Secret.from_name(
|
20
|
+
"aws-secret",
|
21
|
+
required_keys=["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
|
22
|
+
# Note: providing AWS_REGION can help when automatic detection of the bucket region fails.
|
23
|
+
)
|
24
|
+
|
25
|
+
@app.function(
|
26
|
+
volumes={
|
27
|
+
"/my-mount": modal.CloudBucketMount(
|
28
|
+
bucket_name="s3-bucket-name",
|
29
|
+
secret=secret,
|
30
|
+
read_only=True
|
31
|
+
)
|
32
|
+
}
|
33
|
+
)
|
34
|
+
def f():
|
35
|
+
subprocess.run(["ls", "/my-mount"], check=True)
|
36
|
+
```
|
37
|
+
|
38
|
+
**Cloudflare R2 Usage**
|
39
|
+
|
40
|
+
Cloudflare R2 is [S3-compatible](https://developers.cloudflare.com/r2/api/s3/api/) so its setup looks
|
41
|
+
very similar to S3. But additionally the `bucket_endpoint_url` argument must be passed.
|
42
|
+
|
43
|
+
```python
|
44
|
+
import subprocess
|
45
|
+
|
46
|
+
app = modal.App()
|
47
|
+
secret = modal.Secret.from_name(
|
48
|
+
"r2-secret",
|
49
|
+
required_keys=["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
|
50
|
+
)
|
51
|
+
|
52
|
+
@app.function(
|
53
|
+
volumes={
|
54
|
+
"/my-mount": modal.CloudBucketMount(
|
55
|
+
bucket_name="my-r2-bucket",
|
56
|
+
bucket_endpoint_url="https://<ACCOUNT ID>.r2.cloudflarestorage.com",
|
57
|
+
secret=secret,
|
58
|
+
read_only=True
|
59
|
+
)
|
60
|
+
}
|
61
|
+
)
|
62
|
+
def f():
|
63
|
+
subprocess.run(["ls", "/my-mount"], check=True)
|
64
|
+
```
|
65
|
+
|
66
|
+
**Google GCS Usage**
|
67
|
+
|
68
|
+
Google Cloud Storage (GCS) is [S3-compatible](https://cloud.google.com/storage/docs/interoperability).
|
69
|
+
GCS Buckets also require a secret with Google-specific key names (see below) populated with
|
70
|
+
a [HMAC key](https://cloud.google.com/storage/docs/authentication/managing-hmackeys#create).
|
71
|
+
|
72
|
+
```python
|
73
|
+
import subprocess
|
74
|
+
|
75
|
+
app = modal.App()
|
76
|
+
gcp_hmac_secret = modal.Secret.from_name(
|
77
|
+
"gcp-secret",
|
78
|
+
required_keys=["GOOGLE_ACCESS_KEY_ID", "GOOGLE_ACCESS_KEY_SECRET"]
|
79
|
+
)
|
80
|
+
|
81
|
+
@app.function(
|
82
|
+
volumes={
|
83
|
+
"/my-mount": modal.CloudBucketMount(
|
84
|
+
bucket_name="my-gcs-bucket",
|
85
|
+
bucket_endpoint_url="https://storage.googleapis.com",
|
86
|
+
secret=gcp_hmac_secret,
|
87
|
+
)
|
88
|
+
}
|
89
|
+
)
|
90
|
+
def f():
|
91
|
+
subprocess.run(["ls", "/my-mount"], check=True)
|
92
|
+
```
|
93
|
+
"""
|
94
|
+
|
6
95
|
bucket_name: str
|
7
96
|
bucket_endpoint_url: typing.Optional[str]
|
8
97
|
key_prefix: typing.Optional[str]
|
@@ -20,15 +109,114 @@ class _CloudBucketMount:
|
|
20
109
|
oidc_auth_role_arn: typing.Optional[str] = None,
|
21
110
|
read_only: bool = False,
|
22
111
|
requester_pays: bool = False,
|
23
|
-
) -> None:
|
24
|
-
|
25
|
-
|
112
|
+
) -> None:
|
113
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
114
|
+
...
|
115
|
+
|
116
|
+
def __repr__(self):
|
117
|
+
"""Return repr(self)."""
|
118
|
+
...
|
119
|
+
|
120
|
+
def __eq__(self, other):
|
121
|
+
"""Return self==value."""
|
122
|
+
...
|
26
123
|
|
27
124
|
def cloud_bucket_mounts_to_proto(
|
28
125
|
mounts: list[tuple[str, _CloudBucketMount]],
|
29
|
-
) -> list[modal_proto.api_pb2.CloudBucketMount]:
|
126
|
+
) -> list[modal_proto.api_pb2.CloudBucketMount]:
|
127
|
+
"""Helper function to convert `CloudBucketMount` to a list of protobufs that can be passed to the server."""
|
128
|
+
...
|
30
129
|
|
31
130
|
class CloudBucketMount:
|
131
|
+
"""Mounts a cloud bucket to your container. Currently supports AWS S3 buckets.
|
132
|
+
|
133
|
+
S3 buckets are mounted using [AWS S3 Mountpoint](https://github.com/awslabs/mountpoint-s3).
|
134
|
+
S3 mounts are optimized for reading large files sequentially. It does not support every file operation; consult
|
135
|
+
[the AWS S3 Mountpoint documentation](https://github.com/awslabs/mountpoint-s3/blob/main/doc/SEMANTICS.md)
|
136
|
+
for more information.
|
137
|
+
|
138
|
+
**AWS S3 Usage**
|
139
|
+
|
140
|
+
```python
|
141
|
+
import subprocess
|
142
|
+
|
143
|
+
app = modal.App()
|
144
|
+
secret = modal.Secret.from_name(
|
145
|
+
"aws-secret",
|
146
|
+
required_keys=["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
|
147
|
+
# Note: providing AWS_REGION can help when automatic detection of the bucket region fails.
|
148
|
+
)
|
149
|
+
|
150
|
+
@app.function(
|
151
|
+
volumes={
|
152
|
+
"/my-mount": modal.CloudBucketMount(
|
153
|
+
bucket_name="s3-bucket-name",
|
154
|
+
secret=secret,
|
155
|
+
read_only=True
|
156
|
+
)
|
157
|
+
}
|
158
|
+
)
|
159
|
+
def f():
|
160
|
+
subprocess.run(["ls", "/my-mount"], check=True)
|
161
|
+
```
|
162
|
+
|
163
|
+
**Cloudflare R2 Usage**
|
164
|
+
|
165
|
+
Cloudflare R2 is [S3-compatible](https://developers.cloudflare.com/r2/api/s3/api/) so its setup looks
|
166
|
+
very similar to S3. But additionally the `bucket_endpoint_url` argument must be passed.
|
167
|
+
|
168
|
+
```python
|
169
|
+
import subprocess
|
170
|
+
|
171
|
+
app = modal.App()
|
172
|
+
secret = modal.Secret.from_name(
|
173
|
+
"r2-secret",
|
174
|
+
required_keys=["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
|
175
|
+
)
|
176
|
+
|
177
|
+
@app.function(
|
178
|
+
volumes={
|
179
|
+
"/my-mount": modal.CloudBucketMount(
|
180
|
+
bucket_name="my-r2-bucket",
|
181
|
+
bucket_endpoint_url="https://<ACCOUNT ID>.r2.cloudflarestorage.com",
|
182
|
+
secret=secret,
|
183
|
+
read_only=True
|
184
|
+
)
|
185
|
+
}
|
186
|
+
)
|
187
|
+
def f():
|
188
|
+
subprocess.run(["ls", "/my-mount"], check=True)
|
189
|
+
```
|
190
|
+
|
191
|
+
**Google GCS Usage**
|
192
|
+
|
193
|
+
Google Cloud Storage (GCS) is [S3-compatible](https://cloud.google.com/storage/docs/interoperability).
|
194
|
+
GCS Buckets also require a secret with Google-specific key names (see below) populated with
|
195
|
+
a [HMAC key](https://cloud.google.com/storage/docs/authentication/managing-hmackeys#create).
|
196
|
+
|
197
|
+
```python
|
198
|
+
import subprocess
|
199
|
+
|
200
|
+
app = modal.App()
|
201
|
+
gcp_hmac_secret = modal.Secret.from_name(
|
202
|
+
"gcp-secret",
|
203
|
+
required_keys=["GOOGLE_ACCESS_KEY_ID", "GOOGLE_ACCESS_KEY_SECRET"]
|
204
|
+
)
|
205
|
+
|
206
|
+
@app.function(
|
207
|
+
volumes={
|
208
|
+
"/my-mount": modal.CloudBucketMount(
|
209
|
+
bucket_name="my-gcs-bucket",
|
210
|
+
bucket_endpoint_url="https://storage.googleapis.com",
|
211
|
+
secret=gcp_hmac_secret,
|
212
|
+
)
|
213
|
+
}
|
214
|
+
)
|
215
|
+
def f():
|
216
|
+
subprocess.run(["ls", "/my-mount"], check=True)
|
217
|
+
```
|
218
|
+
"""
|
219
|
+
|
32
220
|
bucket_name: str
|
33
221
|
bucket_endpoint_url: typing.Optional[str]
|
34
222
|
key_prefix: typing.Optional[str]
|