modal 1.0.4.dev12__py3-none-any.whl → 1.0.5__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/_functions.py +84 -46
- modal/_partial_function.py +1 -1
- modal/_runtime/container_io_manager.pyi +222 -40
- modal/_runtime/execution_context.pyi +60 -6
- modal/_serialization.py +25 -2
- modal/_tunnel.pyi +380 -12
- modal/_utils/async_utils.py +1 -1
- modal/_utils/blob_utils.py +56 -19
- modal/_utils/function_utils.py +33 -7
- modal/_utils/grpc_utils.py +11 -4
- modal/app.py +5 -5
- modal/app.pyi +658 -48
- modal/cli/run.py +2 -1
- modal/client.pyi +224 -36
- modal/cloud_bucket_mount.pyi +192 -4
- modal/cls.py +7 -7
- modal/cls.pyi +442 -35
- modal/container_process.pyi +103 -14
- modal/dict.py +4 -4
- modal/dict.pyi +453 -51
- modal/environments.pyi +41 -9
- modal/exception.py +6 -2
- modal/experimental/__init__.py +90 -0
- modal/experimental/ipython.py +11 -7
- modal/file_io.pyi +236 -45
- modal/functions.pyi +573 -65
- modal/gpu.py +1 -1
- modal/image.py +1 -1
- modal/image.pyi +1256 -74
- modal/io_streams.py +8 -4
- modal/io_streams.pyi +348 -38
- modal/mount.pyi +261 -31
- modal/network_file_system.py +3 -3
- modal/network_file_system.pyi +307 -26
- modal/object.pyi +48 -9
- modal/parallel_map.py +93 -19
- modal/parallel_map.pyi +160 -15
- modal/partial_function.pyi +255 -14
- modal/proxy.py +1 -1
- modal/proxy.pyi +28 -3
- modal/queue.py +4 -4
- modal/queue.pyi +447 -30
- modal/runner.pyi +160 -22
- modal/sandbox.py +8 -7
- modal/sandbox.pyi +310 -50
- modal/schedule.py +1 -1
- modal/secret.py +2 -2
- modal/secret.pyi +164 -15
- modal/snapshot.pyi +25 -4
- modal/token_flow.pyi +28 -8
- modal/volume.py +41 -4
- modal/volume.pyi +693 -59
- {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/METADATA +3 -3
- {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/RECORD +67 -67
- modal_proto/api.proto +56 -0
- modal_proto/api_grpc.py +48 -0
- modal_proto/api_pb2.py +874 -780
- modal_proto/api_pb2.pyi +194 -8
- modal_proto/api_pb2_grpc.py +100 -0
- modal_proto/api_pb2_grpc.pyi +32 -0
- modal_proto/modal_api_grpc.py +3 -0
- modal_version/__init__.py +1 -1
- {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/WHEEL +0 -0
- {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/entry_points.txt +0 -0
- {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/top_level.txt +0 -0
modal/secret.pyi
CHANGED
@@ -5,16 +5,82 @@ import typing
|
|
5
5
|
import typing_extensions
|
6
6
|
|
7
7
|
class _Secret(modal._object._Object):
|
8
|
+
"""Secrets provide a dictionary of environment variables for images.
|
9
|
+
|
10
|
+
Secrets are a secure way to add credentials and other sensitive information
|
11
|
+
to the containers your functions run in. You can create and edit secrets on
|
12
|
+
[the dashboard](https://modal.com/secrets), or programmatically from Python code.
|
13
|
+
|
14
|
+
See [the secrets guide page](https://modal.com/docs/guide/secrets) for more information.
|
15
|
+
"""
|
8
16
|
@staticmethod
|
9
|
-
def from_dict(env_dict: dict[str, typing.Optional[str]] = {}) -> _Secret:
|
17
|
+
def from_dict(env_dict: dict[str, typing.Optional[str]] = {}) -> _Secret:
|
18
|
+
"""Create a secret from a str-str dictionary. Values can also be `None`, which is ignored.
|
19
|
+
|
20
|
+
Usage:
|
21
|
+
```python
|
22
|
+
@app.function(secrets=[modal.Secret.from_dict({"FOO": "bar"})])
|
23
|
+
def run():
|
24
|
+
print(os.environ["FOO"])
|
25
|
+
```
|
26
|
+
"""
|
27
|
+
...
|
28
|
+
|
10
29
|
@staticmethod
|
11
|
-
def from_local_environ(env_keys: list[str]) -> _Secret:
|
30
|
+
def from_local_environ(env_keys: list[str]) -> _Secret:
|
31
|
+
"""Create secrets from local environment variables automatically."""
|
32
|
+
...
|
33
|
+
|
12
34
|
@staticmethod
|
13
|
-
def from_dotenv(path=None, *, filename=".env") -> _Secret:
|
35
|
+
def from_dotenv(path=None, *, filename=".env") -> _Secret:
|
36
|
+
"""Create secrets from a .env file automatically.
|
37
|
+
|
38
|
+
If no argument is provided, it will use the current working directory as the starting
|
39
|
+
point for finding a `.env` file. Note that it does not use the location of the module
|
40
|
+
calling `Secret.from_dotenv`.
|
41
|
+
|
42
|
+
If called with an argument, it will use that as a starting point for finding `.env` files.
|
43
|
+
In particular, you can call it like this:
|
44
|
+
```python
|
45
|
+
@app.function(secrets=[modal.Secret.from_dotenv(__file__)])
|
46
|
+
def run():
|
47
|
+
print(os.environ["USERNAME"]) # Assumes USERNAME is defined in your .env file
|
48
|
+
```
|
49
|
+
|
50
|
+
This will use the location of the script calling `modal.Secret.from_dotenv` as a
|
51
|
+
starting point for finding the `.env` file.
|
52
|
+
|
53
|
+
A file named `.env` is expected by default, but this can be overridden with the `filename`
|
54
|
+
keyword argument:
|
55
|
+
|
56
|
+
```python
|
57
|
+
@app.function(secrets=[modal.Secret.from_dotenv(filename=".env-dev")])
|
58
|
+
def run():
|
59
|
+
...
|
60
|
+
```
|
61
|
+
"""
|
62
|
+
...
|
63
|
+
|
14
64
|
@staticmethod
|
15
65
|
def from_name(
|
16
66
|
name: str, *, namespace=1, environment_name: typing.Optional[str] = None, required_keys: list[str] = []
|
17
|
-
) -> _Secret:
|
67
|
+
) -> _Secret:
|
68
|
+
"""Reference a Secret by its name.
|
69
|
+
|
70
|
+
In contrast to most other Modal objects, named Secrets must be provisioned
|
71
|
+
from the Dashboard. See other methods for alternate ways of creating a new
|
72
|
+
Secret from code.
|
73
|
+
|
74
|
+
```python
|
75
|
+
secret = modal.Secret.from_name("my-secret")
|
76
|
+
|
77
|
+
@app.function(secrets=[secret])
|
78
|
+
def run():
|
79
|
+
...
|
80
|
+
```
|
81
|
+
"""
|
82
|
+
...
|
83
|
+
|
18
84
|
@staticmethod
|
19
85
|
async def lookup(
|
20
86
|
name: str,
|
@@ -22,7 +88,10 @@ class _Secret(modal._object._Object):
|
|
22
88
|
client: typing.Optional[modal.client._Client] = None,
|
23
89
|
environment_name: typing.Optional[str] = None,
|
24
90
|
required_keys: list[str] = [],
|
25
|
-
) -> _Secret:
|
91
|
+
) -> _Secret:
|
92
|
+
"""mdmd:hidden"""
|
93
|
+
...
|
94
|
+
|
26
95
|
@staticmethod
|
27
96
|
async def create_deployed(
|
28
97
|
deployment_name: str,
|
@@ -31,20 +100,90 @@ class _Secret(modal._object._Object):
|
|
31
100
|
client: typing.Optional[modal.client._Client] = None,
|
32
101
|
environment_name: typing.Optional[str] = None,
|
33
102
|
overwrite: bool = False,
|
34
|
-
) -> str:
|
103
|
+
) -> str:
|
104
|
+
"""mdmd:hidden"""
|
105
|
+
...
|
35
106
|
|
36
107
|
class Secret(modal.object.Object):
|
37
|
-
|
108
|
+
"""Secrets provide a dictionary of environment variables for images.
|
109
|
+
|
110
|
+
Secrets are a secure way to add credentials and other sensitive information
|
111
|
+
to the containers your functions run in. You can create and edit secrets on
|
112
|
+
[the dashboard](https://modal.com/secrets), or programmatically from Python code.
|
113
|
+
|
114
|
+
See [the secrets guide page](https://modal.com/docs/guide/secrets) for more information.
|
115
|
+
"""
|
116
|
+
def __init__(self, *args, **kwargs):
|
117
|
+
"""mdmd:hidden"""
|
118
|
+
...
|
119
|
+
|
38
120
|
@staticmethod
|
39
|
-
def from_dict(env_dict: dict[str, typing.Optional[str]] = {}) -> Secret:
|
121
|
+
def from_dict(env_dict: dict[str, typing.Optional[str]] = {}) -> Secret:
|
122
|
+
"""Create a secret from a str-str dictionary. Values can also be `None`, which is ignored.
|
123
|
+
|
124
|
+
Usage:
|
125
|
+
```python
|
126
|
+
@app.function(secrets=[modal.Secret.from_dict({"FOO": "bar"})])
|
127
|
+
def run():
|
128
|
+
print(os.environ["FOO"])
|
129
|
+
```
|
130
|
+
"""
|
131
|
+
...
|
132
|
+
|
40
133
|
@staticmethod
|
41
|
-
def from_local_environ(env_keys: list[str]) -> Secret:
|
134
|
+
def from_local_environ(env_keys: list[str]) -> Secret:
|
135
|
+
"""Create secrets from local environment variables automatically."""
|
136
|
+
...
|
137
|
+
|
42
138
|
@staticmethod
|
43
|
-
def from_dotenv(path=None, *, filename=".env") -> Secret:
|
139
|
+
def from_dotenv(path=None, *, filename=".env") -> Secret:
|
140
|
+
"""Create secrets from a .env file automatically.
|
141
|
+
|
142
|
+
If no argument is provided, it will use the current working directory as the starting
|
143
|
+
point for finding a `.env` file. Note that it does not use the location of the module
|
144
|
+
calling `Secret.from_dotenv`.
|
145
|
+
|
146
|
+
If called with an argument, it will use that as a starting point for finding `.env` files.
|
147
|
+
In particular, you can call it like this:
|
148
|
+
```python
|
149
|
+
@app.function(secrets=[modal.Secret.from_dotenv(__file__)])
|
150
|
+
def run():
|
151
|
+
print(os.environ["USERNAME"]) # Assumes USERNAME is defined in your .env file
|
152
|
+
```
|
153
|
+
|
154
|
+
This will use the location of the script calling `modal.Secret.from_dotenv` as a
|
155
|
+
starting point for finding the `.env` file.
|
156
|
+
|
157
|
+
A file named `.env` is expected by default, but this can be overridden with the `filename`
|
158
|
+
keyword argument:
|
159
|
+
|
160
|
+
```python
|
161
|
+
@app.function(secrets=[modal.Secret.from_dotenv(filename=".env-dev")])
|
162
|
+
def run():
|
163
|
+
...
|
164
|
+
```
|
165
|
+
"""
|
166
|
+
...
|
167
|
+
|
44
168
|
@staticmethod
|
45
169
|
def from_name(
|
46
170
|
name: str, *, namespace=1, environment_name: typing.Optional[str] = None, required_keys: list[str] = []
|
47
|
-
) -> Secret:
|
171
|
+
) -> Secret:
|
172
|
+
"""Reference a Secret by its name.
|
173
|
+
|
174
|
+
In contrast to most other Modal objects, named Secrets must be provisioned
|
175
|
+
from the Dashboard. See other methods for alternate ways of creating a new
|
176
|
+
Secret from code.
|
177
|
+
|
178
|
+
```python
|
179
|
+
secret = modal.Secret.from_name("my-secret")
|
180
|
+
|
181
|
+
@app.function(secrets=[secret])
|
182
|
+
def run():
|
183
|
+
...
|
184
|
+
```
|
185
|
+
"""
|
186
|
+
...
|
48
187
|
|
49
188
|
class __lookup_spec(typing_extensions.Protocol):
|
50
189
|
def __call__(
|
@@ -55,7 +194,10 @@ class Secret(modal.object.Object):
|
|
55
194
|
client: typing.Optional[modal.client.Client] = None,
|
56
195
|
environment_name: typing.Optional[str] = None,
|
57
196
|
required_keys: list[str] = [],
|
58
|
-
) -> Secret:
|
197
|
+
) -> Secret:
|
198
|
+
"""mdmd:hidden"""
|
199
|
+
...
|
200
|
+
|
59
201
|
async def aio(
|
60
202
|
self,
|
61
203
|
/,
|
@@ -64,7 +206,9 @@ class Secret(modal.object.Object):
|
|
64
206
|
client: typing.Optional[modal.client.Client] = None,
|
65
207
|
environment_name: typing.Optional[str] = None,
|
66
208
|
required_keys: list[str] = [],
|
67
|
-
) -> Secret:
|
209
|
+
) -> Secret:
|
210
|
+
"""mdmd:hidden"""
|
211
|
+
...
|
68
212
|
|
69
213
|
lookup: __lookup_spec
|
70
214
|
|
@@ -78,7 +222,10 @@ class Secret(modal.object.Object):
|
|
78
222
|
client: typing.Optional[modal.client.Client] = None,
|
79
223
|
environment_name: typing.Optional[str] = None,
|
80
224
|
overwrite: bool = False,
|
81
|
-
) -> str:
|
225
|
+
) -> str:
|
226
|
+
"""mdmd:hidden"""
|
227
|
+
...
|
228
|
+
|
82
229
|
async def aio(
|
83
230
|
self,
|
84
231
|
/,
|
@@ -88,6 +235,8 @@ class Secret(modal.object.Object):
|
|
88
235
|
client: typing.Optional[modal.client.Client] = None,
|
89
236
|
environment_name: typing.Optional[str] = None,
|
90
237
|
overwrite: bool = False,
|
91
|
-
) -> str:
|
238
|
+
) -> str:
|
239
|
+
"""mdmd:hidden"""
|
240
|
+
...
|
92
241
|
|
93
242
|
create_deployed: __create_deployed_spec
|
modal/snapshot.pyi
CHANGED
@@ -5,14 +5,35 @@ import typing
|
|
5
5
|
import typing_extensions
|
6
6
|
|
7
7
|
class _SandboxSnapshot(modal._object._Object):
|
8
|
+
"""> Sandbox memory snapshots are in **early preview**.
|
9
|
+
|
10
|
+
A `SandboxSnapshot` object lets you interact with a stored Sandbox snapshot that was created by calling
|
11
|
+
`._experimental_snapshot()` on a Sandbox instance. This includes both the filesystem and memory state of
|
12
|
+
the original Sandbox at the time the snapshot was taken.
|
13
|
+
"""
|
8
14
|
@staticmethod
|
9
|
-
async def from_id(sandbox_snapshot_id: str, client: typing.Optional[modal.client._Client] = None):
|
15
|
+
async def from_id(sandbox_snapshot_id: str, client: typing.Optional[modal.client._Client] = None):
|
16
|
+
"""Construct a `SandboxSnapshot` object from a sandbox snapshot ID."""
|
17
|
+
...
|
10
18
|
|
11
19
|
class SandboxSnapshot(modal.object.Object):
|
12
|
-
|
20
|
+
"""> Sandbox memory snapshots are in **early preview**.
|
21
|
+
|
22
|
+
A `SandboxSnapshot` object lets you interact with a stored Sandbox snapshot that was created by calling
|
23
|
+
`._experimental_snapshot()` on a Sandbox instance. This includes both the filesystem and memory state of
|
24
|
+
the original Sandbox at the time the snapshot was taken.
|
25
|
+
"""
|
26
|
+
def __init__(self, *args, **kwargs):
|
27
|
+
"""mdmd:hidden"""
|
28
|
+
...
|
13
29
|
|
14
30
|
class __from_id_spec(typing_extensions.Protocol):
|
15
|
-
def __call__(self, /, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None):
|
16
|
-
|
31
|
+
def __call__(self, /, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None):
|
32
|
+
"""Construct a `SandboxSnapshot` object from a sandbox snapshot ID."""
|
33
|
+
...
|
34
|
+
|
35
|
+
async def aio(self, /, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None):
|
36
|
+
"""Construct a `SandboxSnapshot` object from a sandbox snapshot ID."""
|
37
|
+
...
|
17
38
|
|
18
39
|
from_id: __from_id_spec
|
modal/token_flow.pyi
CHANGED
@@ -5,13 +5,21 @@ import typing
|
|
5
5
|
import typing_extensions
|
6
6
|
|
7
7
|
class _TokenFlow:
|
8
|
-
def __init__(self, client: modal.client._Client):
|
8
|
+
def __init__(self, client: modal.client._Client):
|
9
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
10
|
+
...
|
11
|
+
|
9
12
|
def start(
|
10
13
|
self, utm_source: typing.Optional[str] = None, next_url: typing.Optional[str] = None
|
11
|
-
) -> typing.AsyncContextManager[tuple[str, str, str]]:
|
14
|
+
) -> typing.AsyncContextManager[tuple[str, str, str]]:
|
15
|
+
"""mdmd:hidden"""
|
16
|
+
...
|
17
|
+
|
12
18
|
async def finish(
|
13
19
|
self, timeout: float = 40.0, grpc_extra_timeout: float = 5.0
|
14
|
-
) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]:
|
20
|
+
) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]:
|
21
|
+
"""mdmd:hidden"""
|
22
|
+
...
|
15
23
|
|
16
24
|
SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
|
17
25
|
|
@@ -21,20 +29,30 @@ class TokenFlow:
|
|
21
29
|
class __start_spec(typing_extensions.Protocol[SUPERSELF]):
|
22
30
|
def __call__(
|
23
31
|
self, /, utm_source: typing.Optional[str] = None, next_url: typing.Optional[str] = None
|
24
|
-
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[tuple[str, str, str]]:
|
32
|
+
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[tuple[str, str, str]]:
|
33
|
+
"""mdmd:hidden"""
|
34
|
+
...
|
35
|
+
|
25
36
|
def aio(
|
26
37
|
self, /, utm_source: typing.Optional[str] = None, next_url: typing.Optional[str] = None
|
27
|
-
) -> typing.AsyncContextManager[tuple[str, str, str]]:
|
38
|
+
) -> typing.AsyncContextManager[tuple[str, str, str]]:
|
39
|
+
"""mdmd:hidden"""
|
40
|
+
...
|
28
41
|
|
29
42
|
start: __start_spec[typing_extensions.Self]
|
30
43
|
|
31
44
|
class __finish_spec(typing_extensions.Protocol[SUPERSELF]):
|
32
45
|
def __call__(
|
33
46
|
self, /, timeout: float = 40.0, grpc_extra_timeout: float = 5.0
|
34
|
-
) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]:
|
47
|
+
) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]:
|
48
|
+
"""mdmd:hidden"""
|
49
|
+
...
|
50
|
+
|
35
51
|
async def aio(
|
36
52
|
self, /, timeout: float = 40.0, grpc_extra_timeout: float = 5.0
|
37
|
-
) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]:
|
53
|
+
) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]:
|
54
|
+
"""mdmd:hidden"""
|
55
|
+
...
|
38
56
|
|
39
57
|
finish: __finish_spec[typing_extensions.Self]
|
40
58
|
|
@@ -55,4 +73,6 @@ async def _set_token(
|
|
55
73
|
verify: bool = True,
|
56
74
|
server_url: typing.Optional[str] = None,
|
57
75
|
): ...
|
58
|
-
def _open_url(url: str) -> bool:
|
76
|
+
def _open_url(url: str) -> bool:
|
77
|
+
"""Opens url in web browser, making sure we use a modern one (not Lynx etc)"""
|
78
|
+
...
|
modal/volume.py
CHANGED
@@ -135,6 +135,34 @@ class _Volume(_Object, type_prefix="vo"):
|
|
135
135
|
|
136
136
|
_lock: Optional[asyncio.Lock] = None
|
137
137
|
_metadata: "typing.Optional[api_pb2.VolumeMetadata]"
|
138
|
+
_read_only: bool = False
|
139
|
+
|
140
|
+
def read_only(self) -> "_Volume":
|
141
|
+
"""Configure Volume to mount as read-only.
|
142
|
+
|
143
|
+
**Example**
|
144
|
+
|
145
|
+
```python
|
146
|
+
import modal
|
147
|
+
|
148
|
+
volume = modal.Volume.from_name("my-volume", create_if_missing=True)
|
149
|
+
|
150
|
+
@app.function(volumes={"/mnt/items": volume.read_only()})
|
151
|
+
def f():
|
152
|
+
with open("/mnt/items/my-file.txt") as f:
|
153
|
+
return f.read()
|
154
|
+
```
|
155
|
+
|
156
|
+
The Volume is mounted as a read-only volume in a function. Any file system write operation into the
|
157
|
+
mounted volume will result in an error.
|
158
|
+
"""
|
159
|
+
|
160
|
+
async def _load(new_volume: _Volume, resolver: Resolver, existing_object_id: Optional[str]):
|
161
|
+
new_volume._initialize_from_other(self)
|
162
|
+
new_volume._read_only = True
|
163
|
+
|
164
|
+
obj = _Volume._from_loader(_load, "Volume()", hydrate_lazily=True, deps=lambda: [self])
|
165
|
+
return obj
|
138
166
|
|
139
167
|
async def _get_lock(self):
|
140
168
|
# To (mostly*) prevent multiple concurrent operations on the same volume, which can cause problems under
|
@@ -161,9 +189,9 @@ class _Volume(_Object, type_prefix="vo"):
|
|
161
189
|
) -> "_Volume":
|
162
190
|
"""Reference a Volume by name, creating if necessary.
|
163
191
|
|
164
|
-
|
165
|
-
|
166
|
-
|
192
|
+
This is a lazy method that defers hydrating the local
|
193
|
+
object with metadata from Modal servers until the first
|
194
|
+
time is is actually used.
|
167
195
|
|
168
196
|
```python
|
169
197
|
vol = modal.Volume.from_name("my-volume", create_if_missing=True)
|
@@ -405,7 +433,7 @@ class _Volume(_Object, type_prefix="vo"):
|
|
405
433
|
|
406
434
|
Note - this function is primarily intended to be used outside of a Modal App.
|
407
435
|
For more information on downloading files from a Modal Volume, see
|
408
|
-
[the guide](/docs/guide/volumes).
|
436
|
+
[the guide](https://modal.com/docs/guide/volumes).
|
409
437
|
|
410
438
|
**Example:**
|
411
439
|
|
@@ -495,6 +523,9 @@ class _Volume(_Object, type_prefix="vo"):
|
|
495
523
|
@live_method
|
496
524
|
async def remove_file(self, path: str, recursive: bool = False) -> None:
|
497
525
|
"""Remove a file or directory from a volume."""
|
526
|
+
if self._read_only:
|
527
|
+
raise InvalidError("Read-only Volume can not be written to")
|
528
|
+
|
498
529
|
if self._is_v1:
|
499
530
|
req = api_pb2.VolumeRemoveFileRequest(volume_id=self.object_id, path=path, recursive=recursive)
|
500
531
|
await retry_transient_errors(self._client.stub.VolumeRemoveFile, req)
|
@@ -527,6 +558,9 @@ class _Volume(_Object, type_prefix="vo"):
|
|
527
558
|
like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
|
528
559
|
the volume mounted as a filesystem, e.g. when running a script on your local computer.
|
529
560
|
"""
|
561
|
+
if self._read_only:
|
562
|
+
raise InvalidError("Read-only Volume can not be written to")
|
563
|
+
|
530
564
|
if self._is_v1:
|
531
565
|
if recursive:
|
532
566
|
raise ValueError("`recursive` is not supported for V1 volumes")
|
@@ -560,6 +594,9 @@ class _Volume(_Object, type_prefix="vo"):
|
|
560
594
|
batch.put_file(io.BytesIO(b"some data"), "/foobar")
|
561
595
|
```
|
562
596
|
"""
|
597
|
+
if self._read_only:
|
598
|
+
raise InvalidError("Read-only Volume can not be written to")
|
599
|
+
|
563
600
|
return _AbstractVolumeUploadContextManager.resolve(
|
564
601
|
self._metadata.version, self.object_id, self._client, force=force
|
565
602
|
)
|