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/app.pyi
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import collections.abc
|
|
2
2
|
import modal._functions
|
|
3
|
+
import modal._load_context
|
|
3
4
|
import modal._partial_function
|
|
4
5
|
import modal._utils.function_utils
|
|
5
6
|
import modal.client
|
|
@@ -26,8 +27,14 @@ class _LocalEntrypoint:
|
|
|
26
27
|
_info: modal._utils.function_utils.FunctionInfo
|
|
27
28
|
_app: _App
|
|
28
29
|
|
|
29
|
-
def __init__(self, info: modal._utils.function_utils.FunctionInfo, app: _App) -> None:
|
|
30
|
-
|
|
30
|
+
def __init__(self, info: modal._utils.function_utils.FunctionInfo, app: _App) -> None:
|
|
31
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
32
|
+
...
|
|
33
|
+
|
|
34
|
+
def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> typing.Any:
|
|
35
|
+
"""Call self as a function."""
|
|
36
|
+
...
|
|
37
|
+
|
|
31
38
|
@property
|
|
32
39
|
def info(self) -> modal._utils.function_utils.FunctionInfo: ...
|
|
33
40
|
@property
|
|
@@ -68,40 +75,134 @@ class _FunctionDecoratorType:
|
|
|
68
75
|
self, func: collections.abc.Callable[P, ReturnType]
|
|
69
76
|
) -> modal.functions.Function[P, ReturnType, ReturnType]: ...
|
|
70
77
|
|
|
78
|
+
class _LocalAppState:
|
|
79
|
+
"""All state for apps that's part of the local/definition state"""
|
|
80
|
+
|
|
81
|
+
functions: dict[str, modal._functions._Function]
|
|
82
|
+
classes: dict[str, modal.cls._Cls]
|
|
83
|
+
image_default: typing.Optional[modal.image._Image]
|
|
84
|
+
web_endpoints: list[str]
|
|
85
|
+
local_entrypoints: dict[str, _LocalEntrypoint]
|
|
86
|
+
tags: dict[str, str]
|
|
87
|
+
include_source_default: bool
|
|
88
|
+
secrets_default: collections.abc.Sequence[modal.secret._Secret]
|
|
89
|
+
volumes_default: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume]
|
|
90
|
+
|
|
91
|
+
def __init__(
|
|
92
|
+
self,
|
|
93
|
+
functions: dict[str, modal._functions._Function],
|
|
94
|
+
classes: dict[str, modal.cls._Cls],
|
|
95
|
+
image_default: typing.Optional[modal.image._Image],
|
|
96
|
+
web_endpoints: list[str],
|
|
97
|
+
local_entrypoints: dict[str, _LocalEntrypoint],
|
|
98
|
+
tags: dict[str, str],
|
|
99
|
+
include_source_default: bool,
|
|
100
|
+
secrets_default: collections.abc.Sequence[modal.secret._Secret],
|
|
101
|
+
volumes_default: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume],
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
104
|
+
...
|
|
105
|
+
|
|
106
|
+
def __repr__(self):
|
|
107
|
+
"""Return repr(self)."""
|
|
108
|
+
...
|
|
109
|
+
|
|
110
|
+
def __eq__(self, other):
|
|
111
|
+
"""Return self==value."""
|
|
112
|
+
...
|
|
113
|
+
|
|
71
114
|
class _App:
|
|
115
|
+
"""A Modal App is a group of functions and classes that are deployed together.
|
|
116
|
+
|
|
117
|
+
The app serves at least three purposes:
|
|
118
|
+
|
|
119
|
+
* A unit of deployment for functions and classes.
|
|
120
|
+
* Syncing of identities of (primarily) functions and classes across processes
|
|
121
|
+
(your local Python interpreter and every Modal container active in your application).
|
|
122
|
+
* Manage log collection for everything that happens inside your code.
|
|
123
|
+
|
|
124
|
+
**Registering functions with an app**
|
|
125
|
+
|
|
126
|
+
The most common way to explicitly register an Object with an app is through the
|
|
127
|
+
`@app.function()` decorator. It both registers the annotated function itself and
|
|
128
|
+
other passed objects, like schedules and secrets, with the app:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
import modal
|
|
132
|
+
|
|
133
|
+
app = modal.App()
|
|
134
|
+
|
|
135
|
+
@app.function(
|
|
136
|
+
secrets=[modal.Secret.from_name("some_secret")],
|
|
137
|
+
schedule=modal.Period(days=1),
|
|
138
|
+
)
|
|
139
|
+
def foo():
|
|
140
|
+
pass
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
In this example, the secret and schedule are registered with the app.
|
|
144
|
+
"""
|
|
145
|
+
|
|
72
146
|
_all_apps: typing.ClassVar[dict[typing.Optional[str], list[_App]]]
|
|
73
147
|
_container_app: typing.ClassVar[typing.Optional[_App]]
|
|
74
148
|
_name: typing.Optional[str]
|
|
75
149
|
_description: typing.Optional[str]
|
|
76
|
-
|
|
77
|
-
_classes: dict[str, modal.cls._Cls]
|
|
78
|
-
_image: typing.Optional[modal.image._Image]
|
|
79
|
-
_secrets: collections.abc.Sequence[modal.secret._Secret]
|
|
80
|
-
_volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume]
|
|
81
|
-
_web_endpoints: list[str]
|
|
82
|
-
_local_entrypoints: dict[str, _LocalEntrypoint]
|
|
150
|
+
_local_state_attr: typing.Optional[_LocalAppState]
|
|
83
151
|
_app_id: typing.Optional[str]
|
|
84
152
|
_running_app: typing.Optional[modal.running_app.RunningApp]
|
|
85
153
|
_client: typing.Optional[modal.client._Client]
|
|
86
|
-
|
|
154
|
+
_root_load_context: modal._load_context.LoadContext
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def _local_state(self) -> _LocalAppState:
|
|
158
|
+
"""For internal use only. Do not use this property directly."""
|
|
159
|
+
...
|
|
87
160
|
|
|
88
161
|
def __init__(
|
|
89
162
|
self,
|
|
90
163
|
name: typing.Optional[str] = None,
|
|
91
164
|
*,
|
|
165
|
+
tags: typing.Optional[dict[str, str]] = None,
|
|
92
166
|
image: typing.Optional[modal.image._Image] = None,
|
|
93
167
|
secrets: collections.abc.Sequence[modal.secret._Secret] = [],
|
|
94
168
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume] = {},
|
|
95
|
-
include_source:
|
|
96
|
-
) -> None:
|
|
169
|
+
include_source: bool = True,
|
|
170
|
+
) -> None:
|
|
171
|
+
"""Construct a new app, optionally with default image, mounts, secrets, or volumes.
|
|
172
|
+
|
|
173
|
+
```python notest
|
|
174
|
+
image = modal.Image.debian_slim().pip_install(...)
|
|
175
|
+
secret = modal.Secret.from_name("my-secret")
|
|
176
|
+
volume = modal.Volume.from_name("my-data")
|
|
177
|
+
app = modal.App(image=image, secrets=[secret], volumes={"/mnt/data": volume})
|
|
178
|
+
```
|
|
179
|
+
"""
|
|
180
|
+
...
|
|
181
|
+
|
|
97
182
|
@property
|
|
98
|
-
def name(self) -> typing.Optional[str]:
|
|
183
|
+
def name(self) -> typing.Optional[str]:
|
|
184
|
+
"""The user-provided name of the App."""
|
|
185
|
+
...
|
|
186
|
+
|
|
99
187
|
@property
|
|
100
|
-
def is_interactive(self) -> bool:
|
|
188
|
+
def is_interactive(self) -> bool:
|
|
189
|
+
"""mdmd:hidden
|
|
190
|
+
Whether the current app for the app is running in interactive mode.
|
|
191
|
+
|
|
192
|
+
Note: this method will likely be deprecated in the future.
|
|
193
|
+
"""
|
|
194
|
+
...
|
|
195
|
+
|
|
101
196
|
@property
|
|
102
|
-
def app_id(self) -> typing.Optional[str]:
|
|
197
|
+
def app_id(self) -> typing.Optional[str]:
|
|
198
|
+
"""Return the app_id of a running or stopped app."""
|
|
199
|
+
...
|
|
200
|
+
|
|
103
201
|
@property
|
|
104
|
-
def description(self) -> typing.Optional[str]:
|
|
202
|
+
def description(self) -> typing.Optional[str]:
|
|
203
|
+
"""The App's `name`, if available, or a fallback descriptive identifier."""
|
|
204
|
+
...
|
|
205
|
+
|
|
105
206
|
@staticmethod
|
|
106
207
|
async def lookup(
|
|
107
208
|
name: str,
|
|
@@ -109,13 +210,44 @@ class _App:
|
|
|
109
210
|
client: typing.Optional[modal.client._Client] = None,
|
|
110
211
|
environment_name: typing.Optional[str] = None,
|
|
111
212
|
create_if_missing: bool = False,
|
|
112
|
-
) -> _App:
|
|
113
|
-
|
|
213
|
+
) -> _App:
|
|
214
|
+
"""Look up an App with a given name, creating a new App if necessary.
|
|
215
|
+
|
|
216
|
+
Note that Apps created through this method will be in a deployed state,
|
|
217
|
+
but they will not have any associated Functions or Classes. This method
|
|
218
|
+
is mainly useful for creating an App to associate with a Sandbox:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
app = modal.App.lookup("my-app", create_if_missing=True)
|
|
222
|
+
modal.Sandbox.create("echo", "hi", app=app)
|
|
223
|
+
```
|
|
224
|
+
"""
|
|
225
|
+
...
|
|
226
|
+
|
|
227
|
+
def set_description(self, description: str):
|
|
228
|
+
"""mdmd:hidden
|
|
229
|
+
Set the description of the App before it starts running.
|
|
230
|
+
|
|
231
|
+
Note: we don't recommend using the method and may deprecate it in the future.
|
|
232
|
+
"""
|
|
233
|
+
...
|
|
234
|
+
|
|
114
235
|
def _validate_blueprint_value(self, key: str, value: typing.Any): ...
|
|
115
236
|
@property
|
|
116
|
-
def image(self) -> modal.image._Image:
|
|
237
|
+
def image(self) -> modal.image._Image:
|
|
238
|
+
"""mdmd:hidden
|
|
239
|
+
Retrieve the Image that will be used as the default for any Functions registered to the App.
|
|
240
|
+
|
|
241
|
+
Note: This property is only relevant in the build phase and won't be populated on a deployed
|
|
242
|
+
App that is retrieved via `modal.App.lookup`. It is likely to be deprecated in the future.
|
|
243
|
+
"""
|
|
244
|
+
...
|
|
245
|
+
|
|
117
246
|
@image.setter
|
|
118
|
-
def image(self, value):
|
|
247
|
+
def image(self, value):
|
|
248
|
+
"""mdmd:hidden"""
|
|
249
|
+
...
|
|
250
|
+
|
|
119
251
|
def _uncreate_all_objects(self): ...
|
|
120
252
|
def _set_local_app(
|
|
121
253
|
self, client: modal.client._Client, running_app: modal.running_app.RunningApp
|
|
@@ -127,7 +259,47 @@ class _App:
|
|
|
127
259
|
detach: bool = False,
|
|
128
260
|
interactive: bool = False,
|
|
129
261
|
environment_name: typing.Optional[str] = None,
|
|
130
|
-
) -> typing.AsyncContextManager[_App]:
|
|
262
|
+
) -> typing.AsyncContextManager[_App]:
|
|
263
|
+
"""Context manager that runs an ephemeral app on Modal.
|
|
264
|
+
|
|
265
|
+
Use this as the main entry point for your Modal application. All calls
|
|
266
|
+
to Modal Functions should be made within the scope of this context
|
|
267
|
+
manager, and they will correspond to the current App.
|
|
268
|
+
|
|
269
|
+
**Example**
|
|
270
|
+
|
|
271
|
+
```python notest
|
|
272
|
+
with app.run():
|
|
273
|
+
some_modal_function.remote()
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
To enable output printing (i.e., to see App logs), use `modal.enable_output()`:
|
|
277
|
+
|
|
278
|
+
```python notest
|
|
279
|
+
with modal.enable_output():
|
|
280
|
+
with app.run():
|
|
281
|
+
some_modal_function.remote()
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Note that you should not invoke this in global scope of a file where you have
|
|
285
|
+
Modal Functions or Classes defined, since that would run the block when the Function
|
|
286
|
+
or Cls is imported in your containers as well. If you want to run it as your entrypoint,
|
|
287
|
+
consider protecting it:
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
if __name__ == "__main__":
|
|
291
|
+
with app.run():
|
|
292
|
+
some_modal_function.remote()
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
You can then run your script with:
|
|
296
|
+
|
|
297
|
+
```shell
|
|
298
|
+
python app_module.py
|
|
299
|
+
```
|
|
300
|
+
"""
|
|
301
|
+
...
|
|
302
|
+
|
|
131
303
|
async def deploy(
|
|
132
304
|
self,
|
|
133
305
|
*,
|
|
@@ -135,30 +307,162 @@ class _App:
|
|
|
135
307
|
environment_name: typing.Optional[str] = None,
|
|
136
308
|
tag: str = "",
|
|
137
309
|
client: typing.Optional[modal.client._Client] = None,
|
|
138
|
-
) -> typing_extensions.Self:
|
|
310
|
+
) -> typing_extensions.Self:
|
|
311
|
+
"""Deploy the App so that it is available persistently.
|
|
312
|
+
|
|
313
|
+
Deployed Apps will be avaible for lookup or web-based invocations until they are stopped.
|
|
314
|
+
Unlike with `App.run`, this method will return as soon as the deployment completes.
|
|
315
|
+
|
|
316
|
+
This method is a programmatic alternative to the `modal deploy` CLI command.
|
|
317
|
+
|
|
318
|
+
Examples:
|
|
319
|
+
|
|
320
|
+
```python notest
|
|
321
|
+
app = App("my-app")
|
|
322
|
+
app.deploy()
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
To enable output printing (i.e., to see build logs), use `modal.enable_output()`:
|
|
326
|
+
|
|
327
|
+
```python notest
|
|
328
|
+
app = App("my-app")
|
|
329
|
+
with modal.enable_output():
|
|
330
|
+
app.deploy()
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Unlike with `App.run`, Function logs will not stream back to the local client after the
|
|
334
|
+
App is deployed.
|
|
335
|
+
|
|
336
|
+
Note that you should not invoke this method in global scope, as that would redeploy
|
|
337
|
+
the App every time the file is imported. If you want to write a programmatic deployment
|
|
338
|
+
script, protect this call so that it only runs when the file is executed directly:
|
|
339
|
+
|
|
340
|
+
```python notest
|
|
341
|
+
if __name__ == "__main__":
|
|
342
|
+
with modal.enable_output():
|
|
343
|
+
app.deploy()
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Then you can deploy your app with:
|
|
347
|
+
|
|
348
|
+
```shell
|
|
349
|
+
python app_module.py
|
|
350
|
+
```
|
|
351
|
+
"""
|
|
352
|
+
...
|
|
353
|
+
|
|
139
354
|
def _get_default_image(self): ...
|
|
140
355
|
def _get_watch_mounts(self): ...
|
|
141
356
|
def _add_function(self, function: modal._functions._Function, is_web_endpoint: bool): ...
|
|
142
357
|
def _add_class(self, tag: str, cls: modal.cls._Cls): ...
|
|
143
358
|
def _init_container(self, client: modal.client._Client, running_app: modal.running_app.RunningApp): ...
|
|
144
359
|
@property
|
|
145
|
-
def registered_functions(self) -> dict[str, modal._functions._Function]:
|
|
360
|
+
def registered_functions(self) -> dict[str, modal._functions._Function]:
|
|
361
|
+
"""mdmd:hidden
|
|
362
|
+
All modal.Function objects registered on the app.
|
|
363
|
+
|
|
364
|
+
Note: this property is populated only during the build phase, and it is not
|
|
365
|
+
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
366
|
+
This method is likely to be deprecated in the future in favor of a different
|
|
367
|
+
approach for retrieving the layout of a deployed App.
|
|
368
|
+
"""
|
|
369
|
+
...
|
|
370
|
+
|
|
146
371
|
@property
|
|
147
|
-
def registered_classes(self) -> dict[str, modal.cls._Cls]:
|
|
372
|
+
def registered_classes(self) -> dict[str, modal.cls._Cls]:
|
|
373
|
+
"""mdmd:hidden
|
|
374
|
+
All modal.Cls objects registered on the app.
|
|
375
|
+
|
|
376
|
+
Note: this property is populated only during the build phase, and it is not
|
|
377
|
+
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
378
|
+
This method is likely to be deprecated in the future in favor of a different
|
|
379
|
+
approach for retrieving the layout of a deployed App.
|
|
380
|
+
"""
|
|
381
|
+
...
|
|
382
|
+
|
|
148
383
|
@property
|
|
149
|
-
def registered_entrypoints(self) -> dict[str, _LocalEntrypoint]:
|
|
384
|
+
def registered_entrypoints(self) -> dict[str, _LocalEntrypoint]:
|
|
385
|
+
"""mdmd:hidden
|
|
386
|
+
All local CLI entrypoints registered on the app.
|
|
387
|
+
|
|
388
|
+
Note: this property is populated only during the build phase, and it is not
|
|
389
|
+
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
390
|
+
This method is likely to be deprecated in the future.
|
|
391
|
+
"""
|
|
392
|
+
...
|
|
393
|
+
|
|
150
394
|
@property
|
|
151
|
-
def registered_web_endpoints(self) -> list[str]:
|
|
395
|
+
def registered_web_endpoints(self) -> list[str]:
|
|
396
|
+
"""mdmd:hidden
|
|
397
|
+
Names of web endpoint (ie. webhook) functions registered on the app.
|
|
398
|
+
|
|
399
|
+
Note: this property is populated only during the build phase, and it is not
|
|
400
|
+
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
401
|
+
This method is likely to be deprecated in the future in favor of a different
|
|
402
|
+
approach for retrieving the layout of a deployed App.
|
|
403
|
+
"""
|
|
404
|
+
...
|
|
405
|
+
|
|
152
406
|
def local_entrypoint(
|
|
153
407
|
self, _warn_parentheses_missing: typing.Any = None, *, name: typing.Optional[str] = None
|
|
154
|
-
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], _LocalEntrypoint]:
|
|
408
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], _LocalEntrypoint]:
|
|
409
|
+
"""Decorate a function to be used as a CLI entrypoint for a Modal App.
|
|
410
|
+
|
|
411
|
+
These functions can be used to define code that runs locally to set up the app,
|
|
412
|
+
and act as an entrypoint to start Modal functions from. Note that regular
|
|
413
|
+
Modal functions can also be used as CLI entrypoints, but unlike `local_entrypoint`,
|
|
414
|
+
those functions are executed remotely directly.
|
|
415
|
+
|
|
416
|
+
**Example**
|
|
417
|
+
|
|
418
|
+
```python
|
|
419
|
+
@app.local_entrypoint()
|
|
420
|
+
def main():
|
|
421
|
+
some_modal_function.remote()
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
You can call the function using `modal run` directly from the CLI:
|
|
425
|
+
|
|
426
|
+
```shell
|
|
427
|
+
modal run app_module.py
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Note that an explicit [`app.run()`](https://modal.com/docs/reference/modal.App#run) is not needed, as an
|
|
431
|
+
[app](https://modal.com/docs/guide/apps) is automatically created for you.
|
|
432
|
+
|
|
433
|
+
**Multiple Entrypoints**
|
|
434
|
+
|
|
435
|
+
If you have multiple `local_entrypoint` functions, you can qualify the name of your app and function:
|
|
436
|
+
|
|
437
|
+
```shell
|
|
438
|
+
modal run app_module.py::app.some_other_function
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
**Parsing Arguments**
|
|
442
|
+
|
|
443
|
+
If your entrypoint function take arguments with primitive types, `modal run` automatically parses them as
|
|
444
|
+
CLI options.
|
|
445
|
+
For example, the following function can be called with `modal run app_module.py --foo 1 --bar "hello"`:
|
|
446
|
+
|
|
447
|
+
```python
|
|
448
|
+
@app.local_entrypoint()
|
|
449
|
+
def main(foo: int, bar: str):
|
|
450
|
+
some_modal_function.call(foo, bar)
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Currently, `str`, `int`, `float`, `bool`, and `datetime.datetime` are supported.
|
|
454
|
+
Use `modal run app_module.py --help` for more information on usage.
|
|
455
|
+
"""
|
|
456
|
+
...
|
|
457
|
+
|
|
155
458
|
def function(
|
|
156
459
|
self,
|
|
157
|
-
_warn_parentheses_missing
|
|
460
|
+
_warn_parentheses_missing=None,
|
|
158
461
|
*,
|
|
159
462
|
image: typing.Optional[modal.image._Image] = None,
|
|
160
463
|
schedule: typing.Optional[modal.schedule.Schedule] = None,
|
|
161
|
-
|
|
464
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
465
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
|
|
162
466
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig, list[typing.Union[None, str, modal.gpu._GPUConfig]]] = None,
|
|
163
467
|
serialized: bool = False,
|
|
164
468
|
network_file_systems: dict[
|
|
@@ -177,7 +481,8 @@ class _App:
|
|
|
177
481
|
scaledown_window: typing.Optional[int] = None,
|
|
178
482
|
proxy: typing.Optional[modal.proxy._Proxy] = None,
|
|
179
483
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
|
180
|
-
timeout:
|
|
484
|
+
timeout: int = 300,
|
|
485
|
+
startup_timeout: typing.Optional[int] = None,
|
|
181
486
|
name: typing.Optional[str] = None,
|
|
182
487
|
is_generator: typing.Optional[bool] = None,
|
|
183
488
|
cloud: typing.Optional[str] = None,
|
|
@@ -192,24 +497,27 @@ class _App:
|
|
|
192
497
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
193
498
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
194
499
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
195
|
-
|
|
500
|
+
_experimental_restrict_output: bool = False,
|
|
196
501
|
keep_warm: typing.Optional[int] = None,
|
|
197
502
|
concurrency_limit: typing.Optional[int] = None,
|
|
198
503
|
container_idle_timeout: typing.Optional[int] = None,
|
|
199
504
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
200
505
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
201
|
-
|
|
202
|
-
|
|
506
|
+
) -> _FunctionDecoratorType:
|
|
507
|
+
"""Decorator to register a new Modal Function with this App."""
|
|
508
|
+
...
|
|
509
|
+
|
|
203
510
|
@typing_extensions.dataclass_transform(
|
|
204
511
|
field_specifiers=(modal.cls.parameter,),
|
|
205
512
|
kw_only_default=True,
|
|
206
513
|
)
|
|
207
514
|
def cls(
|
|
208
515
|
self,
|
|
209
|
-
_warn_parentheses_missing
|
|
516
|
+
_warn_parentheses_missing=None,
|
|
210
517
|
*,
|
|
211
518
|
image: typing.Optional[modal.image._Image] = None,
|
|
212
|
-
|
|
519
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
520
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
|
|
213
521
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig, list[typing.Union[None, str, modal.gpu._GPUConfig]]] = None,
|
|
214
522
|
serialized: bool = False,
|
|
215
523
|
network_file_systems: dict[
|
|
@@ -228,71 +536,190 @@ class _App:
|
|
|
228
536
|
scaledown_window: typing.Optional[int] = None,
|
|
229
537
|
proxy: typing.Optional[modal.proxy._Proxy] = None,
|
|
230
538
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
|
231
|
-
timeout:
|
|
539
|
+
timeout: int = 300,
|
|
540
|
+
startup_timeout: typing.Optional[int] = None,
|
|
232
541
|
cloud: typing.Optional[str] = None,
|
|
233
542
|
region: typing.Union[str, collections.abc.Sequence[str], None] = None,
|
|
234
543
|
enable_memory_snapshot: bool = False,
|
|
235
544
|
block_network: bool = False,
|
|
236
545
|
restrict_modal_access: bool = False,
|
|
237
546
|
max_inputs: typing.Optional[int] = None,
|
|
547
|
+
i6pn: typing.Optional[bool] = None,
|
|
238
548
|
include_source: typing.Optional[bool] = None,
|
|
239
549
|
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
240
550
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
241
551
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
242
552
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
243
|
-
|
|
553
|
+
_experimental_restrict_output: bool = False,
|
|
244
554
|
keep_warm: typing.Optional[int] = None,
|
|
245
555
|
concurrency_limit: typing.Optional[int] = None,
|
|
246
556
|
container_idle_timeout: typing.Optional[int] = None,
|
|
247
557
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
248
558
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
559
|
+
) -> collections.abc.Callable[[typing.Union[CLS_T, modal._partial_function._PartialFunction]], CLS_T]:
|
|
560
|
+
"""Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App."""
|
|
561
|
+
...
|
|
562
|
+
|
|
563
|
+
def include(self, /, other_app: _App, inherit_tags: bool = True) -> typing_extensions.Self:
|
|
564
|
+
"""Include another App's objects in this one.
|
|
565
|
+
|
|
566
|
+
Useful for splitting up Modal Apps across different self-contained files.
|
|
567
|
+
|
|
568
|
+
```python
|
|
569
|
+
app_a = modal.App("a")
|
|
570
|
+
@app.function()
|
|
571
|
+
def foo():
|
|
572
|
+
...
|
|
573
|
+
|
|
574
|
+
app_b = modal.App("b")
|
|
575
|
+
@app.function()
|
|
576
|
+
def bar():
|
|
577
|
+
...
|
|
578
|
+
|
|
579
|
+
app_a.include(app_b)
|
|
580
|
+
|
|
581
|
+
@app_a.local_entrypoint()
|
|
582
|
+
def main():
|
|
583
|
+
# use function declared on the included app
|
|
584
|
+
bar.remote()
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
When `inherit_tags=True` any tags set on the other App will be inherited by this App
|
|
588
|
+
(with this App's tags taking precedence in the case of conflicts).
|
|
589
|
+
"""
|
|
590
|
+
...
|
|
591
|
+
|
|
592
|
+
async def set_tags(
|
|
593
|
+
self, tags: collections.abc.Mapping[str, str], *, client: typing.Optional[modal.client._Client] = None
|
|
594
|
+
) -> None:
|
|
595
|
+
"""Attach key-value metadata to the App.
|
|
596
|
+
|
|
597
|
+
Tag metadata can be used to add organization-specific context to the App and can be
|
|
598
|
+
included in billing reports and other informational APIs. Tags can also be set in
|
|
599
|
+
the App constructor.
|
|
600
|
+
|
|
601
|
+
Any tags set on the App before calling this method will be removed if they are not
|
|
602
|
+
included in the argument (i.e., this method does not have `.update()` semantics).
|
|
603
|
+
"""
|
|
604
|
+
...
|
|
605
|
+
|
|
606
|
+
async def get_tags(self, *, client: typing.Optional[modal.client._Client] = None) -> dict[str, str]:
|
|
607
|
+
"""Get the tags that are currently attached to the App."""
|
|
608
|
+
...
|
|
609
|
+
|
|
610
|
+
def _logs(self, client: typing.Optional[modal.client._Client] = None) -> collections.abc.AsyncGenerator[str, None]:
|
|
611
|
+
"""Stream logs from the app.
|
|
612
|
+
|
|
613
|
+
This method is considered private and its interface may change - use at your own risk!
|
|
614
|
+
"""
|
|
615
|
+
...
|
|
616
|
+
|
|
255
617
|
@classmethod
|
|
256
|
-
def _get_container_app(cls) -> typing.Optional[_App]:
|
|
618
|
+
def _get_container_app(cls) -> typing.Optional[_App]:
|
|
619
|
+
"""Returns the `App` running inside a container.
|
|
620
|
+
|
|
621
|
+
This will return `None` outside of a Modal container.
|
|
622
|
+
"""
|
|
623
|
+
...
|
|
624
|
+
|
|
257
625
|
@classmethod
|
|
258
|
-
def _reset_container_app(cls):
|
|
626
|
+
def _reset_container_app(cls):
|
|
627
|
+
"""Only used for tests."""
|
|
628
|
+
...
|
|
259
629
|
|
|
260
630
|
SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
|
|
261
631
|
|
|
262
632
|
class App:
|
|
633
|
+
"""A Modal App is a group of functions and classes that are deployed together.
|
|
634
|
+
|
|
635
|
+
The app serves at least three purposes:
|
|
636
|
+
|
|
637
|
+
* A unit of deployment for functions and classes.
|
|
638
|
+
* Syncing of identities of (primarily) functions and classes across processes
|
|
639
|
+
(your local Python interpreter and every Modal container active in your application).
|
|
640
|
+
* Manage log collection for everything that happens inside your code.
|
|
641
|
+
|
|
642
|
+
**Registering functions with an app**
|
|
643
|
+
|
|
644
|
+
The most common way to explicitly register an Object with an app is through the
|
|
645
|
+
`@app.function()` decorator. It both registers the annotated function itself and
|
|
646
|
+
other passed objects, like schedules and secrets, with the app:
|
|
647
|
+
|
|
648
|
+
```python
|
|
649
|
+
import modal
|
|
650
|
+
|
|
651
|
+
app = modal.App()
|
|
652
|
+
|
|
653
|
+
@app.function(
|
|
654
|
+
secrets=[modal.Secret.from_name("some_secret")],
|
|
655
|
+
schedule=modal.Period(days=1),
|
|
656
|
+
)
|
|
657
|
+
def foo():
|
|
658
|
+
pass
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
In this example, the secret and schedule are registered with the app.
|
|
662
|
+
"""
|
|
663
|
+
|
|
263
664
|
_all_apps: typing.ClassVar[dict[typing.Optional[str], list[App]]]
|
|
264
665
|
_container_app: typing.ClassVar[typing.Optional[App]]
|
|
265
666
|
_name: typing.Optional[str]
|
|
266
667
|
_description: typing.Optional[str]
|
|
267
|
-
|
|
268
|
-
_classes: dict[str, modal.cls.Cls]
|
|
269
|
-
_image: typing.Optional[modal.image.Image]
|
|
270
|
-
_secrets: collections.abc.Sequence[modal.secret.Secret]
|
|
271
|
-
_volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume.Volume]
|
|
272
|
-
_web_endpoints: list[str]
|
|
273
|
-
_local_entrypoints: dict[str, LocalEntrypoint]
|
|
668
|
+
_local_state_attr: typing.Optional[_LocalAppState]
|
|
274
669
|
_app_id: typing.Optional[str]
|
|
275
670
|
_running_app: typing.Optional[modal.running_app.RunningApp]
|
|
276
671
|
_client: typing.Optional[modal.client.Client]
|
|
277
|
-
|
|
672
|
+
_root_load_context: modal._load_context.LoadContext
|
|
278
673
|
|
|
279
674
|
def __init__(
|
|
280
675
|
self,
|
|
281
676
|
name: typing.Optional[str] = None,
|
|
282
677
|
*,
|
|
678
|
+
tags: typing.Optional[dict[str, str]] = None,
|
|
283
679
|
image: typing.Optional[modal.image.Image] = None,
|
|
284
680
|
secrets: collections.abc.Sequence[modal.secret.Secret] = [],
|
|
285
681
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume.Volume] = {},
|
|
286
|
-
include_source:
|
|
287
|
-
) -> None:
|
|
682
|
+
include_source: bool = True,
|
|
683
|
+
) -> None:
|
|
684
|
+
"""Construct a new app, optionally with default image, mounts, secrets, or volumes.
|
|
685
|
+
|
|
686
|
+
```python notest
|
|
687
|
+
image = modal.Image.debian_slim().pip_install(...)
|
|
688
|
+
secret = modal.Secret.from_name("my-secret")
|
|
689
|
+
volume = modal.Volume.from_name("my-data")
|
|
690
|
+
app = modal.App(image=image, secrets=[secret], volumes={"/mnt/data": volume})
|
|
691
|
+
```
|
|
692
|
+
"""
|
|
693
|
+
...
|
|
694
|
+
|
|
288
695
|
@property
|
|
289
|
-
def
|
|
696
|
+
def _local_state(self) -> _LocalAppState:
|
|
697
|
+
"""For internal use only. Do not use this property directly."""
|
|
698
|
+
...
|
|
699
|
+
|
|
290
700
|
@property
|
|
291
|
-
def
|
|
701
|
+
def name(self) -> typing.Optional[str]:
|
|
702
|
+
"""The user-provided name of the App."""
|
|
703
|
+
...
|
|
704
|
+
|
|
705
|
+
@property
|
|
706
|
+
def is_interactive(self) -> bool:
|
|
707
|
+
"""mdmd:hidden
|
|
708
|
+
Whether the current app for the app is running in interactive mode.
|
|
709
|
+
|
|
710
|
+
Note: this method will likely be deprecated in the future.
|
|
711
|
+
"""
|
|
712
|
+
...
|
|
713
|
+
|
|
292
714
|
@property
|
|
293
|
-
def app_id(self) -> typing.Optional[str]:
|
|
715
|
+
def app_id(self) -> typing.Optional[str]:
|
|
716
|
+
"""Return the app_id of a running or stopped app."""
|
|
717
|
+
...
|
|
718
|
+
|
|
294
719
|
@property
|
|
295
|
-
def description(self) -> typing.Optional[str]:
|
|
720
|
+
def description(self) -> typing.Optional[str]:
|
|
721
|
+
"""The App's `name`, if available, or a fallback descriptive identifier."""
|
|
722
|
+
...
|
|
296
723
|
|
|
297
724
|
class __lookup_spec(typing_extensions.Protocol):
|
|
298
725
|
def __call__(
|
|
@@ -303,7 +730,20 @@ class App:
|
|
|
303
730
|
client: typing.Optional[modal.client.Client] = None,
|
|
304
731
|
environment_name: typing.Optional[str] = None,
|
|
305
732
|
create_if_missing: bool = False,
|
|
306
|
-
) -> App:
|
|
733
|
+
) -> App:
|
|
734
|
+
"""Look up an App with a given name, creating a new App if necessary.
|
|
735
|
+
|
|
736
|
+
Note that Apps created through this method will be in a deployed state,
|
|
737
|
+
but they will not have any associated Functions or Classes. This method
|
|
738
|
+
is mainly useful for creating an App to associate with a Sandbox:
|
|
739
|
+
|
|
740
|
+
```python
|
|
741
|
+
app = modal.App.lookup("my-app", create_if_missing=True)
|
|
742
|
+
modal.Sandbox.create("echo", "hi", app=app)
|
|
743
|
+
```
|
|
744
|
+
"""
|
|
745
|
+
...
|
|
746
|
+
|
|
307
747
|
async def aio(
|
|
308
748
|
self,
|
|
309
749
|
/,
|
|
@@ -312,16 +752,46 @@ class App:
|
|
|
312
752
|
client: typing.Optional[modal.client.Client] = None,
|
|
313
753
|
environment_name: typing.Optional[str] = None,
|
|
314
754
|
create_if_missing: bool = False,
|
|
315
|
-
) -> App:
|
|
755
|
+
) -> App:
|
|
756
|
+
"""Look up an App with a given name, creating a new App if necessary.
|
|
757
|
+
|
|
758
|
+
Note that Apps created through this method will be in a deployed state,
|
|
759
|
+
but they will not have any associated Functions or Classes. This method
|
|
760
|
+
is mainly useful for creating an App to associate with a Sandbox:
|
|
761
|
+
|
|
762
|
+
```python
|
|
763
|
+
app = modal.App.lookup("my-app", create_if_missing=True)
|
|
764
|
+
modal.Sandbox.create("echo", "hi", app=app)
|
|
765
|
+
```
|
|
766
|
+
"""
|
|
767
|
+
...
|
|
316
768
|
|
|
317
769
|
lookup: __lookup_spec
|
|
318
770
|
|
|
319
|
-
def set_description(self, description: str):
|
|
771
|
+
def set_description(self, description: str):
|
|
772
|
+
"""mdmd:hidden
|
|
773
|
+
Set the description of the App before it starts running.
|
|
774
|
+
|
|
775
|
+
Note: we don't recommend using the method and may deprecate it in the future.
|
|
776
|
+
"""
|
|
777
|
+
...
|
|
778
|
+
|
|
320
779
|
def _validate_blueprint_value(self, key: str, value: typing.Any): ...
|
|
321
780
|
@property
|
|
322
|
-
def image(self) -> modal.image.Image:
|
|
781
|
+
def image(self) -> modal.image.Image:
|
|
782
|
+
"""mdmd:hidden
|
|
783
|
+
Retrieve the Image that will be used as the default for any Functions registered to the App.
|
|
784
|
+
|
|
785
|
+
Note: This property is only relevant in the build phase and won't be populated on a deployed
|
|
786
|
+
App that is retrieved via `modal.App.lookup`. It is likely to be deprecated in the future.
|
|
787
|
+
"""
|
|
788
|
+
...
|
|
789
|
+
|
|
323
790
|
@image.setter
|
|
324
|
-
def image(self, value):
|
|
791
|
+
def image(self, value):
|
|
792
|
+
"""mdmd:hidden"""
|
|
793
|
+
...
|
|
794
|
+
|
|
325
795
|
def _uncreate_all_objects(self): ...
|
|
326
796
|
|
|
327
797
|
class ___set_local_app_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
@@ -343,7 +813,47 @@ class App:
|
|
|
343
813
|
detach: bool = False,
|
|
344
814
|
interactive: bool = False,
|
|
345
815
|
environment_name: typing.Optional[str] = None,
|
|
346
|
-
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[App]:
|
|
816
|
+
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[App]:
|
|
817
|
+
"""Context manager that runs an ephemeral app on Modal.
|
|
818
|
+
|
|
819
|
+
Use this as the main entry point for your Modal application. All calls
|
|
820
|
+
to Modal Functions should be made within the scope of this context
|
|
821
|
+
manager, and they will correspond to the current App.
|
|
822
|
+
|
|
823
|
+
**Example**
|
|
824
|
+
|
|
825
|
+
```python notest
|
|
826
|
+
with app.run():
|
|
827
|
+
some_modal_function.remote()
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
To enable output printing (i.e., to see App logs), use `modal.enable_output()`:
|
|
831
|
+
|
|
832
|
+
```python notest
|
|
833
|
+
with modal.enable_output():
|
|
834
|
+
with app.run():
|
|
835
|
+
some_modal_function.remote()
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
Note that you should not invoke this in global scope of a file where you have
|
|
839
|
+
Modal Functions or Classes defined, since that would run the block when the Function
|
|
840
|
+
or Cls is imported in your containers as well. If you want to run it as your entrypoint,
|
|
841
|
+
consider protecting it:
|
|
842
|
+
|
|
843
|
+
```python
|
|
844
|
+
if __name__ == "__main__":
|
|
845
|
+
with app.run():
|
|
846
|
+
some_modal_function.remote()
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
You can then run your script with:
|
|
850
|
+
|
|
851
|
+
```shell
|
|
852
|
+
python app_module.py
|
|
853
|
+
```
|
|
854
|
+
"""
|
|
855
|
+
...
|
|
856
|
+
|
|
347
857
|
def aio(
|
|
348
858
|
self,
|
|
349
859
|
/,
|
|
@@ -352,7 +862,46 @@ class App:
|
|
|
352
862
|
detach: bool = False,
|
|
353
863
|
interactive: bool = False,
|
|
354
864
|
environment_name: typing.Optional[str] = None,
|
|
355
|
-
) -> typing.AsyncContextManager[App]:
|
|
865
|
+
) -> typing.AsyncContextManager[App]:
|
|
866
|
+
"""Context manager that runs an ephemeral app on Modal.
|
|
867
|
+
|
|
868
|
+
Use this as the main entry point for your Modal application. All calls
|
|
869
|
+
to Modal Functions should be made within the scope of this context
|
|
870
|
+
manager, and they will correspond to the current App.
|
|
871
|
+
|
|
872
|
+
**Example**
|
|
873
|
+
|
|
874
|
+
```python notest
|
|
875
|
+
with app.run():
|
|
876
|
+
some_modal_function.remote()
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
To enable output printing (i.e., to see App logs), use `modal.enable_output()`:
|
|
880
|
+
|
|
881
|
+
```python notest
|
|
882
|
+
with modal.enable_output():
|
|
883
|
+
with app.run():
|
|
884
|
+
some_modal_function.remote()
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
Note that you should not invoke this in global scope of a file where you have
|
|
888
|
+
Modal Functions or Classes defined, since that would run the block when the Function
|
|
889
|
+
or Cls is imported in your containers as well. If you want to run it as your entrypoint,
|
|
890
|
+
consider protecting it:
|
|
891
|
+
|
|
892
|
+
```python
|
|
893
|
+
if __name__ == "__main__":
|
|
894
|
+
with app.run():
|
|
895
|
+
some_modal_function.remote()
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
You can then run your script with:
|
|
899
|
+
|
|
900
|
+
```shell
|
|
901
|
+
python app_module.py
|
|
902
|
+
```
|
|
903
|
+
"""
|
|
904
|
+
...
|
|
356
905
|
|
|
357
906
|
run: __run_spec[typing_extensions.Self]
|
|
358
907
|
|
|
@@ -365,7 +914,50 @@ class App:
|
|
|
365
914
|
environment_name: typing.Optional[str] = None,
|
|
366
915
|
tag: str = "",
|
|
367
916
|
client: typing.Optional[modal.client.Client] = None,
|
|
368
|
-
) -> SUPERSELF:
|
|
917
|
+
) -> SUPERSELF:
|
|
918
|
+
"""Deploy the App so that it is available persistently.
|
|
919
|
+
|
|
920
|
+
Deployed Apps will be avaible for lookup or web-based invocations until they are stopped.
|
|
921
|
+
Unlike with `App.run`, this method will return as soon as the deployment completes.
|
|
922
|
+
|
|
923
|
+
This method is a programmatic alternative to the `modal deploy` CLI command.
|
|
924
|
+
|
|
925
|
+
Examples:
|
|
926
|
+
|
|
927
|
+
```python notest
|
|
928
|
+
app = App("my-app")
|
|
929
|
+
app.deploy()
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
To enable output printing (i.e., to see build logs), use `modal.enable_output()`:
|
|
933
|
+
|
|
934
|
+
```python notest
|
|
935
|
+
app = App("my-app")
|
|
936
|
+
with modal.enable_output():
|
|
937
|
+
app.deploy()
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
Unlike with `App.run`, Function logs will not stream back to the local client after the
|
|
941
|
+
App is deployed.
|
|
942
|
+
|
|
943
|
+
Note that you should not invoke this method in global scope, as that would redeploy
|
|
944
|
+
the App every time the file is imported. If you want to write a programmatic deployment
|
|
945
|
+
script, protect this call so that it only runs when the file is executed directly:
|
|
946
|
+
|
|
947
|
+
```python notest
|
|
948
|
+
if __name__ == "__main__":
|
|
949
|
+
with modal.enable_output():
|
|
950
|
+
app.deploy()
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
Then you can deploy your app with:
|
|
954
|
+
|
|
955
|
+
```shell
|
|
956
|
+
python app_module.py
|
|
957
|
+
```
|
|
958
|
+
"""
|
|
959
|
+
...
|
|
960
|
+
|
|
369
961
|
async def aio(
|
|
370
962
|
self,
|
|
371
963
|
/,
|
|
@@ -374,7 +966,49 @@ class App:
|
|
|
374
966
|
environment_name: typing.Optional[str] = None,
|
|
375
967
|
tag: str = "",
|
|
376
968
|
client: typing.Optional[modal.client.Client] = None,
|
|
377
|
-
) -> SUPERSELF:
|
|
969
|
+
) -> SUPERSELF:
|
|
970
|
+
"""Deploy the App so that it is available persistently.
|
|
971
|
+
|
|
972
|
+
Deployed Apps will be avaible for lookup or web-based invocations until they are stopped.
|
|
973
|
+
Unlike with `App.run`, this method will return as soon as the deployment completes.
|
|
974
|
+
|
|
975
|
+
This method is a programmatic alternative to the `modal deploy` CLI command.
|
|
976
|
+
|
|
977
|
+
Examples:
|
|
978
|
+
|
|
979
|
+
```python notest
|
|
980
|
+
app = App("my-app")
|
|
981
|
+
app.deploy()
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
To enable output printing (i.e., to see build logs), use `modal.enable_output()`:
|
|
985
|
+
|
|
986
|
+
```python notest
|
|
987
|
+
app = App("my-app")
|
|
988
|
+
with modal.enable_output():
|
|
989
|
+
app.deploy()
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
Unlike with `App.run`, Function logs will not stream back to the local client after the
|
|
993
|
+
App is deployed.
|
|
994
|
+
|
|
995
|
+
Note that you should not invoke this method in global scope, as that would redeploy
|
|
996
|
+
the App every time the file is imported. If you want to write a programmatic deployment
|
|
997
|
+
script, protect this call so that it only runs when the file is executed directly:
|
|
998
|
+
|
|
999
|
+
```python notest
|
|
1000
|
+
if __name__ == "__main__":
|
|
1001
|
+
with modal.enable_output():
|
|
1002
|
+
app.deploy()
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
Then you can deploy your app with:
|
|
1006
|
+
|
|
1007
|
+
```shell
|
|
1008
|
+
python app_module.py
|
|
1009
|
+
```
|
|
1010
|
+
"""
|
|
1011
|
+
...
|
|
378
1012
|
|
|
379
1013
|
deploy: __deploy_spec[typing_extensions.Self]
|
|
380
1014
|
|
|
@@ -384,23 +1018,112 @@ class App:
|
|
|
384
1018
|
def _add_class(self, tag: str, cls: modal.cls.Cls): ...
|
|
385
1019
|
def _init_container(self, client: modal.client.Client, running_app: modal.running_app.RunningApp): ...
|
|
386
1020
|
@property
|
|
387
|
-
def registered_functions(self) -> dict[str, modal.functions.Function]:
|
|
1021
|
+
def registered_functions(self) -> dict[str, modal.functions.Function]:
|
|
1022
|
+
"""mdmd:hidden
|
|
1023
|
+
All modal.Function objects registered on the app.
|
|
1024
|
+
|
|
1025
|
+
Note: this property is populated only during the build phase, and it is not
|
|
1026
|
+
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
1027
|
+
This method is likely to be deprecated in the future in favor of a different
|
|
1028
|
+
approach for retrieving the layout of a deployed App.
|
|
1029
|
+
"""
|
|
1030
|
+
...
|
|
1031
|
+
|
|
388
1032
|
@property
|
|
389
|
-
def registered_classes(self) -> dict[str, modal.cls.Cls]:
|
|
1033
|
+
def registered_classes(self) -> dict[str, modal.cls.Cls]:
|
|
1034
|
+
"""mdmd:hidden
|
|
1035
|
+
All modal.Cls objects registered on the app.
|
|
1036
|
+
|
|
1037
|
+
Note: this property is populated only during the build phase, and it is not
|
|
1038
|
+
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
1039
|
+
This method is likely to be deprecated in the future in favor of a different
|
|
1040
|
+
approach for retrieving the layout of a deployed App.
|
|
1041
|
+
"""
|
|
1042
|
+
...
|
|
1043
|
+
|
|
390
1044
|
@property
|
|
391
|
-
def registered_entrypoints(self) -> dict[str, LocalEntrypoint]:
|
|
1045
|
+
def registered_entrypoints(self) -> dict[str, LocalEntrypoint]:
|
|
1046
|
+
"""mdmd:hidden
|
|
1047
|
+
All local CLI entrypoints registered on the app.
|
|
1048
|
+
|
|
1049
|
+
Note: this property is populated only during the build phase, and it is not
|
|
1050
|
+
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
1051
|
+
This method is likely to be deprecated in the future.
|
|
1052
|
+
"""
|
|
1053
|
+
...
|
|
1054
|
+
|
|
392
1055
|
@property
|
|
393
|
-
def registered_web_endpoints(self) -> list[str]:
|
|
1056
|
+
def registered_web_endpoints(self) -> list[str]:
|
|
1057
|
+
"""mdmd:hidden
|
|
1058
|
+
Names of web endpoint (ie. webhook) functions registered on the app.
|
|
1059
|
+
|
|
1060
|
+
Note: this property is populated only during the build phase, and it is not
|
|
1061
|
+
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
1062
|
+
This method is likely to be deprecated in the future in favor of a different
|
|
1063
|
+
approach for retrieving the layout of a deployed App.
|
|
1064
|
+
"""
|
|
1065
|
+
...
|
|
1066
|
+
|
|
394
1067
|
def local_entrypoint(
|
|
395
1068
|
self, _warn_parentheses_missing: typing.Any = None, *, name: typing.Optional[str] = None
|
|
396
|
-
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], LocalEntrypoint]:
|
|
1069
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], LocalEntrypoint]:
|
|
1070
|
+
"""Decorate a function to be used as a CLI entrypoint for a Modal App.
|
|
1071
|
+
|
|
1072
|
+
These functions can be used to define code that runs locally to set up the app,
|
|
1073
|
+
and act as an entrypoint to start Modal functions from. Note that regular
|
|
1074
|
+
Modal functions can also be used as CLI entrypoints, but unlike `local_entrypoint`,
|
|
1075
|
+
those functions are executed remotely directly.
|
|
1076
|
+
|
|
1077
|
+
**Example**
|
|
1078
|
+
|
|
1079
|
+
```python
|
|
1080
|
+
@app.local_entrypoint()
|
|
1081
|
+
def main():
|
|
1082
|
+
some_modal_function.remote()
|
|
1083
|
+
```
|
|
1084
|
+
|
|
1085
|
+
You can call the function using `modal run` directly from the CLI:
|
|
1086
|
+
|
|
1087
|
+
```shell
|
|
1088
|
+
modal run app_module.py
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
Note that an explicit [`app.run()`](https://modal.com/docs/reference/modal.App#run) is not needed, as an
|
|
1092
|
+
[app](https://modal.com/docs/guide/apps) is automatically created for you.
|
|
1093
|
+
|
|
1094
|
+
**Multiple Entrypoints**
|
|
1095
|
+
|
|
1096
|
+
If you have multiple `local_entrypoint` functions, you can qualify the name of your app and function:
|
|
1097
|
+
|
|
1098
|
+
```shell
|
|
1099
|
+
modal run app_module.py::app.some_other_function
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
**Parsing Arguments**
|
|
1103
|
+
|
|
1104
|
+
If your entrypoint function take arguments with primitive types, `modal run` automatically parses them as
|
|
1105
|
+
CLI options.
|
|
1106
|
+
For example, the following function can be called with `modal run app_module.py --foo 1 --bar "hello"`:
|
|
1107
|
+
|
|
1108
|
+
```python
|
|
1109
|
+
@app.local_entrypoint()
|
|
1110
|
+
def main(foo: int, bar: str):
|
|
1111
|
+
some_modal_function.call(foo, bar)
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
Currently, `str`, `int`, `float`, `bool`, and `datetime.datetime` are supported.
|
|
1115
|
+
Use `modal run app_module.py --help` for more information on usage.
|
|
1116
|
+
"""
|
|
1117
|
+
...
|
|
1118
|
+
|
|
397
1119
|
def function(
|
|
398
1120
|
self,
|
|
399
|
-
_warn_parentheses_missing
|
|
1121
|
+
_warn_parentheses_missing=None,
|
|
400
1122
|
*,
|
|
401
1123
|
image: typing.Optional[modal.image.Image] = None,
|
|
402
1124
|
schedule: typing.Optional[modal.schedule.Schedule] = None,
|
|
403
|
-
|
|
1125
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
1126
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
|
|
404
1127
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig, list[typing.Union[None, str, modal.gpu._GPUConfig]]] = None,
|
|
405
1128
|
serialized: bool = False,
|
|
406
1129
|
network_file_systems: dict[
|
|
@@ -419,7 +1142,8 @@ class App:
|
|
|
419
1142
|
scaledown_window: typing.Optional[int] = None,
|
|
420
1143
|
proxy: typing.Optional[modal.proxy.Proxy] = None,
|
|
421
1144
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
|
422
|
-
timeout:
|
|
1145
|
+
timeout: int = 300,
|
|
1146
|
+
startup_timeout: typing.Optional[int] = None,
|
|
423
1147
|
name: typing.Optional[str] = None,
|
|
424
1148
|
is_generator: typing.Optional[bool] = None,
|
|
425
1149
|
cloud: typing.Optional[str] = None,
|
|
@@ -434,24 +1158,27 @@ class App:
|
|
|
434
1158
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
435
1159
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
436
1160
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
437
|
-
|
|
1161
|
+
_experimental_restrict_output: bool = False,
|
|
438
1162
|
keep_warm: typing.Optional[int] = None,
|
|
439
1163
|
concurrency_limit: typing.Optional[int] = None,
|
|
440
1164
|
container_idle_timeout: typing.Optional[int] = None,
|
|
441
1165
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
442
1166
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
443
|
-
|
|
444
|
-
|
|
1167
|
+
) -> _FunctionDecoratorType:
|
|
1168
|
+
"""Decorator to register a new Modal Function with this App."""
|
|
1169
|
+
...
|
|
1170
|
+
|
|
445
1171
|
@typing_extensions.dataclass_transform(
|
|
446
1172
|
field_specifiers=(modal.cls.parameter,),
|
|
447
1173
|
kw_only_default=True,
|
|
448
1174
|
)
|
|
449
1175
|
def cls(
|
|
450
1176
|
self,
|
|
451
|
-
_warn_parentheses_missing
|
|
1177
|
+
_warn_parentheses_missing=None,
|
|
452
1178
|
*,
|
|
453
1179
|
image: typing.Optional[modal.image.Image] = None,
|
|
454
|
-
|
|
1180
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
1181
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
|
|
455
1182
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig, list[typing.Union[None, str, modal.gpu._GPUConfig]]] = None,
|
|
456
1183
|
serialized: bool = False,
|
|
457
1184
|
network_file_systems: dict[
|
|
@@ -470,41 +1197,131 @@ class App:
|
|
|
470
1197
|
scaledown_window: typing.Optional[int] = None,
|
|
471
1198
|
proxy: typing.Optional[modal.proxy.Proxy] = None,
|
|
472
1199
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
|
473
|
-
timeout:
|
|
1200
|
+
timeout: int = 300,
|
|
1201
|
+
startup_timeout: typing.Optional[int] = None,
|
|
474
1202
|
cloud: typing.Optional[str] = None,
|
|
475
1203
|
region: typing.Union[str, collections.abc.Sequence[str], None] = None,
|
|
476
1204
|
enable_memory_snapshot: bool = False,
|
|
477
1205
|
block_network: bool = False,
|
|
478
1206
|
restrict_modal_access: bool = False,
|
|
479
1207
|
max_inputs: typing.Optional[int] = None,
|
|
1208
|
+
i6pn: typing.Optional[bool] = None,
|
|
480
1209
|
include_source: typing.Optional[bool] = None,
|
|
481
1210
|
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
482
1211
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
483
1212
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
484
1213
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
485
|
-
|
|
1214
|
+
_experimental_restrict_output: bool = False,
|
|
486
1215
|
keep_warm: typing.Optional[int] = None,
|
|
487
1216
|
concurrency_limit: typing.Optional[int] = None,
|
|
488
1217
|
container_idle_timeout: typing.Optional[int] = None,
|
|
489
1218
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
490
1219
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
1220
|
+
) -> collections.abc.Callable[[typing.Union[CLS_T, modal.partial_function.PartialFunction]], CLS_T]:
|
|
1221
|
+
"""Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App."""
|
|
1222
|
+
...
|
|
494
1223
|
|
|
495
|
-
|
|
1224
|
+
def include(self, /, other_app: App, inherit_tags: bool = True) -> typing_extensions.Self:
|
|
1225
|
+
"""Include another App's objects in this one.
|
|
1226
|
+
|
|
1227
|
+
Useful for splitting up Modal Apps across different self-contained files.
|
|
1228
|
+
|
|
1229
|
+
```python
|
|
1230
|
+
app_a = modal.App("a")
|
|
1231
|
+
@app.function()
|
|
1232
|
+
def foo():
|
|
1233
|
+
...
|
|
1234
|
+
|
|
1235
|
+
app_b = modal.App("b")
|
|
1236
|
+
@app.function()
|
|
1237
|
+
def bar():
|
|
1238
|
+
...
|
|
1239
|
+
|
|
1240
|
+
app_a.include(app_b)
|
|
1241
|
+
|
|
1242
|
+
@app_a.local_entrypoint()
|
|
1243
|
+
def main():
|
|
1244
|
+
# use function declared on the included app
|
|
1245
|
+
bar.remote()
|
|
1246
|
+
```
|
|
1247
|
+
|
|
1248
|
+
When `inherit_tags=True` any tags set on the other App will be inherited by this App
|
|
1249
|
+
(with this App's tags taking precedence in the case of conflicts).
|
|
1250
|
+
"""
|
|
1251
|
+
...
|
|
1252
|
+
|
|
1253
|
+
class __set_tags_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
496
1254
|
def __call__(
|
|
497
|
-
self, /, client: typing.Optional[modal.client.Client] = None
|
|
498
|
-
) ->
|
|
1255
|
+
self, /, tags: collections.abc.Mapping[str, str], *, client: typing.Optional[modal.client.Client] = None
|
|
1256
|
+
) -> None:
|
|
1257
|
+
"""Attach key-value metadata to the App.
|
|
1258
|
+
|
|
1259
|
+
Tag metadata can be used to add organization-specific context to the App and can be
|
|
1260
|
+
included in billing reports and other informational APIs. Tags can also be set in
|
|
1261
|
+
the App constructor.
|
|
1262
|
+
|
|
1263
|
+
Any tags set on the App before calling this method will be removed if they are not
|
|
1264
|
+
included in the argument (i.e., this method does not have `.update()` semantics).
|
|
1265
|
+
"""
|
|
1266
|
+
...
|
|
1267
|
+
|
|
1268
|
+
async def aio(
|
|
1269
|
+
self, /, tags: collections.abc.Mapping[str, str], *, client: typing.Optional[modal.client.Client] = None
|
|
1270
|
+
) -> None:
|
|
1271
|
+
"""Attach key-value metadata to the App.
|
|
1272
|
+
|
|
1273
|
+
Tag metadata can be used to add organization-specific context to the App and can be
|
|
1274
|
+
included in billing reports and other informational APIs. Tags can also be set in
|
|
1275
|
+
the App constructor.
|
|
1276
|
+
|
|
1277
|
+
Any tags set on the App before calling this method will be removed if they are not
|
|
1278
|
+
included in the argument (i.e., this method does not have `.update()` semantics).
|
|
1279
|
+
"""
|
|
1280
|
+
...
|
|
1281
|
+
|
|
1282
|
+
set_tags: __set_tags_spec[typing_extensions.Self]
|
|
1283
|
+
|
|
1284
|
+
class __get_tags_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
1285
|
+
def __call__(self, /, *, client: typing.Optional[modal.client.Client] = None) -> dict[str, str]:
|
|
1286
|
+
"""Get the tags that are currently attached to the App."""
|
|
1287
|
+
...
|
|
1288
|
+
|
|
1289
|
+
async def aio(self, /, *, client: typing.Optional[modal.client.Client] = None) -> dict[str, str]:
|
|
1290
|
+
"""Get the tags that are currently attached to the App."""
|
|
1291
|
+
...
|
|
1292
|
+
|
|
1293
|
+
get_tags: __get_tags_spec[typing_extensions.Self]
|
|
1294
|
+
|
|
1295
|
+
class ___logs_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
1296
|
+
def __call__(self, /, client: typing.Optional[modal.client.Client] = None) -> typing.Generator[str, None, None]:
|
|
1297
|
+
"""Stream logs from the app.
|
|
1298
|
+
|
|
1299
|
+
This method is considered private and its interface may change - use at your own risk!
|
|
1300
|
+
"""
|
|
1301
|
+
...
|
|
1302
|
+
|
|
499
1303
|
def aio(
|
|
500
1304
|
self, /, client: typing.Optional[modal.client.Client] = None
|
|
501
|
-
) -> collections.abc.AsyncGenerator[str, None]:
|
|
1305
|
+
) -> collections.abc.AsyncGenerator[str, None]:
|
|
1306
|
+
"""Stream logs from the app.
|
|
1307
|
+
|
|
1308
|
+
This method is considered private and its interface may change - use at your own risk!
|
|
1309
|
+
"""
|
|
1310
|
+
...
|
|
502
1311
|
|
|
503
1312
|
_logs: ___logs_spec[typing_extensions.Self]
|
|
504
1313
|
|
|
505
1314
|
@classmethod
|
|
506
|
-
def _get_container_app(cls) -> typing.Optional[App]:
|
|
1315
|
+
def _get_container_app(cls) -> typing.Optional[App]:
|
|
1316
|
+
"""Returns the `App` running inside a container.
|
|
1317
|
+
|
|
1318
|
+
This will return `None` outside of a Modal container.
|
|
1319
|
+
"""
|
|
1320
|
+
...
|
|
1321
|
+
|
|
507
1322
|
@classmethod
|
|
508
|
-
def _reset_container_app(cls):
|
|
1323
|
+
def _reset_container_app(cls):
|
|
1324
|
+
"""Only used for tests."""
|
|
1325
|
+
...
|
|
509
1326
|
|
|
510
1327
|
_default_image: modal.image._Image
|