modal 1.0.5.dev2__py3-none-any.whl → 1.0.5.dev4__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 +5 -4
- modal/_partial_function.py +1 -1
- modal/_runtime/container_io_manager.pyi +222 -40
- modal/_runtime/execution_context.pyi +60 -6
- modal/_tunnel.pyi +380 -12
- modal/app.py +4 -4
- modal/app.pyi +658 -48
- modal/cli/run.py +2 -1
- modal/client.pyi +224 -28
- modal/cloud_bucket_mount.pyi +192 -4
- modal/cls.py +3 -3
- modal/cls.pyi +442 -35
- modal/container_process.pyi +103 -14
- modal/dict.py +1 -1
- modal/dict.pyi +453 -51
- modal/environments.pyi +41 -9
- modal/exception.py +2 -2
- modal/file_io.pyi +236 -45
- modal/functions.pyi +545 -56
- modal/gpu.py +1 -1
- modal/image.py +1 -1
- 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.py +1 -1
- modal/proxy.pyi +28 -3
- modal/queue.py +1 -1
- modal/queue.pyi +447 -30
- modal/runner.pyi +160 -22
- modal/sandbox.py +7 -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 +1 -1
- modal/volume.pyi +649 -59
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/METADATA +1 -1
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/RECORD +50 -50
- modal_version/__init__.py +1 -1
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/WHEEL +0 -0
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/entry_points.txt +0 -0
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/top_level.txt +0 -0
modal/app.pyi
CHANGED
@@ -26,8 +26,14 @@ class _LocalEntrypoint:
|
|
26
26
|
_info: modal._utils.function_utils.FunctionInfo
|
27
27
|
_app: _App
|
28
28
|
|
29
|
-
def __init__(self, info: modal._utils.function_utils.FunctionInfo, app: _App) -> None:
|
30
|
-
|
29
|
+
def __init__(self, info: modal._utils.function_utils.FunctionInfo, app: _App) -> None:
|
30
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
31
|
+
...
|
32
|
+
|
33
|
+
def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> typing.Any:
|
34
|
+
"""Call self as a function."""
|
35
|
+
...
|
36
|
+
|
31
37
|
@property
|
32
38
|
def info(self) -> modal._utils.function_utils.FunctionInfo: ...
|
33
39
|
@property
|
@@ -69,6 +75,37 @@ class _FunctionDecoratorType:
|
|
69
75
|
) -> modal.functions.Function[P, ReturnType, ReturnType]: ...
|
70
76
|
|
71
77
|
class _App:
|
78
|
+
"""A Modal App is a group of functions and classes that are deployed together.
|
79
|
+
|
80
|
+
The app serves at least three purposes:
|
81
|
+
|
82
|
+
* A unit of deployment for functions and classes.
|
83
|
+
* Syncing of identities of (primarily) functions and classes across processes
|
84
|
+
(your local Python interpreter and every Modal container active in your application).
|
85
|
+
* Manage log collection for everything that happens inside your code.
|
86
|
+
|
87
|
+
**Registering functions with an app**
|
88
|
+
|
89
|
+
The most common way to explicitly register an Object with an app is through the
|
90
|
+
`@app.function()` decorator. It both registers the annotated function itself and
|
91
|
+
other passed objects, like schedules and secrets, with the app:
|
92
|
+
|
93
|
+
```python
|
94
|
+
import modal
|
95
|
+
|
96
|
+
app = modal.App()
|
97
|
+
|
98
|
+
@app.function(
|
99
|
+
secrets=[modal.Secret.from_name("some_secret")],
|
100
|
+
schedule=modal.Period(days=1),
|
101
|
+
)
|
102
|
+
def foo():
|
103
|
+
pass
|
104
|
+
```
|
105
|
+
|
106
|
+
In this example, the secret and schedule are registered with the app.
|
107
|
+
"""
|
108
|
+
|
72
109
|
_all_apps: typing.ClassVar[dict[typing.Optional[str], list[_App]]]
|
73
110
|
_container_app: typing.ClassVar[typing.Optional[_App]]
|
74
111
|
_name: typing.Optional[str]
|
@@ -93,15 +130,38 @@ class _App:
|
|
93
130
|
secrets: collections.abc.Sequence[modal.secret._Secret] = [],
|
94
131
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume] = {},
|
95
132
|
include_source: typing.Optional[bool] = None,
|
96
|
-
) -> None:
|
133
|
+
) -> None:
|
134
|
+
"""Construct a new app, optionally with default image, mounts, secrets, or volumes.
|
135
|
+
|
136
|
+
```python notest
|
137
|
+
image = modal.Image.debian_slim().pip_install(...)
|
138
|
+
secret = modal.Secret.from_name("my-secret")
|
139
|
+
volume = modal.Volume.from_name("my-data")
|
140
|
+
app = modal.App(image=image, secrets=[secret], volumes={"/mnt/data": volume})
|
141
|
+
```
|
142
|
+
"""
|
143
|
+
...
|
144
|
+
|
97
145
|
@property
|
98
|
-
def name(self) -> typing.Optional[str]:
|
146
|
+
def name(self) -> typing.Optional[str]:
|
147
|
+
"""The user-provided name of the App."""
|
148
|
+
...
|
149
|
+
|
99
150
|
@property
|
100
|
-
def is_interactive(self) -> bool:
|
151
|
+
def is_interactive(self) -> bool:
|
152
|
+
"""Whether the current app for the app is running in interactive mode."""
|
153
|
+
...
|
154
|
+
|
101
155
|
@property
|
102
|
-
def app_id(self) -> typing.Optional[str]:
|
156
|
+
def app_id(self) -> typing.Optional[str]:
|
157
|
+
"""Return the app_id of a running or stopped app."""
|
158
|
+
...
|
159
|
+
|
103
160
|
@property
|
104
|
-
def description(self) -> typing.Optional[str]:
|
161
|
+
def description(self) -> typing.Optional[str]:
|
162
|
+
"""The App's `name`, if available, or a fallback descriptive identifier."""
|
163
|
+
...
|
164
|
+
|
105
165
|
@staticmethod
|
106
166
|
async def lookup(
|
107
167
|
name: str,
|
@@ -109,7 +169,20 @@ class _App:
|
|
109
169
|
client: typing.Optional[modal.client._Client] = None,
|
110
170
|
environment_name: typing.Optional[str] = None,
|
111
171
|
create_if_missing: bool = False,
|
112
|
-
) -> _App:
|
172
|
+
) -> _App:
|
173
|
+
"""Look up an App with a given name, creating a new App if necessary.
|
174
|
+
|
175
|
+
Note that Apps created through this method will be in a deployed state,
|
176
|
+
but they will not have any associated Functions or Classes. This method
|
177
|
+
is mainly useful for creating an App to associate with a Sandbox:
|
178
|
+
|
179
|
+
```python
|
180
|
+
app = modal.App.lookup("my-app", create_if_missing=True)
|
181
|
+
modal.Sandbox.create("echo", "hi", app=app)
|
182
|
+
```
|
183
|
+
"""
|
184
|
+
...
|
185
|
+
|
113
186
|
def set_description(self, description: str): ...
|
114
187
|
def _validate_blueprint_value(self, key: str, value: typing.Any): ...
|
115
188
|
@property
|
@@ -127,7 +200,47 @@ class _App:
|
|
127
200
|
detach: bool = False,
|
128
201
|
interactive: bool = False,
|
129
202
|
environment_name: typing.Optional[str] = None,
|
130
|
-
) -> typing.AsyncContextManager[_App]:
|
203
|
+
) -> typing.AsyncContextManager[_App]:
|
204
|
+
"""Context manager that runs an ephemeral app on Modal.
|
205
|
+
|
206
|
+
Use this as the main entry point for your Modal application. All calls
|
207
|
+
to Modal Functions should be made within the scope of this context
|
208
|
+
manager, and they will correspond to the current App.
|
209
|
+
|
210
|
+
**Example**
|
211
|
+
|
212
|
+
```python notest
|
213
|
+
with app.run():
|
214
|
+
some_modal_function.remote()
|
215
|
+
```
|
216
|
+
|
217
|
+
To enable output printing (i.e., to see App logs), use `modal.enable_output()`:
|
218
|
+
|
219
|
+
```python notest
|
220
|
+
with modal.enable_output():
|
221
|
+
with app.run():
|
222
|
+
some_modal_function.remote()
|
223
|
+
```
|
224
|
+
|
225
|
+
Note that you should not invoke this in global scope of a file where you have
|
226
|
+
Modal Functions or Classes defined, since that would run the block when the Function
|
227
|
+
or Cls is imported in your containers as well. If you want to run it as your entrypoint,
|
228
|
+
consider protecting it:
|
229
|
+
|
230
|
+
```python
|
231
|
+
if __name__ == "__main__":
|
232
|
+
with app.run():
|
233
|
+
some_modal_function.remote()
|
234
|
+
```
|
235
|
+
|
236
|
+
You can then run your script with:
|
237
|
+
|
238
|
+
```shell
|
239
|
+
python app_module.py
|
240
|
+
```
|
241
|
+
"""
|
242
|
+
...
|
243
|
+
|
131
244
|
async def deploy(
|
132
245
|
self,
|
133
246
|
*,
|
@@ -135,23 +248,127 @@ class _App:
|
|
135
248
|
environment_name: typing.Optional[str] = None,
|
136
249
|
tag: str = "",
|
137
250
|
client: typing.Optional[modal.client._Client] = None,
|
138
|
-
) -> typing_extensions.Self:
|
251
|
+
) -> typing_extensions.Self:
|
252
|
+
"""Deploy the App so that it is available persistently.
|
253
|
+
|
254
|
+
Deployed Apps will be avaible for lookup or web-based invocations until they are stopped.
|
255
|
+
Unlike with `App.run`, this method will return as soon as the deployment completes.
|
256
|
+
|
257
|
+
This method is a programmatic alternative to the `modal deploy` CLI command.
|
258
|
+
|
259
|
+
Examples:
|
260
|
+
|
261
|
+
```python notest
|
262
|
+
app = App("my-app")
|
263
|
+
app.deploy()
|
264
|
+
```
|
265
|
+
|
266
|
+
To enable output printing (i.e., to see build logs), use `modal.enable_output()`:
|
267
|
+
|
268
|
+
```python notest
|
269
|
+
app = App("my-app")
|
270
|
+
with modal.enable_output():
|
271
|
+
app.deploy()
|
272
|
+
```
|
273
|
+
|
274
|
+
Unlike with `App.run`, Function logs will not stream back to the local client after the
|
275
|
+
App is deployed.
|
276
|
+
|
277
|
+
Note that you should not invoke this method in global scope, as that would redeploy
|
278
|
+
the App every time the file is imported. If you want to write a programmatic deployment
|
279
|
+
script, protect this call so that it only runs when the file is executed directly:
|
280
|
+
|
281
|
+
```python notest
|
282
|
+
if __name__ == "__main__":
|
283
|
+
with modal.enable_output():
|
284
|
+
app.deploy()
|
285
|
+
```
|
286
|
+
|
287
|
+
Then you can deploy your app with:
|
288
|
+
|
289
|
+
```shell
|
290
|
+
python app_module.py
|
291
|
+
```
|
292
|
+
"""
|
293
|
+
...
|
294
|
+
|
139
295
|
def _get_default_image(self): ...
|
140
296
|
def _get_watch_mounts(self): ...
|
141
297
|
def _add_function(self, function: modal._functions._Function, is_web_endpoint: bool): ...
|
142
298
|
def _add_class(self, tag: str, cls: modal.cls._Cls): ...
|
143
299
|
def _init_container(self, client: modal.client._Client, running_app: modal.running_app.RunningApp): ...
|
144
300
|
@property
|
145
|
-
def registered_functions(self) -> dict[str, modal._functions._Function]:
|
301
|
+
def registered_functions(self) -> dict[str, modal._functions._Function]:
|
302
|
+
"""All modal.Function objects registered on the app."""
|
303
|
+
...
|
304
|
+
|
146
305
|
@property
|
147
|
-
def registered_classes(self) -> dict[str, modal.cls._Cls]:
|
306
|
+
def registered_classes(self) -> dict[str, modal.cls._Cls]:
|
307
|
+
"""All modal.Cls objects registered on the app."""
|
308
|
+
...
|
309
|
+
|
148
310
|
@property
|
149
|
-
def registered_entrypoints(self) -> dict[str, _LocalEntrypoint]:
|
311
|
+
def registered_entrypoints(self) -> dict[str, _LocalEntrypoint]:
|
312
|
+
"""All local CLI entrypoints registered on the app."""
|
313
|
+
...
|
314
|
+
|
150
315
|
@property
|
151
|
-
def registered_web_endpoints(self) -> list[str]:
|
316
|
+
def registered_web_endpoints(self) -> list[str]:
|
317
|
+
"""Names of web endpoint (ie. webhook) functions registered on the app."""
|
318
|
+
...
|
319
|
+
|
152
320
|
def local_entrypoint(
|
153
321
|
self, _warn_parentheses_missing: typing.Any = None, *, name: typing.Optional[str] = None
|
154
|
-
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], _LocalEntrypoint]:
|
322
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], _LocalEntrypoint]:
|
323
|
+
"""Decorate a function to be used as a CLI entrypoint for a Modal App.
|
324
|
+
|
325
|
+
These functions can be used to define code that runs locally to set up the app,
|
326
|
+
and act as an entrypoint to start Modal functions from. Note that regular
|
327
|
+
Modal functions can also be used as CLI entrypoints, but unlike `local_entrypoint`,
|
328
|
+
those functions are executed remotely directly.
|
329
|
+
|
330
|
+
**Example**
|
331
|
+
|
332
|
+
```python
|
333
|
+
@app.local_entrypoint()
|
334
|
+
def main():
|
335
|
+
some_modal_function.remote()
|
336
|
+
```
|
337
|
+
|
338
|
+
You can call the function using `modal run` directly from the CLI:
|
339
|
+
|
340
|
+
```shell
|
341
|
+
modal run app_module.py
|
342
|
+
```
|
343
|
+
|
344
|
+
Note that an explicit [`app.run()`](https://modal.com/docs/reference/modal.App#run) is not needed, as an
|
345
|
+
[app](https://modal.com/docs/guide/apps) is automatically created for you.
|
346
|
+
|
347
|
+
**Multiple Entrypoints**
|
348
|
+
|
349
|
+
If you have multiple `local_entrypoint` functions, you can qualify the name of your app and function:
|
350
|
+
|
351
|
+
```shell
|
352
|
+
modal run app_module.py::app.some_other_function
|
353
|
+
```
|
354
|
+
|
355
|
+
**Parsing Arguments**
|
356
|
+
|
357
|
+
If your entrypoint function take arguments with primitive types, `modal run` automatically parses them as
|
358
|
+
CLI options.
|
359
|
+
For example, the following function can be called with `modal run app_module.py --foo 1 --bar "hello"`:
|
360
|
+
|
361
|
+
```python
|
362
|
+
@app.local_entrypoint()
|
363
|
+
def main(foo: int, bar: str):
|
364
|
+
some_modal_function.call(foo, bar)
|
365
|
+
```
|
366
|
+
|
367
|
+
Currently, `str`, `int`, `float`, `bool`, and `datetime.datetime` are supported.
|
368
|
+
Use `modal run app_module.py --help` for more information on usage.
|
369
|
+
"""
|
370
|
+
...
|
371
|
+
|
155
372
|
def function(
|
156
373
|
self,
|
157
374
|
_warn_parentheses_missing: typing.Any = None,
|
@@ -199,7 +416,10 @@ class _App:
|
|
199
416
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
200
417
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
201
418
|
allow_cross_region_volumes: typing.Optional[bool] = None,
|
202
|
-
) -> _FunctionDecoratorType:
|
419
|
+
) -> _FunctionDecoratorType:
|
420
|
+
"""Decorator to register a new Modal Function with this App."""
|
421
|
+
...
|
422
|
+
|
203
423
|
@typing_extensions.dataclass_transform(
|
204
424
|
field_specifiers=(modal.cls.parameter,),
|
205
425
|
kw_only_default=True,
|
@@ -247,19 +467,90 @@ class _App:
|
|
247
467
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
248
468
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
249
469
|
allow_cross_region_volumes: typing.Optional[bool] = None,
|
250
|
-
) -> collections.abc.Callable[[typing.Union[CLS_T, modal._partial_function._PartialFunction]], CLS_T]:
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
) ->
|
470
|
+
) -> collections.abc.Callable[[typing.Union[CLS_T, modal._partial_function._PartialFunction]], CLS_T]:
|
471
|
+
"""Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App."""
|
472
|
+
...
|
473
|
+
|
474
|
+
def include(self, /, other_app: _App) -> typing_extensions.Self:
|
475
|
+
"""Include another App's objects in this one.
|
476
|
+
|
477
|
+
Useful for splitting up Modal Apps across different self-contained files.
|
478
|
+
|
479
|
+
```python
|
480
|
+
app_a = modal.App("a")
|
481
|
+
@app.function()
|
482
|
+
def foo():
|
483
|
+
...
|
484
|
+
|
485
|
+
app_b = modal.App("b")
|
486
|
+
@app.function()
|
487
|
+
def bar():
|
488
|
+
...
|
489
|
+
|
490
|
+
app_a.include(app_b)
|
491
|
+
|
492
|
+
@app_a.local_entrypoint()
|
493
|
+
def main():
|
494
|
+
# use function declared on the included app
|
495
|
+
bar.remote()
|
496
|
+
```
|
497
|
+
"""
|
498
|
+
...
|
499
|
+
|
500
|
+
def _logs(self, client: typing.Optional[modal.client._Client] = None) -> collections.abc.AsyncGenerator[str, None]:
|
501
|
+
"""Stream logs from the app.
|
502
|
+
|
503
|
+
This method is considered private and its interface may change - use at your own risk!
|
504
|
+
"""
|
505
|
+
...
|
506
|
+
|
255
507
|
@classmethod
|
256
|
-
def _get_container_app(cls) -> typing.Optional[_App]:
|
508
|
+
def _get_container_app(cls) -> typing.Optional[_App]:
|
509
|
+
"""Returns the `App` running inside a container.
|
510
|
+
|
511
|
+
This will return `None` outside of a Modal container.
|
512
|
+
"""
|
513
|
+
...
|
514
|
+
|
257
515
|
@classmethod
|
258
|
-
def _reset_container_app(cls):
|
516
|
+
def _reset_container_app(cls):
|
517
|
+
"""Only used for tests."""
|
518
|
+
...
|
259
519
|
|
260
520
|
SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
|
261
521
|
|
262
522
|
class App:
|
523
|
+
"""A Modal App is a group of functions and classes that are deployed together.
|
524
|
+
|
525
|
+
The app serves at least three purposes:
|
526
|
+
|
527
|
+
* A unit of deployment for functions and classes.
|
528
|
+
* Syncing of identities of (primarily) functions and classes across processes
|
529
|
+
(your local Python interpreter and every Modal container active in your application).
|
530
|
+
* Manage log collection for everything that happens inside your code.
|
531
|
+
|
532
|
+
**Registering functions with an app**
|
533
|
+
|
534
|
+
The most common way to explicitly register an Object with an app is through the
|
535
|
+
`@app.function()` decorator. It both registers the annotated function itself and
|
536
|
+
other passed objects, like schedules and secrets, with the app:
|
537
|
+
|
538
|
+
```python
|
539
|
+
import modal
|
540
|
+
|
541
|
+
app = modal.App()
|
542
|
+
|
543
|
+
@app.function(
|
544
|
+
secrets=[modal.Secret.from_name("some_secret")],
|
545
|
+
schedule=modal.Period(days=1),
|
546
|
+
)
|
547
|
+
def foo():
|
548
|
+
pass
|
549
|
+
```
|
550
|
+
|
551
|
+
In this example, the secret and schedule are registered with the app.
|
552
|
+
"""
|
553
|
+
|
263
554
|
_all_apps: typing.ClassVar[dict[typing.Optional[str], list[App]]]
|
264
555
|
_container_app: typing.ClassVar[typing.Optional[App]]
|
265
556
|
_name: typing.Optional[str]
|
@@ -284,15 +575,37 @@ class App:
|
|
284
575
|
secrets: collections.abc.Sequence[modal.secret.Secret] = [],
|
285
576
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume.Volume] = {},
|
286
577
|
include_source: typing.Optional[bool] = None,
|
287
|
-
) -> None:
|
578
|
+
) -> None:
|
579
|
+
"""Construct a new app, optionally with default image, mounts, secrets, or volumes.
|
580
|
+
|
581
|
+
```python notest
|
582
|
+
image = modal.Image.debian_slim().pip_install(...)
|
583
|
+
secret = modal.Secret.from_name("my-secret")
|
584
|
+
volume = modal.Volume.from_name("my-data")
|
585
|
+
app = modal.App(image=image, secrets=[secret], volumes={"/mnt/data": volume})
|
586
|
+
```
|
587
|
+
"""
|
588
|
+
...
|
589
|
+
|
288
590
|
@property
|
289
|
-
def name(self) -> typing.Optional[str]:
|
591
|
+
def name(self) -> typing.Optional[str]:
|
592
|
+
"""The user-provided name of the App."""
|
593
|
+
...
|
594
|
+
|
290
595
|
@property
|
291
|
-
def is_interactive(self) -> bool:
|
596
|
+
def is_interactive(self) -> bool:
|
597
|
+
"""Whether the current app for the app is running in interactive mode."""
|
598
|
+
...
|
599
|
+
|
292
600
|
@property
|
293
|
-
def app_id(self) -> typing.Optional[str]:
|
601
|
+
def app_id(self) -> typing.Optional[str]:
|
602
|
+
"""Return the app_id of a running or stopped app."""
|
603
|
+
...
|
604
|
+
|
294
605
|
@property
|
295
|
-
def description(self) -> typing.Optional[str]:
|
606
|
+
def description(self) -> typing.Optional[str]:
|
607
|
+
"""The App's `name`, if available, or a fallback descriptive identifier."""
|
608
|
+
...
|
296
609
|
|
297
610
|
class __lookup_spec(typing_extensions.Protocol):
|
298
611
|
def __call__(
|
@@ -303,7 +616,20 @@ class App:
|
|
303
616
|
client: typing.Optional[modal.client.Client] = None,
|
304
617
|
environment_name: typing.Optional[str] = None,
|
305
618
|
create_if_missing: bool = False,
|
306
|
-
) -> App:
|
619
|
+
) -> App:
|
620
|
+
"""Look up an App with a given name, creating a new App if necessary.
|
621
|
+
|
622
|
+
Note that Apps created through this method will be in a deployed state,
|
623
|
+
but they will not have any associated Functions or Classes. This method
|
624
|
+
is mainly useful for creating an App to associate with a Sandbox:
|
625
|
+
|
626
|
+
```python
|
627
|
+
app = modal.App.lookup("my-app", create_if_missing=True)
|
628
|
+
modal.Sandbox.create("echo", "hi", app=app)
|
629
|
+
```
|
630
|
+
"""
|
631
|
+
...
|
632
|
+
|
307
633
|
async def aio(
|
308
634
|
self,
|
309
635
|
/,
|
@@ -312,7 +638,19 @@ class App:
|
|
312
638
|
client: typing.Optional[modal.client.Client] = None,
|
313
639
|
environment_name: typing.Optional[str] = None,
|
314
640
|
create_if_missing: bool = False,
|
315
|
-
) -> App:
|
641
|
+
) -> App:
|
642
|
+
"""Look up an App with a given name, creating a new App if necessary.
|
643
|
+
|
644
|
+
Note that Apps created through this method will be in a deployed state,
|
645
|
+
but they will not have any associated Functions or Classes. This method
|
646
|
+
is mainly useful for creating an App to associate with a Sandbox:
|
647
|
+
|
648
|
+
```python
|
649
|
+
app = modal.App.lookup("my-app", create_if_missing=True)
|
650
|
+
modal.Sandbox.create("echo", "hi", app=app)
|
651
|
+
```
|
652
|
+
"""
|
653
|
+
...
|
316
654
|
|
317
655
|
lookup: __lookup_spec
|
318
656
|
|
@@ -343,7 +681,47 @@ class App:
|
|
343
681
|
detach: bool = False,
|
344
682
|
interactive: bool = False,
|
345
683
|
environment_name: typing.Optional[str] = None,
|
346
|
-
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[App]:
|
684
|
+
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[App]:
|
685
|
+
"""Context manager that runs an ephemeral app on Modal.
|
686
|
+
|
687
|
+
Use this as the main entry point for your Modal application. All calls
|
688
|
+
to Modal Functions should be made within the scope of this context
|
689
|
+
manager, and they will correspond to the current App.
|
690
|
+
|
691
|
+
**Example**
|
692
|
+
|
693
|
+
```python notest
|
694
|
+
with app.run():
|
695
|
+
some_modal_function.remote()
|
696
|
+
```
|
697
|
+
|
698
|
+
To enable output printing (i.e., to see App logs), use `modal.enable_output()`:
|
699
|
+
|
700
|
+
```python notest
|
701
|
+
with modal.enable_output():
|
702
|
+
with app.run():
|
703
|
+
some_modal_function.remote()
|
704
|
+
```
|
705
|
+
|
706
|
+
Note that you should not invoke this in global scope of a file where you have
|
707
|
+
Modal Functions or Classes defined, since that would run the block when the Function
|
708
|
+
or Cls is imported in your containers as well. If you want to run it as your entrypoint,
|
709
|
+
consider protecting it:
|
710
|
+
|
711
|
+
```python
|
712
|
+
if __name__ == "__main__":
|
713
|
+
with app.run():
|
714
|
+
some_modal_function.remote()
|
715
|
+
```
|
716
|
+
|
717
|
+
You can then run your script with:
|
718
|
+
|
719
|
+
```shell
|
720
|
+
python app_module.py
|
721
|
+
```
|
722
|
+
"""
|
723
|
+
...
|
724
|
+
|
347
725
|
def aio(
|
348
726
|
self,
|
349
727
|
/,
|
@@ -352,7 +730,46 @@ class App:
|
|
352
730
|
detach: bool = False,
|
353
731
|
interactive: bool = False,
|
354
732
|
environment_name: typing.Optional[str] = None,
|
355
|
-
) -> typing.AsyncContextManager[App]:
|
733
|
+
) -> typing.AsyncContextManager[App]:
|
734
|
+
"""Context manager that runs an ephemeral app on Modal.
|
735
|
+
|
736
|
+
Use this as the main entry point for your Modal application. All calls
|
737
|
+
to Modal Functions should be made within the scope of this context
|
738
|
+
manager, and they will correspond to the current App.
|
739
|
+
|
740
|
+
**Example**
|
741
|
+
|
742
|
+
```python notest
|
743
|
+
with app.run():
|
744
|
+
some_modal_function.remote()
|
745
|
+
```
|
746
|
+
|
747
|
+
To enable output printing (i.e., to see App logs), use `modal.enable_output()`:
|
748
|
+
|
749
|
+
```python notest
|
750
|
+
with modal.enable_output():
|
751
|
+
with app.run():
|
752
|
+
some_modal_function.remote()
|
753
|
+
```
|
754
|
+
|
755
|
+
Note that you should not invoke this in global scope of a file where you have
|
756
|
+
Modal Functions or Classes defined, since that would run the block when the Function
|
757
|
+
or Cls is imported in your containers as well. If you want to run it as your entrypoint,
|
758
|
+
consider protecting it:
|
759
|
+
|
760
|
+
```python
|
761
|
+
if __name__ == "__main__":
|
762
|
+
with app.run():
|
763
|
+
some_modal_function.remote()
|
764
|
+
```
|
765
|
+
|
766
|
+
You can then run your script with:
|
767
|
+
|
768
|
+
```shell
|
769
|
+
python app_module.py
|
770
|
+
```
|
771
|
+
"""
|
772
|
+
...
|
356
773
|
|
357
774
|
run: __run_spec[typing_extensions.Self]
|
358
775
|
|
@@ -365,7 +782,50 @@ class App:
|
|
365
782
|
environment_name: typing.Optional[str] = None,
|
366
783
|
tag: str = "",
|
367
784
|
client: typing.Optional[modal.client.Client] = None,
|
368
|
-
) -> SUPERSELF:
|
785
|
+
) -> SUPERSELF:
|
786
|
+
"""Deploy the App so that it is available persistently.
|
787
|
+
|
788
|
+
Deployed Apps will be avaible for lookup or web-based invocations until they are stopped.
|
789
|
+
Unlike with `App.run`, this method will return as soon as the deployment completes.
|
790
|
+
|
791
|
+
This method is a programmatic alternative to the `modal deploy` CLI command.
|
792
|
+
|
793
|
+
Examples:
|
794
|
+
|
795
|
+
```python notest
|
796
|
+
app = App("my-app")
|
797
|
+
app.deploy()
|
798
|
+
```
|
799
|
+
|
800
|
+
To enable output printing (i.e., to see build logs), use `modal.enable_output()`:
|
801
|
+
|
802
|
+
```python notest
|
803
|
+
app = App("my-app")
|
804
|
+
with modal.enable_output():
|
805
|
+
app.deploy()
|
806
|
+
```
|
807
|
+
|
808
|
+
Unlike with `App.run`, Function logs will not stream back to the local client after the
|
809
|
+
App is deployed.
|
810
|
+
|
811
|
+
Note that you should not invoke this method in global scope, as that would redeploy
|
812
|
+
the App every time the file is imported. If you want to write a programmatic deployment
|
813
|
+
script, protect this call so that it only runs when the file is executed directly:
|
814
|
+
|
815
|
+
```python notest
|
816
|
+
if __name__ == "__main__":
|
817
|
+
with modal.enable_output():
|
818
|
+
app.deploy()
|
819
|
+
```
|
820
|
+
|
821
|
+
Then you can deploy your app with:
|
822
|
+
|
823
|
+
```shell
|
824
|
+
python app_module.py
|
825
|
+
```
|
826
|
+
"""
|
827
|
+
...
|
828
|
+
|
369
829
|
async def aio(
|
370
830
|
self,
|
371
831
|
/,
|
@@ -374,7 +834,49 @@ class App:
|
|
374
834
|
environment_name: typing.Optional[str] = None,
|
375
835
|
tag: str = "",
|
376
836
|
client: typing.Optional[modal.client.Client] = None,
|
377
|
-
) -> SUPERSELF:
|
837
|
+
) -> SUPERSELF:
|
838
|
+
"""Deploy the App so that it is available persistently.
|
839
|
+
|
840
|
+
Deployed Apps will be avaible for lookup or web-based invocations until they are stopped.
|
841
|
+
Unlike with `App.run`, this method will return as soon as the deployment completes.
|
842
|
+
|
843
|
+
This method is a programmatic alternative to the `modal deploy` CLI command.
|
844
|
+
|
845
|
+
Examples:
|
846
|
+
|
847
|
+
```python notest
|
848
|
+
app = App("my-app")
|
849
|
+
app.deploy()
|
850
|
+
```
|
851
|
+
|
852
|
+
To enable output printing (i.e., to see build logs), use `modal.enable_output()`:
|
853
|
+
|
854
|
+
```python notest
|
855
|
+
app = App("my-app")
|
856
|
+
with modal.enable_output():
|
857
|
+
app.deploy()
|
858
|
+
```
|
859
|
+
|
860
|
+
Unlike with `App.run`, Function logs will not stream back to the local client after the
|
861
|
+
App is deployed.
|
862
|
+
|
863
|
+
Note that you should not invoke this method in global scope, as that would redeploy
|
864
|
+
the App every time the file is imported. If you want to write a programmatic deployment
|
865
|
+
script, protect this call so that it only runs when the file is executed directly:
|
866
|
+
|
867
|
+
```python notest
|
868
|
+
if __name__ == "__main__":
|
869
|
+
with modal.enable_output():
|
870
|
+
app.deploy()
|
871
|
+
```
|
872
|
+
|
873
|
+
Then you can deploy your app with:
|
874
|
+
|
875
|
+
```shell
|
876
|
+
python app_module.py
|
877
|
+
```
|
878
|
+
"""
|
879
|
+
...
|
378
880
|
|
379
881
|
deploy: __deploy_spec[typing_extensions.Self]
|
380
882
|
|
@@ -384,16 +886,77 @@ class App:
|
|
384
886
|
def _add_class(self, tag: str, cls: modal.cls.Cls): ...
|
385
887
|
def _init_container(self, client: modal.client.Client, running_app: modal.running_app.RunningApp): ...
|
386
888
|
@property
|
387
|
-
def registered_functions(self) -> dict[str, modal.functions.Function]:
|
889
|
+
def registered_functions(self) -> dict[str, modal.functions.Function]:
|
890
|
+
"""All modal.Function objects registered on the app."""
|
891
|
+
...
|
892
|
+
|
388
893
|
@property
|
389
|
-
def registered_classes(self) -> dict[str, modal.cls.Cls]:
|
894
|
+
def registered_classes(self) -> dict[str, modal.cls.Cls]:
|
895
|
+
"""All modal.Cls objects registered on the app."""
|
896
|
+
...
|
897
|
+
|
390
898
|
@property
|
391
|
-
def registered_entrypoints(self) -> dict[str, LocalEntrypoint]:
|
899
|
+
def registered_entrypoints(self) -> dict[str, LocalEntrypoint]:
|
900
|
+
"""All local CLI entrypoints registered on the app."""
|
901
|
+
...
|
902
|
+
|
392
903
|
@property
|
393
|
-
def registered_web_endpoints(self) -> list[str]:
|
904
|
+
def registered_web_endpoints(self) -> list[str]:
|
905
|
+
"""Names of web endpoint (ie. webhook) functions registered on the app."""
|
906
|
+
...
|
907
|
+
|
394
908
|
def local_entrypoint(
|
395
909
|
self, _warn_parentheses_missing: typing.Any = None, *, name: typing.Optional[str] = None
|
396
|
-
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], LocalEntrypoint]:
|
910
|
+
) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], LocalEntrypoint]:
|
911
|
+
"""Decorate a function to be used as a CLI entrypoint for a Modal App.
|
912
|
+
|
913
|
+
These functions can be used to define code that runs locally to set up the app,
|
914
|
+
and act as an entrypoint to start Modal functions from. Note that regular
|
915
|
+
Modal functions can also be used as CLI entrypoints, but unlike `local_entrypoint`,
|
916
|
+
those functions are executed remotely directly.
|
917
|
+
|
918
|
+
**Example**
|
919
|
+
|
920
|
+
```python
|
921
|
+
@app.local_entrypoint()
|
922
|
+
def main():
|
923
|
+
some_modal_function.remote()
|
924
|
+
```
|
925
|
+
|
926
|
+
You can call the function using `modal run` directly from the CLI:
|
927
|
+
|
928
|
+
```shell
|
929
|
+
modal run app_module.py
|
930
|
+
```
|
931
|
+
|
932
|
+
Note that an explicit [`app.run()`](https://modal.com/docs/reference/modal.App#run) is not needed, as an
|
933
|
+
[app](https://modal.com/docs/guide/apps) is automatically created for you.
|
934
|
+
|
935
|
+
**Multiple Entrypoints**
|
936
|
+
|
937
|
+
If you have multiple `local_entrypoint` functions, you can qualify the name of your app and function:
|
938
|
+
|
939
|
+
```shell
|
940
|
+
modal run app_module.py::app.some_other_function
|
941
|
+
```
|
942
|
+
|
943
|
+
**Parsing Arguments**
|
944
|
+
|
945
|
+
If your entrypoint function take arguments with primitive types, `modal run` automatically parses them as
|
946
|
+
CLI options.
|
947
|
+
For example, the following function can be called with `modal run app_module.py --foo 1 --bar "hello"`:
|
948
|
+
|
949
|
+
```python
|
950
|
+
@app.local_entrypoint()
|
951
|
+
def main(foo: int, bar: str):
|
952
|
+
some_modal_function.call(foo, bar)
|
953
|
+
```
|
954
|
+
|
955
|
+
Currently, `str`, `int`, `float`, `bool`, and `datetime.datetime` are supported.
|
956
|
+
Use `modal run app_module.py --help` for more information on usage.
|
957
|
+
"""
|
958
|
+
...
|
959
|
+
|
397
960
|
def function(
|
398
961
|
self,
|
399
962
|
_warn_parentheses_missing: typing.Any = None,
|
@@ -441,7 +1004,10 @@ class App:
|
|
441
1004
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
442
1005
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
443
1006
|
allow_cross_region_volumes: typing.Optional[bool] = None,
|
444
|
-
) -> _FunctionDecoratorType:
|
1007
|
+
) -> _FunctionDecoratorType:
|
1008
|
+
"""Decorator to register a new Modal Function with this App."""
|
1009
|
+
...
|
1010
|
+
|
445
1011
|
@typing_extensions.dataclass_transform(
|
446
1012
|
field_specifiers=(modal.cls.parameter,),
|
447
1013
|
kw_only_default=True,
|
@@ -489,22 +1055,66 @@ class App:
|
|
489
1055
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
490
1056
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
491
1057
|
allow_cross_region_volumes: typing.Optional[bool] = None,
|
492
|
-
) -> collections.abc.Callable[[typing.Union[CLS_T, modal.partial_function.PartialFunction]], CLS_T]:
|
493
|
-
|
1058
|
+
) -> collections.abc.Callable[[typing.Union[CLS_T, modal.partial_function.PartialFunction]], CLS_T]:
|
1059
|
+
"""Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App."""
|
1060
|
+
...
|
1061
|
+
|
1062
|
+
def include(self, /, other_app: App) -> typing_extensions.Self:
|
1063
|
+
"""Include another App's objects in this one.
|
1064
|
+
|
1065
|
+
Useful for splitting up Modal Apps across different self-contained files.
|
1066
|
+
|
1067
|
+
```python
|
1068
|
+
app_a = modal.App("a")
|
1069
|
+
@app.function()
|
1070
|
+
def foo():
|
1071
|
+
...
|
1072
|
+
|
1073
|
+
app_b = modal.App("b")
|
1074
|
+
@app.function()
|
1075
|
+
def bar():
|
1076
|
+
...
|
1077
|
+
|
1078
|
+
app_a.include(app_b)
|
1079
|
+
|
1080
|
+
@app_a.local_entrypoint()
|
1081
|
+
def main():
|
1082
|
+
# use function declared on the included app
|
1083
|
+
bar.remote()
|
1084
|
+
```
|
1085
|
+
"""
|
1086
|
+
...
|
494
1087
|
|
495
1088
|
class ___logs_spec(typing_extensions.Protocol[SUPERSELF]):
|
496
|
-
def __call__(
|
497
|
-
|
498
|
-
|
1089
|
+
def __call__(self, /, client: typing.Optional[modal.client.Client] = None) -> typing.Generator[str, None, None]:
|
1090
|
+
"""Stream logs from the app.
|
1091
|
+
|
1092
|
+
This method is considered private and its interface may change - use at your own risk!
|
1093
|
+
"""
|
1094
|
+
...
|
1095
|
+
|
499
1096
|
def aio(
|
500
1097
|
self, /, client: typing.Optional[modal.client.Client] = None
|
501
|
-
) -> collections.abc.AsyncGenerator[str, None]:
|
1098
|
+
) -> collections.abc.AsyncGenerator[str, None]:
|
1099
|
+
"""Stream logs from the app.
|
1100
|
+
|
1101
|
+
This method is considered private and its interface may change - use at your own risk!
|
1102
|
+
"""
|
1103
|
+
...
|
502
1104
|
|
503
1105
|
_logs: ___logs_spec[typing_extensions.Self]
|
504
1106
|
|
505
1107
|
@classmethod
|
506
|
-
def _get_container_app(cls) -> typing.Optional[App]:
|
1108
|
+
def _get_container_app(cls) -> typing.Optional[App]:
|
1109
|
+
"""Returns the `App` running inside a container.
|
1110
|
+
|
1111
|
+
This will return `None` outside of a Modal container.
|
1112
|
+
"""
|
1113
|
+
...
|
1114
|
+
|
507
1115
|
@classmethod
|
508
|
-
def _reset_container_app(cls):
|
1116
|
+
def _reset_container_app(cls):
|
1117
|
+
"""Only used for tests."""
|
1118
|
+
...
|
509
1119
|
|
510
1120
|
_default_image: modal.image._Image
|